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