add required ASN.1 custom-types functions

This commit is contained in:
Steffen Jaeckel 2017-11-22 13:24:37 +01:00
parent 1b3a757345
commit 0d02137a8e
4 changed files with 798 additions and 0 deletions

View File

@ -617,6 +617,18 @@ int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen,
int der_length_sequence(ltc_asn1_list *list, unsigned long inlen,
unsigned long *outlen);
/* Custom-types */
int der_encode_custom_type(const ltc_asn1_list *root,
unsigned char *out, unsigned long *outlen);
int der_decode_custom_type(const unsigned char *in, unsigned long inlen,
ltc_asn1_list *root);
int der_length_custom_type(const ltc_asn1_list *root,
unsigned long *outlen,
unsigned long *payloadlen);
#ifdef LTC_SOURCE
/* internal helper functions */
int der_encode_asn1_identifier(const ltc_asn1_list *id, unsigned char *out, unsigned long *outlen);

View File

@ -0,0 +1,339 @@
/* 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_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)
{
int err, i;
ltc_asn1_type type;
ltc_asn1_list ident, *list;
unsigned long size, x, y, z, blksize, outlen;
unsigned char* in_new = NULL;
void *data;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(root != NULL);
if (root->type != LTC_ASN1_CUSTOM_TYPE) {
return CRYPT_INVALID_PACKET;
}
/* get blk size */
if (inlen < 2) {
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;
}
x = 0;
y = inlen;
if ((err = der_decode_asn1_identifier(in, &y, &ident)) != CRYPT_OK) {
goto LBL_ERR;
}
if ((ident.type != root->type) ||
(ident.class != root->class) ||
(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->pc == LTC_ASN1_PC_PRIMITIVE) {
if (der_asn1_type_to_identifier_map[list[0].type] == -1) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
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 (x + blksize > inlen) {
err = CRYPT_INVALID_PACKET;
goto LBL_ERR;
}
/* mark all as unused */
for (i = 0; i < (int)outlen; i++) {
list[i].used = 0;
}
/* ok read data */
inlen = blksize;
for (i = 0; i < (int)outlen; i++) {
z = 0;
type = list[i].type;
size = list[i].size;
data = list[i].data;
if (type == LTC_ASN1_EOL) {
break;
}
if (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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
goto LBL_ERR;
}
break;
case LTC_ASN1_GENERALIZEDTIME:
z = inlen;
if ((err = der_decode_generalizedtime(in + x, &z, data)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SET:
z = inlen;
if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) {
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;
if ((err = der_decode_sequence(in + x, z, data, size)) != CRYPT_OK) {
goto LBL_ERR;
}
if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_CUSTOM_TYPE:
z = inlen;
if ((err = der_decode_custom_type(in + x, z, &list[i])) != CRYPT_OK) {
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) {
goto LBL_ERR;
}
break;
case LTC_ASN1_CONSTRUCTED:
case LTC_ASN1_CONTEXT_SPECIFIC:
case LTC_ASN1_EOL:
err = CRYPT_INVALID_ARG;
goto LBL_ERR;
}
x += z;
inlen -= z;
list[i].used = 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 (inlen == 0) {
err = CRYPT_OK;
} 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$ */

View File

@ -0,0 +1,234 @@
/* 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_custom_type.c
ASN.1 DER, encode a Custom Type, Steffen Jaeckel
*/
#ifdef LTC_DER
/**
Encode a Custom Type
This function is a bit special compared to the others, as it requires the
root-ltc_asn1_list where the type is defined.
@param root The root of the list of items to encode
@param out [out] The destination
@param outlen [in/out] The size of the output
@return CRYPT_OK on success
*/
int der_encode_custom_type(const ltc_asn1_list *root,
unsigned char *out, unsigned long *outlen)
{
int err;
ltc_asn1_type type;
ltc_asn1_list *list;
unsigned long size, x, y, z, i, inlen, id_len;
void *data;
LTC_ARGCHK(root != NULL);
LTC_ARGCHK(out != NULL);
LTC_ARGCHK(outlen != NULL);
/* get size of output that will be required */
y = 0; z = 0;
if ((err = der_length_custom_type(root, &y, &z)) != CRYPT_OK) return CRYPT_INVALID_ARG;
/* too big ? */
if (*outlen < y) {
*outlen = y;
err = CRYPT_BUFFER_OVERFLOW;
goto LBL_ERR;
}
/* get length of the identifier, so we know the offset where to start writing */
if ((err = der_length_asn1_identifier(root, &id_len)) != CRYPT_OK) return CRYPT_INVALID_ARG;
x = id_len;
if (root->pc == LTC_ASN1_PC_PRIMITIVE) {
/* In case it's a PRIMITIVE type we encode directly to the output
* but leave space for a potentially longer identifier as it will
* simply be replaced afterwards.
*/
x -= 1;
} else {
/* store length, identifier will be added later */
y = *outlen - x;
if ((err = der_encode_asn1_length(z, &out[x], &y)) != CRYPT_OK) {
goto LBL_ERR;
}
x += y;
}
list = root->data;
inlen = root->size;
/* store data */
*outlen -= x;
for (i = 0; i < inlen; i++) {
type = list[i].type;
size = list[i].size;
data = list[i].data;
if (type == LTC_ASN1_EOL) {
break;
}
switch (type) {
case LTC_ASN1_BOOLEAN:
z = *outlen;
if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_INTEGER:
z = *outlen;
if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SHORT_INTEGER:
z = *outlen;
if ((err = der_encode_short_integer(*((unsigned long*)data), out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_BIT_STRING:
z = *outlen;
if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_RAW_BIT_STRING:
z = *outlen;
if ((err = der_encode_raw_bit_string(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_OCTET_STRING:
z = *outlen;
if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_NULL:
out[x] = 0x05;
out[x+1] = 0x00;
z = 2;
break;
case LTC_ASN1_OBJECT_IDENTIFIER:
z = *outlen;
if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_IA5_STRING:
z = *outlen;
if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_PRINTABLE_STRING:
z = *outlen;
if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_UTF8_STRING:
z = *outlen;
if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_UTCTIME:
z = *outlen;
if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_GENERALIZEDTIME:
z = *outlen;
if ((err = der_encode_generalizedtime(data, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SET:
z = *outlen;
if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SETOF:
z = *outlen;
if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_SEQUENCE:
z = *outlen;
if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_CUSTOM_TYPE:
z = *outlen;
if ((err = der_encode_custom_type(&list[i], out + x, &z)) != CRYPT_OK) {
goto LBL_ERR;
}
break;
case LTC_ASN1_CHOICE:
case LTC_ASN1_CONSTRUCTED:
case LTC_ASN1_CONTEXT_SPECIFIC:
case LTC_ASN1_EOL:
case LTC_ASN1_TELETEX_STRING:
err = CRYPT_INVALID_ARG;
goto LBL_ERR;
}
x += z;
*outlen -= z;
}
if ((err = der_encode_asn1_identifier(root, out, &id_len)) != CRYPT_OK) {
goto LBL_ERR;
}
*outlen = x;
err = CRYPT_OK;
LBL_ERR:
return err;
}
#endif
/* ref: $Format:%D$ */
/* git commit: $Format:%H$ */
/* commit time: $Format:%ai$ */

View File

@ -0,0 +1,213 @@
/* 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_custom_type.c
ASN.1 DER, length of a custom type, Steffen Jaeckel
*/
#ifdef LTC_DER
/**
Get the length of a DER custom type
This function is a bit special compared to the others, as it requires the
root-ltc_asn1_list where the type is defined.
@param root The root of the struct to encode
@param outlen [out] The length required in octets to store it
@param payloadlen [out] The length of the payload in octets
@return CRYPT_OK on success
*/
int der_length_custom_type(const ltc_asn1_list *root, unsigned long *outlen, unsigned long *payloadlen)
{
int err;
ltc_asn1_list *list;
ltc_asn1_type type;
unsigned long size, x, y, i, inlen, id_len;
void *data;
LTC_ARGCHK(root != NULL);
LTC_ARGCHK(outlen != NULL);
if ((root->pc == LTC_ASN1_PC_PRIMITIVE) && (root->size != 1)) {
/* In case it's a PRIMITIVE element there has to be
* exactly one element following.
*/
return CRYPT_INVALID_PACKET;
}
/* get size of output that will be required */
if ((err = der_length_asn1_identifier(root, &id_len)) != CRYPT_OK) {
return err;
}
y = id_len;
inlen = root->size;
list = root->data;
for (i = 0; i < inlen; i++) {
type = list[i].type;
size = list[i].size;
data = list[i].data;
if (type == LTC_ASN1_EOL) {
break;
}
/* some items may be optional during import */
if (!list[i].used && list[i].optional) continue;
switch (type) {
case LTC_ASN1_BOOLEAN:
if ((err = der_length_boolean(&x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_INTEGER:
if ((err = der_length_integer(data, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_SHORT_INTEGER:
if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_BIT_STRING:
case LTC_ASN1_RAW_BIT_STRING:
if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_OCTET_STRING:
if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_NULL:
y += 2;
break;
case LTC_ASN1_OBJECT_IDENTIFIER:
if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_IA5_STRING:
if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_TELETEX_STRING:
if ((err = der_length_teletex_string(data, size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_PRINTABLE_STRING:
if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_UTCTIME:
if ((err = der_length_utctime(data, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_GENERALIZEDTIME:
if ((err = der_length_generalizedtime(data, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_UTF8_STRING:
if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_CUSTOM_TYPE:
if ((err = der_length_custom_type(&list[i], &x, NULL)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_SET:
case LTC_ASN1_SETOF:
case LTC_ASN1_SEQUENCE:
if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
y += x;
break;
case LTC_ASN1_CHOICE:
case LTC_ASN1_CONSTRUCTED:
case LTC_ASN1_CONTEXT_SPECIFIC:
case LTC_ASN1_EOL:
err = CRYPT_INVALID_ARG;
goto LBL_ERR;
}
}
if (root->pc == LTC_ASN1_PC_PRIMITIVE) {
/* In case it's a PRIMITIVE element we're going
* to only replace the identifier of the one element
* by the custom identifier.
*/
y -= 1;
if (payloadlen != NULL) {
*payloadlen = y - id_len;
}
} else {
/* calc length of length */
if ((err = der_length_asn1_length(y, &x)) != CRYPT_OK) {
goto LBL_ERR;
}
if (payloadlen != NULL) {
*payloadlen = y - id_len;
}
y += x;
}
/* store size */
*outlen = y;
LBL_ERR:
return err;
}
#endif
/* ref: $Format:%D$ */
/* git commit: $Format:%H$ */
/* commit time: $Format:%ai$ */