Merge remote-tracking branch 'origin/pr/577' into baremetal
diff --git a/ChangeLog b/ChangeLog
index e769dc2..c25bed4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,14 @@
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 670bd10..62c3c2e 100644
--- a/include/mbedtls/x509_crt.h
+++ b/include/mbedtls/x509_crt.h
@@ -52,6 +52,8 @@
*/
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 @@
/**
* \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 ebd118d..2c93311 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -829,8 +829,10 @@
/*
* 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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 d023c8d..acf8e9b 100644
--- a/tests/data_files/Makefile
+++ b/tests/data_files/Makefile
@@ -44,7 +44,9 @@
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 @@
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 0000000..fcf45cd
--- /dev/null
+++ b/tests/data_files/server1.der
Binary files differ
diff --git a/tests/data_files/server2.der b/tests/data_files/server2.der
new file mode 100644
index 0000000..ec03190
--- /dev/null
+++ b/tests/data_files/server2.der
Binary files differ
diff --git a/tests/data_files/test-ca.der b/tests/data_files/test-ca.der
new file mode 100644
index 0000000..039fb9e
--- /dev/null
+++ b/tests/data_files/test-ca.der
Binary files differ
diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
index 6786c36..042d653 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -2,14 +2,26 @@
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 552c494..a921310 100644
--- a/tests/suites/test_suite_x509parse.function
+++ b/tests/suites/test_suite_x509parse.function
@@ -505,8 +505,22 @@
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 );