diff --git a/ChangeLog b/ChangeLog index c1f693819b..2f725041af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,16 @@ mbed TLS ChangeLog (Sorted per branch, date) = mbed TLS 2.x.x branch released xxxx-xx-xx +Features + * 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 * Fix a compilation issue with mbedtls_ecp_restart_ctx not being defined when MBEDTLS_ECP_ALT is defined. Reported by jwhui. Fixes #2242. diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index 3dd5922486..72c39019b1 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 1b1f0a771b..a766daf592 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -834,8 +834,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; @@ -852,7 +854,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; @@ -870,25 +872,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 { @@ -1091,8 +1094,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; @@ -1124,7 +1129,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; @@ -1138,11 +1143,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; @@ -2699,7 +2720,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 2ed32e6890..ff89476866 100644 --- a/tests/data_files/Makefile +++ b/tests/data_files/Makefile @@ -45,7 +45,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=$@ @@ -873,7 +875,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 0000000000..fcf45cd7cc 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 0000000000..ec03190e12 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 0000000000..039fb9e430 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 6786c361c7..042d653b51 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 8914bd0d10..4a826082bd 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function @@ -513,8 +513,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 );