Merge pull request #1080 from gilles-peskine-arm/parse_attribute_value_der_encoded-buffer_overflow

Fix buffer overflow in parse_attribute_value_der_encoded
diff --git a/library/x509_create.c b/library/x509_create.c
index 1c489a3..2583cdd 100644
--- a/library/x509_create.c
+++ b/library/x509_create.c
@@ -185,51 +185,107 @@
     return 0;
 }
 
-static int parse_attribute_value_der_encoded(const char *s,
-                                             int len,
-                                             unsigned char *data,
-                                             size_t *data_len,
-                                             int *tag)
+/** Parse a hexstring containing a DER-encoded string.
+ *
+ * \param s         A string of \p len bytes hexadecimal digits.
+ * \param len       Number of bytes to read from \p s.
+ * \param data      Output buffer of size \p data_size.
+ *                  On success, it contains the payload that's DER-encoded
+ *                  in the input (content without the tag and length).
+ *                  If the DER tag is a string tag, the payload is guaranteed
+ *                  not to contain null bytes.
+ * \param data_size Length of the \p data buffer.
+ * \param data_len  On success, the length of the parsed string.
+ *                  It is guaranteed to be less than
+ *                  #MBEDTLS_X509_MAX_DN_NAME_SIZE.
+ * \param tag       The ASN.1 tag that the payload in \p data is encoded in.
+ *
+ * \retval          0 on success.
+ * \retval          #MBEDTLS_ERR_X509_INVALID_NAME if \p s does not contain
+ *                  a valid hexstring,
+ *                  or if the decoded hexstring is not valid DER,
+ *                  or if the payload does not fit in \p data,
+ *                  or if the payload is more than
+ *                  #MBEDTLS_X509_MAX_DN_NAME_SIZE bytes,
+ *                  of if \p *tag is an ASN.1 string tag and the payload
+ *                  contains a null byte.
+ * \retval          #MBEDTLS_ERR_X509_ALLOC_FAILED on low memory.
+ */
+static int parse_attribute_value_hex_der_encoded(const char *s,
+                                                 size_t len,
+                                                 unsigned char *data,
+                                                 size_t data_size,
+                                                 size_t *data_len,
+                                                 int *tag)
 {
-    const char *c = s;
-    const char *end = c + len;
-    unsigned char asn1_der_buf[MBEDTLS_X509_MAX_DN_NAME_SIZE];
-    unsigned char *asn1_der_end;
-    unsigned char *p;
-    unsigned char *d = data;
-    int n;
+    /* Step 1: preliminary length checks. */
+    /* Each byte is encoded by exactly two hexadecimal digits. */
+    if (len % 2 != 0) {
+        /* Odd number of hex digits */
+        return MBEDTLS_ERR_X509_INVALID_NAME;
+    }
+    size_t const der_length = len / 2;
+    if (der_length > MBEDTLS_X509_MAX_DN_NAME_SIZE + 4) {
+        /* The payload would be more than MBEDTLS_X509_MAX_DN_NAME_SIZE
+         * (after subtracting the ASN.1 tag and length). Reject this early
+         * to avoid allocating a large intermediate buffer. */
+        return MBEDTLS_ERR_X509_INVALID_NAME;
+    }
+    if (der_length < 1) {
+        /* Avoid empty-buffer shenanigans. A valid DER encoding is never
+         * empty. */
+        return MBEDTLS_ERR_X509_INVALID_NAME;
+    }
 
-    /* Converting from hexstring to raw binary so we can use asn1parse.c */
-    if ((len < 5) || (*c != '#')) {
-        return MBEDTLS_ERR_X509_INVALID_NAME;
+    /* Step 2: Decode the hex string into an intermediate buffer. */
+    unsigned char *der = mbedtls_calloc(1, der_length);
+    if (der == NULL) {
+        return MBEDTLS_ERR_X509_ALLOC_FAILED;
     }
-    c++;
-    if ((*tag = hexpair_to_int(c)) == -1) {
-        return MBEDTLS_ERR_X509_INVALID_NAME;
-    }
-    c += 2;
-    p = asn1_der_buf;
-    for (p = asn1_der_buf; c < end; c += 2) {
-        if ((c + 1 >= end) || (n = hexpair_to_int(c)) == -1) {
-            return MBEDTLS_ERR_X509_INVALID_NAME;
+    /* Beyond this point, der needs to be freed on exit. */
+    for (size_t i = 0; i < der_length; i++) {
+        int c = hexpair_to_int(s + 2 * i);
+        if (c < 0) {
+            goto error;
         }
-        if (MBEDTLS_ASN1_IS_STRING_TAG(*tag) && n == 0) {
-            return MBEDTLS_ERR_X509_INVALID_NAME;
+        der[i] = c;
+    }
+
+    /* Step 3: decode the DER. */
+    /* We've checked that der_length >= 1 above. */
+    *tag = der[0];
+    unsigned char *p = der + 1;
+    if (mbedtls_asn1_get_len(&p, der + der_length, data_len) != 0) {
+        goto error;
+    }
+    /* Now p points to the first byte of the payload inside der,
+     * and *data_len is the length of the payload. */
+
+    /* Step 4: payload validation */
+    if (*data_len > MBEDTLS_X509_MAX_DN_NAME_SIZE) {
+        goto error;
+    }
+    /* Strings must not contain null bytes. */
+    if (MBEDTLS_ASN1_IS_STRING_TAG(*tag)) {
+        for (size_t i = 0; i < *data_len; i++) {
+            if (p[i] == 0) {
+                goto error;
+            }
         }
-        *(p++) = n;
-    }
-    asn1_der_end = p;
-
-    p = asn1_der_buf;
-    if (mbedtls_asn1_get_len(&p, asn1_der_end, data_len) != 0) {
-        return MBEDTLS_ERR_X509_INVALID_NAME;
     }
 
-    while (p < asn1_der_end) {
-        *(d++) = *(p++);
+    /* Step 5: output the payload. */
+    if (*data_len > data_size) {
+        goto error;
     }
+    memcpy(data, p, *data_len);
+    mbedtls_free(der);
 
     return 0;
+
+error:
+    mbedtls_free(der);
+    return MBEDTLS_ERR_X509_INVALID_NAME;
 }
 
 int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name)
@@ -273,11 +329,14 @@
                 mbedtls_free(oid.p);
                 return MBEDTLS_ERR_X509_INVALID_NAME;
             } else if (*s == '#') {
-                if ((parse_ret =
-                         parse_attribute_value_der_encoded(s, (int) (c - s), data, &data_len,
-                                                           &tag)) != 0) {
+                /* We know that c >= s (loop invariant) and c != s (in this
+                 * else branch), hence c - s - 1 >= 0. */
+                parse_ret = parse_attribute_value_hex_der_encoded(
+                    s + 1, c - s - 1,
+                    data, sizeof(data), &data_len, &tag);
+                if (parse_ret != 0) {
                     mbedtls_free(oid.p);
-                    return MBEDTLS_ERR_X509_INVALID_NAME;
+                    return parse_ret;
                 }
             } else {
                 if (numericoid) {
diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
index 37679c1..cff25ff 100644
--- a/tests/suites/test_suite_x509write.data
+++ b/tests/suites/test_suite_x509write.data
@@ -167,73 +167,101 @@
 x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"01":"20190210144406":"20290210144406":MBEDTLS_MD_SHA1:0:0:"NULL":0:0:1:-1:"data_files/server1.allSubjectAltNames.crt":0:0:"data_files/test-ca.crt":1
 
 X509 String to Names #1
-mbedtls_x509_string_to_names:"C=NL,O=Offspark\\, Inc., OU=PolarSSL":"C=NL, O=Offspark\\, Inc., OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL,O=Offspark\\, Inc., OU=PolarSSL":"C=NL, O=Offspark\\, Inc., OU=PolarSSL":0:0
 
 X509 String to Names #2
-mbedtls_x509_string_to_names:"C=NL, O=Offspark, Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Offspark, Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #3 (Name precisely 255 bytes)
-mbedtls_x509_string_to_names:"C=NL, O=123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345,OU=PolarSSL":"C=NL, O=123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, O=123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345,OU=PolarSSL":"C=NL, O=123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345, OU=PolarSSL":0:0
 
 X509 String to Names #4 (Name larger than 255 bytes)
-mbedtls_x509_string_to_names:"C=NL, O=1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #5 (Escape non-allowed characters)
-mbedtls_x509_string_to_names:"C=NL, O=Offspark\\a Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Offspark\\a Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #6 (Escape at end)
-mbedtls_x509_string_to_names:"C=NL, O=Offspark\\":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Offspark\\":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #7 (Invalid, no '=' or ',')
-mbedtls_x509_string_to_names:"ABC123":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"ABC123":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #8 (Escaped valid characters)
-mbedtls_x509_string_to_names:"C=NL, O=Offspark\\+ \\> \\=, OU=PolarSSL":"C=NL, O=Offspark\\+ \\> \\=, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, O=Offspark\\+ \\> \\=, OU=PolarSSL":"C=NL, O=Offspark\\+ \\> \\=, OU=PolarSSL":0:0
 
 X509 String to Names #9 (Escaped ascii hexpairs uppercase encoded)
-mbedtls_x509_string_to_names:"C=NL, O=\\4F\\66\\66\\73\\70\\61\\72\\6B, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, O=\\4F\\66\\66\\73\\70\\61\\72\\6B, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0:0
 
 X509 String to Names #10 (Escaped ascii hexpairs lowercase encoded)
-mbedtls_x509_string_to_names:"C=NL, O=\\4f\\66\\66\\73\\70\\61\\72\\6b, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, O=\\4f\\66\\66\\73\\70\\61\\72\\6b, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0:0
 
 X509 String to Names #11 (Invalid hexpair escape at end of string)
-mbedtls_x509_string_to_names:"C=NL, O=\\4f\\66\\66\\73\\70\\61\\72\\6, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=\\4f\\66\\66\\73\\70\\61\\72\\6, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #12 (Reject escaped null hexpair)
-mbedtls_x509_string_to_names:"C=NL, O=Of\\00spark, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Of\\00spark, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #13 (Invalid hexpairs)
-mbedtls_x509_string_to_names:"C=NL, O=Of\\flspark, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=Of\\flspark, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #14 (Accept numercoid/hexstring)
-mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C084F6666737061726B, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C084F6666737061726B, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0:0
+
+# TODO: Should the trailing garbage be ignored?
+X509 String to Names (hexstring: trailing garbage after DER is ignored)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C084F6666737061726Baa, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0:0
+
+X509 String to Names: long hexstring (payload=256 bytes)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C82010041414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141, OU=PolarSSL":"C=NL, O=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, OU=PolarSSL":0:MAY_FAIL_DN_GETS
+
+X509 String to Names: long hexstring (payload=257 bytes)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C820101aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #15 (Odd length DER hexstring)
-mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C084F6666737061726, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C084F6666737061726, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
-X509 String to Names #16 (Length mismatch DER hexstring)
-mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C0B4F6666737061726B, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+X509 String to Names (empty DER hexstring)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
-X509 String to Names #17 (Invalid OID)
-mbedtls_x509_string_to_names:"C=NL, 10.5.4.10=#0C084F6666737061726B, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+X509 String to Names (empty DER hexstring at end)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#":"":MBEDTLS_ERR_X509_INVALID_NAME:0
+
+X509 String to Names (1-byte DER hexstring)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
+
+X509 String to Names (1-byte DER hexstring at end)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C":"":MBEDTLS_ERR_X509_INVALID_NAME:0
+
+X509 String to Names #16 (hexstring: DER length exceeds available data)
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#0C0B4F6666737061726B, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
+
+X509 String to Names #17 (hexstring: Invalid OID)
+mbedtls_x509_string_to_names:"C=NL, 10.5.4.10=#0C084F6666737061726B, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 String to Names #18 (short name and hexstring)
-mbedtls_x509_string_to_names:"C=NL, O=#0C084F6666737061726B, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, O=#0C084F6666737061726B, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":0:0
+
+X509 String to Names (null byte in hexstring with string type)
+mbedtls_x509_string_to_names:"C=NL, O=#0C0100, OU=PolarSSL":"C=NL, O=Offspark, OU=PolarSSL":MBEDTLS_ERR_X509_INVALID_NAME:0
+
+X509 String to Names (null byte in hexstring with non-string type)
+mbedtls_x509_string_to_names:"C=NL, O=#040100, OU=PolarSSL":"C=NL, O=\\x00, OU=PolarSSL":0:MAY_FAIL_GET_NAME
 
 X509 String to Names #19 (Accept non-ascii hexpairs)
-mbedtls_x509_string_to_names:"C=NL, O=Of\\CCspark, OU=PolarSSL":"C=NL, O=Of\\CCspark, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, O=Of\\CCspark, OU=PolarSSL":"C=NL, O=Of\\CCspark, OU=PolarSSL":0:0
 
 X509 String to Names #20 (Reject empty AttributeValue)
-mbedtls_x509_string_to_names:"C=NL, O=, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME
+mbedtls_x509_string_to_names:"C=NL, O=, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME:0
 
 X509 Round trip test (Escaped characters)
-mbedtls_x509_string_to_names:"CN=Lu\\C4\\8Di\\C4\\87, O=Offspark, OU=PolarSSL":"CN=Lu\\C4\\8Di\\C4\\87, O=Offspark, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"CN=Lu\\C4\\8Di\\C4\\87, O=Offspark, OU=PolarSSL":"CN=Lu\\C4\\8Di\\C4\\87, O=Offspark, OU=PolarSSL":0:0
 
 X509 Round trip test (hexstring output for non string input)
-mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#03084F6666737061726B, OU=PolarSSL":"C=NL, O=#03084F6666737061726B, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10=#03084F6666737061726B, OU=PolarSSL":"C=NL, O=#03084F6666737061726B, OU=PolarSSL":0:0
 
 X509 Round trip test (numercoid hexstring output for unknown OID)
-mbedtls_x509_string_to_names:"C=NL, 2.5.4.10.234.532=#0C084F6666737061726B, OU=PolarSSL":"C=NL, 2.5.4.10.234.532=#0C084F6666737061726B, OU=PolarSSL":0
+mbedtls_x509_string_to_names:"C=NL, 2.5.4.10.234.532=#0C084F6666737061726B, OU=PolarSSL":"C=NL, 2.5.4.10.234.532=#0C084F6666737061726B, OU=PolarSSL":0:0
 
 Check max serial length
 x509_set_serial_check:
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index 26988ce..a7ed262 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -125,6 +125,12 @@
     return ret;
 }
 #endif  /* MBEDTLS_X509_CSR_WRITE_C */
+
+/* Due to inconsistencies in the input size limits applied by different
+ * library functions, some write-parse tests may fail. */
+#define MAY_FAIL_GET_NAME       0x0001
+#define MAY_FAIL_DN_GETS        0x0002
+
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -687,8 +693,8 @@
 /* END_CASE */
 
 /* BEGIN_CASE depends_on:MBEDTLS_X509_CREATE_C:MBEDTLS_X509_USE_C */
-void mbedtls_x509_string_to_names(char *name, char *parsed_name, int result
-                                  )
+void mbedtls_x509_string_to_names(char *name, char *parsed_name,
+                                  int result, int may_fail)
 {
     int ret;
     size_t len = 0;
@@ -704,22 +710,32 @@
     c = buf + sizeof(buf);
 
     ret = mbedtls_x509_string_to_names(&names, name);
-    TEST_ASSERT(ret == result);
+    TEST_EQUAL(ret, result);
 
     if (ret != 0) {
         goto exit;
     }
 
     ret = mbedtls_x509_write_names(&c, buf, names);
-    TEST_ASSERT(ret > 0);
+    TEST_LE_S(1, ret);
 
-    TEST_ASSERT(mbedtls_asn1_get_tag(&c, buf + sizeof(buf), &len,
-                                     MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0);
-    TEST_ASSERT(mbedtls_x509_get_name(&c, buf + sizeof(buf), &parsed) == 0);
+    TEST_EQUAL(mbedtls_asn1_get_tag(&c, buf + sizeof(buf), &len,
+                                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE), 0);
+    ret = mbedtls_x509_get_name(&c, buf + sizeof(buf), &parsed);
+    if ((may_fail & MAY_FAIL_GET_NAME) && ret < 0) {
+        /* Validation inconsistency between mbedtls_x509_string_to_names() and
+         * mbedtls_x509_get_name(). Accept it for now. */
+        goto exit;
+    }
+    TEST_EQUAL(ret, 0);
 
     ret = mbedtls_x509_dn_gets((char *) out, sizeof(out), &parsed);
-    TEST_ASSERT(ret > 0);
-
+    if ((may_fail & MAY_FAIL_DN_GETS) && ret < 0) {
+        /* Validation inconsistency between mbedtls_x509_string_to_names() and
+         * mbedtls_x509_dn_gets(). Accept it for now. */
+        goto exit;
+    }
+    TEST_LE_S(1, ret);
     TEST_ASSERT(strcmp((char *) out, parsed_name) == 0);
 
 exit: