blob: 46dc23a7e8a00a9fc6c981dd7498a2d589bd081b [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/* 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 der_decode_custom_type.c
ASN.1 DER, decode a Custom type, Steffen Jaeckel
*/
#ifdef LTC_DER
/**
Decode a Custom type
@param in The DER encoded input
@param inlen The size of the input
@param root The item that defines the custom type to decode
@return CRYPT_OK on success
*/
int der_decode_custom_type(const unsigned char *in, unsigned long inlen,
ltc_asn1_list *root)
{
LTC_ARGCHK(root != NULL);
return der_decode_custom_type_ex(in, inlen, root, NULL, 0, LTC_DER_SEQ_ORDERED | LTC_DER_SEQ_RELAXED);
}
/**
Extended-decode a Custom type
This function is used to decode custom types and sequences/sets
For custom types root is used
For sequences/sets list and outlen are used
@param in The DER encoded input
@param inlen The size of the input
@param root The item that defines the custom type to decode
@param list The list of items to decode
@param outlen The number of items in the list
@param flags c.f. enum ltc_der_seq
@return CRYPT_OK on success
*/
int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen,
ltc_asn1_list *root,
ltc_asn1_list *list, unsigned long outlen,
unsigned int flags)
{
int err, seq_err, i, ordered;
ltc_asn1_type type;
ltc_asn1_list ident;
unsigned long size, x, y, z, blksize;
unsigned char* in_new = NULL;
void *data;
LTC_ARGCHK(in != NULL);
/* get blk size */
if (inlen < 2) {
return CRYPT_INVALID_PACKET;
}
x = 0;
if (root == NULL) {
LTC_ARGCHK(list != NULL);
/* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */
if (in[x] != 0x30 && in[x] != 0x31) {
return CRYPT_INVALID_PACKET;
}
++x;
} else {
if (root->type != LTC_ASN1_CUSTOM_TYPE) {
return CRYPT_INVALID_PACKET;
}
/* Alloc a copy of the data for primitive handling. */
if (root->pc == LTC_ASN1_PC_PRIMITIVE) {
in_new = XMALLOC(inlen);
if (in_new == NULL) {
return CRYPT_MEM;
}
XMEMCPY(in_new, in, inlen);
in = in_new;
}
y = inlen;
if ((err = der_decode_asn1_identifier(in, &y, &ident)) != CRYPT_OK) {
goto LBL_ERR;
}
if ((ident.type != root->type) ||
(ident.klass != root->klass) ||
(ident.pc != root->pc) ||
(ident.tag != root->tag)) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
x += y;
list = root->data;
outlen = root->size;
}
if (root != NULL && root->pc == LTC_ASN1_PC_PRIMITIVE) {
if (((unsigned long)root->used >= der_asn1_type_to_identifier_map_sz) ||
(der_asn1_type_to_identifier_map[root->used] == -1)) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
root->type = (ltc_asn1_type)root->used;
list = root;
outlen = 1;
x -= 1;
in_new[x] = (unsigned char)der_asn1_type_to_identifier_map[list[0].type];
blksize = inlen - x;
} else {
y = inlen - x;
if ((err = der_decode_asn1_length(&in[x], &y, &blksize)) != CRYPT_OK) {
goto LBL_ERR;
}
x += y;
}
/* would this blksize overflow? */
if (blksize > (inlen - x)) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
/* mark all as unused */
for (i = 0; i < (int)outlen; i++) {
list[i].used = 0;
}
ordered = flags & LTC_DER_SEQ_ORDERED;
/* ok read data */
seq_err = CRYPT_OK;
blksize += x;
inlen -= x;
for (i = 0; i < (int)outlen; i++) {
z = 0;
type = list[i].type;
size = list[i].size;
data = list[i].data;
if (!ordered && list[i].used == 1) { continue; }
if (type == LTC_ASN1_EOL) {
break;
}
if (root != NULL && root->pc == LTC_ASN1_PC_PRIMITIVE && i != 0) {
err = CRYPT_PK_ASN1_ERROR;
goto LBL_ERR;
}
switch (type) {
case LTC_ASN1_BOOLEAN:
z = inlen;
if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
if ((err = der_length_boolean(&z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_INTEGER:
z = inlen;
if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
if ((err = der_length_integer(data, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SHORT_INTEGER:
z = inlen;
if ((err = der_decode_short_integer(in + x, z, data)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
if ((err = der_length_short_integer(((unsigned long*)data)[0], &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_BIT_STRING:
z = inlen;
if ((err = der_decode_bit_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_RAW_BIT_STRING:
z = inlen;
if ((err = der_decode_raw_bit_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_OCTET_STRING:
z = inlen;
if ((err = der_decode_octet_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_NULL:
if (inlen < 2 || in[x] != 0x05 || in[x+1] != 0x00) {
if (!ordered || list[i].optional) { continue; }
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
z = 2;
break;
case LTC_ASN1_OBJECT_IDENTIFIER:
z = inlen;
if ((err = der_decode_object_identifier(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_object_identifier(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_TELETEX_STRING:
z = inlen;
if ((err = der_decode_teletex_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_teletex_string(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_IA5_STRING:
z = inlen;
if ((err = der_decode_ia5_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_ia5_string(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_PRINTABLE_STRING:
z = inlen;
if ((err = der_decode_printable_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_printable_string(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_UTF8_STRING:
z = inlen;
if ((err = der_decode_utf8_string(in + x, z, data, &size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
list[i].size = size;
if ((err = der_length_utf8_string(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_UTCTIME:
z = inlen;
if ((err = der_decode_utctime(in + x, &z, data)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
break;
case LTC_ASN1_GENERALIZEDTIME:
z = inlen;
if ((err = der_decode_generalizedtime(in + x, &z, data)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
break;
case LTC_ASN1_SET:
z = inlen;
if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SETOF:
case LTC_ASN1_SEQUENCE:
/* detect if we have the right type */
if ((type == LTC_ASN1_SETOF && (in[x] & 0x3F) != 0x31) || (type == LTC_ASN1_SEQUENCE && (in[x] & 0x3F) != 0x30)) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
z = inlen;
err = der_decode_sequence_ex(in + x, z, data, size, flags);
if (err == CRYPT_INPUT_TOO_LONG) {
seq_err = CRYPT_INPUT_TOO_LONG;
err = CRYPT_OK;
}
if (err != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_CUSTOM_TYPE:
z = inlen;
err = der_decode_custom_type(in + x, z, &list[i]);
if (err == CRYPT_INPUT_TOO_LONG) {
seq_err = CRYPT_INPUT_TOO_LONG;
err = CRYPT_OK;
}
if (err != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
if ((err = der_length_custom_type(&list[i], &z, NULL)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_CHOICE:
z = inlen;
if ((err = der_decode_choice(in + x, &z, data, size)) != CRYPT_OK) {
if (!ordered || list[i].optional) { continue; }
goto LBL_ERR;
}
break;
case LTC_ASN1_EOL:
default:
err = CRYPT_INVALID_ARG;
goto LBL_ERR;
}
x += z;
inlen -= z;
list[i].used = 1;
if (!ordered) {
/* restart the decoder */
i = -1;
}
}
for (i = 0; i < (int)outlen; i++) {
if (list[i].used == 0 && list[i].optional == 0) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
}
if (blksize == x && seq_err == CRYPT_OK && inlen == 0) {
/* everything decoded and no errors in nested sequences */
err = CRYPT_OK;
} else if (blksize == x && seq_err == CRYPT_INPUT_TOO_LONG && inlen == 0) {
/* a sequence reported too-long input, but now we've decoded everything */
err = CRYPT_OK;
} else if (blksize != x && ((flags & LTC_DER_SEQ_STRICT) == LTC_DER_SEQ_STRICT)) {
err = CRYPT_INVALID_PACKET;
} else {
err = CRYPT_INPUT_TOO_LONG;
}
LBL_ERR:
if (in_new != NULL) {
XFREE(in_new);
}
return err;
}
#endif
/* ref: $Format:%D$ */
/* git commit: $Format:%H$ */
/* commit time: $Format:%ai$ */