Browse Source

add ASN.1-length functions

Steffen Jaeckel 8 years ago
parent
commit
64875d3a8f

+ 5 - 0
src/headers/tomcrypt_pk.h

@@ -549,6 +549,11 @@ typedef struct ltc_asn1_list_ {
       LTC_MACRO_list[LTC_MACRO_temp].optional = 0;   \
    } while (0)
 
+int der_encode_asn1_length(unsigned long len, unsigned char* out, unsigned long* outlen);
+int der_decode_asn1_length(const unsigned char* len, unsigned long* lenlen, unsigned long* outlen);
+int der_length_asn1_length(unsigned long len, unsigned long *outlen);
+
+
 /* SEQUENCE */
 int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
                            unsigned char *out,  unsigned long *outlen, int type_of);

+ 67 - 0
src/pk/asn1/der/general/der_decode_asn1_length.c

@@ -0,0 +1,67 @@
+/* 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.h"
+
+/**
+  @file der_decode_asn1_length.c
+  ASN.1 DER, decode the ASN.1 Length field, Steffen Jaeckel
+*/
+
+#ifdef LTC_DER
+/**
+  Decode the ASN.1 Length field
+  @param in       Where to read the length field from
+  @param inlen    [in/out] The size of in available/read
+  @param outlen   [out] The decoded ASN.1 length
+  @return CRYPT_OK if successful
+*/
+int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsigned long *outlen)
+{
+   unsigned long real_len, decoded_len, offset, i;
+
+   LTC_ARGCHK(in    != NULL);
+   LTC_ARGCHK(inlen != NULL);
+
+   if (*inlen < 1) {
+      return CRYPT_BUFFER_OVERFLOW;
+   }
+
+   real_len = in[0];
+
+   if (real_len < 128) {
+      decoded_len = real_len;
+      offset = 1;
+   } else {
+      real_len &= 0x7F;
+      if (real_len == 0) {
+         return CRYPT_PK_ASN1_ERROR;
+      } else if (real_len > sizeof(decoded_len)) {
+         return CRYPT_OVERFLOW;
+      } else if (real_len > (*inlen - 1)) {
+         return CRYPT_BUFFER_OVERFLOW;
+      }
+      decoded_len = 0;
+      offset = 1 + real_len;
+      for (i = 0; i < real_len; i++) {
+         decoded_len = (decoded_len << 8) | in[1 + i];
+      }
+   }
+
+   if (outlen != NULL) *outlen = decoded_len;
+   if (decoded_len > (*inlen - offset)) return CRYPT_OVERFLOW;
+   *inlen = offset;
+
+   return CRYPT_OK;
+}
+
+#endif
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 121 - 0
src/pk/asn1/der/general/der_encode_asn1_length.c

@@ -0,0 +1,121 @@
+/* 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.h"
+
+/**
+  @file der_encode_asn1_length.c
+  ASN.1 DER, encode the ASN.1 length field, Steffen Jaeckel
+*/
+
+#ifdef LTC_DER
+/**
+  Encode the ASN.1 length field
+  @param len      The length to encode
+  @param out      Where to write the length field to
+  @param outlen   [in/out] The size of out available/written
+  @return CRYPT_OK if successful
+*/
+int der_encode_asn1_length(unsigned long len, unsigned char *out, unsigned long *outlen)
+{
+   unsigned long x, y;
+
+   LTC_ARGCHK(outlen != NULL);
+
+   x = len;
+   y = 0;
+
+   while(x != 0) {
+      y++;
+      x >>= 8;
+   }
+   if (y == 0) {
+      return CRYPT_PK_ASN1_ERROR;
+   }
+
+   if (out == NULL) {
+      if (len < 128) {
+         x = y;
+      } else {
+         x = y + 1;
+      }
+   } else {
+      if (*outlen < y) {
+         return CRYPT_BUFFER_OVERFLOW;
+      }
+      x = 0;
+      if (len < 128) {
+         out[x++] = (unsigned char)len;
+      } else if (len <= 0xffUL) {
+         out[x++] = 0x81;
+         out[x++] = (unsigned char)len;
+      } else if (len <= 0xffffUL) {
+         out[x++] = 0x82;
+         out[x++] = (unsigned char)((len>>8UL)&255);
+         out[x++] = (unsigned char)(len&255);
+      } else if (len <= 0xffffffUL) {
+         out[x++] = 0x83;
+         out[x++] = (unsigned char)((len>>16UL)&255);
+         out[x++] = (unsigned char)((len>>8UL)&255);
+         out[x++] = (unsigned char)(len&255);
+      } else if (len <= 0xffffffffUL) {
+         out[x++] = 0x84;
+         out[x++] = (unsigned char)((len>>24UL)&255);
+         out[x++] = (unsigned char)((len>>16UL)&255);
+         out[x++] = (unsigned char)((len>>8UL)&255);
+         out[x++] = (unsigned char)(len&255);
+   #if ULONG_MAX == ULLONG_MAX
+      } else if (len <= 0xffffffffffULL) {
+         out[x++] = 0x85;
+         out[x++] = (unsigned char)((len>>32ULL)&255);
+         out[x++] = (unsigned char)((len>>24ULL)&255);
+         out[x++] = (unsigned char)((len>>16ULL)&255);
+         out[x++] = (unsigned char)((len>>8ULL)&255);
+         out[x++] = (unsigned char)(len&255);
+      } else if (len <= 0xffffffffffffULL) {
+         out[x++] = 0x86;
+         out[x++] = (unsigned char)((len>>40ULL)&255);
+         out[x++] = (unsigned char)((len>>32ULL)&255);
+         out[x++] = (unsigned char)((len>>24ULL)&255);
+         out[x++] = (unsigned char)((len>>16ULL)&255);
+         out[x++] = (unsigned char)((len>>8ULL)&255);
+         out[x++] = (unsigned char)(len&255);
+      } else if (len <= 0xffffffffffffffULL) {
+         out[x++] = 0x87;
+         out[x++] = (unsigned char)((len>>48ULL)&255);
+         out[x++] = (unsigned char)((len>>40ULL)&255);
+         out[x++] = (unsigned char)((len>>32ULL)&255);
+         out[x++] = (unsigned char)((len>>24ULL)&255);
+         out[x++] = (unsigned char)((len>>16ULL)&255);
+         out[x++] = (unsigned char)((len>>8ULL)&255);
+         out[x++] = (unsigned char)(len&255);
+      } else if (len <= 0xffffffffffffffffULL) {
+         out[x++] = 0x88;
+         out[x++] = (unsigned char)((len>>56ULL)&255);
+         out[x++] = (unsigned char)((len>>48ULL)&255);
+         out[x++] = (unsigned char)((len>>40ULL)&255);
+         out[x++] = (unsigned char)((len>>32ULL)&255);
+         out[x++] = (unsigned char)((len>>24ULL)&255);
+         out[x++] = (unsigned char)((len>>16ULL)&255);
+         out[x++] = (unsigned char)((len>>8ULL)&255);
+         out[x++] = (unsigned char)(len&255);
+   #endif
+      } else {
+         return CRYPT_INPUT_TOO_LONG;
+      }
+   }
+   *outlen = x;
+
+   return CRYPT_OK;
+}
+
+#endif
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 32 - 0
src/pk/asn1/der/general/der_length_asn1_length.c

@@ -0,0 +1,32 @@
+/* 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.h"
+
+/**
+  @file der_length_asn1_length.c
+  ASN.1 DER, determine the length of the ASN.1 length field, Steffen Jaeckel
+*/
+
+#ifdef LTC_DER
+/**
+  Determine the length required to encode len in the ASN.1 length field
+  @param len      The length to encode
+  @param outlen   [out] The length that's required to store len
+  @return CRYPT_OK if successful
+*/
+int der_length_asn1_length(unsigned long len, unsigned long *outlen)
+{
+   return der_encode_asn1_length(len, NULL, outlen);
+}
+
+#endif
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 4 - 18
src/pk/asn1/der/sequence/der_decode_sequence_ex.c

@@ -51,25 +51,11 @@ int der_decode_sequence_ex(const unsigned char *in, unsigned long  inlen,
    /* check if the msb is set, which signals that the
     * 7 lsb bits represent the number of bytes of the length
     */
-   if (in[x] < 128) {
-      blksize = in[x++];
-   } else {
-      if (in[x] < 0x81 || in[x] > 0x83) {
-         return CRYPT_INVALID_PACKET;
-      }
-      y = in[x++] & 0x7F;
-
-      /* would reading the len bytes overrun? */
-      if (x + y > inlen) {
-         return CRYPT_INVALID_PACKET;
-      }
-
-      /* read len */
-      blksize = 0;
-      while (y--) {
-          blksize = (blksize << 8) | (unsigned long)in[x++];
-      }
+   y = inlen - x;
+   if ((err = der_decode_asn1_length(&in[x], &y, &blksize)) != CRYPT_OK) {
+      return err;
    }
+   x += y;
 
    /* would this blksize overflow? */
    if (x + blksize > inlen) {

+ 4 - 38
src/pk/asn1/der/sequence/der_decode_sequence_flexi.c

@@ -15,42 +15,6 @@
 
 #ifdef LTC_DER
 
-static unsigned long _fetch_length(const unsigned char *in, unsigned long inlen, unsigned long *data_offset)
-{
-   unsigned long x, z;
-
-   *data_offset = 0;
-
-   /* skip type and read len */
-   if (inlen < 2) {
-      return 0xFFFFFFFF;
-   }
-   ++in; ++(*data_offset);
-
-   /* read len */
-   x = *in++; ++(*data_offset);
-
-   /* <128 means literal */
-   if (x < 128) {
-      return x+*data_offset;
-   }
-   x     &= 0x7F; /* the lower 7 bits are the length of the length */
-   inlen -= 2;
-
-   /* len means len of len! */
-   if (x == 0 || x > 4 || x > inlen) {
-      return 0xFFFFFFFF;
-   }
-
-   *data_offset += x;
-   z = 0;
-   while (x--) {
-      z = (z<<8) | ((unsigned long)*in);
-      ++in;
-   }
-   return z+*data_offset;
-}
-
 static int _new_element(ltc_asn1_list **l)
 {
    /* alloc new link */
@@ -103,8 +67,10 @@ int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc
       type = *in;
 
       /* fetch length */
-      len = _fetch_length(in, *inlen, &data_offset);
-      if (len > *inlen) {
+      data_offset = *inlen;
+      if ((err = der_decode_asn1_length(in, &data_offset, &len)) != CRYPT_OK) {
+         goto error;
+      } else if (len > *inlen) {
          err = CRYPT_INVALID_PACKET;
          goto error;
       }

+ 7 - 18
src/pk/asn1/der/sequence/der_length_sequence.c

@@ -33,7 +33,7 @@ int der_length_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
 {
    int           err;
    ltc_asn1_type type;
-   unsigned long size, x, y, i, z;
+   unsigned long size, x, y, i;
    void          *data;
 
    LTC_ARGCHK(list    != NULL);
@@ -178,27 +178,16 @@ int der_length_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
        }
    }
 
-   /* calc header size */
-   z = y;
-   if (y < 128) {
-      y += 2;
-   } else if (y < 256) {
-      /* 0x30 0x81 LL */
-      y += 3;
-   } else if (y < 65536UL) {
-      /* 0x30 0x82 LL LL */
-      y += 4;
-   } else if (y < 16777216UL) {
-      /* 0x30 0x83 LL LL LL */
-      y += 5;
-   } else {
-      err = CRYPT_INVALID_ARG;
+   if ((err = der_length_asn1_length(y, &x)) != CRYPT_OK) {
       goto LBL_ERR;
    }
 
+   if (payloadlen != NULL) {
+      *payloadlen = y;
+   }
+
    /* store size */
-   if (payloadlen) *payloadlen = z;
-   *outlen = y;
+   *outlen = y + x + 1;
    err     = CRYPT_OK;
 
 LBL_ERR:

+ 6 - 0
tests/der_test.c

@@ -1127,6 +1127,12 @@ int der_test(void)
 
    der_cacert_test();
 
+      /* we have to modify x to be larger than the encoded
+       * length as der_decode_asn1_length() checks also if
+       * the encoded length is reasonable in regards to the
+       * available buffer size.
+       */
+      x = y + x;
    DO(mp_init_multi(&a, &b, &c, &d, &e, &f, &g, NULL));
    for (zz = 0; zz < 16; zz++) {
 #ifdef USE_TFM