Add function to parse an OID from a string
Signed-off-by: David Horstmann <david.horstmann@arm.com>
diff --git a/include/mbedtls/oid.h b/include/mbedtls/oid.h
index a592e63..1284aa9 100644
--- a/include/mbedtls/oid.h
+++ b/include/mbedtls/oid.h
@@ -467,6 +467,20 @@
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
*
* \param oid OID to use
diff --git a/library/oid.c b/library/oid.c
index 86214b2..9b8bef6 100644
--- a/library/oid.c
+++ b/library/oid.c
@@ -895,4 +895,168 @@
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 */