summaryrefslogtreecommitdiff
path: root/library/asn1parse.c
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2024-10-08 13:56:50 -0600
committerTom Rini <trini@konsulko.com>2024-10-08 13:56:50 -0600
commit0344c602eadc0802776b65ff90f0a02c856cf53c (patch)
tree236a705740939b84ff37d68ae650061dd14c3449 /library/asn1parse.c
Squashed 'lib/mbedtls/external/mbedtls/' content from commit 2ca6c285a0dd
git-subtree-dir: lib/mbedtls/external/mbedtls git-subtree-split: 2ca6c285a0dd3f33982dd57299012dacab1ff206
Diffstat (limited to 'library/asn1parse.c')
-rw-r--r--library/asn1parse.c468
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, &params->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(&params, 0, sizeof(mbedtls_asn1_buf));
+
+ if ((ret = mbedtls_asn1_get_alg(p, end, alg, &params)) != 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 */