+@@ -1219,6 +1285,98 @@ cleanup:
+ }
+ #endif /* MBEDTLS_FS_IO */
+
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static const char x509_directory_name_label[] = "directoryName=(";
++static const char x509_directory_name_epilogue[] = ")";
++
++/* Length of label constant excluding terminating null. */
++#define LABEL_LEN( label ) ( sizeof ( label ) - 1 )
++
++static int x509_info_subject_alt_name( char **buf, size_t *size,
++ const mbedtls_x509_general_names *subject_alt_name )
++{
++ int ret;
++ size_t i;
++ size_t n = *size;
++ char *p = *buf;
++ const mbedtls_x509_general_names *cur = subject_alt_name;
++ const char *sep = "";
++ size_t sep_len = 0;
++
++ while( cur != NULL )
++ {
++ switch ( cur->general_name.name_type )
++ {
++ case MBEDTLS_X509_GENERALNAME_DNSNAME:
++ i = cur->general_name.dns_name.len + sep_len;
++
++ if( i >= n )
++ {
++ *p = '\0';
++ return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++ }
++
++ n -= i;
++ for( i = 0; i < sep_len; i++ )
++ *p++ = sep[i];
++ for( i = 0; i < cur->general_name.dns_name.len; i++ )
++ *p++ = cur->general_name.dns_name.p[i];
++
++ break;
++
++ case MBEDTLS_X509_GENERALNAME_DIRECTORYNAME:
++ i = sep_len + LABEL_LEN( x509_directory_name_label );
++ if( i >= n )
++ {
++ *p = '\0';
++ return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++ }
++
++ n -= i;
++ for( i = 0; i < sep_len; i++ )
++ *p++ = sep[i];
++ for( i = 0; i < LABEL_LEN( x509_directory_name_label ); i++ )
++ *p++ = x509_directory_name_label[i];
++
++ ret = mbedtls_x509_dn_gets( p, n, cur->general_name.directory_name );
++ if( ret < 0 || ( (size_t) ret ) >= n )
++ {
++ *p = '\0';
++ return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++ }
++
++ n -= ret;
++ p += ret;
++
++ i = LABEL_LEN( x509_directory_name_epilogue );
++
++ if( i >= n )
++ {
++ *p = '\0';
++ return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++ }
++
++ n -= i;
++ for( i = 0; i < LABEL_LEN( x509_directory_name_epilogue ); i++ )
++ *p++ = x509_directory_name_epilogue[i];
++
++ break;
++ }
++
++ sep = ", ";
++ sep_len = 2;
++
++ cur = cur->next;
++ }
++
++ *p = '\0';
++
++ *size = n;
++ *buf = p;
++
++ return( 0 );
++}
++#else
+ static int x509_info_subject_alt_name( char **buf, size_t *size,
+ const mbedtls_x509_sequence *subject_alt_name )
+ {
+@@ -1256,6 +1414,7 @@ static int x509_info_subject_alt_name( char **buf, size_t *size,
+
+ return( 0 );
+ }
++#endif
+
+ #define PRINT_ITEM(i) \
+ { \
+@@ -2188,7 +2347,11 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
+ int pathlen = 0, selfsigned = 0;
+ mbedtls_x509_crt *parent;
+ mbedtls_x509_name *name;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ mbedtls_x509_general_names *cur = NULL;
++#else
+ mbedtls_x509_sequence *cur = NULL;
++#endif
+ mbedtls_pk_type_t pk_type;
+
+ if( profile == NULL )
+@@ -2207,6 +2370,22 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
+
+ while( cur != NULL )
+ {
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ /* Only consider dNSName subject alternative names for this check; ignore other types. */
++ if ( cur->general_name.name_type == MBEDTLS_X509_GENERALNAME_DNSNAME )
++ {
++ if ( cur->general_name.dns_name.len == cn_len &&
++ x509_memcasecmp( cn, cur->general_name.dns_name.p, cn_len ) == 0 )
++ break;
++
++ if ( cur->general_name.dns_name.len > 2 &&
++ memcmp( cur->general_name.dns_name.p, "*.", 2 ) == 0 &&
++ x509_check_wildcard( cn, &cur->general_name.dns_name ) == 0 )
++ {
++ break;
++ }
++ }
++#else
+ if( cur->buf.len == cn_len &&
+ x509_memcasecmp( cn, cur->buf.p, cn_len ) == 0 )
+ break;
+@@ -2217,6 +2396,7 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
+ {
+ break;
+ }
++#endif
+
+ cur = cur->next;
+ }
+@@ -2318,6 +2498,10 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
+ mbedtls_x509_crt *cert_prv;
+ mbedtls_x509_name *name_cur;
+ mbedtls_x509_name *name_prv;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ mbedtls_x509_general_names *san_cur;
++ mbedtls_x509_general_names *san_prv;
++#endif
+ mbedtls_x509_sequence *seq_cur;
+ mbedtls_x509_sequence *seq_prv;
+
+@@ -2359,6 +2543,38 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
+ mbedtls_free( seq_prv );
+ }
+
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ if ( cert_cur->subject_alt_names.general_name.name_type == MBEDTLS_X509_GENERALNAME_DIRECTORYNAME )
++ {
++ name_cur = cert_cur->subject_alt_names.general_name.directory_name;
++ while ( name_cur != NULL )
++ {
++ name_prv = name_cur;
++ name_cur = name_cur->next;
++ mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
++ mbedtls_free( name_prv );
++ }
++ }
++ san_cur = cert_cur->subject_alt_names.next;
++ while ( san_cur != NULL )
++ {
++ san_prv = san_cur;
++ san_cur = san_cur->next;
++ if ( san_prv->general_name.name_type == MBEDTLS_X509_GENERALNAME_DIRECTORYNAME )
++ {
++ name_cur = san_prv->general_name.directory_name;
++ while ( name_cur != NULL )
++ {
++ name_prv = name_cur;
++ name_cur = name_cur->next;
++ mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
++ mbedtls_free( name_prv );
++ }
++ }
++ mbedtls_zeroize( san_prv, sizeof( mbedtls_x509_general_names ));
++ mbedtls_free( san_prv );
++ }
++#else
+ seq_cur = cert_cur->subject_alt_names.next;
+ while( seq_cur != NULL )
+ {
+@@ -2367,6 +2583,7 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
+ mbedtls_zeroize( seq_prv, sizeof( mbedtls_x509_sequence ) );
+ mbedtls_free( seq_prv );
+ }
++#endif
+
+ if( cert_cur->raw.p != NULL )
+ {
+diff --git a/library/x509write_crt.c b/library/x509write_crt.c
+index d1d9a22..55f194d 100644
+--- a/library/x509write_crt.c
++++ b/library/x509write_crt.c
+@@ -263,6 +263,63 @@ int mbedtls_x509write_crt_set_ns_cert_type( mbedtls_x509write_cert *ctx,
+ return( 0 );
+ }
+
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static int x509write_crt_set_subject_alt_name( unsigned char **c, unsigned char *buf,
++ const mbedtls_x509_general_name *name )
++{
++ int ret;
++ size_t len = 0;
++
++ switch ( name->name_type )
++ {
++ case MBEDTLS_X509_GENERALNAME_DNSNAME:
++ MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( c, buf, name->dns_name.p, name->dns_name.len ) );
++ MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( c, buf, name->dns_name.len ) );
++ MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( c, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) );
++ break;
++
++ case MBEDTLS_X509_GENERALNAME_DIRECTORYNAME:
++ MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( c, buf, name->directory_name ) );
++ 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_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 4 ) );
++ break;
++
++ default:
++ return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
++ }
++
++ return( (int)len );
++}
++
++int mbedtls_x509write_crt_set_subject_alt_names( mbedtls_x509write_cert *ctx,
++ const mbedtls_x509_general_names *names )
++{
++ int ret;
++ unsigned char buf[2048];
++ unsigned char *c = buf + sizeof( buf );
++ size_t len = 0;
++ const mbedtls_x509_general_names *cur;
++
++ for ( cur = names; cur != NULL; cur = cur->next )
++ {
++ MBEDTLS_ASN1_CHK_ADD( len, x509write_crt_set_subject_alt_name( &c, buf, &cur->general_name ) );
++ }
++
++ 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_SUBJECT_ALT_NAME,
++ MBEDTLS_OID_SIZE( MBEDTLS_OID_SUBJECT_ALT_NAME ),
++ 0, c, len );
++
++ if( ret != 0 )
++ return( ret );
++
++ return( 0 );
++}
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++
+ static int x509_write_time( unsigned char **p, unsigned char *start,
+ const char *time, size_t size )
+ {
+diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
+index a1d71e1..b334656 100644
+--- a/programs/ssl/ssl_client2.c
++++ b/programs/ssl/ssl_client2.c
+@@ -58,6 +58,7 @@ int main( void )
+ #include "mbedtls/error.h"
+ #include "mbedtls/debug.h"
+ #include "mbedtls/timing.h"
++#include "mbedtls/oid.h"
+
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -103,6 +104,8 @@ int main( void )
+ #define DFL_FALLBACK -1
+ #define DFL_EXTENDED_MS -1
+ #define DFL_ETM -1
++#define DFL_EKU_CLIENT ""
++#define DFL_EKU_SERVER ""
+
+ #define GET_REQUEST "GET %s HTTP/1.0\r\nExtra-header: "
+ #define GET_REQUEST_END "\r\n\r\n"
+@@ -222,6 +225,14 @@ int main( void )
+ #define USAGE_ECJPAKE ""
+ #endif
+
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++#define USAGE_EKU \
++ " eku=%%s-%%s default: client-server\n" \
++ " options for each: server, client, codesign\n"
++#else
++#define USAGE_EKU ""
++#endif
++
+ #define USAGE \
+ "\n usage: ssl_client2 param=<>...\n" \
+ "\n acceptable parameters:\n" \
+@@ -261,6 +272,7 @@ int main( void )
+ USAGE_ETM \
+ USAGE_RECSPLIT \
+ USAGE_DHMLEN \
++ USAGE_EKU \
+ "\n" \
+ " arc4=%%d default: (library default: 0)\n" \
+ " min_version=%%s default: (library default: tls1)\n" \
+@@ -317,6 +329,10 @@ struct options
+ int fallback; /* is this a fallback connection? */
+ int extended_ms; /* negotiate extended master secret? */
+ int etm; /* negotiate encrypt then mac? */
++ const char *eku_cli; /* EKU to check for in client cert */
++ size_t eku_cli_len; /* length of eku_cli */
++ const char *eku_srv; /* EKU to check for in server cert */
++ size_t eku_srv_len; /* length of eku_srv */
+ } opt;
+
+ static void my_debug( void *ctx, int level,
+@@ -507,6 +523,10 @@ int main( int argc, char *argv[] )
+ opt.fallback = DFL_FALLBACK;
+ opt.extended_ms = DFL_EXTENDED_MS;
+ opt.etm = DFL_ETM;
++ opt.eku_cli = DFL_EKU_CLIENT;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE( DFL_EKU_CLIENT );
++ opt.eku_srv = DFL_EKU_SERVER;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE( DFL_EKU_SERVER );
+
+ for( i = 1; i < argc; i++ )
+ {
+@@ -797,6 +817,47 @@ int main( int argc, char *argv[] )
+ if( opt.dhmlen < 0 )
+ goto usage;
+ }
++ else if ( strcmp(p, "eku") == 0 )
++ {
++ if ( ( p = strchr( q, '-' ) ) == NULL )
++ goto usage;
++ *p++ = '\0';
++ if ( strcmp( q, "server" ) == 0 )
++ {
++ opt.eku_cli = MBEDTLS_OID_SERVER_AUTH;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH);
++ }
++ else if ( strcmp( q, "client" ) == 0 )
++ {
++ opt.eku_cli = MBEDTLS_OID_CLIENT_AUTH;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH);
++ }
++ else if ( strcmp( q, "codesign" ) == 0 )
++ {
++ opt.eku_cli = MBEDTLS_OID_CODE_SIGNING;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CODE_SIGNING);
++ }
++ else
++ goto usage;
++
++ if ( strcmp( p, "server" ) == 0 )
++ {
++ opt.eku_srv = MBEDTLS_OID_SERVER_AUTH;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH);
++ }
++ else if ( strcmp( p, "client" ) == 0 )
++ {
++ opt.eku_srv = MBEDTLS_OID_CLIENT_AUTH;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH);
++ }
++ else if ( strcmp( p, "codesign" ) == 0 )
++ {
++ opt.eku_srv = MBEDTLS_OID_CODE_SIGNING;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CODE_SIGNING);
++ }
++ else
++ goto usage;
++ }
+ else
+ goto usage;
+ }
+@@ -1088,6 +1149,19 @@ int main( int argc, char *argv[] )
+ goto exit;
+ }
+
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++ if ( opt.eku_cli_len > 0 && opt.eku_srv_len > 0 )
++ {
++ if( ( ret = mbedtls_ssl_conf_ekus( &conf,
++ opt.eku_cli, opt.eku_cli_len,
++ opt.eku_srv, opt.eku_srv_len ) ) != 0 )
++ {
++ mbedtls_printf( " failed\n ! mbedtls_ssl_config_ekus returned -0x%x\n\n", -ret );
++ goto exit;
++ }
++ }
++#endif
++
+ #if defined(MBEDTLS_X509_CRT_PARSE_C)
+ if( opt.debug_level > 0 )
+ mbedtls_ssl_conf_verify( &conf, my_verify, NULL );
+diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
+index 18bda59..e65576b 100644
+--- a/programs/ssl/ssl_server2.c
++++ b/programs/ssl/ssl_server2.c
+@@ -59,6 +59,7 @@ int main( void )
+ #include "mbedtls/error.h"
+ #include "mbedtls/debug.h"
+ #include "mbedtls/timing.h"
++#include "mbedtls/oid.h"
+
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -136,6 +137,8 @@ int main( void )
+ #define DFL_BADMAC_LIMIT -1
+ #define DFL_EXTENDED_MS -1
+ #define DFL_ETM -1
++#define DFL_EKU_CLIENT ""
++#define DFL_EKU_SERVER ""
+
+ #define LONG_RESPONSE "<p>01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
+ "02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
+@@ -304,6 +307,14 @@ int main( void )
+ #define USAGE_ECJPAKE ""
+ #endif
+
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++#define USAGE_EKU \
++ " eku=%%s-%%s default: client-server\n" \
++ " options for each: server, client, codesign\n"
++#else
++#define USAGE_EKU ""
++#endif
++
+ #define USAGE \
+ "\n usage: ssl_server2 param=<>...\n" \
+ "\n acceptable parameters:\n" \
+@@ -338,6 +349,7 @@ int main( void )
+ USAGE_ALPN \
+ USAGE_EMS \
+ USAGE_ETM \
++ USAGE_EKU \
+ "\n" \
+ " arc4=%%d default: (library default: 0)\n" \
+ " min_version=%%s default: (library default: tls1)\n" \
+@@ -400,6 +412,10 @@ struct options
+ uint32_t hs_to_min; /* Initial value of DTLS handshake timer */
+ uint32_t hs_to_max; /* Max value of DTLS handshake timer */
+ int badmac_limit; /* Limit of records with bad MAC */
++ const char *eku_cli; /* EKU to check for in client cert */
++ size_t eku_cli_len; /* length of eku_cli */
++ const char *eku_srv; /* EKU to check for in server cert */
++ size_t eku_srv_len; /* length of eku_srv */
+ } opt;
+
+ static void my_debug( void *ctx, int level,
+@@ -943,6 +959,10 @@ int main( int argc, char *argv[] )
+ opt.badmac_limit = DFL_BADMAC_LIMIT;
+ opt.extended_ms = DFL_EXTENDED_MS;
+ opt.etm = DFL_ETM;
++ opt.eku_cli = DFL_EKU_CLIENT;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE( DFL_EKU_CLIENT );
++ opt.eku_srv = DFL_EKU_SERVER;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE( DFL_EKU_SERVER );
+
+ for( i = 1; i < argc; i++ )
+ {
+@@ -1232,6 +1252,47 @@ int main( int argc, char *argv[] )
+ {
+ opt.sni = q;
+ }
++ else if( strcmp( p, "eku" ) == 0 )
++ {
++ if( ( p = strchr( q, '-' ) ) == NULL )
++ goto usage;
++ *p++ = '\0';
++ if( strcmp( q, "server" ) == 0 )
++ {
++ opt.eku_cli = MBEDTLS_OID_SERVER_AUTH;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH );
++ }
++ else if( strcmp( q, "client" ) == 0 )
++ {
++ opt.eku_cli = MBEDTLS_OID_CLIENT_AUTH;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CLIENT_AUTH );
++ }
++ else if( strcmp( q, "codesign" ) == 0 )
++ {
++ opt.eku_cli = MBEDTLS_OID_CODE_SIGNING;
++ opt.eku_cli_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CODE_SIGNING );
++ }
++ else
++ goto usage;
++
++ if( strcmp( p, "server" ) == 0 )
++ {
++ opt.eku_srv = MBEDTLS_OID_SERVER_AUTH;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH );
++ }
++ else if( strcmp( p, "client" ) == 0 )
++ {
++ opt.eku_srv = MBEDTLS_OID_CLIENT_AUTH;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CLIENT_AUTH );
++ }
++ else if( strcmp( p, "codesign" ) == 0 )
++ {
++ opt.eku_srv = MBEDTLS_OID_CODE_SIGNING;
++ opt.eku_srv_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CODE_SIGNING );
++ }
++ else
++ goto usage;
++ }
+ else
+ goto usage;
+ }
+@@ -1608,6 +1669,20 @@ int main( int argc, char *argv[] )
+ goto exit;
+ }
+
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++ if ( opt.eku_cli_len > 0 && opt.eku_srv_len > 0 )
++ {
++ if( ( ret = mbedtls_ssl_conf_ekus( &conf,
++ opt.eku_cli, opt.eku_cli_len,
++ opt.eku_srv, opt.eku_srv_len ) ) != 0 )
++ {
++ mbedtls_printf( " failed\n ! mbedtls_ssl_config_ekus returned -0x%x\n\n", -ret );
++ goto exit;
++ }
++ }
++#endif
++
++
+ if( opt.auth_mode != DFL_AUTH_MODE )
+ mbedtls_ssl_conf_authmode( &conf, opt.auth_mode );
+
+diff --git a/programs/x509/cert_write.c b/programs/x509/cert_write.c
+index 66e5f1d..1405e71 100644
+--- a/programs/x509/cert_write.c
++++ b/programs/x509/cert_write.c
+@@ -66,6 +66,16 @@ int main( void )
+ #define USAGE_CSR ""
+ #endif /* MBEDTLS_X509_CSR_PARSE_C */
+
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++#define USAGE_SUBJ_ALT_NAME \
++ " subj_alt_name=%%s default: (empty)\n" \
++ " Comma-separated-list of values:\n" \
++ " dns_name=%%s\n" \
++ " directory_name=(OU=%%s;CN=%%s;...)\n"
++#else
++#define USAGE_SUBJ_ALT_NAME ""
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++
+ #define DFL_ISSUER_CRT ""
+ #define DFL_REQUEST_FILE ""
+ #define DFL_SUBJECT_KEY "subject.key"
+@@ -127,6 +137,7 @@ int main( void )
+ " ssl_ca\n" \
+ " email_ca\n" \
+ " object_signing_ca\n" \
++ USAGE_SUBJ_ALT_NAME \
+ "\n"
+
+ /*
+@@ -151,6 +162,9 @@ struct options
+ int max_pathlen; /* maximum CA path length */
+ unsigned char key_usage; /* key usage flags */
+ unsigned char ns_cert_type; /* NS cert type */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ mbedtls_x509_general_names subj_alt_names; /* Subject alternative names */
++#endif
+ } opt;
+
+ int write_certificate( mbedtls_x509write_cert *crt, const char *output_file,
+@@ -182,6 +196,58 @@ int write_certificate( mbedtls_x509write_cert *crt, const char *output_file,
+ return( 0 );
+ }
+
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static int add_subj_alt_name( mbedtls_x509_general_names **cur, const mbedtls_x509_general_name *add )
++{
++ mbedtls_x509_general_names *new_cur = *cur;
++
++ if ( new_cur->general_name.name_type != 0 )
++ {
++ if (new_cur->next != NULL)
++ return( -1 );
++
++ new_cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_general_names ) );
++
++ if (new_cur->next == NULL)
++ return( -1 );
++
++ new_cur = new_cur->next;
++ }
++
++ memcpy( &new_cur->general_name, add, sizeof( mbedtls_x509_general_name ) );
++
++ *cur = new_cur;
++
++ return( 0 );
++}
++
++static void subj_alt_names_free( mbedtls_x509_general_names *names )
++{
++ mbedtls_x509_general_names *cur = names;
++ mbedtls_x509_general_names *prv;
++
++ while ( cur != NULL )
++ {
++ prv = cur;
++ cur = cur->next;
++
++ if ( prv->general_name.name_type == MBEDTLS_X509_GENERALNAME_DIRECTORYNAME )
++ {
++ mbedtls_asn1_free_named_data_list( &prv->general_name.directory_name );
++ }
++
++ /*
++ * The first node is part of the opt struct and not heap allocated, so don't free it.
++ * Every other loop, free the node.
++ */
++ if ( prv != names )
++ {
++ mbedtls_free( prv );
++ }
++ }
++}
++#endif
++
+ int main( int argc, char *argv[] )
+ {
+ int ret = 0;
+@@ -202,6 +268,10 @@ int main( int argc, char *argv[] )
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ const char *pers = "crt example app";
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ mbedtls_x509_general_names *name_cur = &opt.subj_alt_names;
++ mbedtls_x509_general_name name_tmp;
++#endif
+
+ /*
+ * Set to sane values
+@@ -243,6 +313,9 @@ int main( int argc, char *argv[] )
+ opt.max_pathlen = DFL_MAX_PATHLEN;
+ opt.key_usage = DFL_KEY_USAGE;
+ opt.ns_cert_type = DFL_NS_CERT_TYPE;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ memset( &opt.subj_alt_names, 0, sizeof( opt.subj_alt_names ) );
++#endif
+
+ for( i = 1; i < argc; i++ )
+ {
+@@ -358,6 +431,86 @@ int main( int argc, char *argv[] )
+ q = r;
+ }
+ }
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ else if( strcmp( p, "subj_alt_name" ) == 0 )
++ {
++ while( q != NULL )
++ {
++ char *s;
++
++ if( ( r = strchr( q, ',' ) ) != NULL )
++ *r++ = '\0';
++
++ if( ( s = strchr( q, '=' ) ) == NULL )
++ goto usage;
++
++ *s++ = '\0';
++
++ if( strcmp( q, "dns_name" ) == 0 )
++ {
++ name_tmp.name_type = MBEDTLS_X509_GENERALNAME_DNSNAME;
++ name_tmp.dns_name.len = strlen( s );
++ name_tmp.dns_name.p = (unsigned char *)s;
++ /* tag field doesn't need to be set for writing. */
++ }
++ else if( strcmp( q, "directory_name" ) == 0 )
++ {
++ char *rp, *tmp;
++
++ if ( *s != '(' )
++ goto usage;
++
++ if ( ( rp = strchr( s + 1, ')' ) ) == NULL )
++ goto usage;
++
++ /*
++ * Replace semicolons in the parenthesized list with commas and temporarily
++ * terminate with null so we can use mbedtls_x509_string_to_names, call it,
++ * and then change them back so the commas don't interfere with later parsing.
++ */
++
++ for ( tmp = s + 1; tmp < rp; tmp++ )
++ {
++ if ( *tmp == ';' )
++ {
++ *tmp = ',';
++ }
++ }
++
++ *rp = '\0';
++
++ name_tmp.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
++ name_tmp.directory_name = NULL;
++ ret = mbedtls_x509_string_to_names( &name_tmp.directory_name, s + 1 );
++
++ if ( ret < 0 )
++ {
++ mbedtls_strerror( ret, buf, 1024 );
++ mbedtls_printf( " failed\n ! mbedtls_x509_string_to_names returned %d - %s\n", ret, buf );
++ goto exit;
++ }
++
++ for ( tmp = s + 1; tmp < rp; tmp++ )
++ {
++ if ( *tmp == ',' )
++ {
++ *tmp = ';';
++ }
++ }
++
++ *rp = ')';
++
++ }
++ else
++ goto usage;
++
++ if ( add_subj_alt_name( &name_cur, &name_tmp ) != 0 )
++ goto exit;
++
++ q = r;
++ }
++ }
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
+ else
+ goto usage;
+ }
+@@ -632,6 +785,24 @@ int main( int argc, char *argv[] )
+ mbedtls_printf( " ok\n" );
+ }
+
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ if ( opt.subj_alt_names.general_name.name_type )
++ {
++ mbedtls_printf( " . Adding the Subject Alternative Name extension ..." );
++ fflush( stdout );
++
++ ret = mbedtls_x509write_crt_set_subject_alt_names( &crt, &opt.subj_alt_names );
++ if ( ret != 0 )
++ {
++ mbedtls_strerror( ret, buf, 1024 );
++ mbedtls_printf( " failed\n ! mbedtls_x509write_crt_set_subject_alt_names returned -0x%02x - %s\n\n", -ret, buf );
++ goto exit;
++ }
++
++ mbedtls_printf( " ok\n" );
++ }
++#endif
++
+ /*
+ * 1.2. Writing the request
+ */
+@@ -649,6 +820,9 @@ int main( int argc, char *argv[] )
+ mbedtls_printf( " ok\n" );
+
+ exit:
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++ subj_alt_names_free( &opt.subj_alt_names );
++#endif
+ mbedtls_x509write_crt_free( &crt );
+ mbedtls_pk_free( &loaded_subject_key );
+ mbedtls_pk_free( &loaded_issuer_key );
+diff --git a/tests/data_files/server1-bothnames.crt b/tests/data_files/server1-bothnames.crt
+new file mode 100644
+index 0000000..b7e1f40
+--- /dev/null
++++ b/tests/data_files/server1-bothnames.crt
+@@ -0,0 +1,22 @@
++-----BEGIN CERTIFICATE-----
++MIIDoTCCAomgAwIBAgIBATANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER
++MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
++MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G
++A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN
++BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/
++uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD
++d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf
++CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr
++lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w
++bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB
++o4GuMIGrMAkGA1UdEwQCMAAwHQYDVR0OBBYEFB901j8pwXR0RTsFEiw9qL1DWQKm
++MB8GA1UdIwQYMBaAFLRa5KWz3tJS9rnVppUP6z68x/3/MF4GA1UdEQRXMFWkPzA9
++MQswCQYDVQQGEwJOTDERMA8GA1UEChMIUG9sYXJTU0wxGzAZBgNVBAMTElBvbGFy
++U1NMIFNlcnZlciAxQYISb3RoZXIucG9sYXJzc2wub3JnMA0GCSqGSIb3DQEBCwUA
++A4IBAQCUqTN1WjWPMNpflyDTOEq+DEbNgj83ghhB6cCxHCGNT12CpBvQDh4bj5QC
++c77x8kN5fwy+HO1QIwHhBtjLC0IUfKvJUaTbwvIDH1NotrxSg/ft3HG/ijbTrX9/
++kxDXDcHkOlNX4Y5cJnU7UBm81n7WInGvGqperXTIDgW5CEkQp2ErtxvSZkNtYe82
++0DwVIQwjbF5hxFxQPGEzM1AANMyzOYKNtg6S7ElbtU4NQqvcBNr3vaG5ipMrkf2u
++kfJZAs+bPo+UyuFBCRGmOMgOAUrK4WvvxtqKFZDdty+pyTiR25x55Upfum7jxeBt
++Nnu2TUYcY472fXbZA5Y7/gGnmmRe
++-----END CERTIFICATE-----
+diff --git a/tests/data_files/server1-directoryname.crt b/tests/data_files/server1-directoryname.crt
+new file mode 100644
+index 0000000..b220230
+--- /dev/null
++++ b/tests/data_files/server1-directoryname.crt
+@@ -0,0 +1,22 @@
++-----BEGIN CERTIFICATE-----
++MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER
++MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
++MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G
++A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN
++BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/
++uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD
++d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf
++CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr
++lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w
++bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB
++o4GaMIGXMAkGA1UdEwQCMAAwHQYDVR0OBBYEFB901j8pwXR0RTsFEiw9qL1DWQKm
++MB8GA1UdIwQYMBaAFLRa5KWz3tJS9rnVppUP6z68x/3/MEoGA1UdEQRDMEGkPzA9
++MQswCQYDVQQGEwJOTDERMA8GA1UEChMIUG9sYXJTU0wxGzAZBgNVBAMTElBvbGFy
++U1NMIFNlcnZlciAxQTANBgkqhkiG9w0BAQsFAAOCAQEAYb6K1UU+6eUdfMD/vayr
++36VEOFd2SA2AxBgj2GKAB7IyKgT6bZTo6SyfJWtvWlp7Ofjc9nQ2iObNRqN1499t
++DmAb3IM9yXuG00GVVg5brC3RtX1w4KJci2z+R2FX+HPAJW5E2rlU7OIm0FfF2aV6
++0qhHeJSCwi8DkS6U6dQW3vmFXf4sEVtyRkY5Zh3pEcSb3mNpvxNdq1Ro8TRmOGHe
++HrHAWxgBTeUJ0Dv/TNn39FNyvzwvWDFFxhwgs4+43N7FuSM6XUA86mkwLAsrjfWx
++fKNjq7fpJX2dS08ISV/RismU6RGt/TW4PXrtvEIfy1+kaMDSt+ffz1C1cuSen7xz
++6w==
++-----END CERTIFICATE-----
+diff --git a/tests/data_files/server1-dnsname.crt b/tests/data_files/server1-dnsname.crt
+new file mode 100644
+index 0000000..f3bad52
+--- /dev/null
++++ b/tests/data_files/server1-dnsname.crt
+@@ -0,0 +1,21 @@
++-----BEGIN CERTIFICATE-----
++MIIDXjCCAkagAwIBAgIBATANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER
++MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
++MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G
++A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN
++BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/
++uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD
++d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf
++CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr
++lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w
++bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB
++o2wwajAJBgNVHRMEAjAAMB0GA1UdDgQWBBQfdNY/KcF0dEU7BRIsPai9Q1kCpjAf
++BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zAdBgNVHREEFjAUghJvdGhl
++ci5wb2xhcnNzbC5vcmcwDQYJKoZIhvcNAQELBQADggEBAFTQDT3BP8qKss7yVjZM
++8RkFF9GuqgCYPWyyuixzxYU5OJzzo/g5ZQ37wfcOMIUA7Ygs0Pv/L0n5PzncJqwO
++mJpQRghbI4yp2qVsKpdx+SVTS9aDm/madjREbSzk/tXVskINbuaWQ/azKbB6MReu
++XXgcw/iROpKJl+WCVr0cy6EAWPd3QBNLYyoAKpMQT2HXfQT/AhEZJINSYrkJeHCt
++BgTGq8tvXE2OtcjTcmp+KIpkXA6A0laRx5up1xET9wDrdll5AEhXSXDzIEOwIBhJ
++3cExQNdWbO6JdjBi5Qaoq9o7ItQOFSTM6PvRC3MYRwN+WhFdb+4Kmvd5oIArRaHo
++MEM=
++-----END CERTIFICATE-----
+diff --git a/tests/data_files/server11-directoryname.crt b/tests/data_files/server11-directoryname.crt
+new file mode 100644
+index 0000000..d9e0a16
+--- /dev/null
++++ b/tests/data_files/server11-directoryname.crt
+@@ -0,0 +1,10 @@
++-----BEGIN CERTIFICATE-----
++MIIBVzCB/qADAgECAgkAkWvgYjFeWV0wCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI
++VGVzdENlcnQwHhcNMTYxMjEzMjMwNDM3WhcNMzAwODIyMjMwNDM3WjATMREwDwYD
++VQQDDAhUZXN0Q2VydDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOkrKfbStoGN
++oOUTet2ryyRul++FE++6vpEup83bHywiOO1a4JtDgzNJcjj8/uAKsECYQrI1T4Y/
++rpnYu8ff9VGjOzA5MDcGA1UdEQQwMC6kLDAqMRYwFAYDVQQLDA1OYW1lIEFzc2ln
++bmVyMRAwDgYDVQQDDAdNeSBSb2xlMAoGCCqGSM49BAMCA0gAMEUCIGdd/GGuoOXJ
++8Ipl3hy69gb35MmkwEQKuYrud+Qs5XfFAiEAsmEOP4KFZadL23XJfMfGuPn2MDLr
++G9lDDpiediVxGO0=
++-----END CERTIFICATE-----
+diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
+index 57155b8..7e754bb 100755
+--- a/tests/ssl-opt.sh
++++ b/tests/ssl-opt.sh
+@@ -2526,6 +2526,16 @@ run_test "extKeyUsage cli: codeSign -> fail" \
+ -c "Processing of the Certificate handshake message failed" \
+ -C "Ciphersuite is TLS-"
+
++run_test "extKeyUsage cli: codeSign(requested) -> OK " \
++ "$O_SRV -key data_files/server5.key \
++ -cert data_files/server5.eku-cs.crt" \
++ "$P_CLI debug_level=1 eku=client-codesign" \
++ 0 \
++ -C "bad certificate (usage extensions)" \
++ -C "Processing of the Certificate handshake message failed" \
++ -c "Ciphersuite is TLS-"
++
++
+ # Tests for extendedKeyUsage, part 3: server-side checking of client cert
+
+ run_test "extKeyUsage cli-auth: clientAuth -> OK" \
+@@ -2568,6 +2578,14 @@ run_test "extKeyUsage cli-auth: codeSign -> fail (hard)" \
+ -s "bad certificate (usage extensions)" \
+ -s "Processing of the Certificate handshake message failed"
+
++run_test "extKeyUsage cli-auth: codeSign(requested) -> OK" \
++ "$P_SRV debug_level=1 auth_mode=required eku=codesign-server" \
++ "$O_CLI -key data_files/server5.key \
++ -cert data_files/server5.eku-cs.crt" \
++ 0 \
++ -S "bad certificate (usage extensions)" \
++ -S "Processing of the Certificate handshake message failed"
++
+ # Tests for DHM parameters loading
+
+ run_test "DHM parameters: reference" \
+diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
+index c829823..30fff78 100644
+--- a/tests/suites/test_suite_x509parse.data
++++ b/tests/suites/test_suite_x509parse.data
+@@ -122,6 +122,22 @@ X509 certificate v1 with extension
+ depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3:MBEDTLS_SHA1_C
+ x509_cert_info:"data_files/cert_v1_with_ext.crt":"cert. version \: 1\nserial number \: BD\:ED\:44\:C7\:D2\:3E\:C2\:A4\nissuer name \: C=XX, ST=XX, L=XX, O=XX, OU=XX, emailAddress=admin@identity-check.org, CN=identity-check.org\nsubject name \: C=XX, ST=XX, L=XX, O=XX, OU=XX, emailAddress=admin@identity-check.org, CN=identity-check.org\nissued on \: 2013-07-04 16\:17\:02\nexpires on \: 2014-07-04 16\:17\:02\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nsubject alt name \: identity-check.org, www.identity-check.org\n"
+
++X509 Certificate information EC directoryName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server11-directoryname.crt":"cert. version \: 3\nserial number \: 91\:6B\:E0\:62\:31\:5E\:59\:5D\nissuer name \: CN=TestCert\nsubject name \: CN=TestCert\nissued on \: 2016-12-13 23\:04\:37\nexpires on \: 2030-08-22 23\:04\:37\nsigned using \: ECDSA with SHA256\nEC key size \: 256 bits\nsubject alt name \: directoryName=(OU=Name Assigner, CN=My Role)\n"
++
++X509 Certificate information dNSName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server1-dnsname.crt":"cert. version \: 3\nserial number \: 01\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=PolarSSL Server 1\nissued on \: 2011-02-12 14\:44\:06\nexpires on \: 2021-02-12 14\:44\:06\nsigned using \: RSA with SHA-256\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: other.polarssl.org\n"
++
++X509 Certificate information directoryName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server1-directoryname.crt":"cert. version \: 3\nserial number \: 01\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=PolarSSL Server 1\nissued on \: 2011-02-12 14\:44\:06\nexpires on \: 2021-02-12 14\:44\:06\nsigned using \: RSA with SHA-256\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: directoryName=(C=NL, O=PolarSSL, CN=PolarSSL Server 1A)\n"
++
++X509 Certificate information dNSName+directoryName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server1-bothnames.crt":"cert. version \: 3\nserial number \: 01\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=PolarSSL Server 1\nissued on \: 2011-02-12 14\:44\:06\nexpires on \: 2021-02-12 14\:44\:06\nsigned using \: RSA with SHA-256\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: directoryName=(C=NL, O=PolarSSL, CN=PolarSSL Server 1A), other.polarssl.org\n"
++
+ X509 CRL information #1
+ depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_SHA1_C
+ mbedtls_x509_crl_info:"data_files/crl_expired.pem":"CRL version \: 1\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nthis update \: 2011-02-20 10\:24\:19\nnext update \: 2011-02-20 11\:24\:19\nRevoked certificates\:\nserial number\: 01 revocation date\: 2011-02-12 14\:44\:07\nserial number\: 03 revocation date\: 2011-02-12 14\:44\:07\nsigned using \: RSA with SHA1\n"
+diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
+index d4d2a98..57c2ad2 100644
+--- a/tests/suites/test_suite_x509write.data
++++ b/tests/suites/test_suite_x509write.data
+@@ -58,6 +58,18 @@ Certificate write check Server1 SHA1, version 1
+ depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
+ 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":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA1:0:0:MBEDTLS_X509_CRT_VERSION_1:"data_files/server1.v1.crt"
+
++Certificate write check Server1 SHA256 with dNSName
++depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_crt_subj_alt_name_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":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA256:0:0:-1:1:"data_files/server1-dnsname.crt"
++
++Certificate write check Server1 SHA256 with directoryName
++depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_crt_subj_alt_name_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":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA256:0:0:-1:2:"data_files/server1-directoryname.crt"
++
++Certificate write check Server1 SHA256 with dNSName and directoryName
++depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_crt_subj_alt_name_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":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA256:0:0:-1:3:"data_files/server1-bothnames.crt"
++
+ 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
+
+diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
+index 89be31f..184ac2c 100644
+--- a/tests/suites/test_suite_x509write.function
++++ b/tests/suites/test_suite_x509write.function
+@@ -157,6 +157,131 @@ exit:
+ }
+ /* END_CASE */
+
++/* BEGIN_CASE depends_on:MBEDTLS_PEM_WRITE_C:MBEDTLS_X509_CRT_WRITE_C:MBEDTLS_SHA1_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++void x509_crt_subj_alt_name_check( char *subject_key_file, char *subject_pwd,
++ char *subject_name, char *issuer_key_file,
++ char *issuer_pwd, char *issuer_name,
++ char *serial_str, char *not_before, char *not_after,
++ int md_type, int key_usage, int cert_type, int ver, int subj_alt_names_type,
++ char *cert_check_file )
++{
++ mbedtls_pk_context subject_key, issuer_key;
++ mbedtls_x509write_cert crt;
++ unsigned char buf[4096];
++ unsigned char check_buf[5000];
++ mbedtls_mpi serial;
++ int ret;
++ size_t olen = 0, pem_len = 0;
++ int der_len = -1;
++ FILE *f;
++ rnd_pseudo_info rnd_info;
++
++ memset( &rnd_info, 0x2a, sizeof( rnd_pseudo_info ) );
++ mbedtls_mpi_init( &serial );
++ mbedtls_pk_init( &subject_key );
++ mbedtls_pk_init( &issuer_key );
++
++ TEST_ASSERT( mbedtls_pk_parse_keyfile( &subject_key, subject_key_file,
++ subject_pwd ) == 0 );
++ TEST_ASSERT( mbedtls_pk_parse_keyfile( &issuer_key, issuer_key_file,
++ issuer_pwd ) == 0 );
++ TEST_ASSERT( mbedtls_mpi_read_string( &serial, 10, serial_str ) == 0 );
++
++ mbedtls_x509write_crt_init( &crt );
++ if( ver != -1 )
++ mbedtls_x509write_crt_set_version( &crt, ver );
++ TEST_ASSERT( mbedtls_x509write_crt_set_serial( &crt, &serial ) == 0 );
++ TEST_ASSERT( mbedtls_x509write_crt_set_validity( &crt, not_before,
++ not_after ) == 0 );
++ mbedtls_x509write_crt_set_md_alg( &crt, md_type );
++ TEST_ASSERT( mbedtls_x509write_crt_set_issuer_name( &crt, issuer_name ) == 0 );
++ TEST_ASSERT( mbedtls_x509write_crt_set_subject_name( &crt, subject_name ) == 0 );
++ mbedtls_x509write_crt_set_subject_key( &crt, &subject_key );
++ mbedtls_x509write_crt_set_issuer_key( &crt, &issuer_key );
++
++ if( crt.version >= MBEDTLS_X509_CRT_VERSION_3 )
++ {
++ TEST_ASSERT( mbedtls_x509write_crt_set_basic_constraints( &crt, 0, 0 ) == 0 );
++ TEST_ASSERT( mbedtls_x509write_crt_set_subject_key_identifier( &crt ) == 0 );
++ TEST_ASSERT( mbedtls_x509write_crt_set_authority_key_identifier( &crt ) == 0 );
++ if( key_usage != 0 )
++ TEST_ASSERT( mbedtls_x509write_crt_set_key_usage( &crt, key_usage ) == 0 );
++ if( cert_type != 0 )
++ TEST_ASSERT( mbedtls_x509write_crt_set_ns_cert_type( &crt, cert_type ) == 0 );
++ if( subj_alt_names_type != 0 )
++ {
++ mbedtls_x509_general_names dns_name, directory_name;
++ mbedtls_x509_general_names *first = NULL;
++
++ memset( &dns_name, 0, sizeof( dns_name ) );
++ memset( &directory_name, 0, sizeof( directory_name ) );
++
++ if ( ( subj_alt_names_type & 1 ) == 1 )
++ {
++ dns_name.general_name.name_type = MBEDTLS_X509_GENERALNAME_DNSNAME;
++ /* Nothing will try to modify the memory pointed to by p, so this cast is safe. */
++ dns_name.general_name.dns_name.p = (unsigned char *) "other.polarssl.org";
++ dns_name.general_name.dns_name.len = strlen( (const char *) dns_name.general_name.dns_name.p );
++ first = &dns_name;
++ }
++
++ if ( ( subj_alt_names_type & 2 ) == 2 )
++ {
++ directory_name.general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
++ TEST_ASSERT( mbedtls_x509_string_to_names( &directory_name.general_name.directory_name, "C=NL,O=PolarSSL,CN=PolarSSL Server 1A" ) == 0 );
++ if ( first == NULL )
++ {
++ first = &directory_name;
++ }
++ else
++ {
++ first->next = &directory_name;
++ }
++ }
++
++ TEST_ASSERT( mbedtls_x509write_crt_set_subject_alt_names( &crt, first ) == 0 );
++
++ if ( ( subj_alt_names_type & 2 ) == 2 )
++ {
++ mbedtls_asn1_free_named_data_list( &directory_name.general_name.directory_name );
++ }
++ }
++ }
++
++ ret = mbedtls_x509write_crt_pem( &crt, buf, sizeof(buf),
++ rnd_pseudo_rand, &rnd_info );
++ TEST_ASSERT( ret == 0 );
++
++ pem_len = strlen( (char *) buf );
++
++ f = fopen( cert_check_file, "r" );
++ TEST_ASSERT( f != NULL );
++ olen = fread( check_buf, 1, sizeof(check_buf), f );
++ fclose( f );
++ TEST_ASSERT( olen < sizeof(check_buf) );
++
++ TEST_ASSERT( olen >= pem_len - 1 );
++ TEST_ASSERT( memcmp( buf, check_buf, pem_len - 1 ) == 0 );
++
++ der_len = mbedtls_x509write_crt_der( &crt, buf, sizeof( buf ),
++ rnd_pseudo_rand, &rnd_info );
++ TEST_ASSERT( der_len >= 0 );
++
++ if( der_len == 0 )
++ goto exit;
++
++ ret = mbedtls_x509write_crt_der( &crt, buf, (size_t)( der_len - 1 ),
++ rnd_pseudo_rand, &rnd_info );
++ TEST_ASSERT( ret == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
++
++exit:
++ mbedtls_x509write_crt_free( &crt );
++ mbedtls_pk_free( &issuer_key );
++ mbedtls_pk_free( &subject_key );
++ mbedtls_mpi_free( &serial );
++}
++/* 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 )
+ {