Merge pull request #7898 from AndrzejKurek/csr-rfc822-dn

OPC UA - add support for RFC822 and DirectoryName SubjectAltNames when generating CSR's
diff --git a/ChangeLog.d/add-rfc822-directoryname-csr-gen.txt b/ChangeLog.d/add-rfc822-directoryname-csr-gen.txt
new file mode 100644
index 0000000..ff8693c
--- /dev/null
+++ b/ChangeLog.d/add-rfc822-directoryname-csr-gen.txt
@@ -0,0 +1,3 @@
+Features
+   * Add a possibility to generate CSR's with RCF822 and directoryName subtype
+     of subjectAltName extension in x509 certificates.
diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h
index e21356f..a9267c7 100644
--- a/include/mbedtls/x509.h
+++ b/include/mbedtls/x509.h
@@ -526,6 +526,9 @@
 int mbedtls_x509_info_key_usage(char **buf, size_t *size,
                                 unsigned int key_usage);
 
+int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions,
+                                      const mbedtls_x509_san_list *san_list);
+
 /**
  * \brief          This function parses a CN string as an IP address.
  *
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 03e48df..351edd0 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -99,6 +99,7 @@
     x509_crl.c
     x509_crt.c
     x509_csr.c
+    x509write.c
     x509write_crt.c
     x509write_csr.c
 )
diff --git a/library/Makefile b/library/Makefile
index 194a847..c383c32 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -167,6 +167,7 @@
 	   x509_crl.o \
 	   x509_crt.o \
 	   x509_csr.o \
+   	   x509write.o \
 	   x509write_crt.o \
 	   x509write_csr.o \
 	   pkcs7.o \
diff --git a/library/x509write.c b/library/x509write.c
new file mode 100644
index 0000000..cd3c739
--- /dev/null
+++ b/library/x509write.c
@@ -0,0 +1,186 @@
+/*
+ *  X.509 internal, common functions for writing
+ *
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+#include "common.h"
+#if defined(MBEDTLS_X509_CSR_WRITE_C) || defined(MBEDTLS_X509_CRT_WRITE_C)
+
+#include "mbedtls/x509_crt.h"
+#include "mbedtls/asn1write.h"
+#include "mbedtls/error.h"
+#include "mbedtls/oid.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/platform_util.h"
+#include "mbedtls/md.h"
+
+#include <string.h>
+#include <stdint.h>
+
+#if defined(MBEDTLS_PEM_WRITE_C)
+#include "mbedtls/pem.h"
+#endif /* MBEDTLS_PEM_WRITE_C */
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "psa/crypto.h"
+#include "mbedtls/psa_util.h"
+#include "md_psa.h"
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+#define CHECK_OVERFLOW_ADD(a, b) \
+    do                         \
+    {                           \
+        if (a > SIZE_MAX - (b)) \
+        { \
+            return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \
+        }                            \
+        a += b; \
+    } while (0)
+
+int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions,
+                                      const mbedtls_x509_san_list *san_list)
+{
+    int ret = 0;
+    const mbedtls_x509_san_list *cur;
+    unsigned char *buf;
+    unsigned char *p;
+    size_t len;
+    size_t buflen = 0;
+
+    /* Determine the maximum size of the SubjectAltName list */
+    for (cur = san_list; cur != NULL; cur = cur->next) {
+        /* Calculate size of the required buffer */
+        switch (cur->node.type) {
+            case MBEDTLS_X509_SAN_DNS_NAME:
+            case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER:
+            case MBEDTLS_X509_SAN_IP_ADDRESS:
+            case MBEDTLS_X509_SAN_RFC822_NAME:
+                /* length of value for each name entry,
+                 * maximum 4 bytes for the length field,
+                 * 1 byte for the tag/type.
+                 */
+                CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len);
+                CHECK_OVERFLOW_ADD(buflen, 4 + 1);
+                break;
+            case MBEDTLS_X509_SAN_DIRECTORY_NAME:
+            {
+                const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name;
+                while (chunk != NULL) {
+                    // Max 4 bytes for length, +1 for tag,
+                    // additional 4 max for length, +1 for tag.
+                    // See x509_write_name for more information.
+                    CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1);
+                    CHECK_OVERFLOW_ADD(buflen, chunk->oid.len);
+                    CHECK_OVERFLOW_ADD(buflen, chunk->val.len);
+                    chunk = chunk->next;
+                }
+                CHECK_OVERFLOW_ADD(buflen, 4 + 1);
+                break;
+            }
+            default:
+                /* Not supported - return. */
+                return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
+        }
+    }
+
+    /* Add the extra length field and tag */
+    CHECK_OVERFLOW_ADD(buflen, 4 + 1);
+
+    /* Allocate buffer */
+    buf = mbedtls_calloc(1, buflen);
+    if (buf == NULL) {
+        return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
+    }
+    p = buf + buflen;
+
+    /* Write ASN.1-based structure */
+    cur = san_list;
+    len = 0;
+    while (cur != NULL) {
+        size_t single_san_len = 0;
+        switch (cur->node.type) {
+            case MBEDTLS_X509_SAN_DNS_NAME:
+            case MBEDTLS_X509_SAN_RFC822_NAME:
+            case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER:
+            case MBEDTLS_X509_SAN_IP_ADDRESS:
+            {
+                const unsigned char *unstructured_name =
+                    (const unsigned char *) cur->node.san.unstructured_name.p;
+                size_t unstructured_name_len = cur->node.san.unstructured_name.len;
+
+                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
+                                             mbedtls_asn1_write_raw_buffer(
+                                                 &p, buf,
+                                                 unstructured_name, unstructured_name_len));
+                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len(
+                                                 &p, buf, unstructured_name_len));
+                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
+                                             mbedtls_asn1_write_tag(
+                                                 &p, buf,
+                                                 MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type));
+            }
+            break;
+            case MBEDTLS_X509_SAN_DIRECTORY_NAME:
+                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
+                                             mbedtls_x509_write_names(&p, buf,
+                                                                      (mbedtls_asn1_named_data *) &
+                                                                      cur->node
+                                                                      .san.directory_name));
+                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
+                                             mbedtls_asn1_write_len(&p, buf, single_san_len));
+                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
+                                             mbedtls_asn1_write_tag(&p, buf,
+                                                                    MBEDTLS_ASN1_CONTEXT_SPECIFIC |
+                                                                    MBEDTLS_ASN1_CONSTRUCTED |
+                                                                    MBEDTLS_X509_SAN_DIRECTORY_NAME));
+                break;
+            default:
+                /* Error out on an unsupported SAN */
+                ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
+                goto cleanup;
+        }
+        cur = cur->next;
+        /* check for overflow */
+        if (len > SIZE_MAX - single_san_len) {
+            ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA;
+            goto cleanup;
+        }
+        len += single_san_len;
+    }
+
+    MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
+    MBEDTLS_ASN1_CHK_CLEANUP_ADD(len,
+                                 mbedtls_asn1_write_tag(&p, buf,
+                                                        MBEDTLS_ASN1_CONSTRUCTED |
+                                                        MBEDTLS_ASN1_SEQUENCE));
+
+    ret = mbedtls_x509_set_extension(extensions,
+                                     MBEDTLS_OID_SUBJECT_ALT_NAME,
+                                     MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME),
+                                     0,
+                                     buf + buflen - len, len);
+
+    /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list
+     * was incorrectly calculated and memory is corrupted. */
+    if (p < buf) {
+        ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+    }
+cleanup:
+    mbedtls_free(buf);
+    return ret;
+}
+
+#endif /* MBEDTLS_X509_CSR_WRITE_C || MBEDTLS_X509_CRT_WRITE_C */
diff --git a/library/x509write_crt.c b/library/x509write_crt.c
index 3586a3c..a8a3022 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -48,16 +48,6 @@
 #include "md_psa.h"
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
-#define CHECK_OVERFLOW_ADD(a, b) \
-    do                         \
-    {                           \
-        if (a > SIZE_MAX - (b)) \
-        { \
-            return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \
-        }                            \
-        a += b; \
-    } while (0)
-
 void mbedtls_x509write_crt_init(mbedtls_x509write_cert *ctx)
 {
     memset(ctx, 0, sizeof(mbedtls_x509write_cert));
@@ -166,131 +156,7 @@
 int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *ctx,
                                                        const mbedtls_x509_san_list *san_list)
 {
-    int ret = 0;
-    const mbedtls_x509_san_list *cur;
-    unsigned char *buf;
-    unsigned char *p;
-    size_t len;
-    size_t buflen = 0;
-
-    /* Determine the maximum size of the SubjectAltName list */
-    for (cur = san_list; cur != NULL; cur = cur->next) {
-        /* Calculate size of the required buffer */
-        switch (cur->node.type) {
-            case MBEDTLS_X509_SAN_DNS_NAME:
-            case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER:
-            case MBEDTLS_X509_SAN_IP_ADDRESS:
-            case MBEDTLS_X509_SAN_RFC822_NAME:
-                /* length of value for each name entry,
-                 * maximum 4 bytes for the length field,
-                 * 1 byte for the tag/type.
-                 */
-                CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len);
-                CHECK_OVERFLOW_ADD(buflen, 4 + 1);
-                break;
-            case MBEDTLS_X509_SAN_DIRECTORY_NAME:
-            {
-                const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name;
-                while (chunk != NULL) {
-                    // Max 4 bytes for length, +1 for tag,
-                    // additional 4 max for length, +1 for tag.
-                    // See x509_write_name for more information.
-                    CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1);
-                    CHECK_OVERFLOW_ADD(buflen, chunk->oid.len);
-                    CHECK_OVERFLOW_ADD(buflen, chunk->val.len);
-                    chunk = chunk->next;
-                }
-                CHECK_OVERFLOW_ADD(buflen, 4 + 1);
-                break;
-            }
-            default:
-                /* Not supported - return. */
-                return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
-        }
-    }
-
-    /* Add the extra length field and tag */
-    CHECK_OVERFLOW_ADD(buflen, 4 + 1);
-
-    /* Allocate buffer */
-    buf = mbedtls_calloc(1, buflen);
-    if (buf == NULL) {
-        return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
-    }
-    p = buf + buflen;
-
-    /* Write ASN.1-based structure */
-    cur = san_list;
-    len = 0;
-    while (cur != NULL) {
-        size_t single_san_len = 0;
-        switch (cur->node.type) {
-            case MBEDTLS_X509_SAN_DNS_NAME:
-            case MBEDTLS_X509_SAN_RFC822_NAME:
-            case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER:
-            case MBEDTLS_X509_SAN_IP_ADDRESS:
-            {
-                const unsigned char *unstructured_name =
-                    (const unsigned char *) cur->node.san.unstructured_name.p;
-                size_t unstructured_name_len = cur->node.san.unstructured_name.len;
-
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
-                                             mbedtls_asn1_write_raw_buffer(
-                                                 &p, buf,
-                                                 unstructured_name, unstructured_name_len));
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len(
-                                                 &p, buf, unstructured_name_len));
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
-                                             mbedtls_asn1_write_tag(
-                                                 &p, buf,
-                                                 MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type));
-            }
-            break;
-            case MBEDTLS_X509_SAN_DIRECTORY_NAME:
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
-                                             mbedtls_x509_write_names(&p, buf,
-                                                                      (mbedtls_asn1_named_data *) &
-                                                                      cur->node
-                                                                      .san.directory_name));
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
-                                             mbedtls_asn1_write_len(&p, buf, single_san_len));
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len,
-                                             mbedtls_asn1_write_tag(&p, buf,
-                                                                    MBEDTLS_ASN1_CONTEXT_SPECIFIC |
-                                                                    MBEDTLS_ASN1_CONSTRUCTED |
-                                                                    MBEDTLS_X509_SAN_DIRECTORY_NAME));
-                break;
-            default:
-                /* Error out on an unsupported SAN */
-                ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
-                goto cleanup;
-        }
-        cur = cur->next;
-        /* check for overflow */
-        if (len > SIZE_MAX - single_san_len) {
-            ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA;
-            goto cleanup;
-        }
-        len += single_san_len;
-    }
-
-    MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
-    MBEDTLS_ASN1_CHK_CLEANUP_ADD(len,
-                                 mbedtls_asn1_write_tag(&p, buf,
-                                                        MBEDTLS_ASN1_CONSTRUCTED |
-                                                        MBEDTLS_ASN1_SEQUENCE));
-
-    ret = mbedtls_x509write_crt_set_extension(
-        ctx,
-        MBEDTLS_OID_SUBJECT_ALT_NAME,
-        MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME),
-        0,
-        buf + buflen - len,
-        len);
-
-cleanup:
-    mbedtls_free(buf);
-    return ret;
+    return mbedtls_x509_write_set_san_common(&ctx->extensions, san_list);
 }
 
 
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index 5d3d176..d996052 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -89,100 +89,7 @@
 int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx,
                                                        const mbedtls_x509_san_list *san_list)
 {
-    int ret = 0;
-    const mbedtls_x509_san_list *cur;
-    unsigned char *buf;
-    unsigned char *p;
-    size_t len;
-    size_t buflen = 0;
-
-    /* Determine the maximum size of the SubjectAltName list */
-    for (cur = san_list; cur != NULL; cur = cur->next) {
-        /* Calculate size of the required buffer */
-        switch (cur->node.type) {
-            case MBEDTLS_X509_SAN_DNS_NAME:
-            case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER:
-            case MBEDTLS_X509_SAN_IP_ADDRESS:
-                /* length of value for each name entry,
-                 * maximum 4 bytes for the length field,
-                 * 1 byte for the tag/type.
-                 */
-                buflen += cur->node.san.unstructured_name.len + 4 + 1;
-                break;
-
-            default:
-                /* Not supported - skip. */
-                break;
-        }
-    }
-
-    /* Add the extra length field and tag */
-    buflen += 4 + 1;
-
-    /* Allocate buffer */
-    buf = mbedtls_calloc(1, buflen);
-    if (buf == NULL) {
-        return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
-    }
-
-    mbedtls_platform_zeroize(buf, buflen);
-    p = buf + buflen;
-
-    /* Write ASN.1-based structure */
-    cur = san_list;
-    len = 0;
-    while (cur != NULL) {
-        switch (cur->node.type) {
-            case MBEDTLS_X509_SAN_DNS_NAME:
-            case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER:
-            case MBEDTLS_X509_SAN_IP_ADDRESS:
-            {
-                const unsigned char *unstructured_name =
-                    (const unsigned char *) cur->node.san.unstructured_name.p;
-                size_t unstructured_name_len = cur->node.san.unstructured_name.len;
-
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(len,
-                                             mbedtls_asn1_write_raw_buffer(
-                                                 &p, buf,
-                                                 unstructured_name, unstructured_name_len));
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(
-                                                 &p, buf, unstructured_name_len));
-                MBEDTLS_ASN1_CHK_CLEANUP_ADD(len,
-                                             mbedtls_asn1_write_tag(
-                                                 &p, buf,
-                                                 MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type));
-            }
-            break;
-            default:
-                /* Skip unsupported names. */
-                break;
-        }
-        cur = cur->next;
-    }
-
-    MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
-    MBEDTLS_ASN1_CHK_CLEANUP_ADD(len,
-                                 mbedtls_asn1_write_tag(&p, buf,
-                                                        MBEDTLS_ASN1_CONSTRUCTED |
-                                                        MBEDTLS_ASN1_SEQUENCE));
-
-    ret = mbedtls_x509write_csr_set_extension(
-        ctx,
-        MBEDTLS_OID_SUBJECT_ALT_NAME,
-        MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME),
-        0,
-        buf + buflen - len,
-        len);
-
-    /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list
-     * was incorrectly calculated and memory is corrupted. */
-    if (p < buf) {
-        ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
-    }
-
-cleanup:
-    mbedtls_free(buf);
-    return ret;
+    return mbedtls_x509_write_set_san_common(&ctx->extensions, san_list);
 }
 
 int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage)
diff --git a/programs/x509/cert_req.c b/programs/x509/cert_req.c
index 23e816b..558d8cc 100644
--- a/programs/x509/cert_req.c
+++ b/programs/x509/cert_req.c
@@ -23,10 +23,10 @@
 /* md.h is included this early since MD_CAN_XXX macros are defined there. */
 #include "mbedtls/md.h"
 
-#if !defined(MBEDTLS_X509_CSR_WRITE_C) || !defined(MBEDTLS_FS_IO) ||  \
+#if !defined(MBEDTLS_X509_CSR_WRITE_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \
     !defined(MBEDTLS_PK_PARSE_C) || !defined(MBEDTLS_MD_CAN_SHA256) || \
     !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CTR_DRBG_C) || \
-    !defined(MBEDTLS_PEM_WRITE_C)
+    !defined(MBEDTLS_PEM_WRITE_C) || !defined(MBEDTLS_FS_IO)
 int main(void)
 {
     mbedtls_printf("MBEDTLS_X509_CSR_WRITE_C and/or MBEDTLS_FS_IO and/or "
@@ -66,10 +66,12 @@
     "    output_file=%%s      default: cert.req\n"      \
     "    subject_name=%%s     default: CN=Cert,O=mbed TLS,C=UK\n"   \
     "    san=%%s              default: (none)\n"       \
-    "                        Semicolon-separated-list of values:\n"     \
-    "                          DNS:value\n"            \
-    "                          URI:value\n"            \
-    "                          IP:value (Only IPv4 is supported)\n"             \
+    "                           Semicolon-separated-list of values:\n" \
+    "                           DNS:value\n"            \
+    "                           URI:value\n"            \
+    "                           RFC822:value\n"         \
+    "                           IP:value (Only IPv4 is supported)\n" \
+    "                           DN:list of comma separated key=value pairs\n" \
     "    key_usage=%%s        default: (empty)\n"       \
     "                        Comma-separated-list of values:\n"     \
     "                          digital_signature\n"     \
@@ -153,12 +155,13 @@
     mbedtls_pk_context key;
     char buf[1024];
     int i;
-    char *p, *q, *r, *subtype_value;
+    char *p, *q, *r;
     mbedtls_x509write_csr req;
     mbedtls_entropy_context entropy;
     mbedtls_ctr_drbg_context ctr_drbg;
     const char *pers = "csr example app";
     mbedtls_x509_san_list *cur, *prev;
+    mbedtls_asn1_named_data *ext_san_dirname = NULL;
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
     uint8_t ip[4] = { 0 };
 #endif
@@ -218,11 +221,34 @@
         } else if (strcmp(p, "subject_name") == 0) {
             opt.subject_name = q;
         } else if (strcmp(p, "san") == 0) {
+            char *subtype_value;
             prev = NULL;
 
             while (q != NULL) {
-                if ((r = strchr(q, ';')) != NULL) {
+                char *semicolon;
+                r = q;
+
+                /* Find the first non-escaped ; occurrence and remove escaped ones */
+                do {
+                    if ((semicolon = strchr(r, ';')) != NULL) {
+                        if (*(semicolon-1) != '\\') {
+                            r = semicolon;
+                            break;
+                        }
+                        /* Remove the escape character */
+                        size_t size_left = strlen(semicolon);
+                        memmove(semicolon-1, semicolon, size_left);
+                        *(semicolon + size_left - 1) = '\0';
+                        /* r will now point at the character after the semicolon */
+                        r = semicolon;
+                    }
+
+                } while (semicolon != NULL);
+
+                if (semicolon != NULL) {
                     *r++ = '\0';
+                } else {
+                    r = NULL;
                 }
 
                 cur = mbedtls_calloc(1, sizeof(mbedtls_x509_san_list));
@@ -236,13 +262,13 @@
                 if ((subtype_value = strchr(q, ':')) != NULL) {
                     *subtype_value++ = '\0';
                 }
-
-                if (strcmp(q, "URI") == 0) {
+                if (strcmp(q, "RFC822") == 0) {
+                    cur->node.type = MBEDTLS_X509_SAN_RFC822_NAME;
+                } else if (strcmp(q, "URI") == 0) {
                     cur->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER;
                 } else if (strcmp(q, "DNS") == 0) {
                     cur->node.type = MBEDTLS_X509_SAN_DNS_NAME;
                 } else if (strcmp(q, "IP") == 0) {
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
                     size_t ip_len = 0;
                     cur->node.type = MBEDTLS_X509_SAN_IP_ADDRESS;
                     ip_len = mbedtls_x509_crt_parse_cn_inet_pton(subtype_value, ip);
@@ -251,21 +277,28 @@
                                        subtype_value);
                         goto exit;
                     }
-#else
-                    mbedtls_printf("IP SAN parsing requires MBEDTLS_X509_CRT_PARSE_C to be defined");
-                    goto exit;
-#endif
+                    cur->node.san.unstructured_name.p = (unsigned char *) ip;
+                    cur->node.san.unstructured_name.len = sizeof(ip);
+                } else if (strcmp(q, "DN") == 0) {
+                    cur->node.type = MBEDTLS_X509_SAN_DIRECTORY_NAME;
+                    if ((ret = mbedtls_x509_string_to_names(&ext_san_dirname,
+                                                            subtype_value)) != 0) {
+                        mbedtls_strerror(ret, buf, sizeof(buf));
+                        mbedtls_printf(
+                            " failed\n  !  mbedtls_x509_string_to_names "
+                            "returned -0x%04x - %s\n\n",
+                            (unsigned int) -ret, buf);
+                        goto exit;
+                    }
+                    cur->node.san.directory_name = *ext_san_dirname;
                 } else {
                     mbedtls_free(cur);
                     goto usage;
                 }
 
-                if (strcmp(q, "IP") == 0) {
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
-                    cur->node.san.unstructured_name.p = (unsigned char *) ip;
-                    cur->node.san.unstructured_name.len = sizeof(ip);
-#endif
-                } else {
+                if (cur->node.type == MBEDTLS_X509_SAN_RFC822_NAME ||
+                    cur->node.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER ||
+                    cur->node.type == MBEDTLS_X509_SAN_DNS_NAME) {
                     q = subtype_value;
                     cur->node.san.unstructured_name.p = (unsigned char *) q;
                     cur->node.san.unstructured_name.len = strlen(q);
@@ -280,7 +313,6 @@
                 prev = cur;
                 q = r;
             }
-
         } else if (strcmp(p, "md") == 0) {
             const mbedtls_md_info_t *md_info =
                 mbedtls_md_info_from_string(q);
@@ -467,6 +499,7 @@
     }
 
     mbedtls_x509write_csr_free(&req);
+    mbedtls_asn1_free_named_data_list(&ext_san_dirname);
     mbedtls_pk_free(&key);
     mbedtls_ctr_drbg_free(&ctr_drbg);
     mbedtls_entropy_free(&entropy);
diff --git a/tests/data_files/Makefile b/tests/data_files/Makefile
index 5230a30..37a3405 100644
--- a/tests/data_files/Makefile
+++ b/tests/data_files/Makefile
@@ -1304,8 +1304,7 @@
 
 server1.req.sha256.ext: server1.key
 	# Generating this with OpenSSL as a comparison point to test we're getting the same result
-	openssl req -new -out $@ -key $< -subj '/C=NL/O=PolarSSL/CN=PolarSSL Server 1' -sha256 -addext "extendedKeyUsage=serverAuth" -addext "subjectAltName=URI:http://pki.example.com/,IP:127.1.1.0,DNS:example.com"
-all_final += server1.req.sha256.ext
+	openssl req -new -out $@ -key $< -subj '/C=NL/O=PolarSSL/CN=PolarSSL Server 1' -sha256 -config server1.req.sha256.conf
 
 parse_input/server1.req.sha384 server1.req.sha384: server1.key
 	$(MBEDTLS_CERT_REQ) output_file=$@ filename=$< subject_name="C=NL,O=PolarSSL,CN=PolarSSL Server 1" md=SHA384
diff --git a/tests/data_files/server1.req.sha256.conf b/tests/data_files/server1.req.sha256.conf
new file mode 100644
index 0000000..0d35818
--- /dev/null
+++ b/tests/data_files/server1.req.sha256.conf
@@ -0,0 +1,17 @@
+req_extensions = req_ext
+
+[req_ext]
+extendedKeyUsage = serverAuth
+subjectAltName = @alt_names
+
+[alt_names]
+email = mail@example.com
+DNS = example.com
+dirName = dirname_sect
+IP = 127.0.0.1
+URI = http://pki.example.com
+
+[dirname_sect]
+C=UK
+O=Mbed TLS
+CN=Mbed TLS directoryName SAN
diff --git a/tests/data_files/server1.req.sha256.ext b/tests/data_files/server1.req.sha256.ext
index c5ff5c5..1bb05da 100644
--- a/tests/data_files/server1.req.sha256.ext
+++ b/tests/data_files/server1.req.sha256.ext
@@ -1,18 +1,20 @@
 -----BEGIN CERTIFICATE REQUEST-----
-MIIC3jCCAcYCAQAwPDELMAkGA1UEBhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRow
+MIIDPzCCAicCAQAwPDELMAkGA1UEBhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRow
 GAYDVQQDDBFQb2xhclNTTCBTZXJ2ZXIgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
 ADCCAQoCggEBAKkCHz1AatVVU4v9Nu6CZS4VYV6Jv7joRZDb7ogWUtPxQ1BHlhJZ
 ZIdr/SvgRvlzvt3PkuGRW+1moG+JKXlFgNCDatVBQ3dfOXwJBEeCsFc5cO2j7BUZ
 HqgzCEfBBUKp/UzDtN/dBh9NEFFAZ3MTD0D4bYElXwqxU8YwfhU5rPla7n+SnqYF
 W+cTl4W1I5LZ1CQG1QkliXUH3aYajz8JGb6tZSxk65Wb3P5BXhem2mxbacwCuhQs
 FiScStzN0PdSZ3PxLaAj/X70McotcMqJCwTbLqZPcG6ezr1YieJTWZ5uWpJl4og/
-DJQZo93l6J2VE+0p26twEtxaymsXq1KCVLECAwEAAaBdMFsGCSqGSIb3DQEJDjFO
-MEwwEwYDVR0lBAwwCgYIKwYBBQUHAwEwNQYDVR0RBC4wLIYXaHR0cDovL3BraS5l
-eGFtcGxlLmNvbS+HBH8BAQCCC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IB
-AQCGmTIXEUvTqwChkzRtxPIQDDchrMnCXgUrTSxre5nvUOpjVlcIIPGWAwxRovfe
-pW6OaGZ/3xD0dRAcOW08sTD6GRUazFrubPA1eZiNC7vYdWV59qm84N5yRR/s8Hm+
-okwI47m7W9C0pfaNXchgFUQBn16TrZxPXklbCpBJ/TFV+1ODY0sJPHYiCFpYI+Jz
-YuJmadP2BHucl8wv2RyVHywOmV1sDc74i9igVrBCAh8wu+kqImMtrnkGZDxrnj/L
-5P1eDfdqG2cN+s40RnMQMosh3UfqpNV/bTgAqBPP2uluT9L1KpWcjZeuvisOgVTq
-XwFI5s34fen2DUVw6MWNfbDK
+DJQZo93l6J2VE+0p26twEtxaymsXq1KCVLECAwEAAaCBvTCBugYJKoZIhvcNAQkO
+MYGsMIGpMBMGA1UdJQQMMAoGCCsGAQUFBwMBMIGRBgNVHREEgYkwgYaBEG1haWxA
+ZXhhbXBsZS5jb22CC2V4YW1wbGUuY29tpEcwRTELMAkGA1UEBhMCVUsxETAPBgNV
+BAoMCE1iZWQgVExTMSMwIQYDVQQDDBpNYmVkIFRMUyBkaXJlY3RvcnlOYW1lIFNB
+TocEfwAAAYYWaHR0cDovL3BraS5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOC
+AQEAclrNmmgAoa4ctwyBwD1T8jbyBeuxTf+ifc+MQ6pE7YuYSlanHE5l/CoVlW14
+lR5gA01pWJJ7T8RBvo25OqXbvMFSafeGXpSHOG69A6p/7YULtbPuS6uvtdR0m3t+
+2IacL0q5FsSmPw07RNfVGDFniKVqD8eAuYnhFztk0+uZVYD4xGezUckb2wTbzFpu
+lUA/NhoWfCyV44TDR5fy23qNXywEhatDU/3nMmSJpBVy4y7J6BQVCl/fbyuKIOqu
+0OVP+FvANSO46twA9+38hI+/nPuVwtbBvg1aLBMbLZ3Egi2uozokYFYL22JYNGJo
+XORQgR66Sdrvfhiug+F5xmldCg==
 -----END CERTIFICATE REQUEST-----
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index 04a7931..26988ce 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -153,24 +153,44 @@
     mbedtls_x509_san_list san_ip;
     mbedtls_x509_san_list san_dns;
     mbedtls_x509_san_list san_uri;
+    mbedtls_x509_san_list san_mail;
+    mbedtls_x509_san_list san_dn;
     mbedtls_x509_san_list *san_list = NULL;
-    const char san_ip_name[] = { 0x7f, 0x01, 0x01, 0x00 }; // 127.1.1.0
+    mbedtls_asn1_named_data *ext_san_dirname = NULL;
+
+    const char san_ip_name[] = { 0x7f, 0x00, 0x00, 0x01 }; // 127.0.0.1
     const char *san_dns_name = "example.com";
-    const char *san_uri_name = "http://pki.example.com/";
+    const char *san_dn_name = "C=UK,O=Mbed TLS,CN=Mbed TLS directoryName SAN";
+    const char *san_mail_name = "mail@example.com";
+    const char *san_uri_name = "http://pki.example.com";
+
+    san_mail.node.type = MBEDTLS_X509_SAN_RFC822_NAME;
+    san_mail.node.san.unstructured_name.p = (unsigned char *) san_mail_name;
+    san_mail.node.san.unstructured_name.len = strlen(san_mail_name);
+    san_mail.next = NULL;
+
+    san_dns.node.type = MBEDTLS_X509_SAN_DNS_NAME;
+    san_dns.node.san.unstructured_name.p = (unsigned char *) san_dns_name;
+    san_dns.node.san.unstructured_name.len = strlen(san_dns_name);
+    san_dns.next = &san_mail;
+
+    san_dn.node.type = MBEDTLS_X509_SAN_DIRECTORY_NAME;
+    TEST_ASSERT(mbedtls_x509_string_to_names(&ext_san_dirname,
+                                             san_dn_name) == 0);
+    san_dn.node.san.directory_name = *ext_san_dirname;
+    san_dn.next = &san_dns;
+
+    san_ip.node.type = MBEDTLS_X509_SAN_IP_ADDRESS;
+    san_ip.node.san.unstructured_name.p = (unsigned char *) san_ip_name;
+    san_ip.node.san.unstructured_name.len = sizeof(san_ip_name);
+    san_ip.next = &san_dn;
 
     san_uri.node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER;
     san_uri.node.san.unstructured_name.p = (unsigned char *) san_uri_name;
     san_uri.node.san.unstructured_name.len = strlen(san_uri_name);
-    san_uri.next = NULL;
-    san_ip.node.type = MBEDTLS_X509_SAN_IP_ADDRESS;
-    san_ip.node.san.unstructured_name.p = (unsigned char *) san_ip_name;
-    san_ip.node.san.unstructured_name.len = sizeof(san_ip_name);
-    san_ip.next = &san_uri;
-    san_dns.node.type = MBEDTLS_X509_SAN_DNS_NAME;
-    san_dns.node.san.unstructured_name.p = (unsigned char *) san_dns_name;
-    san_dns.node.san.unstructured_name.len = strlen(san_dns_name);
-    san_dns.next = &san_ip;
-    san_list = &san_dns;
+    san_uri.next = &san_ip;
+
+    san_list = &san_uri;
 
     memset(&rnd_info, 0x2a, sizeof(mbedtls_test_rnd_pseudo_info));
 
@@ -244,6 +264,7 @@
     TEST_ASSERT(ret == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL);
 
 exit:
+    mbedtls_asn1_free_named_data_list(&ext_san_dirname);
     mbedtls_x509write_csr_free(&req);
     mbedtls_pk_free(&key);
     MD_OR_USE_PSA_DONE();