فهرست منبع

Initial commit of SSH+ECDSA signature format

Wrap signature format in #ifdef LTC_SSH
Update docs
Code review fixes
Replace strcmp/memcmp with XSTRCMP/XMEMCMP for check-source
Fix for check-defines
XSTRCMP/XMEMCMP != 0
GCC7.3 wants only literal strings for sprintf format
Code review changes
Rework SSH decoding and tests
Fix encoding and tests
COMPARE_TESTVECTOR macro
Single return point in ssh_decode_sequence_multi
Actually use XSTRNCPY rather than just defining it
More code review fixes
Code review tweaks
Ensure it's not possible to read past buffer end
Keep track of size remaining, not end pointer
Russ Williams 7 سال پیش
والد
کامیت
b4b50cc0c6

+ 2 - 1
doc/crypt.tex

@@ -5631,6 +5631,7 @@ The following signature formats are suported:
 \hline LTC\_ECCSIG\_ANSIX962 & ASN.1 encoded, ANSI X9.62 \\
 \hline LTC\_ECCSIG\_RFC7518 & raw R, S values as defined in RFC7518 \\
 \hline LTC\_ECCSIG\_ETH27 & raw R, S, V values (V has 27 added) \\
+\hline LTC\_ECCSIG\_RFC5656 & SSH+ECDSA format as defined in RFC5656 \\
 \hline
 \end{tabular}
 \end{center}
@@ -7792,7 +7793,7 @@ Currently LibTomCrypt will detect x86-32, x86-64, MIPS R5900, SPARC and SPARC64
 There are also options you can specify from the \textit{tomcrypt\_custom.h} header file.
 
 \subsection{X memory routines}
-\index{XMALLOC}\index{XREALLOC}\index{XCALLOC}\index{XFREE}\index{XMEMSET}\index{XMEMCPY}\index{XMEMMOVE}\index{XMEMCMP}\index{XSTRCMP}
+\index{XMALLOC}\index{XREALLOC}\index{XCALLOC}\index{XFREE}\index{XMEMSET}\index{XMEMCPY}\index{XMEMMOVE}\index{XMEMCMP}\index{XSTRCMP}\index{XSTRNCPY}
 At the top of tomcrypt\_custom.h are a series of macros denoted as XMALLOC, XCALLOC, XREALLOC, XFREE, and so on.  They resolve to
 the name of the respective functions from the standard C library by default.  This lets you substitute in your own memory routines.
 If you substitute in your own functions they must behave like the standard C library functions in terms of what they expect as input and

+ 2 - 0
helper.pl

@@ -53,6 +53,8 @@ sub check_source {
       push @{$troubles->{unwanted_memmove}}, $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bmemmove\s*\(/;
       push @{$troubles->{unwanted_memcmp}},  $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bmemcmp\s*\(/;
       push @{$troubles->{unwanted_strcmp}},  $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bstrcmp\s*\(/;
+      push @{$troubles->{unwanted_strcpy}},  $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bstrcpy\s*\(/;
+      push @{$troubles->{unwanted_strncpy}}, $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bstrncpy\s*\(/;
       push @{$troubles->{unwanted_clock}},   $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bclock\s*\(/;
       push @{$troubles->{unwanted_qsort}},   $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bqsort\s*\(/;
       push @{$troubles->{sizeof_no_brackets}}, $lineno if $file =~ /^src\/.*\.c$/ && $l =~ /\bsizeof\s*[^\(]/;

+ 7 - 2
src/headers/tomcrypt_custom.h

@@ -43,7 +43,10 @@
 #define XMEM_NEQ  mem_neq
 #endif
 #ifndef XSTRCMP
-#define XSTRCMP strcmp
+#define XSTRCMP  strcmp
+#endif
+#ifndef XSTRNCPY
+#define XSTRNCPY strncpy
 #endif
 
 #ifndef XCLOCK
@@ -56,7 +59,7 @@
 
 #if ( defined(malloc) || defined(realloc) || defined(calloc) || defined(free) || \
       defined(memset) || defined(memcpy) || defined(memcmp) || defined(strcmp) || \
-      defined(clock) || defined(qsort) ) && !defined(LTC_NO_PROTOTYPES)
+      defined(strncpy) || defined(clock) || defined(qsort) ) && !defined(LTC_NO_PROTOTYPES)
 #define LTC_NO_PROTOTYPES
 #endif
 
@@ -494,6 +497,8 @@
 
 #define LTC_CRC32
 
+#define LTC_SSH
+
 #define LTC_PADDING
 
 #define LTC_PBES

+ 17 - 0
src/headers/tomcrypt_misc.h

@@ -154,6 +154,23 @@ int padding_pad(unsigned char *data, unsigned long length, unsigned long* padded
 int padding_depad(const unsigned char *data, unsigned long *length, unsigned long mode);
 #endif  /* LTC_PADDING */
 
+#ifdef LTC_SSH
+typedef enum ssh_data_type_ {
+   LTC_SSHDATA_BYTE,
+   LTC_SSHDATA_BOOLEAN,
+   LTC_SSHDATA_UINT32,
+   LTC_SSHDATA_UINT64,
+   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, ...);
+#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);
 
 /* ref:         $Format:%D$ */

+ 3 - 1
src/headers/tomcrypt_pk.h

@@ -251,7 +251,9 @@ typedef enum ecc_signature_type_ {
    /* raw R, S values */
    LTC_ECCSIG_RFC7518    = 0x1,
    /* raw R, S, V (+27) values */
-   LTC_ECCSIG_ETH27      = 0x2
+   LTC_ECCSIG_ETH27      = 0x2,
+   /* SSH + ECDSA signature format defined by RFC5656 */
+   LTC_ECCSIG_RFC5656    = 0x3,
 } ecc_signature_type;
 
 /** the ECC params provided */

+ 4 - 0
src/headers/tomcrypt_private.h

@@ -226,6 +226,10 @@ int ecc_copy_curve(const ecc_key *srckey, ecc_key *key);
 int ecc_set_curve_by_size(int size, ecc_key *key);
 int ecc_import_subject_public_key_info(const unsigned char *in, unsigned long inlen, ecc_key *key);
 
+#ifdef LTC_SSH
+int ecc_ssh_ecdsa_encode_name(char *buffer, unsigned long *buflen, const ecc_key *key);
+#endif
+
 /* low level functions */
 ecc_point *ltc_ecc_new_point(void);
 void       ltc_ecc_del_point(ecc_point *p);

+ 3 - 0
src/misc/crypt/crypt.c

@@ -452,6 +452,9 @@ const char *crypt_build_settings =
     " PBES1 "
     " PBES2 "
 #endif
+#if defined(LTC_SSH)
+    " SSH "
+#endif
 #if defined(LTC_DEVRANDOM)
     " LTC_DEVRANDOM "
 #endif

+ 156 - 0
src/misc/ssh/ssh_decode_sequence_multi.c

@@ -0,0 +1,156 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+#include "tomcrypt_private.h"
+#include <stdarg.h>
+
+/**
+   @file ssh_decode_sequence_multi.c
+   SSH data type representation as per RFC4251, Russ Williams
+*/
+
+#ifdef LTC_SSH
+
+/**
+  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>
+  @return CRYPT_OK on success
+*/
+int ssh_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...)
+{
+   int           err;
+   va_list       args;
+   ssh_data_type type;
+   void          *vdata;
+   unsigned char *cdata;
+   char          *sdata;
+   ulong32       *u32data;
+   ulong64       *u64data;
+   unsigned long size, bufsize;
+
+   LTC_ARGCHK(in    != NULL);
+
+   /* Decode values from buffer */
+   va_start(args, inlen);
+   while ((type = (ssh_data_type)va_arg(args, int)) != LTC_SSHDATA_EOL) {
+      /* Size of length field */
+      if (type == LTC_SSHDATA_STRING ||
+          type == LTC_SSHDATA_NAMELIST ||
+          type == LTC_SSHDATA_MPINT)
+      {
+         /* Check we'll not read too far */
+         if (inlen < 4) {
+            err = CRYPT_BUFFER_OVERFLOW;
+            goto error;
+         }
+      }
+
+      /* Calculate (or read) length of data */
+      switch (type) {
+         case LTC_SSHDATA_BYTE:
+         case LTC_SSHDATA_BOOLEAN:
+            size = 1;
+            break;
+         case LTC_SSHDATA_UINT32:
+            size = 4;
+            break;
+         case LTC_SSHDATA_UINT64:
+            size = 8;
+            break;
+         case LTC_SSHDATA_STRING:
+         case LTC_SSHDATA_NAMELIST:
+         case LTC_SSHDATA_MPINT:
+            LOAD32H(size, in);
+            in += 4;
+            inlen -= 4;
+            break;
+
+         case LTC_SSHDATA_EOL:
+            /* Should never get here */
+            err = CRYPT_INVALID_ARG;
+            goto error;
+      }
+
+      /* Check we'll not read too far */
+      if (inlen < size) {
+         err = CRYPT_BUFFER_OVERFLOW;
+         goto error;
+      } else {
+         inlen -= size;
+      }
+
+      /* Read data */
+      switch (type) {
+         case LTC_SSHDATA_BYTE:
+            cdata = va_arg(args, unsigned char*);
+            *cdata = *in++;
+            break;
+         case LTC_SSHDATA_BOOLEAN:
+            cdata = va_arg(args, unsigned char*);
+            /*
+               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*);
+            LOAD32H(*u32data, in);
+            in += 4;
+            break;
+         case LTC_SSHDATA_UINT64:
+            u64data = va_arg(args, ulong64*);
+            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 > 0) {
+               if (size >= bufsize) {
+                  err = CRYPT_BUFFER_OVERFLOW;
+                  goto error;
+               }
+               XSTRNCPY(sdata, (const char *)in, size);
+               sdata[size] = '\0'; /* strncpy doesn't NUL-terminate */
+            } else {
+               *sdata = '\0';
+            }
+            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 ((err = mp_read_unsigned_bin(vdata, (unsigned char *)in, size)) != CRYPT_OK)          { goto error; }
+            }
+            in += size;
+            break;
+
+         case LTC_SSHDATA_EOL:
+            /* Should never get here */
+            err = CRYPT_INVALID_ARG;
+            goto error;
+      }
+   }
+   err = CRYPT_OK;
+
+error:
+   va_end(args);
+   return err;
+}
+
+#endif
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 168 - 0
src/misc/ssh/ssh_encode_sequence_multi.c

@@ -0,0 +1,168 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+#include "tomcrypt_private.h"
+#include <stdarg.h>
+
+/**
+   @file ssh_encode_sequence_multi.c
+   SSH data type representation as per RFC4251, Russ Williams
+*/
+
+#ifdef LTC_SSH
+
+/**
+  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*)
+  @return CRYPT_OK on success
+*/
+int ssh_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...)
+{
+   int           err;
+   va_list       args;
+   unsigned long size;
+   ssh_data_type type;
+   void         *vdata;
+   const char   *sdata;
+   int           idata;
+   ulong32       u32data;
+   ulong64       u64data;
+
+   LTC_ARGCHK(out    != NULL);
+   LTC_ARGCHK(outlen != NULL);
+
+   /* Check values and calculate output size */
+   size = 0;
+   va_start(args, outlen);
+   while ((type = (ssh_data_type)va_arg(args, int)) != LTC_SSHDATA_EOL) {
+      switch (type) {
+         case LTC_SSHDATA_BYTE:
+         case LTC_SSHDATA_BOOLEAN: /* Both stored as 1 byte */
+            LTC_UNUSED_PARAM( va_arg(args, int) );
+            size++;
+            break;
+         case LTC_SSHDATA_UINT32:
+            LTC_UNUSED_PARAM( va_arg(args, ulong32) );
+            size += 4;
+            break;
+         case LTC_SSHDATA_UINT64:
+            LTC_UNUSED_PARAM( va_arg(args, ulong64) );
+            size += 8;
+            break;
+         case LTC_SSHDATA_STRING:
+         case LTC_SSHDATA_NAMELIST:
+            sdata = va_arg(args, char*);
+            size += 4;
+            size += strlen(sdata);
+            break;
+         case LTC_SSHDATA_MPINT:
+            vdata = va_arg(args, void*);
+            /* Calculate size */
+            size += 4;
+            if (mp_iszero(vdata) != LTC_MP_YES) {
+               size += mp_unsigned_bin_size(vdata);
+               if ((mp_count_bits(vdata) & 7) == 0) size++; /* Zero padding if high bit set */
+            }
+            break;
+
+         case LTC_SSHDATA_EOL: /* Should never get here */
+            err = CRYPT_INVALID_ARG;
+            goto error;
+      }
+   }
+   va_end(args);
+
+   /* Check we have sufficient space */
+   if (*outlen < size) {
+      *outlen = size;
+      err = CRYPT_BUFFER_OVERFLOW;
+      goto errornoargs;
+   }
+   *outlen = size;
+
+   /* Encode values into buffer */
+   va_start(args, outlen);
+   while ((type = (ssh_data_type)va_arg(args, int)) != LTC_SSHDATA_EOL) {
+      switch (type) {
+         case LTC_SSHDATA_BYTE:
+            idata = va_arg(args, int);
+
+            *out++ = (unsigned char)(idata & 255);
+            break;
+         case LTC_SSHDATA_BOOLEAN:
+            idata = va_arg(args, int);
+
+            /*
+               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:
+            u32data = va_arg(args, ulong32);
+            STORE32H(u32data, out);
+            out += 4;
+            break;
+         case LTC_SSHDATA_UINT64:
+            u64data = va_arg(args, ulong64);
+            STORE64H(u64data, out);
+            out += 8;
+            break;
+         case LTC_SSHDATA_STRING:
+         case LTC_SSHDATA_NAMELIST:
+            sdata = va_arg(args, char*);
+            size = strlen(sdata);
+            STORE32H(size, out);
+            out += 4;
+            XSTRNCPY((char *)out, sdata, size);
+            out += size;
+            break;
+         case LTC_SSHDATA_MPINT:
+            vdata = va_arg(args, void*);
+            if (mp_iszero(vdata) == LTC_MP_YES) {
+               STORE32H(0, out);
+               out += 4;
+            } else {
+               size = mp_unsigned_bin_size(vdata);
+               if ((mp_count_bits(vdata) & 7) == 0) {
+                  /* Zero padding if high bit set */
+                  STORE32H(size+1, out);
+                  out += 4;
+                  *out++ = 0;
+               } else {
+                  STORE32H(size, out);
+                  out += 4;
+               }
+               if ((err = mp_to_unsigned_bin(vdata, out)) != CRYPT_OK) {
+                  err = CRYPT_ERROR;
+                  goto error;
+               }
+               out += size;
+            }
+            break;
+
+         case LTC_SSHDATA_EOL: /* Should never get here */
+            err = CRYPT_INVALID_ARG;
+            goto error;
+      }
+   }
+   err = CRYPT_OK;
+
+error:
+   va_end(args);
+errornoargs:
+   return err;
+}
+
+#endif
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 22 - 2
src/pk/ecc/ecc_recover_key.c

@@ -92,8 +92,7 @@ int ecc_recover_key(const unsigned char *sig,  unsigned long siglen,
    }
    else if (sigformat == LTC_ECCSIG_ETH27) {
       /* Ethereum (v,r,s) format */
-      if (key->dp.oidlen != 5   || key->dp.oid[0] != 1 || key->dp.oid[1] != 3 ||
-          key->dp.oid[2] != 132 || key->dp.oid[3] != 0 || key->dp.oid[4] != 10) {
+      if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) {
          /* Only valid for secp256k1 - OID 1.3.132.0.10 */
          err = CRYPT_ERROR; goto error;
       }
@@ -112,6 +111,27 @@ int ecc_recover_key(const unsigned char *sig,  unsigned long siglen,
       if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig,  32)) != CRYPT_OK)                       { goto error; }
       if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+32, 32)) != CRYPT_OK)                     { goto error; }
    }
+#ifdef LTC_SSH
+   else if (sigformat == LTC_ECCSIG_RFC5656) {
+      char name[64], name2[64];
+      unsigned long namelen = sizeof(name2);
+
+      /* Decode as SSH data sequence, per RFC4251 */
+      if ((err = ssh_decode_sequence_multi(sig, siglen,
+                                           LTC_SSHDATA_STRING, name, 64,
+                                           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) {
+         err = CRYPT_INVALID_ARG;
+         goto error;
+      }
+   }
+#endif
    else {
       /* Unknown signature format */
       err = CRYPT_ERROR;

+ 16 - 2
src/pk/ecc/ecc_sign_hash.c

@@ -141,8 +141,7 @@ int ecc_sign_hash_ex(const unsigned char *in,  unsigned long inlen,
    }
    else if (sigformat == LTC_ECCSIG_ETH27) {
       /* Ethereum (v,r,s) format */
-      if (key->dp.oidlen != 5   || key->dp.oid[0] != 1 || key->dp.oid[1] != 3 ||
-          key->dp.oid[2] != 132 || key->dp.oid[3] != 0 || key->dp.oid[4] != 10) {
+      if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) {
          /* Only valid for secp256k1 - OID 1.3.132.0.10 */
          err = CRYPT_ERROR; goto errnokey;
       }
@@ -156,6 +155,21 @@ int ecc_sign_hash_ex(const unsigned char *in,  unsigned long inlen,
       *outlen = 65;
       err = CRYPT_OK;
    }
+#ifdef LTC_SSH
+   else if (sigformat == LTC_ECCSIG_RFC5656) {
+      /* Get identifier string */
+      char name[64];
+      unsigned long namelen = sizeof(name);
+      if ((err = ecc_ssh_ecdsa_encode_name(name, &namelen, key)) != CRYPT_OK) { goto errnokey; }
+
+      /* Store as SSH data sequence, per RFC4251 */
+      err = ssh_encode_sequence_multi(out, outlen,
+                                      LTC_SSHDATA_STRING, name,
+                                      LTC_SSHDATA_MPINT,  r,
+                                      LTC_SSHDATA_MPINT,  s,
+                                      LTC_SSHDATA_EOL,    NULL);
+   }
+#endif
    else {
       /* Unknown signature format */
       err = CRYPT_ERROR;

+ 70 - 0
src/pk/ecc/ecc_ssh_ecdsa_encode_name.c

@@ -0,0 +1,70 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+#include "tomcrypt_private.h"
+
+/**
+   @file ecc_ssh_ecdsa_encode_name.c
+   Curve/OID to SSH+ECDSA name string mapping per RFC5656
+   Russ Williams
+*/
+
+/**
+  Curve/OID to SSH+ECDSA name string mapping
+  @param buffer    [out] The destination for the name
+  @param buflen    [in/out] The max size and resulting size (including terminator) of the name
+  @param key       A public or private ECC key
+  @return CRYPT_OK if successful
+*/
+int ecc_ssh_ecdsa_encode_name(char *buffer, unsigned long *buflen, const ecc_key *key)
+{
+   char oidstr[64];
+   unsigned long oidlen = sizeof(oidstr);
+   unsigned long size = 0;
+   int err;
+
+   LTC_ARGCHK(buffer != NULL);
+   LTC_ARGCHK(buflen != NULL);
+   LTC_ARGCHK(key != NULL);
+
+   /* Get the OID of the curve */
+   if ((err = ecc_get_oid_str(oidstr, &oidlen, key)) != CRYPT_OK) goto error;
+
+   /* Check for three named curves: nistp256, nistp384, nistp521 */
+   if (XSTRCMP("1.2.840.10045.3.1.7", oidstr) == 0) {
+      /* nistp256 - secp256r1 - OID 1.2.840.10045.3.1.7 */
+      size = snprintf(buffer, *buflen, "ecdsa-sha2-nistp256");
+   }
+   else if (XSTRCMP("1.3.132.0.34", oidstr) == 0) {
+      /* nistp384 - secp384r1 - OID 1.3.132.0.34 */
+      size = snprintf(buffer, *buflen, "ecdsa-sha2-nistp384");
+   }
+   else if (XSTRCMP("1.3.132.0.35", oidstr) == 0) {
+      /* nistp521 - secp521r1 - OID 1.3.132.0.35 */
+      size = snprintf(buffer, *buflen, "ecdsa-sha2-nistp521");
+   } else {
+      /* Otherwise we use the OID... */
+      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) {
+      err = CRYPT_BUFFER_OVERFLOW;
+   } else {
+      err = CRYPT_OK;
+   }
+   *buflen = size + 1; /* the string length + NUL byte */
+
+error:
+   return err;
+}
+
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 22 - 2
src/pk/ecc/ecc_verify_hash.c

@@ -86,8 +86,7 @@ int ecc_verify_hash_ex(const unsigned char *sig,  unsigned long siglen,
    }
    else if (sigformat == LTC_ECCSIG_ETH27) {
       /* Ethereum (v,r,s) format */
-      if (key->dp.oidlen != 5   || key->dp.oid[0] != 1 || key->dp.oid[1] != 3 ||
-          key->dp.oid[2] != 132 || key->dp.oid[3] != 0 || key->dp.oid[4] != 10) {
+      if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) {
          /* Only valid for secp256k1 - OID 1.3.132.0.10 */
          err = CRYPT_ERROR; goto error;
       }
@@ -98,6 +97,27 @@ int ecc_verify_hash_ex(const unsigned char *sig,  unsigned long siglen,
       if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig,  32)) != CRYPT_OK)                       { goto error; }
       if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+32, 32)) != CRYPT_OK)                     { goto error; }
    }
+#ifdef LTC_SSH
+   else if (sigformat == LTC_ECCSIG_RFC5656) {
+      char name[64], name2[64];
+      unsigned long namelen = sizeof(name2);
+
+      /* Decode as SSH data sequence, per RFC4251 */
+      if ((err = ssh_decode_sequence_multi(sig, siglen,
+                                           LTC_SSHDATA_STRING, name, 64,
+                                           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) {
+         err = CRYPT_INVALID_ARG;
+         goto error;
+      }
+   }
+#endif
    else {
       /* Unknown signature format */
       err = CRYPT_ERROR;

+ 1 - 0
tests/common.h

@@ -23,6 +23,7 @@ extern prng_state yarrow_prng;
 #define SHOULD_FAIL(x) do { run_cmd((x) != CRYPT_OK ? 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)
 
 #if !((defined(_WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__))
 #define LTC_TEST_READDIR

+ 101 - 174
tests/ecc_test.c

@@ -37,6 +37,93 @@ static unsigned int sizes[] = {
 #endif
 };
 
+static const char* curvenames[] = {
+#ifdef LTC_ECC_SECP112R1
+   "SECP112R1", "ECC-112",
+   "secp112r1",              /* name is case-insensitive */
+   "S E C-P-1_1_2r1",        /* should pass fuzzy matching */
+#endif
+#ifdef LTC_ECC_SECP112R2
+   "SECP112R2",
+#endif
+#ifdef LTC_ECC_SECP128R1
+   "SECP128R1", "ECC-128",
+#endif
+#ifdef LTC_ECC_SECP128R2
+   "SECP128R2",
+#endif
+#ifdef LTC_ECC_SECP160R1
+   "SECP160R1", "ECC-160",
+#endif
+#ifdef LTC_ECC_SECP160R2
+   "SECP160R2",
+#endif
+#ifdef LTC_ECC_SECP160K1
+   "SECP160K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP160R1
+   "BRAINPOOLP160R1",
+#endif
+#ifdef LTC_ECC_SECP192R1
+   "SECP192R1", "NISTP192", "PRIME192V1", "ECC-192", "P-192",
+#endif
+#ifdef LTC_ECC_PRIME192V2
+   "PRIME192V2",
+#endif
+#ifdef LTC_ECC_PRIME192V3
+   "PRIME192V3",
+#endif
+#ifdef LTC_ECC_SECP192K1
+   "SECP192K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP192R1
+   "BRAINPOOLP192R1",
+#endif
+#ifdef LTC_ECC_SECP224R1
+   "SECP224R1", "NISTP224", "ECC-224", "P-224",
+#endif
+#ifdef LTC_ECC_SECP224K1
+   "SECP224K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP224R1
+   "BRAINPOOLP224R1",
+#endif
+#ifdef LTC_ECC_PRIME239V1
+   "PRIME239V1",
+#endif
+#ifdef LTC_ECC_PRIME239V2
+   "PRIME239V2",
+#endif
+#ifdef LTC_ECC_PRIME239V3
+   "PRIME239V3",
+#endif
+#ifdef LTC_ECC_SECP256R1
+   "SECP256R1", "NISTP256", "PRIME256V1", "ECC-256", "P-256",
+#endif
+#ifdef LTC_ECC_SECP256K1
+   "SECP256K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP256R1
+   "BRAINPOOLP256R1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP320R1
+   "BRAINPOOLP320R1",
+#endif
+#ifdef LTC_ECC_SECP384R1
+   "SECP384R1", "NISTP384", "ECC-384", "P-384",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP384R1
+   "BRAINPOOLP384R1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP512R1
+   "BRAINPOOLP512R1",
+#endif
+#ifdef LTC_ECC_SECP521R1
+   "SECP521R1", "NISTP521", "ECC-521", "P-521",
+#endif
+};
+
+
 #ifdef LTC_ECC_SHAMIR
 static int _ecc_test_shamir(void)
 {
@@ -423,91 +510,6 @@ static int _ecc_key_cmp(const int should_type, const ecc_key *should, const ecc_
 
 static int _ecc_new_api(void)
 {
-   const char* names[] = {
-#ifdef LTC_ECC_SECP112R1
-      "SECP112R1", "ECC-112",
-      "secp112r1",              /* name is case-insensitive */
-      "S E C-P-1_1_2r1",        /* should pass fuzzy matching */
-#endif
-#ifdef LTC_ECC_SECP112R2
-      "SECP112R2",
-#endif
-#ifdef LTC_ECC_SECP128R1
-      "SECP128R1", "ECC-128",
-#endif
-#ifdef LTC_ECC_SECP128R2
-      "SECP128R2",
-#endif
-#ifdef LTC_ECC_SECP160R1
-      "SECP160R1", "ECC-160",
-#endif
-#ifdef LTC_ECC_SECP160R2
-      "SECP160R2",
-#endif
-#ifdef LTC_ECC_SECP160K1
-      "SECP160K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP160R1
-      "BRAINPOOLP160R1",
-#endif
-#ifdef LTC_ECC_SECP192R1
-      "SECP192R1", "NISTP192", "PRIME192V1", "ECC-192", "P-192",
-#endif
-#ifdef LTC_ECC_PRIME192V2
-      "PRIME192V2",
-#endif
-#ifdef LTC_ECC_PRIME192V3
-      "PRIME192V3",
-#endif
-#ifdef LTC_ECC_SECP192K1
-      "SECP192K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP192R1
-      "BRAINPOOLP192R1",
-#endif
-#ifdef LTC_ECC_SECP224R1
-      "SECP224R1", "NISTP224", "ECC-224", "P-224",
-#endif
-#ifdef LTC_ECC_SECP224K1
-      "SECP224K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP224R1
-      "BRAINPOOLP224R1",
-#endif
-#ifdef LTC_ECC_PRIME239V1
-      "PRIME239V1",
-#endif
-#ifdef LTC_ECC_PRIME239V2
-      "PRIME239V2",
-#endif
-#ifdef LTC_ECC_PRIME239V3
-      "PRIME239V3",
-#endif
-#ifdef LTC_ECC_SECP256R1
-      "SECP256R1", "NISTP256", "PRIME256V1", "ECC-256", "P-256",
-#endif
-#ifdef LTC_ECC_SECP256K1
-      "SECP256K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP256R1
-      "BRAINPOOLP256R1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP320R1
-      "BRAINPOOLP320R1",
-#endif
-#ifdef LTC_ECC_SECP384R1
-      "SECP384R1", "NISTP384", "ECC-384", "P-384",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP384R1
-      "BRAINPOOLP384R1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP512R1
-      "BRAINPOOLP512R1",
-#endif
-#ifdef LTC_ECC_SECP521R1
-      "SECP521R1", "NISTP521", "ECC-521", "P-521",
-#endif
-   };
    int i, j, stat;
    const ltc_ecc_curve* dp;
    ecc_key key, privkey, pubkey;
@@ -516,8 +518,8 @@ static int _ecc_new_api(void)
    unsigned char data16[16] = { 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1 };
    unsigned long len16;
 
-   for (i = 0; i < (int)(sizeof(names)/sizeof(names[0])); i++) {
-      DO(ecc_find_curve(names[i], &dp));
+   for (i = 0; i < (int)(sizeof(curvenames)/sizeof(curvenames[0])); i++) {
+      DO(ecc_find_curve(curvenames[i], &dp));
       /* make new key */
       DO(ecc_make_key_ex(&yarrow_prng, find_prng ("yarrow"), &key, dp));
       len = sizeof(buf);
@@ -572,6 +574,16 @@ static int _ecc_new_api(void)
       DO(ecc_verify_hash(buf, len, data16, 16, &stat, &pubkey));
       if (stat != 1) return CRYPT_FAIL_TESTVECTOR;
 
+#ifdef LTC_SSH
+      /* test SSH+ECDSA/RFC5656 signature */
+      len = sizeof(buf);
+      DO(ecc_sign_hash_ex(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"),
+                          LTC_ECCSIG_RFC5656, NULL, &privkey));
+      stat = 0;
+      DO(ecc_verify_hash_ex(buf, len, data16, 16, LTC_ECCSIG_RFC5656, &stat, &pubkey));
+      if (stat != 1) return CRYPT_FAIL_TESTVECTOR;
+#endif
+
 #ifdef LTC_ECC_SHAMIR
       if (strcmp(ltc_mp.name, "TomsFastMath") != 0) {
          /* XXX-FIXME: TFM does not support sqrtmod_prime */
@@ -1477,91 +1489,6 @@ static int _ecc_import_export(void) {
 #ifdef LTC_ECC_SHAMIR
 static int _ecc_test_recovery(void)
 {
-   const char* names[] = {
-#ifdef LTC_ECC_SECP112R1
-      "SECP112R1", "ECC-112",
-      "secp112r1",              /* name is case-insensitive */
-      "S E C-P-1_1_2r1",        /* should pass fuzzy matching */
-#endif
-#ifdef LTC_ECC_SECP112R2
-      "SECP112R2",
-#endif
-#ifdef LTC_ECC_SECP128R1
-      "SECP128R1", "ECC-128",
-#endif
-#ifdef LTC_ECC_SECP128R2
-      "SECP128R2",
-#endif
-#ifdef LTC_ECC_SECP160R1
-      "SECP160R1", "ECC-160",
-#endif
-#ifdef LTC_ECC_SECP160R2
-      "SECP160R2",
-#endif
-#ifdef LTC_ECC_SECP160K1
-      "SECP160K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP160R1
-      "BRAINPOOLP160R1",
-#endif
-#ifdef LTC_ECC_SECP192R1
-      "SECP192R1", "NISTP192", "PRIME192V1", "ECC-192", "P-192",
-#endif
-#ifdef LTC_ECC_PRIME192V2
-      "PRIME192V2",
-#endif
-#ifdef LTC_ECC_PRIME192V3
-      "PRIME192V3",
-#endif
-#ifdef LTC_ECC_SECP192K1
-      "SECP192K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP192R1
-      "BRAINPOOLP192R1",
-#endif
-#ifdef LTC_ECC_SECP224R1
-      "SECP224R1", "NISTP224", "ECC-224", "P-224",
-#endif
-#ifdef LTC_ECC_SECP224K1
-      "SECP224K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP224R1
-      "BRAINPOOLP224R1",
-#endif
-#ifdef LTC_ECC_PRIME239V1
-      "PRIME239V1",
-#endif
-#ifdef LTC_ECC_PRIME239V2
-      "PRIME239V2",
-#endif
-#ifdef LTC_ECC_PRIME239V3
-      "PRIME239V3",
-#endif
-#ifdef LTC_ECC_SECP256R1
-      "SECP256R1", "NISTP256", "PRIME256V1", "ECC-256", "P-256",
-#endif
-#ifdef LTC_ECC_SECP256K1
-      "SECP256K1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP256R1
-      "BRAINPOOLP256R1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP320R1
-      "BRAINPOOLP320R1",
-#endif
-#ifdef LTC_ECC_SECP384R1
-      "SECP384R1", "NISTP384", "ECC-384", "P-384",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP384R1
-      "BRAINPOOLP384R1",
-#endif
-#ifdef LTC_ECC_BRAINPOOLP512R1
-      "BRAINPOOLP512R1",
-#endif
-#ifdef LTC_ECC_SECP521R1
-      "SECP521R1", "NISTP521", "ECC-521", "P-521",
-#endif
-   };
    int i, recid, stat;
    const ltc_ecc_curve* dp;
    ecc_key key, privkey, pubkey, reckey;
@@ -1611,8 +1538,8 @@ static int _ecc_test_recovery(void)
    ecc_free(&pubkey);
 #endif
 
-   for (i = 0; i < (int)(sizeof(names)/sizeof(names[0])); i++) {
-      DO(ecc_find_curve(names[i], &dp));
+   for (i = 0; i < (int)(sizeof(curvenames)/sizeof(curvenames[0])); i++) {
+      DO(ecc_find_curve(curvenames[i], &dp));
 
       /* generate new key */
       DO(ecc_set_curve(dp, &key));

+ 3 - 0
tests/misc_test.c

@@ -33,6 +33,9 @@ int misc_test(void)
 #endif
 #ifdef LTC_CRC32
    DO(crc32_test());
+#endif
+#ifdef LTC_SSH
+   DO(ssh_test());
 #endif
    return 0;
 }

+ 310 - 0
tests/ssh_test.c

@@ -0,0 +1,310 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+#include "tomcrypt_test.h"
+
+/**
+  @file ssh_test.c
+  Support for SSH data formats (RFC4251), Russ Williams
+*/
+
+#ifdef LTC_SSH
+
+#define BUFSIZE 64
+
+/**
+  Test vectors from from RFC4251, section 5
+
+  uint32: "the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 aa"
+
+  string: "the US-ASCII string "testing" is represented as 00 00 00 07 t e s t i n g"
+
+  mpint:
+  value (hex)        representation (hex)
+  -----------        --------------------
+  0                  00 00 00 00
+  9a378f9b2e332a7    00 00 00 08 09 a3 78 f9 b2 e3 32 a7
+  80                 00 00 00 02 00 80
+  -1234              00 00 00 02 ed cc
+  -deadbeef          00 00 00 05 ff 21 52 41 11
+
+  name-list:
+  value                      representation (hex)
+  -----                      --------------------
+  (), the empty name-list    00 00 00 00
+  ("zlib")                   00 00 00 04 7a 6c 69 62
+  ("zlib,none")              00 00 00 09 7a 6c 69 62 2c 6e 6f 6e 65
+*/
+static const unsigned char  byte1[] = {0x01};
+static const unsigned char  byte2[] = {0x71};
+static const unsigned char uint32[] = {0x29, 0xb7, 0xf4, 0xaa};
+static const unsigned char uint64[] = {0x09, 0xa3, 0x78, 0xf9, 0xb2, 0xe3, 0x32, 0xa7};
+static const unsigned char string[] = {0x00, 0x00, 0x00, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67};
+static const unsigned char mpint1[] = {0x00, 0x00, 0x00, 0x00};
+static const unsigned char mpint2[] = {0x00, 0x00, 0x00, 0x08, 0x09, 0xa3, 0x78, 0xf9, 0xb2, 0xe3, 0x32, 0xa7};
+static const unsigned char mpint3[] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x80};
+static const unsigned char nlist1[] = {0x00, 0x00, 0x00, 0x00};
+static const unsigned char nlist2[] = {0x00, 0x00, 0x00, 0x04, 0x7a, 0x6c, 0x69, 0x62};
+static const unsigned char nlist3[] = {0x00, 0x00, 0x00, 0x09, 0x7a, 0x6c, 0x69, 0x62, 0x2c, 0x6e, 0x6f, 0x6e, 0x65};
+
+
+/**
+  LTC_SSH encoding test
+  @return CRYPT_OK if successful
+*/
+static int _ssh_encoding_test(void)
+{
+   unsigned char buffer[BUFSIZE];
+   unsigned long buflen;
+   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;
+
+
+   /* byte */
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_BYTE, 0x01,
+                                LTC_SSHDATA_EOL,  NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, byte1, sizeof(byte1), "enc-byte",    1);
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_BYTE, 0x71,
+                                LTC_SSHDATA_EOL,  NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, byte2, sizeof(byte2), "enc-byte",    2);
+   if (XMEMCMP(buffer, byte2, buflen) != 0) return CRYPT_FAIL_TESTVECTOR;
+
+   /* boolean */
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_BOOLEAN, 0x01,
+                                LTC_SSHDATA_EOL,     NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, byte1, sizeof(byte1), "enc-boolean", 1);
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_BOOLEAN, 0x71,
+                                LTC_SSHDATA_EOL,     NULL));
+   /* Should be written out as 0x01 */
+   COMPARE_TESTVECTOR(buffer, buflen, byte1, sizeof(byte1), "enc-boolean", 2);
+
+   /* uint32 */
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_UINT32, 0x29b7f4aa,
+                                LTC_SSHDATA_EOL,    NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, uint32, sizeof(uint32), "enc-uint32", 1);
+
+   /* uint64 */
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_UINT64, CONST64(0x09a378f9b2e332a7),
+                                LTC_SSHDATA_EOL,    NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, uint64, sizeof(uint64), "enc-uint64", 1);
+
+
+   /* string */
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_STRING, "testing",
+                                LTC_SSHDATA_EOL,    NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, string, sizeof(string), "enc-string", 1);
+
+
+   /* mpint */
+   if ((err = mp_init_multi(&zero, &v, NULL)) != CRYPT_OK) {
+      return err;
+   }
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(mp_set(zero, 0));
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_MPINT, zero,
+                                LTC_SSHDATA_EOL,   NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, mpint1, sizeof(mpint1), "enc-mpint",  1);
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(mp_read_radix(v, "9a378f9b2e332a7", 16));
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_MPINT, v,
+                                LTC_SSHDATA_EOL,   NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, mpint2, sizeof(mpint2), "enc-mpint",  2);
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(mp_set(v, 0x80));
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_MPINT, v,
+                                LTC_SSHDATA_EOL,   NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, mpint3, sizeof(mpint3), "enc-mpint",  3);
+
+   mp_clear_multi(v, zero, NULL);
+
+
+   /* name-list */
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_NAMELIST, "",
+                                LTC_SSHDATA_EOL,      NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, nlist1, sizeof(nlist1), "enc-nlist", 1);
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_NAMELIST, "zlib",
+                                LTC_SSHDATA_EOL,      NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, nlist2, sizeof(nlist2), "enc-nlist", 2);
+
+   buflen = BUFSIZE;
+   zeromem(buffer, BUFSIZE);
+   DO(ssh_encode_sequence_multi(buffer, &buflen,
+                                LTC_SSHDATA_NAMELIST, "zlib,none",
+                                LTC_SSHDATA_EOL,      NULL));
+   COMPARE_TESTVECTOR(buffer, buflen, nlist3, sizeof(nlist3), "enc-nlist", 3);
+
+   return CRYPT_OK;
+}
+
+/**
+  LTC_SSH decoding test
+  @return CRYPT_OK if successful
+*/
+static int _ssh_decoding_test(void)
+{
+   char strbuf[BUFSIZE];
+   void *u, *v;
+   ulong32 tmp32;
+   ulong64 tmp64;
+   unsigned char tmp8;
+   int err;
+
+   /* byte */
+   DO(ssh_decode_sequence_multi(byte1, sizeof(byte1),
+                                LTC_SSHDATA_BYTE, &tmp8,
+                                LTC_SSHDATA_EOL,  NULL));
+   if (tmp8 != 0x01) return CRYPT_FAIL_TESTVECTOR;
+
+   DO(ssh_decode_sequence_multi(byte2, sizeof(byte2),
+                                LTC_SSHDATA_BYTE, &tmp8,
+                                LTC_SSHDATA_EOL,  NULL));
+   if (tmp8 != 0x71) return CRYPT_FAIL_TESTVECTOR;
+
+   /* boolean */
+   DO(ssh_decode_sequence_multi(byte1, sizeof(byte1),
+                                LTC_SSHDATA_BOOLEAN, &tmp8,
+                                LTC_SSHDATA_EOL,     NULL));
+   if (tmp8 != 0x01) return CRYPT_FAIL_TESTVECTOR;
+
+   DO(ssh_decode_sequence_multi(byte2, sizeof(byte2),
+                                LTC_SSHDATA_BOOLEAN, &tmp8,
+                                LTC_SSHDATA_EOL,     NULL));
+   if (tmp8 != 0x01) return CRYPT_FAIL_TESTVECTOR;
+
+   /* uint32 */
+   DO(ssh_decode_sequence_multi(uint32, sizeof(uint32),
+                                LTC_SSHDATA_UINT32, &tmp32,
+                                LTC_SSHDATA_EOL,    NULL));
+   if (tmp32 != 0x29b7f4aa) return CRYPT_FAIL_TESTVECTOR;
+
+   /* uint64 */
+   DO(ssh_decode_sequence_multi(uint64, sizeof(uint64),
+                                LTC_SSHDATA_UINT64, &tmp64,
+                                LTC_SSHDATA_EOL,    NULL));
+   if (tmp64 != CONST64(0x09a378f9b2e332a7)) return CRYPT_FAIL_TESTVECTOR;
+
+   /* string */
+   zeromem(strbuf, BUFSIZE);
+   DO(ssh_decode_sequence_multi(string, sizeof(string),
+                                LTC_SSHDATA_STRING, strbuf, BUFSIZE,
+                                LTC_SSHDATA_EOL,    NULL));
+   if (XSTRCMP(strbuf, "testing") != 0) return CRYPT_FAIL_TESTVECTOR;
+
+   /* mpint */
+   if ((err = mp_init_multi(&u, &v, NULL)) != CRYPT_OK) {
+      return err;
+   }
+
+   DO(ssh_decode_sequence_multi(mpint1, sizeof(mpint1),
+                                LTC_SSHDATA_MPINT, v,
+                                LTC_SSHDATA_EOL,   NULL));
+   if (mp_cmp_d(v, 0) != LTC_MP_EQ) return CRYPT_FAIL_TESTVECTOR;
+
+   DO(mp_read_radix(u, "9a378f9b2e332a7", 16));
+   DO(ssh_decode_sequence_multi(mpint2, sizeof(mpint2),
+                                LTC_SSHDATA_MPINT, v,
+                                LTC_SSHDATA_EOL,   NULL));
+   if (mp_cmp(u, v) != LTC_MP_EQ) return CRYPT_FAIL_TESTVECTOR;
+
+   DO(ssh_decode_sequence_multi(mpint3, sizeof(mpint3),
+                                LTC_SSHDATA_MPINT, v,
+                                LTC_SSHDATA_EOL,   NULL));
+   if (mp_cmp_d(v, 0x80) != LTC_MP_EQ) return CRYPT_FAIL_TESTVECTOR;
+
+   mp_clear_multi(v, u, NULL);
+
+   /* name-list */
+   zeromem(strbuf, BUFSIZE);
+   DO(ssh_decode_sequence_multi(nlist1, sizeof(nlist1),
+                                LTC_SSHDATA_NAMELIST, strbuf, BUFSIZE,
+                                LTC_SSHDATA_EOL,      NULL));
+   if (XSTRCMP(strbuf, "") != 0) return CRYPT_FAIL_TESTVECTOR;
+
+   zeromem(strbuf, BUFSIZE);
+   DO(ssh_decode_sequence_multi(nlist2, sizeof(nlist2),
+                                LTC_SSHDATA_NAMELIST, strbuf, BUFSIZE,
+                                LTC_SSHDATA_EOL,      NULL));
+   if (XSTRCMP(strbuf, "zlib") != 0) return CRYPT_FAIL_TESTVECTOR;
+
+   zeromem(strbuf, BUFSIZE);
+   DO(ssh_decode_sequence_multi(nlist3, sizeof(nlist3),
+                                LTC_SSHDATA_NAMELIST, strbuf, BUFSIZE,
+                                LTC_SSHDATA_EOL,      NULL));
+   if (XSTRCMP(strbuf, "zlib,none") != 0) return CRYPT_FAIL_TESTVECTOR;
+
+
+   return CRYPT_OK;
+}
+
+/**
+  LTC_SSH self-test
+  @return CRYPT_OK if successful, CRYPT_NOP if tests have been disabled.
+*/
+int ssh_test(void)
+{
+   DO(_ssh_encoding_test());
+   DO(_ssh_decoding_test());
+
+   return CRYPT_OK;
+}
+
+
+
+#endif
+
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 3 - 0
tests/tomcrypt_test.h

@@ -45,6 +45,9 @@ int prng_test(void);
 int mpi_test(void);
 int padding_test(void);
 
+#ifdef LTC_SSH
+int ssh_test(void);
+#endif
 
 #ifdef LTC_PKCS_1
 struct ltc_prng_descriptor* no_prng_desc_get(void);