From 17d280195a09717f6ac5ce86bf8905ff1a0b4366 Mon Sep 17 00:00:00 2001 From: Ankit-cs Date: Mon, 19 Oct 2026 16:28:44 +0530 Subject: [PATCH] Support OID subidentifiers wider than 32 bits when decoding mbedtls_oid_get_numeric_string previously capped OID subidentifiers at UINT_MAX, returning MBEDTLS_ERR_ASN1_INVALID_DATA for valid, conformant OIDs carrying wider values (such as standard ITU-T X.667 128-bit UUID arcs or arcs > 2^32). Add multi-precision base-128 to base-10 conversion for subidentifiers exceeding 32 bits while retaining the fast integer path for standard subidentifiers. Update test suite with wide subidentifiers including the ITU-T X.667 UUID test vector. Fixes #11001. Signed-off-by: Ankit-cs --- .../oid-decode-large-subidentifier.txt | 3 + library/x509.c | 142 +++++++++++++++--- tests/suites/test_suite_x509parse.data | 19 ++- 3 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 ChangeLog.d/oid-decode-large-subidentifier.txt diff --git a/ChangeLog.d/oid-decode-large-subidentifier.txt b/ChangeLog.d/oid-decode-large-subidentifier.txt new file mode 100644 index 0000000000..473e4b8e31 --- /dev/null +++ b/ChangeLog.d/oid-decode-large-subidentifier.txt @@ -0,0 +1,3 @@ +Bugfix + * In mbedtls_oid_get_numeric_string(), allow OID subidentifiers wider + than 32 bits instead of rejecting them as invalid ASN.1 data. Fixes #11001. diff --git a/library/x509.c b/library/x509.c index d7bc5d2dfb..bef938b52d 100644 --- a/library/x509.c +++ b/library/x509.c @@ -815,6 +815,77 @@ static char nibble_to_hex_digit(int i) return (i < 10) ? (i + '0') : (i - 10 + 'A'); } +#define OID_MAX_SUBID_LEN 64 + +/* Convert a base-128 encoded OID subidentifier to decimal digits. + * If subtract > 0, subtract it first (used for the first subidentifier where + * value >= 80: value = 2 * 40 + c2 => c2 = value - 80). + * Returns the number of characters written on success, or a negative error code. */ +static int oid_subid_to_dec(char *buf, size_t size, + const unsigned char *subid, size_t len, + unsigned int subtract) +{ + unsigned char digits[OID_MAX_SUBID_LEN]; + char dec_buf[OID_MAX_SUBID_LEN * 3]; + size_t dec_count = 0; + size_t start = 0; + + if (len > OID_MAX_SUBID_LEN) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + + for (size_t i = 0; i < len; i++) { + digits[i] = subid[i] & 0x7F; + } + + if (subtract > 0) { + unsigned int borrow = subtract; + for (size_t i = len; i > 0; i--) { + if (digits[i - 1] >= borrow) { + digits[i - 1] -= (unsigned char) borrow; + borrow = 0; + break; + } else { + digits[i - 1] = (unsigned char) (digits[i - 1] + 128 - borrow); + borrow = 1; + } + } + } + + while (start < len && digits[start] == 0) { + start++; + } + + if (start == len) { + dec_buf[dec_count++] = '0'; + } else { + while (start < len) { + unsigned int rem = 0; + for (size_t i = start; i < len; i++) { + unsigned int cur = (rem << 7) | digits[i]; + digits[i] = (unsigned char) (cur / 10); + rem = cur % 10; + } + dec_buf[dec_count++] = (char) ('0' + rem); + while (start < len && digits[start] == 0) { + start++; + } + } + } + + /* Check if buf can hold the digits plus null terminator */ + if (dec_count >= size) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + for (size_t i = 0; i < dec_count; i++) { + buf[i] = dec_buf[dec_count - 1 - i]; + } + buf[dec_count] = '\0'; + + return (int) dec_count; +} + /* Return the x.y.z.... style numeric string for the given OID */ int mbedtls_oid_get_numeric_string(char *buf, size_t size, const mbedtls_asn1_buf *oid) @@ -822,7 +893,7 @@ int mbedtls_oid_get_numeric_string(char *buf, size_t size, int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; char *p = buf; size_t n = size; - unsigned int value = 0; + size_t i = 0; if (size > INT_MAX) { /* Avoid overflow computing return value */ @@ -834,22 +905,41 @@ int mbedtls_oid_get_numeric_string(char *buf, size_t size, return MBEDTLS_ERR_ASN1_OUT_OF_DATA; } - for (size_t i = 0; i < oid->len; i++) { - /* Prevent overflow in value. */ - if (value > (UINT_MAX >> 7)) { - return MBEDTLS_ERR_ASN1_INVALID_DATA; - } - if ((value == 0) && ((oid->p[i]) == 0x80)) { - /* Overlong encoding is not allowed */ + while (i < oid->len) { + size_t subid_start = i; + size_t subid_len; + unsigned int value = 0; + int overflow = 0; + int is_first = (n == size); + + /* Overlong encoding is not allowed */ + if (oid->p[subid_start] == 0x80) { return MBEDTLS_ERR_ASN1_INVALID_DATA; } - value <<= 7; - value |= oid->p[i] & 0x7F; + while (i < oid->len && (oid->p[i] & 0x80) != 0) { + i++; + } - if (!(oid->p[i] & 0x80)) { - /* Last byte */ - if (n == size) { + if (i == oid->len) { + /* Unterminated subidentifier */ + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + subid_len = i - subid_start + 1; + i++; + + /* Try computing value in unsigned int (fast path) */ + for (size_t j = subid_start; j < subid_start + subid_len; j++) { + if (value > (UINT_MAX >> 7)) { + overflow = 1; + break; + } + value = (value << 7) | (oid->p[j] & 0x7F); + } + + if (!overflow) { + if (is_first) { int component1; unsigned int component2; /* First subidentifier contains first two OID components */ @@ -872,14 +962,30 @@ int mbedtls_oid_get_numeric_string(char *buf, size_t size, } n -= (size_t) ret; p += ret; - value = 0; + } else { + size_t prefix_len = is_first ? 2 : 1; + if (n <= prefix_len) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + if (is_first) { + *p++ = '2'; + *p++ = '.'; + } else { + *p++ = '.'; + } + n -= prefix_len; + + ret = oid_subid_to_dec(p, n, &oid->p[subid_start], subid_len, + is_first ? 80 : 0); + if (ret < 0) { + return ret; + } + p += ret; + n -= (size_t) ret; } } - if (value != 0) { - /* Unterminated subidentifier */ - return MBEDTLS_ERR_ASN1_OUT_OF_DATA; - } + *p = '\0'; return (int) (size - n); } diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 6a8e30f524..3ff7d8446b 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2773,8 +2773,8 @@ x509_oid_numstr:"2b06010505070301":"1.3.6.1.5.5.7.3.1":17:PSA_ERROR_BUFFER_TOO_S X509 OID numstring #4 (larger number) x509_oid_numstr:"2a864886f70d":"1.2.840.113549":15:14 -X509 OID numstring #5 (arithmetic overflow) -x509_oid_numstr:"2a8648f9f8f7f6f5f4f3f2f1f001":"":100:MBEDTLS_ERR_ASN1_INVALID_DATA +X509 OID numstring #5 (subidentifier > 32 bits) +x509_oid_numstr:"2a8648f9f8f7f6f5f4f3f2f1f001":"1.2.840.143967032551602640418817":100:32 X509 CRT keyUsage #1 (no extension, expected KU) depends_on:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY:PSA_WANT_ALG_SHA_1 @@ -3507,15 +3507,20 @@ oid_get_numeric_string:"50":0:"2.0" OID get numeric string - 1 byte first subidentifier beyond 2.39 oid_get_numeric_string:"7f":0:"2.47" -# Encodes the number 0x0400000000 as a subidentifier which overflows 32-bits -OID get numeric string - 32-bit overflow -oid_get_numeric_string:"C080808000":MBEDTLS_ERR_ASN1_INVALID_DATA:"" +# Encodes the number 0x0400000000 (17179869184) as a subidentifier (> 32 bits) +OID get numeric string - subidentifier > 32-bit first component +oid_get_numeric_string:"C080808000":0:"2.17179869104" -OID get numeric string - 32-bit overflow, second subidentifier -oid_get_numeric_string:"2BC080808000":MBEDTLS_ERR_ASN1_INVALID_DATA:"" +OID get numeric string - subidentifier > 32-bit second component +oid_get_numeric_string:"2BC080808000":0:"1.3.17179869184" + +# ITU-T X.667 clause 8 UUID arc: 2.25.329800735698586629295641978511506172918 +OID get numeric string - 128-bit UUID subidentifier (X.667) +oid_get_numeric_string:"6983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D776":0:"2.25.329800735698586629295641978511506172918" OID get numeric string - overlong encoding oid_get_numeric_string:"8001":MBEDTLS_ERR_ASN1_INVALID_DATA:"" OID get numeric string - overlong encoding, second subidentifier oid_get_numeric_string:"2B8001":MBEDTLS_ERR_ASN1_INVALID_DATA:"" +