Merge pull request #7106 from davidhorstmann-arm/parse-oid-from-string

Parse an OID from a string
This commit is contained in:
Gilles Peskine 2023-06-06 20:57:17 +02:00 committed by GitHub
commit d598eaf212
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 277 additions and 0 deletions

View file

@ -0,0 +1,3 @@
Features
* Add function mbedtls_oid_from_numeric_string() to parse an OID from a
string to a DER-encoded mbedtls_asn1_buf.

View file

@ -63,6 +63,11 @@
#define MBEDTLS_OID_X509_EXT_FRESHEST_CRL (1 << 14)
#define MBEDTLS_OID_X509_EXT_NS_CERT_TYPE (1 << 16)
/*
* Maximum number of OID components allowed
*/
#define MBEDTLS_OID_MAX_COMPONENTS 128
/*
* Top level OID tuples
*/
@ -478,6 +483,25 @@ 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 dotted-decimal
* representation of an ASN.1 OID into its encoded form
* (e.g. "1.2.840.113549" into "\x2A\x86\x48\x86\xF7\x0D").
* On success, this function allocates oid->buf from the
* heap. It must be freed by the caller using mbedtls_free().
*
* \param oid #mbedtls_asn1_buf to populate with the DER-encoded OID
* \param oid_str string representation of the OID to parse
* \param size length of the OID string, not including any null terminator
*
* \return 0 if successful
* \return #MBEDTLS_ERR_ASN1_INVALID_DATA if \p oid_str does not
* represent a valid OID
* \return #MBEDTLS_ERR_ASN1_ALLOC_FAILED if the function fails to
* allocate oid->buf
*/
int mbedtls_oid_from_numeric_string(mbedtls_asn1_buf *oid, const char *oid_str, size_t size);
/**
* \brief Translate an X.509 extension OID into local values
*

View file

@ -933,4 +933,180 @@ int mbedtls_oid_get_numeric_string(char *buf, size_t size,
return (int) (size - n);
}
static int oid_parse_number(unsigned int *num, const char **p, const char *bound)
{
int ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
*num = 0;
while (*p < bound && **p >= '0' && **p <= '9') {
ret = 0;
if (*num > (UINT_MAX / 10)) {
return MBEDTLS_ERR_ASN1_INVALID_DATA;
}
*num *= 10;
*num += **p - '0';
(*p)++;
}
return ret;
}
static size_t oid_subidentifier_num_bytes(unsigned int value)
{
size_t num_bytes = 0;
do {
value >>= 7;
num_bytes++;
} while (value != 0);
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 *oid_str, size_t size)
{
int ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
const char *str_ptr = oid_str;
const char *str_bound = oid_str + size;
unsigned int val = 0;
unsigned int component1, component2;
size_t encoded_len;
unsigned char *resized_mem;
/* Count the number of dots to get a worst-case allocation size. */
size_t num_dots = 0;
for (size_t i = 0; i < size; i++) {
if (oid_str[i] == '.') {
num_dots++;
}
}
/* Allocate maximum possible required memory:
* There are (num_dots + 1) integer components, but the first 2 share the
* same subidentifier, so we only need num_dots subidentifiers maximum. */
if (num_dots == 0 || (num_dots > MBEDTLS_OID_MAX_COMPONENTS - 1)) {
return MBEDTLS_ERR_ASN1_INVALID_DATA;
}
/* Each byte can store 7 bits, calculate number of bytes for a
* subidentifier:
*
* bytes = ceil(subidentifer_size * 8 / 7)
*/
size_t bytes_per_subidentifier = (((sizeof(unsigned int) * 8) - 1) / 7)
+ 1;
size_t max_possible_bytes = num_dots * bytes_per_subidentifier;
oid->p = mbedtls_calloc(max_possible_bytes, 1);
if (oid->p == NULL) {
return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
}
unsigned char *out_ptr = oid->p;
unsigned char *out_bound = oid->p + max_possible_bytes;
ret = oid_parse_number(&component1, &str_ptr, str_bound);
if (ret != 0) {
goto error;
}
if (component1 > 2) {
/* First component can't be > 2 */
ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
goto error;
}
if (str_ptr >= str_bound || *str_ptr != '.') {
ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
goto error;
}
str_ptr++;
ret = oid_parse_number(&component2, &str_ptr, str_bound);
if (ret != 0) {
goto error;
}
if ((component1 < 2) && (component2 > 39)) {
/* Root nodes 0 and 1 may have up to 40 children, numbered 0-39 */
ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
goto error;
}
if (str_ptr < str_bound) {
if (*str_ptr == '.') {
str_ptr++;
} else {
ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
goto error;
}
}
if (component2 > (UINT_MAX - (component1 * 40))) {
ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
goto error;
}
ret = oid_subidentifier_encode_into(&out_ptr, out_bound,
(component1 * 40) + component2);
if (ret != 0) {
goto error;
}
while (str_ptr < str_bound) {
ret = oid_parse_number(&val, &str_ptr, str_bound);
if (ret != 0) {
goto error;
}
if (str_ptr < str_bound) {
if (*str_ptr == '.') {
str_ptr++;
} else {
ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
goto error;
}
}
ret = oid_subidentifier_encode_into(&out_ptr, out_bound, val);
if (ret != 0) {
goto error;
}
}
encoded_len = out_ptr - oid->p;
resized_mem = mbedtls_calloc(encoded_len, 1);
if (resized_mem == NULL) {
ret = MBEDTLS_ERR_ASN1_ALLOC_FAILED;
goto error;
}
memcpy(resized_mem, oid->p, encoded_len);
mbedtls_free(oid->p);
oid->p = resized_mem;
oid->len = encoded_len;
oid->tag = MBEDTLS_ASN1_OID;
return 0;
error:
mbedtls_free(oid->p);
oid->p = NULL;
oid->len = 0;
return ret;
}
#endif /* MBEDTLS_OID_C */

View file

@ -137,3 +137,51 @@ 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:""
OID from numeric string - hardware module name
oid_from_numeric_string:"1.3.6.1.5.5.7.8.4":0:"2B06010505070804"
OID from numeric string - multi-byte subidentifier
oid_from_numeric_string:"1.1.2108":0:"29903C"
OID from numeric string - second component greater than 39
oid_from_numeric_string:"2.49.0.0.826.0":0:"81010000863A00"
OID from numeric string - multi-byte first subidentifier
oid_from_numeric_string:"2.999":0:"8837"
OID from numeric string - empty string input
oid_from_numeric_string:"":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - first component not a number
oid_from_numeric_string:"abc.1.2":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - second component not a number
oid_from_numeric_string:"1.abc.2":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - first component too large
oid_from_numeric_string:"3.1":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - first component < 2, second > 39
oid_from_numeric_string:"1.40":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - third component not a number
oid_from_numeric_string:"1.2.abc":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - non-'.' separator between first and second
oid_from_numeric_string:"1/2.3.4":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - non-'.' separator between second and third
oid_from_numeric_string:"1.2/3.4":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - non-'.' separator between third and fourth
oid_from_numeric_string:"1.2.3/4":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - OID greater than max length (129 components)
oid_from_numeric_string:"1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1.2.3.4.5.6.7.8.1":MBEDTLS_ERR_ASN1_INVALID_DATA:""
OID from numeric string - OID with maximum subidentifier
oid_from_numeric_string:"2.4294967215":0:"8FFFFFFF7F"
OID from numeric string - OID with overflowing subidentifier
oid_from_numeric_string:"2.4294967216":MBEDTLS_ERR_ASN1_INVALID_DATA:""

View file

@ -119,3 +119,29 @@ void oid_get_numeric_string(data_t *oid, int error_ret, char *result_str)
}
}
/* END_CASE */
/* BEGIN_CASE */
void oid_from_numeric_string(char *oid_str, int error_ret,
data_t *exp_oid_buf)
{
mbedtls_asn1_buf oid = { 0, 0, NULL };
mbedtls_asn1_buf exp_oid = { 0, 0, NULL };
int ret;
exp_oid.tag = MBEDTLS_ASN1_OID;
exp_oid.p = exp_oid_buf->x;
exp_oid.len = exp_oid_buf->len;
ret = mbedtls_oid_from_numeric_string(&oid, oid_str, strlen(oid_str));
if (error_ret == 0) {
TEST_EQUAL(oid.len, exp_oid.len);
TEST_ASSERT(memcmp(oid.p, exp_oid.p, oid.len) == 0);
mbedtls_free(oid.p);
oid.p = NULL;
oid.len = 0;
} else {
TEST_EQUAL(ret, error_ret);
}
}
/* END_CASE */