Add function to parse an OID from a string

Signed-off-by: David Horstmann <david.horstmann@arm.com>
This commit is contained in:
David Horstmann 2023-01-18 18:40:49 +00:00
parent 406b9172ad
commit 92337c0e62
2 changed files with 178 additions and 0 deletions

View file

@ -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
*

View file

@ -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 */