Improve programs/cert_write with a way to set extended key usages

Signed-off-by: Dave Rodgman <dave.rodgman@arm.com>
diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h
index 1ddc997..0a5d1f5 100644
--- a/include/mbedtls/x509_crt.h
+++ b/include/mbedtls/x509_crt.h
@@ -1145,6 +1145,19 @@
                                          unsigned int key_usage );
 
 /**
+ * \brief           Set the Extended Key Usage Extension
+ *                  (e.g. MBEDTLS_OID_SERVER_AUTH)
+ *
+ * \param ctx       CRT context to use
+ * \param exts      extended key usage extensions to set, a sequence of
+ *                  MBEDTLS_ASN1_OID objects
+ *
+ * \return          0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED
+ */
+int mbedtls_x509write_crt_set_ext_key_usage( mbedtls_x509write_cert *ctx,
+                                             const mbedtls_asn1_sequence *exts );
+
+/**
  * \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_crt.c b/library/x509write_crt.c
index 986e1fe..c48c32b 100644
--- a/library/x509write_crt.c
+++ b/library/x509write_crt.c
@@ -296,6 +296,40 @@
     return( 0 );
 }
 
+int mbedtls_x509write_crt_set_ext_key_usage( mbedtls_x509write_cert *ctx,
+                                             const mbedtls_asn1_sequence *exts )
+{
+    unsigned char buf[256];
+    unsigned char *c = buf + sizeof(buf);
+    int ret;
+    size_t len = 0;
+
+    if( exts == NULL )
+        return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+
+    while( exts != NULL )
+    {
+        if( exts->buf.tag != MBEDTLS_ASN1_OID )
+            return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( &c, buf, exts->buf.p, exts->buf.len ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, exts->buf.len ) );
+        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_OID ) );
+        exts = exts->next;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
+
+    ret = mbedtls_x509write_crt_set_extension( ctx,
+                         MBEDTLS_OID_EXTENDED_KEY_USAGE,
+                         MBEDTLS_OID_SIZE( MBEDTLS_OID_EXTENDED_KEY_USAGE ),
+                         1, c, len );
+    if( ret != 0 )
+        return( ret );
+
+    return( 0 );
+}
+
 int mbedtls_x509write_crt_set_ns_cert_type( mbedtls_x509write_cert *ctx,
                                     unsigned char ns_cert_type )
 {
diff --git a/programs/x509/cert_write.c b/programs/x509/cert_write.c
index 793982d..da0a624 100644
--- a/programs/x509/cert_write.c
+++ b/programs/x509/cert_write.c
@@ -1,3 +1,4 @@
+
 /*
  *  Certificate generation and signing
  *
@@ -47,6 +48,7 @@
 
 #include "mbedtls/x509_crt.h"
 #include "mbedtls/x509_csr.h"
+#include "mbedtls/oid.h"
 #include "mbedtls/entropy.h"
 #include "mbedtls/ctr_drbg.h"
 #include "mbedtls/md.h"
@@ -56,6 +58,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#define SET_OID(x, oid) \
+    do { x.len = MBEDTLS_OID_SIZE(oid); x.p = (unsigned char*)oid; } while( 0 )
+
 #if defined(MBEDTLS_X509_CSR_PARSE_C)
 #define USAGE_CSR                                                           \
     "    request_file=%%s         default: (empty)\n"                           \
@@ -81,6 +86,7 @@
 #define DFL_IS_CA               0
 #define DFL_MAX_PATHLEN         -1
 #define DFL_KEY_USAGE           0
+#define DFL_EXT_KEY_USAGE       0
 #define DFL_NS_CERT_TYPE        0
 #define DFL_VERSION             3
 #define DFL_AUTH_IDENT          1
@@ -138,6 +144,14 @@
     "                            key_cert_sign\n"  \
     "                            crl_sign\n"              \
     "                            (Considered for v3 only)\n"\
+    "    ext_key_usage=%%s        default: (empty)\n"      \
+    "                            Comma-separated-list of values:\n"     \
+    "                            serverAuth\n"             \
+    "                            clientAuth\n"             \
+    "                            codeSigning\n"            \
+    "                            emailProtection\n"        \
+    "                            timeStamping\n"           \
+    "                            OCSPSigning\n"            \
     "    ns_cert_type=%%s         default: (empty)\n"       \
     "                            Comma-separated-list of values:\n"     \
     "                            ssl_client\n"            \
@@ -176,6 +190,7 @@
     int version;                /* CRT version                          */
     mbedtls_md_type_t md;       /* Hash used for signing                */
     unsigned char key_usage;    /* key usage flags                      */
+    mbedtls_asn1_sequence *ext_key_usage; /* extended key usages        */
     unsigned char ns_cert_type; /* NS cert type                         */
 } opt;
 
@@ -227,6 +242,7 @@
 #endif
     mbedtls_x509write_cert crt;
     mbedtls_mpi serial;
+    mbedtls_asn1_sequence *ext_key_usage;
     mbedtls_entropy_context entropy;
     mbedtls_ctr_drbg_context ctr_drbg;
     const char *pers = "crt example app";
@@ -269,6 +285,7 @@
     opt.is_ca               = DFL_IS_CA;
     opt.max_pathlen         = DFL_MAX_PATHLEN;
     opt.key_usage           = DFL_KEY_USAGE;
+    opt.ext_key_usage       = DFL_EXT_KEY_USAGE;
     opt.ns_cert_type        = DFL_NS_CERT_TYPE;
     opt.version             = DFL_VERSION - 1;
     opt.md                  = DFL_DIGEST;
@@ -426,6 +443,35 @@
                 q = r;
             }
         }
+        else if( strcmp( p, "ext_key_usage" ) == 0 )
+        {
+            while( q != NULL )
+            {
+                if( ( r = strchr( q, ',' ) ) != NULL )
+                    *r++ = '\0';
+
+                ext_key_usage = mbedtls_calloc( 1, sizeof(mbedtls_asn1_sequence) );
+                ext_key_usage->next = opt.ext_key_usage;
+                ext_key_usage->buf.tag = MBEDTLS_ASN1_OID;
+                if( strcmp( q, "serverAuth" ) == 0 )
+                    SET_OID( ext_key_usage->buf, MBEDTLS_OID_SERVER_AUTH );
+                else if( strcmp( q, "clientAuth" ) == 0 )
+                    SET_OID( ext_key_usage->buf, MBEDTLS_OID_CLIENT_AUTH );
+                else if( strcmp( q, "codeSigning" ) == 0 )
+                    SET_OID( ext_key_usage->buf, MBEDTLS_OID_CODE_SIGNING );
+                else if( strcmp( q, "emailProtection" ) == 0 )
+                    SET_OID( ext_key_usage->buf, MBEDTLS_OID_EMAIL_PROTECTION );
+                else if( strcmp( q, "timeStamping" ) == 0 )
+                    SET_OID( ext_key_usage->buf, MBEDTLS_OID_TIME_STAMPING );
+                else if( strcmp( q, "OCSPSigning" ) == 0 )
+                    SET_OID( ext_key_usage->buf, MBEDTLS_OID_OCSP_SIGNING );
+                else
+                    goto usage;
+                opt.ext_key_usage = ext_key_usage;
+
+                q = r;
+            }
+        }
         else if( strcmp( p, "ns_cert_type" ) == 0 )
         {
             while( q != NULL )
@@ -744,6 +790,22 @@
         mbedtls_printf( " ok\n" );
     }
 
+    if( opt.ext_key_usage )
+    {
+        mbedtls_printf( "  . Adding the Extended Key Usage extension ..." );
+        fflush( stdout );
+
+        ret = mbedtls_x509write_crt_set_ext_key_usage( &crt, opt.ext_key_usage );
+        if( ret != 0 )
+        {
+            mbedtls_strerror( ret, buf, 1024 );
+            mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_ext_key_usage returned -0x%02x - %s\n\n", -ret, buf );
+            goto exit;
+        }
+
+        mbedtls_printf( " ok\n" );
+    }
+
     if( opt.version == MBEDTLS_X509_CRT_VERSION_3 &&
         opt.ns_cert_type != 0 )
     {