diff options
Diffstat (limited to 'library/asn1parse.c')
-rw-r--r-- | library/asn1parse.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/library/asn1parse.c b/library/asn1parse.c new file mode 100644 index 00000000000..e33fdf71da6 --- /dev/null +++ b/library/asn1parse.c @@ -0,0 +1,468 @@ +/* + * Generic ASN.1 parsing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include "common.h" + +#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_X509_CREATE_C) || \ + defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA) + +#include "mbedtls/asn1.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include <string.h> + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#include "mbedtls/platform.h" + +/* + * ASN.1 DER decoding routines + */ +int mbedtls_asn1_get_len(unsigned char **p, + const unsigned char *end, + size_t *len) +{ + if ((end - *p) < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + if ((**p & 0x80) == 0) { + *len = *(*p)++; + } else { + int n = (**p) & 0x7F; + if (n == 0 || n > 4) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + if ((end - *p) <= n) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + *len = 0; + (*p)++; + while (n--) { + *len = (*len << 8) | **p; + (*p)++; + } + } + + if (*len > (size_t) (end - *p)) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + return 0; +} + +int mbedtls_asn1_get_tag(unsigned char **p, + const unsigned char *end, + size_t *len, int tag) +{ + if ((end - *p) < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + if (**p != tag) { + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG; + } + + (*p)++; + + return mbedtls_asn1_get_len(p, end, len); +} +#endif /* MBEDTLS_ASN1_PARSE_C || MBEDTLS_X509_CREATE_C || MBEDTLS_PSA_UTIL_HAVE_ECDSA */ + +#if defined(MBEDTLS_ASN1_PARSE_C) +int mbedtls_asn1_get_bool(unsigned char **p, + const unsigned char *end, + int *val) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_BOOLEAN)) != 0) { + return ret; + } + + if (len != 1) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + *val = (**p != 0) ? 1 : 0; + (*p)++; + + return 0; +} + +static int asn1_get_tagged_int(unsigned char **p, + const unsigned char *end, + int tag, int *val) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, tag)) != 0) { + return ret; + } + + /* + * len==0 is malformed (0 must be represented as 020100 for INTEGER, + * or 0A0100 for ENUMERATED tags + */ + if (len == 0) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + /* This is a cryptography library. Reject negative integers. */ + if ((**p & 0x80) != 0) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + /* Skip leading zeros. */ + while (len > 0 && **p == 0) { + ++(*p); + --len; + } + + /* Reject integers that don't fit in an int. This code assumes that + * the int type has no padding bit. */ + if (len > sizeof(int)) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + if (len == sizeof(int) && (**p & 0x80) != 0) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + *val = 0; + while (len-- > 0) { + *val = (*val << 8) | **p; + (*p)++; + } + + return 0; +} + +int mbedtls_asn1_get_int(unsigned char **p, + const unsigned char *end, + int *val) +{ + return asn1_get_tagged_int(p, end, MBEDTLS_ASN1_INTEGER, val); +} + +int mbedtls_asn1_get_enum(unsigned char **p, + const unsigned char *end, + int *val) +{ + return asn1_get_tagged_int(p, end, MBEDTLS_ASN1_ENUMERATED, val); +} + +#if defined(MBEDTLS_BIGNUM_C) +int mbedtls_asn1_get_mpi(unsigned char **p, + const unsigned char *end, + mbedtls_mpi *X) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) { + return ret; + } + + ret = mbedtls_mpi_read_binary(X, *p, len); + + *p += len; + + return ret; +} +#endif /* MBEDTLS_BIGNUM_C */ + +int mbedtls_asn1_get_bitstring(unsigned char **p, const unsigned char *end, + mbedtls_asn1_bitstring *bs) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Certificate type is a single byte bitstring */ + if ((ret = mbedtls_asn1_get_tag(p, end, &bs->len, MBEDTLS_ASN1_BIT_STRING)) != 0) { + return ret; + } + + /* Check length, subtract one for actual bit string length */ + if (bs->len < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + bs->len -= 1; + + /* Get number of unused bits, ensure unused bits <= 7 */ + bs->unused_bits = **p; + if (bs->unused_bits > 7) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + (*p)++; + + /* Get actual bitstring */ + bs->p = *p; + *p += bs->len; + + if (*p != end) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + return 0; +} + +/* + * Traverse an ASN.1 "SEQUENCE OF <tag>" + * and call a callback for each entry found. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + unsigned char tag_must_mask, unsigned char tag_must_val, + unsigned char tag_may_mask, unsigned char tag_may_val, + int (*cb)(void *ctx, int tag, + unsigned char *start, size_t len), + void *ctx) +{ + int ret; + size_t len; + + /* Get main sequence tag */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + if (*p + len != end) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + while (*p < end) { + unsigned char const tag = *(*p)++; + + if ((tag & tag_must_mask) != tag_must_val) { + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG; + } + + if ((ret = mbedtls_asn1_get_len(p, end, &len)) != 0) { + return ret; + } + + if ((tag & tag_may_mask) == tag_may_val) { + if (cb != NULL) { + ret = cb(ctx, tag, *p, len); + if (ret != 0) { + return ret; + } + } + } + + *p += len; + } + + return 0; +} + +/* + * Get a bit string without unused bits + */ +int mbedtls_asn1_get_bitstring_null(unsigned char **p, const unsigned char *end, + size_t *len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_tag(p, end, len, MBEDTLS_ASN1_BIT_STRING)) != 0) { + return ret; + } + + if (*len == 0) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + --(*len); + + if (**p != 0) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + ++(*p); + + return 0; +} + +void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq) +{ + while (seq != NULL) { + mbedtls_asn1_sequence *next = seq->next; + mbedtls_free(seq); + seq = next; + } +} + +typedef struct { + int tag; + mbedtls_asn1_sequence *cur; +} asn1_get_sequence_of_cb_ctx_t; + +static int asn1_get_sequence_of_cb(void *ctx, + int tag, + unsigned char *start, + size_t len) +{ + asn1_get_sequence_of_cb_ctx_t *cb_ctx = + (asn1_get_sequence_of_cb_ctx_t *) ctx; + mbedtls_asn1_sequence *cur = + cb_ctx->cur; + + if (cur->buf.p != NULL) { + cur->next = + mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + + if (cur->next == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + + cur = cur->next; + } + + cur->buf.p = start; + cur->buf.len = len; + cur->buf.tag = tag; + + cb_ctx->cur = cur; + return 0; +} + +/* + * Parses and splits an ASN.1 "SEQUENCE OF <tag>" + */ +int mbedtls_asn1_get_sequence_of(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_sequence *cur, + int tag) +{ + asn1_get_sequence_of_cb_ctx_t cb_ctx = { tag, cur }; + memset(cur, 0, sizeof(mbedtls_asn1_sequence)); + return mbedtls_asn1_traverse_sequence_of( + p, end, 0xFF, tag, 0, 0, + asn1_get_sequence_of_cb, &cb_ctx); +} + +int mbedtls_asn1_get_alg(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + if ((end - *p) < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + alg->tag = **p; + end = *p + len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &alg->len, MBEDTLS_ASN1_OID)) != 0) { + return ret; + } + + alg->p = *p; + *p += alg->len; + + if (*p == end) { + mbedtls_platform_zeroize(params, sizeof(mbedtls_asn1_buf)); + return 0; + } + + params->tag = **p; + (*p)++; + + if ((ret = mbedtls_asn1_get_len(p, end, ¶ms->len)) != 0) { + return ret; + } + + params->p = *p; + *p += params->len; + + if (*p != end) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + return 0; +} + +int mbedtls_asn1_get_alg_null(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_asn1_buf params; + + memset(¶ms, 0, sizeof(mbedtls_asn1_buf)); + + if ((ret = mbedtls_asn1_get_alg(p, end, alg, ¶ms)) != 0) { + return ret; + } + + if ((params.tag != MBEDTLS_ASN1_NULL && params.tag != 0) || params.len != 0) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + + return 0; +} + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_asn1_free_named_data(mbedtls_asn1_named_data *cur) +{ + if (cur == NULL) { + return; + } + + mbedtls_free(cur->oid.p); + mbedtls_free(cur->val.p); + + mbedtls_platform_zeroize(cur, sizeof(mbedtls_asn1_named_data)); +} +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +void mbedtls_asn1_free_named_data_list(mbedtls_asn1_named_data **head) +{ + mbedtls_asn1_named_data *cur; + + while ((cur = *head) != NULL) { + *head = cur->next; + mbedtls_free(cur->oid.p); + mbedtls_free(cur->val.p); + mbedtls_free(cur); + } +} + +void mbedtls_asn1_free_named_data_list_shallow(mbedtls_asn1_named_data *name) +{ + for (mbedtls_asn1_named_data *next; name != NULL; name = next) { + next = name->next; + mbedtls_free(name); + } +} + +const mbedtls_asn1_named_data *mbedtls_asn1_find_named_data(const mbedtls_asn1_named_data *list, + const char *oid, size_t len) +{ + while (list != NULL) { + if (list->oid.len == len && + memcmp(list->oid.p, oid, len) == 0) { + break; + } + + list = list->next; + } + + return list; +} + +#endif /* MBEDTLS_ASN1_PARSE_C */ |