x509: add parse/print support for IPs in SAN
RFC 5280 defines many type of names to be used in the subjectAltName
extension of certificate. So far we only supported dNSName, but there is
demand for IP addresses too.
This is the first step, support for verification will be added in the next
commit.
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index d1db0d8..20eca56 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -1319,6 +1319,18 @@
#define MBEDTLS_X509_RSASSA_PSS_SUPPORT
/**
+ * \def MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT
+ *
+ * Enable support for IP addresses (IPv4 and IPv6) in subjectAltName in
+ * certificates. This includes parsing, printing with
+ * \c mbedtls_x509_crt_info(), and verification - see the documentation of
+ * \c mbedtls_x509_crt_verify_with_profile()
+ *
+ * Comment this macro to disallow using IP addresses in
+ */
+#define MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT
+
+/**
* \def MBEDTLS_ZLIB_SUPPORT
*
* If set, the SSL/TLS module uses ZLIB to support compression and
diff --git a/library/version_features.c b/library/version_features.c
index 1575e09..1ddd222 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -414,6 +414,9 @@
#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT)
"MBEDTLS_X509_RSASSA_PSS_SUPPORT",
#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */
+#if defined(MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT)
+ "MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT",
+#endif /* MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT */
#if defined(MBEDTLS_ZLIB_SUPPORT)
"MBEDTLS_ZLIB_SUPPORT",
#endif /* MBEDTLS_ZLIB_SUPPORT */
diff --git a/library/x509_crt.c b/library/x509_crt.c
index 9573438..f915b21 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -438,8 +438,13 @@
* nameAssigner [0] DirectoryString OPTIONAL,
* partyName [1] DirectoryString }
*
- * NOTE: we only parse and use dNSName at this point.
+ * NOTE: we only parse and use dNSName and iPAddress at this point.
+ * NOTE: update x509_info_subject_alt_name() and x509_crt_verify() if and when
+ * adding support for more name types.
*/
+#define X509_CRT_SAN_DNS_NAME 2
+#define X509_CRT_SAN_IP_ADDRESS 7
+
static int x509_get_subject_alt_name( unsigned char **p,
const unsigned char *end,
mbedtls_x509_sequence *subject_alt_name )
@@ -474,8 +479,17 @@
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
- /* Skip everything but DNS name */
- if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) )
+ /* Skip everything but DNS name and IP address */
+#if defined(MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT)
+ if( tag == ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_IP_ADDRESS ) )
+ {
+ /* If IP adress, only valid lengths are 4 and 16 */
+ if( tag_len != 4 && tag_len != 16 )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS );
+ }
+ else
+#endif
+ if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_DNS_NAME ) )
{
*p += tag_len;
continue;
@@ -1213,20 +1227,74 @@
const mbedtls_x509_sequence *cur = subject_alt_name;
const char *sep = "";
size_t sep_len = 0;
+ int ret;
while( cur != NULL )
{
- if( cur->buf.len + sep_len >= n )
+ if( cur->buf.tag == ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_DNS_NAME ) )
{
- *p = '\0';
- return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
- }
+ if( cur->buf.len + sep_len >= n )
+ {
+ *p = '\0';
+ return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
+ }
- n -= cur->buf.len + sep_len;
- for( i = 0; i < sep_len; i++ )
- *p++ = sep[i];
- for( i = 0; i < cur->buf.len; i++ )
- *p++ = cur->buf.p[i];
+ n -= cur->buf.len + sep_len;
+ for( i = 0; i < sep_len; i++ )
+ *p++ = sep[i];
+ for( i = 0; i < cur->buf.len; i++ )
+ *p++ = cur->buf.p[i];
+ }
+#if defined(MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT)
+ else if( cur->buf.tag == ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_IP_ADDRESS ) )
+ {
+ if( cur->buf.len == 4 )
+ {
+ ret = mbedtls_snprintf( p, n, "%sIP:%d.%d.%d.%d", sep,
+ cur->buf.p[0], cur->buf.p[1],
+ cur->buf.p[2], cur->buf.p[4] );
+ MBEDTLS_X509_SAFE_SNPRINTF;
+ }
+ else if( cur->buf.len == 16 )
+ {
+ if( sep_len + 2 >= n )
+ {
+ *p = '\0';
+ return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
+ }
+
+ memcpy( p, sep, sep_len );
+ p += sep_len;
+ n -= sep_len;
+
+ memcpy( p, "IP", 2 );
+ p += 2;
+ n -= 2;
+
+ for( i = 0; i < 8; i++ )
+ {
+ ret = mbedtls_snprintf( p, n, ":%02x%02x",
+ cur->buf.p[2 * i],
+ cur->buf.p[2 * i + 1] );
+ MBEDTLS_X509_SAFE_SNPRINTF;
+ }
+ }
+ else
+ {
+ ret = mbedtls_snprintf( p, n, "IP:???" );
+ MBEDTLS_X509_SAFE_SNPRINTF;
+ }
+ }
+#endif /* MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT */
+ /*
+ * tag == 0 happens when the extension is present but with only unknow
+ * types: we get a list with one uninitialized element
+ */
+ else if( cur->buf.tag != 0 )
+ {
+ ret = mbedtls_snprintf( p, n, "???unknown name type???" );
+ MBEDTLS_X509_SAFE_SNPRINTF;
+ }
sep = ", ";
sep_len = 2;
diff --git a/tests/data_files/server5-san-ip.crt b/tests/data_files/server5-san-ip.crt
new file mode 100644
index 0000000..93b727c
--- /dev/null
+++ b/tests/data_files/server5-san-ip.crt
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBrjCCATOgAwIBAgIBUDAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G
+A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN
+MTYwMTI3MTYzNDQzWhcNMjYwMTI0MTYzNDQzWjAPMQ0wCwYDVQQDEwRUZXN0MFkw
+EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEN8xW2XYJHlpyPsdZLf8gbu58+QaRdNCt
+FLX3aCJZYpJO5QDYIxH/6i/SNF1dFr2KiMJrdw1VzYoqDvoByLTt/6NRME8wTQYD
+VR0RBEYwRIIIZm9vLnRlc3SCCGJhci50ZXN0hwR/AAABhwTAqAAqhxD+gAAAAAAA
+AAAAAAAAAAABhxASNFZ4mrze8A/ey6mHZUMhMAoGCCqGSM49BAMCA2kAMGYCMQCI
+TJvNYovxkfjoMcNqHMbrSSNAity3RSqrAA/loKCiw6kpsEOAItjXwxkHj0WH/4kC
+MQDhdrbfoTxxD6AqNelMlpk2oE4c5ewsA2s53CLICqEy951jw47MqI+LYMmlsS/A
+T5A=
+-----END CERTIFICATE-----
diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
index 2f2137f..8b0e7ac 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -99,8 +99,8 @@
x509_cert_info:"data_files/cert_example_multi.crt":"cert. version \: 3\nserial number \: 11\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=www.example.com\nissued on \: 2012-05-10 13\:23\:41\nexpires on \: 2022-05-11 13\:23\:41\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: example.com, example.net, *.example.org\n"
X509 Certificate information, Subject Alt Name + Key Usage
-depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C
-x509_cert_info:"data_files/cert_example_multi_nocn.crt":"cert. version \: 3\nserial number \: F7\:C6\:7F\:F8\:E9\:A9\:63\:F9\nissuer name \: C=NL\nsubject name \: C=NL\nissued on \: 2014-01-22 10\:04\:33\nexpires on \: 2024-01-22 10\:04\:33\nsigned using \: RSA with SHA1\nRSA key size \: 1024 bits\nbasic constraints \: CA=false\nsubject alt name \: www.shotokan-braunschweig.de, www.massimo-abate.eu\nkey usage \: Digital Signature, Non Repudiation, Key Encipherment\n"
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT
+x509_cert_info:"data_files/cert_example_multi_nocn.crt":"cert. version \: 3\nserial number \: F7\:C6\:7F\:F8\:E9\:A9\:63\:F9\nissuer name \: C=NL\nsubject name \: C=NL\nissued on \: 2014-01-22 10\:04\:33\nexpires on \: 2024-01-22 10\:04\:33\nsigned using \: RSA with SHA1\nRSA key size \: 1024 bits\nbasic constraints \: CA=false\nsubject alt name \: www.shotokan-braunschweig.de, www.massimo-abate.eu, IP\:192.168.1.135, IP\:192.168.69.48\nkey usage \: Digital Signature, Non Repudiation, Key Encipherment\n"
X509 Certificate information, Key Usage + Extended Key Usage
depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C
@@ -118,6 +118,10 @@
depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C
x509_cert_info:"data_files/bitstring-in-dn.pem":"cert. version \: 3\nserial number \: 02\nissuer name \: CN=Test CA 01, ST=Ecnivorp, C=XX, emailAddress=tca@example.com, O=Test CA Authority\nsubject name \: C=XX, O=tca, ST=Ecnivorp, OU=TCA, CN=Client, emailAddress=client@example.com, serialNumber=7101012255, uniqueIdentifier=?7101012255\nissued on \: 2015-03-11 12\:06\:51\nexpires on \: 2025-03-08 12\:06\:51\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: \next key usage \: TLS Web Client Authentication\n"
+X509 Certificate information SAN with IP
+depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT
+x509_cert_info:"data_files/server5-san-ip.crt":"cert. version \: 3\nserial number \: 50\nissuer name \: C=NL, O=PolarSSL, CN=Polarssl Test EC CA\nsubject name \: CN=Test\nissued on \: 2016-01-27 16\:34\:43\nexpires on \: 2026-01-24 16\:34\:43\nsigned using \: ECDSA with SHA256\nEC key size \: 256 bits\nsubject alt name \: foo.test, bar.test, IP\:127.0.0.135, IP\:192.168.0.135, IP\:fe80\:0000\:0000\:0000\:0000\:0000\:0000\:0001, IP\:1234\:5678\:9abc\:def0\:0fde\:cba9\:8765\:4321\n"
+
X509 certificate v1 with extension
depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
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"