1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2 /* SPDX-License-Identifier: Unlicense */
3 #include "tomcrypt_private.h"
4 #include <stdarg.h>
5
6 /**
7 @file ssh_decode_sequence_multi.c
8 SSH data type representation as per RFC4251, Russ Williams
9 */
10
11 #ifdef LTC_SSH
12
13 /**
14 Decode a SSH sequence using a VA list
15 @param in The input buffer
16 @param inlen [in/out] The length of the input buffer and on output the amount of decoded data
17 @remark <...> is of the form <type, data*> (int, <unsigned char*,ulong32*,ulong64*>) except for string&name-list <type, data, size*> (int, void*, unsigned long*)
18 @return CRYPT_OK on success
19 */
ssh_decode_sequence_multi(const unsigned char * in,unsigned long * inlen,...)20 int ssh_decode_sequence_multi(const unsigned char *in, unsigned long *inlen, ...)
21 {
22 int err;
23 va_list args;
24 ssh_data_type type;
25 void *vdata;
26 unsigned char *cdata;
27 char *sdata;
28 ulong32 *u32data;
29 ulong64 *u64data;
30 unsigned long *bufsize;
31 ulong32 size;
32 unsigned long remaining;
33
34 LTC_ARGCHK(in != NULL);
35 LTC_ARGCHK(inlen != NULL);
36
37 remaining = *inlen;
38 /* Decode values from buffer */
39 va_start(args, inlen);
40 while ((type = (ssh_data_type)va_arg(args, int)) != LTC_SSHDATA_EOL) {
41 /* Size of length field */
42 if (type == LTC_SSHDATA_STRING ||
43 type == LTC_SSHDATA_NAMELIST ||
44 type == LTC_SSHDATA_MPINT)
45 {
46 /* Check we'll not read too far */
47 if (remaining < 4) {
48 err = CRYPT_BUFFER_OVERFLOW;
49 goto error;
50 }
51 }
52
53 /* Calculate (or read) length of data */
54 size = 0xFFFFFFFFU;
55 switch (type) {
56 case LTC_SSHDATA_BYTE:
57 case LTC_SSHDATA_BOOLEAN:
58 size = 1;
59 break;
60 case LTC_SSHDATA_UINT32:
61 size = 4;
62 break;
63 case LTC_SSHDATA_UINT64:
64 size = 8;
65 break;
66 case LTC_SSHDATA_STRING:
67 case LTC_SSHDATA_NAMELIST:
68 case LTC_SSHDATA_MPINT:
69 LOAD32H(size, in);
70 in += 4;
71 remaining -= 4;
72 break;
73
74 case LTC_SSHDATA_EOL:
75 default:
76 /* Should never get here */
77 err = CRYPT_INVALID_ARG;
78 goto error;
79 }
80
81 /* Check we'll not read too far */
82 if (remaining < size) {
83 err = CRYPT_BUFFER_OVERFLOW;
84 goto error;
85 } else {
86 remaining -= size;
87 }
88
89 vdata = va_arg(args, void*);
90 if (vdata == NULL) {
91 err = CRYPT_INVALID_ARG;
92 goto error;
93 }
94
95 /* Read data */
96 switch (type) {
97 case LTC_SSHDATA_BYTE:
98 cdata = vdata;
99 *cdata = *in++;
100 break;
101 case LTC_SSHDATA_BOOLEAN:
102 cdata = vdata;
103 /*
104 The value 0 represents FALSE, and the value 1 represents TRUE. All non-zero values MUST be
105 interpreted as TRUE; however, applications MUST NOT store values other than 0 and 1.
106 */
107 *cdata = (*in++)?1:0;
108 break;
109 case LTC_SSHDATA_UINT32:
110 u32data = vdata;
111 LOAD32H(*u32data, in);
112 in += 4;
113 break;
114 case LTC_SSHDATA_UINT64:
115 u64data = vdata;
116 LOAD64H(*u64data, in);
117 in += 8;
118 break;
119 case LTC_SSHDATA_STRING:
120 case LTC_SSHDATA_NAMELIST:
121 sdata = vdata;
122 bufsize = va_arg(args, unsigned long*);
123 if (bufsize == NULL) {
124 err = CRYPT_INVALID_ARG;
125 goto error;
126 }
127 if (size + 1 >= *bufsize) {
128 err = CRYPT_BUFFER_OVERFLOW;
129 goto error;
130 }
131 if (size > 0) {
132 XMEMCPY(sdata, (const char *)in, size);
133 }
134 sdata[size] = '\0';
135 *bufsize = size;
136 in += size;
137 break;
138 case LTC_SSHDATA_MPINT:
139 if (size == 0) {
140 if ((err = mp_set(vdata, 0)) != CRYPT_OK) { goto error; }
141 } else if ((in[0] & 0x80) != 0) {
142 /* Negative number - not supported */
143 err = CRYPT_INVALID_PACKET;
144 goto error;
145 } else {
146 if ((err = mp_read_unsigned_bin(vdata, (unsigned char *)in, size)) != CRYPT_OK) { goto error; }
147 }
148 in += size;
149 break;
150
151 case LTC_SSHDATA_EOL:
152 default:
153 /* Should never get here */
154 err = CRYPT_INVALID_ARG;
155 goto error;
156 }
157 }
158 err = CRYPT_OK;
159
160 *inlen -= remaining;
161
162 error:
163 va_end(args);
164 return err;
165 }
166
167 #endif
168