4cc6522a85
Now that we have padding verification after decryption and since this can be used to validate the password as well there is no need to parse ASN1 content any more, so we can simplify/remove that dependency. Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
468 lines
11 KiB
C
468 lines
11 KiB
C
/*
|
|
* 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 */
|