diff --git a/ChangeLog b/ChangeLog index e769dc27a..c25bed4c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,14 @@ Features mbedtls_ssl_session_load() to allow serializing a session, for example to store it in non-volatile storage, and later using it for TLS session resumption. + * Add a new X.509 API call `mbedtls_x509_parse_der_nocopy()` + which allows copy-less parsing of DER encoded X.509 CRTs, + at the cost of additional lifetime constraints on the input + buffer, but at the benefit of reduced RAM consumption. + +API Changes + * Add a new X.509 API call `mbedtls_x509_parse_der_nocopy()`. + See the Features section for more information. Bugfix * Server's RSA certificate in certs.c was SHA-1 signed. In the default diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index 670bd10d8..62c3c2e54 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -52,6 +52,8 @@ extern "C" { */ typedef struct mbedtls_x509_crt { + int own_buffer; /**< Indicates if \c raw is owned + * by the structure or not. */ mbedtls_x509_buf raw; /**< The raw certificate data (DER). */ mbedtls_x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ @@ -220,16 +222,58 @@ extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb; /** * \brief Parse a single DER formatted certificate and add it - * to the chained list. + * to the end of the provided chained list. * - * \param chain points to the start of the chain - * \param buf buffer holding the certificate DER data - * \param buflen size of the buffer + * \param chain The pointer to the start of the CRT chain to attach to. + * When parsing the first CRT in a chain, this should point + * to an instance of ::mbedtls_x509_crt initialized through + * mbedtls_x509_crt_init(). + * \param buf The buffer holding the DER encoded certificate. + * \param buflen The size in Bytes of \p buf. * - * \return 0 if successful, or a specific X509 or PEM error code + * \note This function makes an internal copy of the CRT buffer + * \p buf. In particular, \p buf may be destroyed or reused + * after this call returns. To avoid duplicating the CRT + * buffer (at the cost of stricter lifetime constraints), + * use mbedtls_x509_crt_parse_der_nocopy() instead. + * + * \return \c 0 if successful. + * \return A negative error code on failure. */ -int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *buf, - size_t buflen ); +int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen ); + +/** + * \brief Parse a single DER formatted certificate and add it + * to the end of the provided chained list. This is a + * variant of mbedtls_x509_crt_parse_der() which takes + * temporary ownership of the CRT buffer until the CRT + * is destroyed. + * + * \param chain The pointer to the start of the CRT chain to attach to. + * When parsing the first CRT in a chain, this should point + * to an instance of ::mbedtls_x509_crt initialized through + * mbedtls_x509_crt_init(). + * \param buf The address of the readable buffer holding the DER encoded + * certificate to use. On success, this buffer must be + * retained and not be changed for the liftetime of the + * CRT chain \p chain, that is, until \p chain is destroyed + * through a call to mbedtls_x509_crt_free(). + * \param buflen The size in Bytes of \p buf. + * + * \note This call is functionally equivalent to + * mbedtls_x509_crt_parse_der(), but it avoids creating a + * copy of the input buffer at the cost of stronger lifetime + * constraints. This is useful in constrained environments + * where duplication of the CRT cannot be tolerated. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_parse_der_nocopy( mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen ); /** * \brief Parse one DER-encoded or one or more concatenated PEM-encoded diff --git a/library/x509_crt.c b/library/x509_crt.c index ebd118db0..2c93311fe 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -829,8 +829,10 @@ static int x509_get_crt_ext( unsigned char **p, /* * Parse and fill a single X.509 certificate in DER format */ -static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, const unsigned char *buf, - size_t buflen ) +static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, + const unsigned char *buf, + size_t buflen, + int make_copy ) { int ret; size_t len; @@ -847,7 +849,7 @@ static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, const unsigned char * if( crt == NULL || buf == NULL ) return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); - // Use the original buffer until we figure out actual length + /* Use the original buffer until we figure out actual length. */ p = (unsigned char*) buf; len = buflen; end = p + len; @@ -865,25 +867,26 @@ static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, const unsigned char * return( MBEDTLS_ERR_X509_INVALID_FORMAT ); } - if( len > (size_t) ( end - p ) ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - } - crt_end = p + len; - - // Create and populate a new buffer for the raw field - crt->raw.len = crt_end - buf; - crt->raw.p = p = mbedtls_calloc( 1, crt->raw.len ); - if( p == NULL ) - return( MBEDTLS_ERR_X509_ALLOC_FAILED ); - - memcpy( p, buf, crt->raw.len ); - - // Direct pointers to the new buffer - p += crt->raw.len - len; end = crt_end = p + len; + crt->raw.len = crt_end - buf; + if( make_copy != 0 ) + { + /* Create and populate a new buffer for the raw field. */ + crt->raw.p = p = mbedtls_calloc( 1, crt->raw.len ); + if( crt->raw.p == NULL ) + return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + + memcpy( crt->raw.p, buf, crt->raw.len ); + crt->own_buffer = 1; + + p += crt->raw.len - len; + end = crt_end = p + len; + } + else + { + crt->raw.p = (unsigned char*) buf; + crt->own_buffer = 0; + } /* * TBSCertificate ::= SEQUENCE { @@ -1086,8 +1089,10 @@ static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, const unsigned char * * Parse one X.509 certificate in DER format from a buffer and add them to a * chained list */ -int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *buf, - size_t buflen ) +static int mbedtls_x509_crt_parse_der_internal( mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen, + int make_copy ) { int ret; mbedtls_x509_crt *crt = chain, *prev = NULL; @@ -1119,7 +1124,7 @@ int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *bu crt = crt->next; } - if( ( ret = x509_crt_parse_der_core( crt, buf, buflen ) ) != 0 ) + if( ( ret = x509_crt_parse_der_core( crt, buf, buflen, make_copy ) ) != 0 ) { if( prev ) prev->next = NULL; @@ -1133,11 +1138,27 @@ int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *bu return( 0 ); } +int mbedtls_x509_crt_parse_der_nocopy( mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen ) +{ + return( mbedtls_x509_crt_parse_der_internal( chain, buf, buflen, 0 ) ); +} + +int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen ) +{ + return( mbedtls_x509_crt_parse_der_internal( chain, buf, buflen, 1 ) ); +} + /* * Parse one or more PEM certificates from a buffer and add them to the chained * list */ -int mbedtls_x509_crt_parse( mbedtls_x509_crt *chain, const unsigned char *buf, size_t buflen ) +int mbedtls_x509_crt_parse( mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen ) { #if defined(MBEDTLS_PEM_PARSE_C) int success = 0, first_error = 0, total_failed = 0; @@ -2675,7 +2696,7 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt ) mbedtls_free( seq_prv ); } - if( cert_cur->raw.p != NULL ) + if( cert_cur->raw.p != NULL && cert_cur->own_buffer ) { mbedtls_platform_zeroize( cert_cur->raw.p, cert_cur->raw.len ); mbedtls_free( cert_cur->raw.p ); diff --git a/tests/data_files/Makefile b/tests/data_files/Makefile index d023c8d0c..acf8e9bac 100644 --- a/tests/data_files/Makefile +++ b/tests/data_files/Makefile @@ -44,7 +44,9 @@ all_intermediate += test-ca.req.sha256 test-ca.crt: $(test_ca_key_file_rsa) test-ca.req.sha256 $(MBEDTLS_CERT_WRITE) is_ca=1 serial=3 request_file=test-ca.req.sha256 selfsign=1 issuer_name="C=NL,O=PolarSSL,CN=PolarSSL Test CA" issuer_key=$(test_ca_key_file_rsa) issuer_pwd=$(test_ca_pwd_rsa) not_before=20110212144400 not_after=20210212144400 md=SHA1 version=3 output_file=$@ -all_final += test-ca.crt +test-ca.der: test-ca.crt + $(OPENSSL) x509 -inform PEM -in $< -outform DER -out $@ +all_final += test-ca.crt test-ca.der test-ca-sha1.crt: $(test_ca_key_file_rsa) test-ca.req.sha256 $(MBEDTLS_CERT_WRITE) is_ca=1 serial=3 request_file=test-ca.req.sha256 selfsign=1 issuer_name="C=NL,O=PolarSSL,CN=PolarSSL Test CA" issuer_key=$(test_ca_key_file_rsa) issuer_pwd=$(test_ca_pwd_rsa) not_before=20110212144400 not_after=20210212144400 md=SHA1 version=3 output_file=$@ @@ -903,7 +905,9 @@ server1_all: server1.crt server1.noauthid.crt server1.crt.openssl server1.v1.crt server2.crt: server2.req.sha256 $(MBEDTLS_CERT_WRITE) request_file=server2.req.sha256 serial=2 issuer_crt=$(test_ca_crt) issuer_key=$(test_ca_key_file_rsa) issuer_pwd=$(test_ca_pwd_rsa) not_before=20110212144406 not_after=20210212144406 md=SHA1 version=3 output_file=$@ -all_final += server2.crt +server2.der: server2.crt + $(OPENSSL) x509 -inform PEM -in $< -outform DER -out $@ +all_final += server2.crt server2.der server2-sha256.crt: server2.req.sha256 $(MBEDTLS_CERT_WRITE) request_file=server2.req.sha256 serial=2 issuer_crt=$(test_ca_crt) issuer_key=$(test_ca_key_file_rsa) issuer_pwd=$(test_ca_pwd_rsa) not_before=20110212144406 not_after=20210212144406 md=SHA256 version=3 output_file=$@ diff --git a/tests/data_files/server1.der b/tests/data_files/server1.der new file mode 100644 index 000000000..fcf45cd7c Binary files /dev/null and b/tests/data_files/server1.der differ diff --git a/tests/data_files/server2.der b/tests/data_files/server2.der new file mode 100644 index 000000000..ec03190e1 Binary files /dev/null and b/tests/data_files/server2.der differ diff --git a/tests/data_files/test-ca.der b/tests/data_files/test-ca.der new file mode 100644 index 000000000..039fb9e43 Binary files /dev/null and b/tests/data_files/test-ca.der differ diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 6786c361c..042d653b5 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2,14 +2,26 @@ X509 Certificate information #1 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA1_C x509_cert_info:"data_files/server1.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 SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\n" +X509 Certificate information #1 (DER) +depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA1_C +x509_cert_info:"data_files/server1.der":"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 SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\n" + X509 Certificate information #2 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA1_C x509_cert_info:"data_files/server2.crt":"cert. version \: 3\nserial number \: 02\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=localhost\nissued on \: 2011-02-12 14\:44\:06\nexpires on \: 2021-02-12 14\:44\:06\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\n" +X509 Certificate information #2 (DER) +depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA1_C +x509_cert_info:"data_files/server2.der":"cert. version \: 3\nserial number \: 02\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=localhost\nissued on \: 2011-02-12 14\:44\:06\nexpires on \: 2021-02-12 14\:44\:06\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\n" + X509 Certificate information #3 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA1_C x509_cert_info:"data_files/test-ca.crt":"cert. version \: 3\nserial number \: 03\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nissued on \: 2011-02-12 14\:44\:00\nexpires on \: 2021-02-12 14\:44\:00\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=true\n" +X509 Certificate information #3 (DER) +depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA1_C +x509_cert_info:"data_files/test-ca.der":"cert. version \: 3\nserial number \: 03\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nissued on \: 2011-02-12 14\:44\:00\nexpires on \: 2021-02-12 14\:44\:00\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=true\n" + X509 Certificate information MD2 Digest depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_MD2_C x509_cert_info:"data_files/cert_md2.crt":"cert. version \: 3\nserial number \: 09\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=PolarSSL Cert MD2\nissued on \: 2009-07-12 10\:56\:59\nexpires on \: 2011-07-12 10\:56\:59\nsigned using \: RSA with MD2\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\n" diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function index 552c494b0..a921310c6 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function @@ -505,8 +505,22 @@ void x509parse_crt( data_t * buf, char * result_str, int result ) mbedtls_x509_crt_init( &crt ); memset( output, 0, 2000 ); + TEST_ASSERT( mbedtls_x509_crt_parse_der( &crt, buf->x, buf->len ) == ( result ) ); + if( ( result ) == 0 ) + { + res = mbedtls_x509_crt_info( (char *) output, 2000, "", &crt ); - TEST_ASSERT( mbedtls_x509_crt_parse( &crt, buf->x, buf->len ) == ( result ) ); + TEST_ASSERT( res != -1 ); + TEST_ASSERT( res != -2 ); + + TEST_ASSERT( strcmp( (char *) output, result_str ) == 0 ); + } + + mbedtls_x509_crt_free( &crt ); + mbedtls_x509_crt_init( &crt ); + memset( output, 0, 2000 ); + + TEST_ASSERT( mbedtls_x509_crt_parse_der_nocopy( &crt, buf->x, buf->len ) == ( result ) ); if( ( result ) == 0 ) { res = mbedtls_x509_crt_info( (char *) output, 2000, "", &crt );