| /* 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. |
| * |
| * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com |
| */ |
| #include "tomcrypt.h" |
| |
| /** |
| @file der_decode_sequence_flexi.c |
| ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis |
| */ |
| |
| #ifdef LTC_DER |
| |
| static unsigned long fetch_length(const unsigned char *in, unsigned long inlen) |
| { |
| unsigned long x, y, z; |
| |
| y = 0; |
| |
| /* skip type and read len */ |
| if (inlen < 2) { |
| return 0xFFFFFFFF; |
| } |
| ++in; ++y; |
| |
| /* read len */ |
| x = *in++; ++y; |
| |
| /* <128 means literal */ |
| if (x < 128) { |
| return x+y; |
| } |
| 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; |
| } |
| |
| y += x; |
| z = 0; |
| while (x--) { |
| z = (z<<8) | ((unsigned long)*in); |
| ++in; |
| } |
| return z+y; |
| } |
| |
| /** |
| ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements. |
| @param in The input buffer |
| @param inlen [in/out] The length of the input buffer and on output the amount of decoded data |
| @param out [out] A pointer to the linked list |
| @return CRYPT_OK on success. |
| */ |
| int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) |
| { |
| ltc_asn1_list *l; |
| unsigned long err, type, len, totlen, x, y; |
| void *realloc_tmp; |
| |
| LTC_ARGCHK(in != NULL); |
| LTC_ARGCHK(inlen != NULL); |
| LTC_ARGCHK(out != NULL); |
| |
| l = NULL; |
| totlen = 0; |
| |
| /* scan the input and and get lengths and what not */ |
| while (*inlen) { |
| /* read the type byte */ |
| type = *in; |
| |
| /* fetch length */ |
| len = fetch_length(in, *inlen); |
| if (len > *inlen) { |
| err = CRYPT_INVALID_PACKET; |
| goto error; |
| } |
| |
| /* alloc new link */ |
| if (l == NULL) { |
| l = XCALLOC(1, sizeof(*l)); |
| if (l == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| } else { |
| l->next = XCALLOC(1, sizeof(*l)); |
| if (l->next == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| l->next->prev = l; |
| l = l->next; |
| } |
| |
| /* now switch on type */ |
| switch (type) { |
| case 0x01: /* BOOLEAN */ |
| l->type = LTC_ASN1_BOOLEAN; |
| l->size = 1; |
| l->data = XCALLOC(1, sizeof(int)); |
| |
| if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_boolean(&len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x02: /* INTEGER */ |
| /* init field */ |
| l->type = LTC_ASN1_INTEGER; |
| l->size = 1; |
| if ((err = mp_init(&l->data)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* decode field */ |
| if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* calc length of object */ |
| if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x03: /* BIT */ |
| /* init field */ |
| l->type = LTC_ASN1_BIT_STRING; |
| l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char. */ |
| |
| if ((l->data = XCALLOC(1, l->size)) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x04: /* OCTET */ |
| |
| /* init field */ |
| l->type = LTC_ASN1_OCTET_STRING; |
| l->size = len; |
| |
| if ((l->data = XCALLOC(1, l->size)) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x05: /* NULL */ |
| |
| /* valid NULL is 0x05 0x00 */ |
| if (in[0] != 0x05 || in[1] != 0x00) { |
| err = CRYPT_INVALID_PACKET; |
| goto error; |
| } |
| |
| /* simple to store ;-) */ |
| l->type = LTC_ASN1_NULL; |
| l->data = NULL; |
| l->size = 0; |
| len = 2; |
| |
| break; |
| |
| case 0x06: /* OID */ |
| |
| /* init field */ |
| l->type = LTC_ASN1_OBJECT_IDENTIFIER; |
| l->size = len; |
| |
| if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* resize it to save a bunch of mem */ |
| if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) { |
| /* out of heap but this is not an error */ |
| break; |
| } |
| l->data = realloc_tmp; |
| break; |
| |
| case 0x0C: /* UTF8 */ |
| |
| /* init field */ |
| l->type = LTC_ASN1_UTF8_STRING; |
| l->size = len; |
| |
| if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x13: /* PRINTABLE */ |
| |
| /* init field */ |
| l->type = LTC_ASN1_PRINTABLE_STRING; |
| l->size = len; |
| |
| if ((l->data = XCALLOC(1, l->size)) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x16: /* IA5 */ |
| |
| /* init field */ |
| l->type = LTC_ASN1_IA5_STRING; |
| l->size = len; |
| |
| if ((l->data = XCALLOC(1, l->size)) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x17: /* UTC TIME */ |
| |
| /* init field */ |
| l->type = LTC_ASN1_UTCTIME; |
| l->size = 1; |
| |
| if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) { |
| err = CRYPT_MEM; |
| goto error; |
| } |
| |
| len = *inlen; |
| if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) { |
| goto error; |
| } |
| |
| if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) { |
| goto error; |
| } |
| break; |
| |
| case 0x30: /* SEQUENCE */ |
| case 0x31: /* SET */ |
| |
| /* init field */ |
| l->type = (type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET; |
| |
| /* we have to decode the SEQUENCE header and get it's length */ |
| |
| /* move past type */ |
| ++in; --(*inlen); |
| |
| /* read length byte */ |
| x = *in++; --(*inlen); |
| |
| /* smallest SEQUENCE/SET header */ |
| y = 2; |
| |
| /* now if it's > 127 the next bytes are the length of the length */ |
| if (x > 128) { |
| x &= 0x7F; |
| in += x; |
| *inlen -= x; |
| |
| /* update sequence header len */ |
| y += x; |
| } |
| |
| /* Sequence elements go as child */ |
| len = len - y; |
| if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) { |
| goto error; |
| } |
| |
| /* len update */ |
| totlen += y; |
| |
| /* link them up y0 */ |
| l->child->parent = l; |
| |
| break; |
| default: |
| /* invalid byte ... this is a soft error */ |
| /* remove link */ |
| l = l->prev; |
| XFREE(l->next); |
| l->next = NULL; |
| goto outside; |
| } |
| |
| /* advance pointers */ |
| totlen += len; |
| in += len; |
| *inlen -= len; |
| } |
| |
| outside: |
| |
| /* rewind l please */ |
| while (l->prev != NULL || l->parent != NULL) { |
| if (l->parent != NULL) { |
| l = l->parent; |
| } else { |
| l = l->prev; |
| } |
| } |
| |
| /* return */ |
| *out = l; |
| *inlen = totlen; |
| return CRYPT_OK; |
| |
| error: |
| /* free list */ |
| der_sequence_free(l); |
| |
| return err; |
| } |
| |
| #endif |
| |
| |
| /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c,v $ */ |
| /* $Revision: 1.25 $ */ |
| /* $Date: 2006/11/26 02:25:18 $ */ |