From 92337c0e620d2162e6fb7f0dbaec286670beefc2 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 18 Jan 2023 18:40:49 +0000 Subject: [PATCH] Add function to parse an OID from a string Signed-off-by: David Horstmann --- include/mbedtls/oid.h | 14 ++++ library/oid.c | 164 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) diff --git a/include/mbedtls/oid.h b/include/mbedtls/oid.h index a592e63c4..1284aa929 100644 --- a/include/mbedtls/oid.h +++ b/include/mbedtls/oid.h @@ -466,6 +466,20 @@ typedef struct mbedtls_oid_descriptor_t { */ int mbedtls_oid_get_numeric_string(char *buf, size_t size, const mbedtls_asn1_buf *oid); +/** + * \brief Translate a string containing a numeric representation + * of an ASN.1 OID into its encoded form + * (e.g. "1.2.840.113549" into "\x2A\x86\x48\x86\xF7\x0D") + * + * \param buf buffer to put representation in + * \param size size of the buffer + * \param oid OID to translate + * + * \return Length of the string written (excluding final NULL) or + * MBEDTLS_ERR_OID_BUF_TOO_SMALL in case of error + */ +int mbedtls_oid_from_numeric_string(mbedtls_asn1_buf *oid, const char *buf, size_t size); + /** * \brief Translate an X.509 extension OID into local values * diff --git a/library/oid.c b/library/oid.c index 86214b23a..9b8bef6de 100644 --- a/library/oid.c +++ b/library/oid.c @@ -895,4 +895,168 @@ int mbedtls_oid_get_numeric_string(char *buf, size_t size, return (int) (size - n); } +static int oid_parse_number(const char **p, const char *bound) +{ + int ret = MBEDTLS_ERR_ASN1_INVALID_DATA; + int num = 0; + while (*p < bound && **p >= '0' && **p <= '9') { + ret = 0; + if (num > (INT_MAX / 10)) { + return MBEDTLS_ERR_OID_BUF_TOO_SMALL; + } + num *= 10; + num += **p - '0'; + (*p)++; + } + if (ret != 0) { + return ret; + } else { + return num; + } +} + +static size_t oid_subidentifier_num_bytes(unsigned int value) +{ + size_t num_bytes = 1; + value >>= 7; + while (value != 0) { + num_bytes++; + value >>= 7; + } + return num_bytes; +} + +static int oid_subidentifier_encode_into(unsigned char **p, + unsigned char *bound, + unsigned int value) +{ + size_t num_bytes = oid_subidentifier_num_bytes(value); + if ((size_t) (bound - *p) < num_bytes) { + return MBEDTLS_ERR_OID_BUF_TOO_SMALL; + } + (*p)[num_bytes - 1] = (unsigned char) (value & 0x7f); + value >>= 7; + + for (size_t i = 2; i <= num_bytes; i++) { + (*p)[num_bytes - i] = 0x80 | (unsigned char) (value & 0x7f); + value >>= 7; + } + *p += num_bytes; + + return 0; +} + +/* Return the OID for the given x.y.z.... style numeric string */ +int mbedtls_oid_from_numeric_string(mbedtls_asn1_buf *oid, + const char *buf, size_t size) +{ + int ret = MBEDTLS_ERR_ASN1_INVALID_DATA; + + const char *str_ptr = buf; + const char *str_bound = buf + size; + int val = 0; + size_t encoded_len; + + int component1 = oid_parse_number(&str_ptr, str_bound); + if (component1 < 0) { + return component1; + } + if (component1 > 2) { + /* First component can't be > 2 */ + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + if (str_ptr >= str_bound || *str_ptr != '.') { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + str_ptr++; + + int component2 = oid_parse_number(&str_ptr, str_bound); + if (component2 < 0) { + return component2; + } + if ((component1 < 2) && (component2 > 38)) { + /* Root nodes 0 and 1 may have up to 39 children */ + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + if (str_ptr < str_bound && *str_ptr != '\0') { + if (*str_ptr == '.') { + str_ptr++; + } else { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + } + + if ((UINT_MAX - (unsigned int) component2) <= + ((unsigned int) component1 * 40)) { + return MBEDTLS_ERR_OID_BUF_TOO_SMALL; + } + encoded_len = oid_subidentifier_num_bytes(((unsigned int) component1 * 40) + + (unsigned int) component2); + + while (str_ptr < str_bound && *str_ptr != '\0') { + val = oid_parse_number(&str_ptr, str_bound); + if (val < 0) { + return val; + } + if (str_ptr < str_bound && *str_ptr != '\0') { + if (*str_ptr == '.') { + str_ptr++; + } else { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + } + + size_t num_bytes = oid_subidentifier_num_bytes(val); + if ((SIZE_MAX - encoded_len) <= num_bytes) { + return MBEDTLS_ERR_OID_BUF_TOO_SMALL; + } + encoded_len += num_bytes; + } + + oid->p = mbedtls_calloc(encoded_len, sizeof(unsigned char)); + if (oid->p == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + oid->len = encoded_len; + + /* Now that we've allocated the buffer, go back to the start and encode */ + str_ptr = buf; + unsigned char *out_ptr = oid->p; + unsigned char *out_bound = oid->p + oid->len; + + /* No need to do validation this time, as we did it on the first pass */ + component1 = oid_parse_number(&str_ptr, str_bound); + /* Skip past the '.' */ + str_ptr++; + component2 = oid_parse_number(&str_ptr, str_bound); + /* Skip past the '.' */ + str_ptr++; + ret = oid_subidentifier_encode_into(&out_ptr, out_bound, + (component1 * 40) + component2); + if (ret != 0) { + mbedtls_free(oid->p); + oid->p = NULL; + oid->len = 0; + return ret; + } + while (str_ptr < str_bound && *str_ptr != '\0') { + val = oid_parse_number(&str_ptr, str_bound); + if (str_ptr < str_bound && *str_ptr == '.') { + /* Skip past the '.' */ + str_ptr++; + } + + ret = oid_subidentifier_encode_into(&out_ptr, out_bound, val); + if (ret != 0) { + mbedtls_free(oid->p); + oid->p = NULL; + oid->len = 0; + return ret; + } + } + oid->tag = MBEDTLS_ASN1_OID; + + return 0; +} + #endif /* MBEDTLS_OID_C */