blob: 19d8c8690ae8d9b0bc57428ee9de63f95fcb963b [file] [log] [blame]
/* 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 $ */