Browse Source

Merge pull request #500 from libtom/fix-ssh-api

Fix SSH API
Steffen Jaeckel 6 năm trước cách đây
mục cha
commit
fcdb14ede1

+ 67 - 0
doc/crypt.tex

@@ -7233,6 +7233,73 @@ int hkdf(           int  hash_idx,
 
 Parameters are as in \textit{hkdf\_extract()} and \textit{hkdf\_expand()}.
 
+
+\mysection{SSH}
+
+The library provides functions to encode and decode SSH data as specified in RFC4251 Ch. 5.
+
+\subsection{Data types}
+
+The following enum is used to indicate a specific SSH data type
+(besides EOL which is an internal one that indicates the end of a sequence).
+
+\begin{figure}[h]
+\begin{center}
+\begin{small}
+\begin{tabular}{|l|l|l|}
+\hline \textbf{Definition}    & \textbf{arg data Type} & \textbf{SSH Type} \\
+\hline LTC\_SSHDATA\_EOL      & -                      & End of SSH data sequence. \\
+\hline LTC\_SSHDATA\_BYTE     & \texttt{unsigned char} & \texttt{byte} type \\
+\hline LTC\_SSHDATA\_BOOLEAN  & \texttt{unsigned char} & \texttt{boolean} type \\
+\hline LTC\_SSHDATA\_UINT32   & \texttt{ulong32}       & \texttt{uint32} \\
+\hline LTC\_SSHDATA\_UINT64   & \texttt{ulong64}       & \texttt{uint64} \\
+\hline LTC\_SSHDATA\_STRING   & \texttt{char*}         & \texttt{string} (one octet per char) \\
+\hline LTC\_SSHDATA\_MPINT    & \texttt{mp\_int}       & \texttt{mpint} \\
+\hline LTC\_SSHDATA\_NAMELIST & \texttt{char*}         & \texttt{name-list} (which works exactly like a \texttt{string}) \\
+\hline
+\end{tabular}
+\caption{List of SSH Supported Types}
+\index{ssh\_data\_type}
+\end{small}
+\end{center}
+\end{figure}
+
+\subsection{De- and Encoding with Multiple Argument Lists}
+
+\index{ssh\_encode\_sequence\_multi()}
+\index{ssh\_decode\_sequence\_multi()}
+
+
+The API works similar to the ASN.1 SEQUENCE multi en- and decoders.
+
+They either encode or decode a sequence of the supported SSH types where the items are specified after the length parameter.
+
+
+\begin{verbatim}
+int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...);
+\end{verbatim}
+
+Where \texttt{out} points to the destination buffer and  \texttt{outlen} points 
+on function invocation to the length of the destination buffer 
+and after returning it will be filled with the number of octets written to the buffer.
+
+The encoding function \texttt{ssh\_encode\_sequence\_multi()} expects its items to be a pair of \texttt{(type, data)},
+except for the \texttt{string} resp. \texttt{name-list} type, which expects the triple \texttt{(type, data, size)}
+with \texttt{size} being of type \texttt{unsigned long}.
+
+
+\begin{verbatim}
+int ssh_decode_sequence_multi(const unsigned char *in, unsigned long *inlen, ...);
+\end{verbatim}
+
+Where \texttt{in} points to the buffer with the sequence to decode and \texttt{inlen} points 
+on function invocation to the length of the sequence
+and after returning it will be filled with the decoded number of octets.
+
+The decoding function \texttt{ssh\_decode\_sequence\_multi()} expects its items to be a pair of \texttt{(type, data*)},
+except for the \texttt{string} resp. \texttt{name-list} type, which expects the triple \texttt{(type, data, size*)}
+with \texttt{size*} being of type \texttt{unsigned long*}.
+
 \chapter{Miscellaneous}
 \mysection{Base64 Encoding and Decoding}
 The library provides functions to encode and decode a RFC 4648 Base64 coding scheme.

+ 2 - 2
src/headers/tomcrypt_misc.h

@@ -163,6 +163,7 @@ int padding_depad(const unsigned char *data, unsigned long *length, unsigned lon
 
 #ifdef LTC_SSH
 typedef enum ssh_data_type_ {
+   LTC_SSHDATA_EOL,
    LTC_SSHDATA_BYTE,
    LTC_SSHDATA_BOOLEAN,
    LTC_SSHDATA_UINT32,
@@ -170,12 +171,11 @@ typedef enum ssh_data_type_ {
    LTC_SSHDATA_STRING,
    LTC_SSHDATA_MPINT,
    LTC_SSHDATA_NAMELIST,
-   LTC_SSHDATA_EOL
 } ssh_data_type;
 
 /* VA list handy helpers with tuples of <type, data> */
 int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...);
-int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...);
+int ssh_decode_sequence_multi(const unsigned char *in, unsigned long *inlen, ...);
 #endif /* LTC_SSH */
 
 int compare_testvector(const void* is, const unsigned long is_len, const void* should, const unsigned long should_len, const char* what, int which);

+ 35 - 22
src/misc/ssh/ssh_decode_sequence_multi.c

@@ -18,12 +18,12 @@
 
 /**
   Decode a SSH sequence using a VA list
-  @param in     Data to decode
-  @param inlen  Length of buffer to decode
-  @remark <...> is of the form <type, data> (int, void*) except for string <type, data, size>
+  @param in     The input buffer
+  @param inlen  [in/out] The length of the input buffer and on output the amount of decoded data
+  @remark <...> is of the form <type, data*> (int, <unsigned char*,ulong32*,ulong64*>) except for string&name-list <type, data, size*> (int, void*, unsigned long*)
   @return CRYPT_OK on success
 */
-int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
+int ssh_decode_sequence_multi(const unsigned char *in, unsigned long *inlen, ...)
 {
    int           err;
    va_list       args;
@@ -33,11 +33,14 @@ int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
    char          *sdata;
    ulong32       *u32data;
    ulong64       *u64data;
-   unsigned long bufsize;
+   unsigned long *bufsize;
    ulong32       size;
+   unsigned long remaining;
 
    LTC_ARGCHK(in    != NULL);
+   LTC_ARGCHK(inlen != NULL);
 
+   remaining = *inlen;
    /* Decode values from buffer */
    va_start(args, inlen);
    while ((type = (ssh_data_type)va_arg(args, int)) != LTC_SSHDATA_EOL) {
@@ -47,7 +50,7 @@ int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
           type == LTC_SSHDATA_MPINT)
       {
          /* Check we'll not read too far */
-         if (inlen < 4) {
+         if (remaining < 4) {
             err = CRYPT_BUFFER_OVERFLOW;
             goto error;
          }
@@ -71,7 +74,7 @@ int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
          case LTC_SSHDATA_MPINT:
             LOAD32H(size, in);
             in += 4;
-            inlen -= 4;
+            remaining -= 4;
             break;
 
          case LTC_SSHDATA_EOL:
@@ -81,55 +84,63 @@ int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
       }
 
       /* Check we'll not read too far */
-      if (inlen < size) {
+      if (remaining < size) {
          err = CRYPT_BUFFER_OVERFLOW;
          goto error;
       } else {
-         inlen -= size;
+         remaining -= size;
+      }
+
+      vdata = va_arg(args, void*);
+      if (vdata == NULL) {
+         err = CRYPT_INVALID_ARG;
+         goto error;
       }
 
       /* Read data */
       switch (type) {
          case LTC_SSHDATA_BYTE:
-            cdata = va_arg(args, unsigned char*);
+            cdata = vdata;
             *cdata = *in++;
             break;
          case LTC_SSHDATA_BOOLEAN:
-            cdata = va_arg(args, unsigned char*);
+            cdata = vdata;
             /*
                The value 0 represents FALSE, and the value 1 represents TRUE.  All non-zero values MUST be
                interpreted as TRUE; however, applications MUST NOT store values other than 0 and 1.
-            */
+             */
             *cdata = (*in++)?1:0;
             break;
          case LTC_SSHDATA_UINT32:
-            u32data = va_arg(args, ulong32*);
+            u32data = vdata;
             LOAD32H(*u32data, in);
             in += 4;
             break;
          case LTC_SSHDATA_UINT64:
-            u64data = va_arg(args, ulong64*);
+            u64data = vdata;
             LOAD64H(*u64data, in);
             in += 8;
             break;
          case LTC_SSHDATA_STRING:
          case LTC_SSHDATA_NAMELIST:
-            sdata = va_arg(args, char*);
-            bufsize = va_arg(args, unsigned long);
-            if (size >= bufsize) {
+            sdata = vdata;
+            bufsize = va_arg(args, unsigned long*);
+            if (bufsize == NULL) {
+               err = CRYPT_INVALID_ARG;
+               goto error;
+            }
+            if (size + 1 >= *bufsize) {
                err = CRYPT_BUFFER_OVERFLOW;
                goto error;
             }
             if (size > 0) {
-               XSTRNCPY(sdata, (const char *)in, size);
-               sdata[size] = '\0'; /* strncpy doesn't NUL-terminate */
-            } else {
-               *sdata = '\0';
+               XMEMCPY(sdata, (const char *)in, size);
             }
+            sdata[size] = '\0';
+            *bufsize = size;
             in += size;
             break;
          case LTC_SSHDATA_MPINT:
-            vdata = va_arg(args, void*);
             if (size == 0) {
                if ((err = mp_set(vdata, 0)) != CRYPT_OK)                                                { goto error; }
             } else if ((in[0] & 0x80) != 0) {
@@ -150,6 +161,8 @@ int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
    }
    err = CRYPT_OK;
 
+   *inlen -= remaining;
+
 error:
    va_end(args);
    return err;

+ 7 - 7
src/misc/ssh/ssh_encode_sequence_multi.c

@@ -20,7 +20,7 @@
   Encode a SSH sequence using a VA list
   @param out    [out] Destination for data
   @param outlen [in/out] Length of buffer and resulting length of output
-  @remark <...> is of the form <type, data> (int, void*)
+  @remark <...> is of the form <type, data> (int, <int,ulong32,ulong64>) except for string&name-list <type, data, size> (int, void*, unsigned long)
   @return CRYPT_OK on success
 */
 int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
@@ -29,8 +29,8 @@ int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
    va_list       args;
    ulong32       size;
    ssh_data_type type;
-   void         *vdata;
-   const char   *sdata;
+   void          *vdata;
+   const char    *sdata;
    int           idata;
    ulong32       u32data;
    ulong64       u64data;
@@ -58,9 +58,9 @@ int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
             break;
          case LTC_SSHDATA_STRING:
          case LTC_SSHDATA_NAMELIST:
-            sdata = va_arg(args, char*);
+            LTC_UNUSED_PARAM( va_arg(args, char*) );
+            size += va_arg(args, unsigned long);
             size += 4;
-            size += strlen(sdata);
             break;
          case LTC_SSHDATA_MPINT:
             vdata = va_arg(args, void*);
@@ -102,7 +102,7 @@ int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
             /*
                The value 0 represents FALSE, and the value 1 represents TRUE.  All non-zero values MUST be
                interpreted as TRUE; however, applications MUST NOT store values other than 0 and 1.
-            */
+             */
             *out++ = (idata)?1:0;
             break;
          case LTC_SSHDATA_UINT32:
@@ -118,7 +118,7 @@ int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
          case LTC_SSHDATA_STRING:
          case LTC_SSHDATA_NAMELIST:
             sdata = va_arg(args, char*);
-            size = strlen(sdata);
+            size = va_arg(args, unsigned long);
             STORE32H(size, out);
             out += 4;
             XMEMCPY(out, sdata, size);

+ 6 - 5
src/pk/ecc/ecc_recover_key.c

@@ -114,19 +114,20 @@ int ecc_recover_key(const unsigned char *sig,  unsigned long siglen,
 #ifdef LTC_SSH
    else if (sigformat == LTC_ECCSIG_RFC5656) {
       char name[64], name2[64];
-      unsigned long namelen = sizeof(name2);
+      unsigned long namelen = sizeof(name);
+      unsigned long name2len = sizeof(name2);
 
       /* Decode as SSH data sequence, per RFC4251 */
-      if ((err = ssh_decode_sequence_multi(sig, siglen,
-                                           LTC_SSHDATA_STRING, name, 64,
+      if ((err = ssh_decode_sequence_multi(sig, &siglen,
+                                           LTC_SSHDATA_STRING, name, &namelen,
                                            LTC_SSHDATA_MPINT,  r,
                                            LTC_SSHDATA_MPINT,  s,
                                            LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK)                      { goto error; }
 
 
       /* Check curve matches identifier string */
-      if ((err = ecc_ssh_ecdsa_encode_name(name2, &namelen, key)) != CRYPT_OK)                                { goto error; }
-      if (XSTRCMP(name,name2) != 0) {
+      if ((err = ecc_ssh_ecdsa_encode_name(name2, &name2len, key)) != CRYPT_OK)                                { goto error; }
+      if ((namelen != name2len) || (XSTRCMP(name, name2) != 0)) {
          err = CRYPT_INVALID_ARG;
          goto error;
       }

+ 1 - 1
src/pk/ecc/ecc_sign_hash.c

@@ -164,7 +164,7 @@ int ecc_sign_hash_ex(const unsigned char *in,  unsigned long inlen,
 
       /* Store as SSH data sequence, per RFC4251 */
       err = ssh_encode_sequence_multi(out, outlen,
-                                      LTC_SSHDATA_STRING, name,
+                                      LTC_SSHDATA_STRING, name, namelen,
                                       LTC_SSHDATA_MPINT,  r,
                                       LTC_SSHDATA_MPINT,  s,
                                       LTC_SSHDATA_EOL,    NULL);

+ 6 - 4
src/pk/ecc/ecc_ssh_ecdsa_encode_name.c

@@ -25,8 +25,7 @@ int ecc_ssh_ecdsa_encode_name(char *buffer, unsigned long *buflen, const ecc_key
 {
    char oidstr[64];
    unsigned long oidlen = sizeof(oidstr);
-   unsigned long size = 0;
-   int err;
+   int err, size = 0;
 
    LTC_ARGCHK(buffer != NULL);
    LTC_ARGCHK(buflen != NULL);
@@ -52,8 +51,11 @@ int ecc_ssh_ecdsa_encode_name(char *buffer, unsigned long *buflen, const ecc_key
       size = snprintf(buffer, *buflen, "ecdsa-sha2-%s", oidstr);
    }
 
-   /* snprintf returns size that would have been written, but limits to buflen-1 chars plus terminator */
-   if (size >= *buflen) {
+   /* snprintf returns a negative value on error
+    * or the size that would have been written, but limits to buflen-1 chars plus terminator */
+   if (size < 0) {
+      err = CRYPT_ERROR;
+   } else if ((unsigned)size >= *buflen) {
       err = CRYPT_BUFFER_OVERFLOW;
    } else {
       err = CRYPT_OK;

+ 6 - 5
src/pk/ecc/ecc_verify_hash.c

@@ -100,19 +100,20 @@ int ecc_verify_hash_ex(const unsigned char *sig,  unsigned long siglen,
 #ifdef LTC_SSH
    else if (sigformat == LTC_ECCSIG_RFC5656) {
       char name[64], name2[64];
-      unsigned long namelen = sizeof(name2);
+      unsigned long namelen = sizeof(name);
+      unsigned long name2len = sizeof(name2);
 
       /* Decode as SSH data sequence, per RFC4251 */
-      if ((err = ssh_decode_sequence_multi(sig, siglen,
-                                           LTC_SSHDATA_STRING, name, 64,
+      if ((err = ssh_decode_sequence_multi(sig, &siglen,
+                                           LTC_SSHDATA_STRING, name, &namelen,
                                            LTC_SSHDATA_MPINT,  r,
                                            LTC_SSHDATA_MPINT,  s,
                                            LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK)                      { goto error; }
 
 
       /* Check curve matches identifier string */
-      if ((err = ecc_ssh_ecdsa_encode_name(name2, &namelen, key)) != CRYPT_OK)                                { goto error; }
-      if (XSTRCMP(name,name2) != 0) {
+      if ((err = ecc_ssh_ecdsa_encode_name(name2, &name2len, key)) != CRYPT_OK)                                { goto error; }
+      if ((namelen != name2len) || (XSTRCMP(name, name2) != 0)) {
          err = CRYPT_INVALID_ARG;
          goto error;
       }

+ 2 - 0
tests/common.h

@@ -17,10 +17,12 @@ extern prng_state yarrow_prng;
 #define DO(x) do { fprintf(stderr, "%s:\n", #x); run_cmd((x), __LINE__, __FILE__, #x, NULL); } while (0)
 #define DOX(x, str) do { fprintf(stderr, "%s - %s:\n", #x, (str)); run_cmd((x), __LINE__, __FILE__, #x, (str)); } while (0)
 #define SHOULD_FAIL(x) do { fprintf(stderr, "%s:\n", #x); run_cmd((x) != CRYPT_OK ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR, __LINE__, __FILE__, #x, NULL); } while (0)
+#define ENSURE(x) do { fprintf(stderr, "%s:\n", #x); run_cmd(((x)) ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR, __LINE__, __FILE__, #x, NULL); } while (0)
 #else
 #define DO(x) do { run_cmd((x), __LINE__, __FILE__, #x, NULL); } while (0)
 #define DOX(x, str) do { run_cmd((x), __LINE__, __FILE__, #x, (str)); } while (0)
 #define SHOULD_FAIL(x) do { run_cmd((x) != CRYPT_OK ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR, __LINE__, __FILE__, #x, NULL); } while (0)
+#define ENSURE(x) do { run_cmd(((x)) ? CRYPT_OK : CRYPT_FAIL_TESTVECTOR, __LINE__, __FILE__, #x, NULL); } while (0)
 #endif
 
 #define COMPARE_TESTVECTOR(i, il, s, sl, wa, wi) do { DO(do_compare_testvector((i), (il), (s), (sl), (wa), (wi))); } while(0)

+ 87 - 38
tests/ssh_test.c

@@ -61,16 +61,16 @@ static int _ssh_encoding_test(void)
 {
    unsigned char buffer[BUFSIZE];
    unsigned long buflen;
+   unsigned long len;
    void *v, *zero;
    int err;
 
    /* Buffer too short */
    buflen = 3;
    zeromem(buffer, BUFSIZE);
-   err = ssh_encode_sequence_multi(buffer, &buflen,
-                                   LTC_SSHDATA_UINT32, 0x29b7f4aa,
-                                   LTC_SSHDATA_EOL,    NULL);
-   if (err != CRYPT_BUFFER_OVERFLOW) return CRYPT_FAIL_TESTVECTOR;
+   SHOULD_FAIL(ssh_encode_sequence_multi(buffer, &buflen,
+                                         LTC_SSHDATA_UINT32, 0x29b7f4aa,
+                                         LTC_SSHDATA_EOL,    NULL));
 
 
    /* byte */
@@ -125,8 +125,9 @@ static int _ssh_encoding_test(void)
    /* string */
    buflen = BUFSIZE;
    zeromem(buffer, BUFSIZE);
+   len = strlen("testing");
    DO(ssh_encode_sequence_multi(buffer, &buflen,
-                                LTC_SSHDATA_STRING, "testing",
+                                LTC_SSHDATA_STRING, "testing", len,
                                 LTC_SSHDATA_EOL,    NULL));
    COMPARE_TESTVECTOR(buffer, buflen, string, sizeof(string), "enc-string", 1);
 
@@ -166,22 +167,25 @@ static int _ssh_encoding_test(void)
    /* name-list */
    buflen = BUFSIZE;
    zeromem(buffer, BUFSIZE);
+   len = strlen("");
    DO(ssh_encode_sequence_multi(buffer, &buflen,
-                                LTC_SSHDATA_NAMELIST, "",
+                                LTC_SSHDATA_NAMELIST, "", len,
                                 LTC_SSHDATA_EOL,      NULL));
    COMPARE_TESTVECTOR(buffer, buflen, nlist1, sizeof(nlist1), "enc-nlist", 1);
 
    buflen = BUFSIZE;
    zeromem(buffer, BUFSIZE);
+   len = strlen("zlib");
    DO(ssh_encode_sequence_multi(buffer, &buflen,
-                                LTC_SSHDATA_NAMELIST, "zlib",
+                                LTC_SSHDATA_NAMELIST, "zlib", len,
                                 LTC_SSHDATA_EOL,      NULL));
    COMPARE_TESTVECTOR(buffer, buflen, nlist2, sizeof(nlist2), "enc-nlist", 2);
 
    buflen = BUFSIZE;
    zeromem(buffer, BUFSIZE);
+   len = strlen("zlib,none");
    DO(ssh_encode_sequence_multi(buffer, &buflen,
-                                LTC_SSHDATA_NAMELIST, "zlib,none",
+                                LTC_SSHDATA_NAMELIST, "zlib,none", len,
                                 LTC_SSHDATA_EOL,      NULL));
    COMPARE_TESTVECTOR(buffer, buflen, nlist3, sizeof(nlist3), "enc-nlist", 3);
 
@@ -196,93 +200,138 @@ static int _ssh_decoding_test(void)
 {
    char strbuf[BUFSIZE];
    void *u, *v;
+   unsigned long size;
    ulong32 tmp32;
    ulong64 tmp64;
    unsigned char tmp8;
+   unsigned long len;
    int err;
 
+   /* Buffer longer */
+   len = sizeof(strbuf);
+   strbuf[0] = 0;
+   DO(ssh_decode_sequence_multi((unsigned char*)strbuf, &len,
+                                LTC_SSHDATA_BYTE, &tmp8,
+                                LTC_SSHDATA_EOL,  NULL));
+   ENSURE(tmp8 == 0x00);
+   ENSURE(len == 1);
+
+
    /* byte */
-   DO(ssh_decode_sequence_multi(byte1, sizeof(byte1),
+   len = sizeof(byte1);
+   DO(ssh_decode_sequence_multi(byte1, &len,
                                 LTC_SSHDATA_BYTE, &tmp8,
                                 LTC_SSHDATA_EOL,  NULL));
-   if (tmp8 != 0x01) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(tmp8 == 0x01);
+   ENSURE(len == 1);
 
-   DO(ssh_decode_sequence_multi(byte2, sizeof(byte2),
+   len = sizeof(byte2);
+   DO(ssh_decode_sequence_multi(byte2, &len,
                                 LTC_SSHDATA_BYTE, &tmp8,
                                 LTC_SSHDATA_EOL,  NULL));
-   if (tmp8 != 0x71) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(tmp8 == 0x71);
+   ENSURE(len == 1);
 
    /* boolean */
-   DO(ssh_decode_sequence_multi(byte1, sizeof(byte1),
+   len = sizeof(byte1);
+   DO(ssh_decode_sequence_multi(byte1, &len,
                                 LTC_SSHDATA_BOOLEAN, &tmp8,
                                 LTC_SSHDATA_EOL,     NULL));
-   if (tmp8 != 0x01) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(tmp8 == 0x01);
+   ENSURE(len == 1);
 
-   DO(ssh_decode_sequence_multi(byte2, sizeof(byte2),
+   len = sizeof(byte2);
+   DO(ssh_decode_sequence_multi(byte2, &len,
                                 LTC_SSHDATA_BOOLEAN, &tmp8,
                                 LTC_SSHDATA_EOL,     NULL));
-   if (tmp8 != 0x01) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(tmp8 == 0x01);
+   ENSURE(len == 1);
 
    /* uint32 */
-   DO(ssh_decode_sequence_multi(uint32, sizeof(uint32),
+   len = sizeof(uint32);
+   DO(ssh_decode_sequence_multi(uint32, &len,
                                 LTC_SSHDATA_UINT32, &tmp32,
                                 LTC_SSHDATA_EOL,    NULL));
-   if (tmp32 != 0x29b7f4aa) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(tmp32 == 0x29b7f4aa);
+   ENSURE(len == 4);
 
    /* uint64 */
-   DO(ssh_decode_sequence_multi(uint64, sizeof(uint64),
+   len = sizeof(uint64);
+   DO(ssh_decode_sequence_multi(uint64, &len,
                                 LTC_SSHDATA_UINT64, &tmp64,
                                 LTC_SSHDATA_EOL,    NULL));
    if (tmp64 != CONST64(0x09a378f9b2e332a7)) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(len == 8);
 
    /* string */
    zeromem(strbuf, BUFSIZE);
-   DO(ssh_decode_sequence_multi(string, sizeof(string),
-                                LTC_SSHDATA_STRING, strbuf, BUFSIZE,
+   size = BUFSIZE;
+   len = sizeof(string);
+   DO(ssh_decode_sequence_multi(string, &len,
+                                LTC_SSHDATA_STRING, strbuf, &size,
                                 LTC_SSHDATA_EOL,    NULL));
-   if (XSTRCMP(strbuf, "testing") != 0) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(strlen("testing") == size);
+   ENSURE(XSTRCMP(strbuf, "testing") == 0);
+   ENSURE(strlen("testing") + 4 == len);
 
    /* mpint */
    if ((err = mp_init_multi(&u, &v, NULL)) != CRYPT_OK) {
       return err;
    }
 
-   DO(ssh_decode_sequence_multi(mpint1, sizeof(mpint1),
+   len = sizeof(mpint1);
+   DO(ssh_decode_sequence_multi(mpint1, &len,
                                 LTC_SSHDATA_MPINT, v,
                                 LTC_SSHDATA_EOL,   NULL));
-   if (mp_cmp_d(v, 0) != LTC_MP_EQ) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(mp_cmp_d(v, 0) == LTC_MP_EQ);
+   ENSURE(sizeof(mpint1) == len);
 
-   DO(mp_read_radix(u, "9a378f9b2e332a7", 16));
-   DO(ssh_decode_sequence_multi(mpint2, sizeof(mpint2),
+   len = sizeof(mpint2);
+   DO(ssh_decode_sequence_multi(mpint2, &len,
                                 LTC_SSHDATA_MPINT, v,
                                 LTC_SSHDATA_EOL,   NULL));
-   if (mp_cmp(u, v) != LTC_MP_EQ) return CRYPT_FAIL_TESTVECTOR;
+   DO(mp_read_radix(u, "9a378f9b2e332a7", 16));
+   ENSURE(mp_cmp(u, v) == LTC_MP_EQ);
+   ENSURE(sizeof(mpint2) == len);
 
-   DO(ssh_decode_sequence_multi(mpint3, sizeof(mpint3),
+   len = sizeof(mpint3);
+   DO(ssh_decode_sequence_multi(mpint3, &len,
                                 LTC_SSHDATA_MPINT, v,
                                 LTC_SSHDATA_EOL,   NULL));
-   if (mp_cmp_d(v, 0x80) != LTC_MP_EQ) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(mp_cmp_d(v, 0x80) == LTC_MP_EQ);
+   ENSURE(sizeof(mpint3) == len);
 
    mp_clear_multi(v, u, NULL);
 
    /* name-list */
    zeromem(strbuf, BUFSIZE);
-   DO(ssh_decode_sequence_multi(nlist1, sizeof(nlist1),
-                                LTC_SSHDATA_NAMELIST, strbuf, BUFSIZE,
+   size = BUFSIZE;
+   len = sizeof(nlist1);
+   DO(ssh_decode_sequence_multi(nlist1, &len,
+                                LTC_SSHDATA_NAMELIST, strbuf, &size,
                                 LTC_SSHDATA_EOL,      NULL));
-   if (XSTRCMP(strbuf, "") != 0) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(strlen("") == size);
+   ENSURE(XSTRCMP(strbuf, "") == 0);
 
    zeromem(strbuf, BUFSIZE);
-   DO(ssh_decode_sequence_multi(nlist2, sizeof(nlist2),
-                                LTC_SSHDATA_NAMELIST, strbuf, BUFSIZE,
+   size = BUFSIZE;
+   len = sizeof(nlist2);
+   DO(ssh_decode_sequence_multi(nlist2, &len,
+                                LTC_SSHDATA_NAMELIST, strbuf, &size,
                                 LTC_SSHDATA_EOL,      NULL));
-   if (XSTRCMP(strbuf, "zlib") != 0) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(strlen("zlib") == size);
+   ENSURE(XSTRCMP(strbuf, "zlib") == 0);
+   ENSURE(strlen("zlib") + 4 == len);
 
    zeromem(strbuf, BUFSIZE);
-   DO(ssh_decode_sequence_multi(nlist3, sizeof(nlist3),
-                                LTC_SSHDATA_NAMELIST, strbuf, BUFSIZE,
+   size = BUFSIZE;
+   len = sizeof(nlist3);
+   DO(ssh_decode_sequence_multi(nlist3, &len,
+                                LTC_SSHDATA_NAMELIST, strbuf, &size,
                                 LTC_SSHDATA_EOL,      NULL));
-   if (XSTRCMP(strbuf, "zlib,none") != 0) return CRYPT_FAIL_TESTVECTOR;
+   ENSURE(strlen("zlib,none") == size);
+   ENSURE(XSTRCMP(strbuf, "zlib,none") == 0);
+   ENSURE(strlen("zlib,none") + 4 == len);
 
 
    return CRYPT_OK;