Added ability to include the SubjectAltName extension to a CSR

Signed-off-by: Hannes Tschofenig <hannes.tschofenig@arm.com>
diff --git a/include/mbedtls/x509_csr.h b/include/mbedtls/x509_csr.h
index 0c204be..2ac5afa 100644
--- a/include/mbedtls/x509_csr.h
+++ b/include/mbedtls/x509_csr.h
@@ -83,6 +83,19 @@
 }
 mbedtls_x509write_csr;
 
+typedef struct mbedtls_x509_san_node {
+    int type;                       /**< Subject Alternative Name types */
+    char *name;                     /**< Value, following the syntax allowed bythe type */
+    size_t len;                     /**< Length of the provided value */
+}
+mbedtls_x509_san_node;
+
+typedef struct mbedtls_x509_san_list {
+    mbedtls_x509_san_node node;
+    struct mbedtls_x509_san_list *next;
+}
+mbedtls_x509_san_list;
+
 #if defined(MBEDTLS_X509_CSR_PARSE_C)
 /**
  * \brief          Load a Certificate Signing Request (CSR) in DER format
@@ -221,6 +234,20 @@
 int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage);
 
 /**
+ * \brief           Set Subject Alternative Name
+ *
+ * \param ctx       CSR context to use
+ * \param san_list  List of SAN values
+ *
+ * \return          0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED
+ *
+ * \note            Only "dnsName", "uniformResourceIdentifier" and "otherName",
+ *                  as defined in RFC 5280, are supported.
+ */
+int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx,
+                                                       const mbedtls_x509_san_list *san_list);
+
+/**
  * \brief           Set the Netscape Cert Type flags
  *                  (e.g. MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT | MBEDTLS_X509_NS_CERT_TYPE_EMAIL)
  *
diff --git a/library/x509write_csr.c b/library/x509write_csr.c
index d8d8e99..1d46fdb 100644
--- a/library/x509write_csr.c
+++ b/library/x509write_csr.c
@@ -85,6 +85,79 @@
                                       critical, val, val_len);
 }
 
+int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx,
+                                                       const mbedtls_x509_san_list *san_list)
+{
+    int ret = 0;
+    size_t sandeep = 0;
+    const mbedtls_x509_san_list *cur = san_list;
+    unsigned char *buf;
+    unsigned char *p;
+    size_t len;
+    size_t buflen = 0;
+
+    /* Determine the maximum size of the SubjectAltName list */
+    while (cur != NULL) {
+        if (cur->node.len <= 0) {
+            return 0;
+        }
+
+        /* Calculate size of the required buffer:
+         * + length of value for each name entry,
+         * + maximum 4 bytes for the length field,
+         * + 1 byte for the tag/type.
+         */
+        buflen += cur->node.len + 4 + 1;
+
+        cur = cur->next;
+    }
+
+    /* 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) {
+        MBEDTLS_ASN1_CHK_ADD(len,
+                             mbedtls_asn1_write_raw_buffer(&p, buf,
+                                                           (const unsigned char *) cur->node.name,
+                                                           cur->node.len));
+        MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, cur->node.len));
+        MBEDTLS_ASN1_CHK_ADD(len,
+                             mbedtls_asn1_write_tag(&p, buf,
+                                                    MBEDTLS_ASN1_CONTEXT_SPECIFIC |
+                                                    cur->node.type));
+
+        cur = cur->next;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
+    MBEDTLS_ASN1_CHK_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);
+
+    mbedtls_free(buf);
+    return ret;
+}
+
 int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage)
 {
     unsigned char buf[4] = { 0 };
diff --git a/programs/x509/cert_req.c b/programs/x509/cert_req.c
index 8ef5932..db80be3 100644
--- a/programs/x509/cert_req.c
+++ b/programs/x509/cert_req.c
@@ -63,6 +63,11 @@
     "    debug_level=%%d      default: 0 (disabled)\n"  \
     "    output_file=%%s      default: cert.req\n"      \
     "    subject_name=%%s     default: CN=Cert,O=mbed TLS,C=UK\n"   \
+    "    san=%%s              default: (none)\n"       \
+    "                        Comma-separated-list of values:\n"     \
+    "                          DNS:value\n"            \
+    "                          URI:value\n"            \
+    "                          OTHER:value\n"            \
     "    key_usage=%%s        default: (empty)\n"       \
     "                        Comma-separated-list of values:\n"     \
     "                          digital_signature\n"     \
@@ -96,16 +101,17 @@
  * global options
  */
 struct options {
-    const char *filename;       /* filename of the key file             */
-    const char *password;       /* password for the key file            */
-    int debug_level;            /* level of debugging                   */
-    const char *output_file;    /* where to store the constructed key file  */
-    const char *subject_name;   /* subject name for certificate request */
-    unsigned char key_usage;    /* key usage flags                      */
-    int force_key_usage;        /* Force adding the KeyUsage extension  */
-    unsigned char ns_cert_type; /* NS cert type                         */
-    int force_ns_cert_type;     /* Force adding NsCertType extension    */
-    mbedtls_md_type_t md_alg;   /* Hash algorithm used for signature.   */
+    const char *filename;             /* filename of the key file             */
+    const char *password;             /* password for the key file            */
+    int debug_level;                  /* level of debugging                   */
+    const char *output_file;          /* where to store the constructed key file  */
+    const char *subject_name;         /* subject name for certificate request   */
+    mbedtls_x509_san_list *san_list;  /* subjectAltName for certificate request */
+    unsigned char key_usage;          /* key usage flags                      */
+    int force_key_usage;              /* Force adding the KeyUsage extension  */
+    unsigned char ns_cert_type;       /* NS cert type                         */
+    int force_ns_cert_type;           /* Force adding NsCertType extension    */
+    mbedtls_md_type_t md_alg;         /* Hash algorithm used for signature.   */
 } opt;
 
 int write_certificate_request(mbedtls_x509write_csr *req, const char *output_file,
@@ -145,11 +151,12 @@
     mbedtls_pk_context key;
     char buf[1024];
     int i;
-    char *p, *q, *r;
+    char *p, *q, *r, *r2;
     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;
 
     /*
      * Set to sane values
@@ -175,6 +182,7 @@
     opt.ns_cert_type        = DFL_NS_CERT_TYPE;
     opt.force_ns_cert_type  = DFL_FORCE_NS_CERT_TYPE;
     opt.md_alg              = DFL_MD_ALG;
+    opt.san_list            = NULL;
 
     for (i = 1; i < argc; i++) {
 
@@ -197,6 +205,52 @@
             }
         } else if (strcmp(p, "subject_name") == 0) {
             opt.subject_name = q;
+        } else if (strcmp(p, "san") == 0) {
+            prev = NULL;
+
+            while (q != NULL) {
+                if ((r = strchr(q, ',')) != NULL) {
+                    *r++ = '\0';
+                }
+
+                cur = mbedtls_calloc(1, sizeof(mbedtls_x509_san_list));
+                if (cur == NULL) {
+                    mbedtls_printf("Not enough memory for subjectAltName list\n");
+                    goto usage;
+                }
+
+                cur->next = NULL;
+
+                if ((r2 = strchr(q, ':')) != NULL) {
+                    *r2++ = '\0';
+                }
+
+                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, "OTHER") == 0) {
+                    cur->node.type = MBEDTLS_X509_SAN_OTHER_NAME;
+                } else {
+                    mbedtls_free(cur);
+                    goto usage;
+                }
+
+                q = r2;
+
+                cur->node.name = q;
+                cur->node.len = strlen(q);
+
+                if (prev == NULL) {
+                    opt.san_list = cur;
+                } else {
+                    prev->next = cur;
+                }
+
+                prev = cur;
+                q = r;
+            }
+
         } else if (strcmp(p, "md") == 0) {
             const mbedtls_md_info_t *md_info =
                 mbedtls_md_info_from_string(q);
@@ -274,14 +328,39 @@
         }
     }
 
+    /* Set the MD algorithm to use for the signature in the CSR */
     mbedtls_x509write_csr_set_md_alg(&req, opt.md_alg);
 
+    /* Set the Key Usage Extension flags in the CSR */
     if (opt.key_usage || opt.force_key_usage == 1) {
-        mbedtls_x509write_csr_set_key_usage(&req, opt.key_usage);
+        ret = mbedtls_x509write_csr_set_key_usage(&req, opt.key_usage);
+
+        if (ret != 0) {
+            mbedtls_printf(" failed\n  !  mbedtls_x509write_csr_set_key_usage returned %d", ret);
+            goto exit;
+        }
     }
 
+    /* Set the Cert Type flags in the CSR */
     if (opt.ns_cert_type || opt.force_ns_cert_type == 1) {
-        mbedtls_x509write_csr_set_ns_cert_type(&req, opt.ns_cert_type);
+        ret = mbedtls_x509write_csr_set_ns_cert_type(&req, opt.ns_cert_type);
+
+        if (ret != 0) {
+            mbedtls_printf(" failed\n  !  mbedtls_x509write_csr_set_ns_cert_type returned %d", ret);
+            goto exit;
+        }
+    }
+
+    /* Set the SubjectAltName in the CSR */
+    if (opt.san_list != NULL) {
+        ret = mbedtls_x509write_csr_set_subject_alternative_name(&req, opt.san_list);
+
+        if (ret != 0) {
+            mbedtls_printf(
+                " failed\n  !  mbedtls_x509write_csr_set_subject_alternative_name returned %d",
+                ret);
+            goto exit;
+        }
     }
 
     /*
@@ -363,6 +442,14 @@
     mbedtls_ctr_drbg_free(&ctr_drbg);
     mbedtls_entropy_free(&entropy);
 
+    cur = opt.san_list;
+    while (cur != NULL) {
+        prev = cur;
+        cur = cur->next;
+        mbedtls_free(prev);
+    }
+
+
     mbedtls_exit(exit_code);
 }
 #endif /* MBEDTLS_X509_CSR_WRITE_C && MBEDTLS_PK_PARSE_C && MBEDTLS_FS_IO &&