From 5605591cc183c5d31e0e8dd17ffea9c1432118fd Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 14:26:30 +0100 Subject: [PATCH 01/17] Report step number when a test case fails Allow test code to declare a "step number". Report the current step number when a test fails. --- tests/suites/helpers.function | 14 ++++++++++++++ tests/suites/host_test.function | 13 ++++++++++--- tests/suites/target_test.function | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function index 1a524a677a..d45fd4ea7d 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -393,6 +393,7 @@ static struct const char *test; const char *filename; int line_no; + unsigned long step; } test_info; @@ -423,6 +424,19 @@ jmp_buf jmp_tmp; /*----------------------------------------------------------------------------*/ /* Helper Functions */ +/** Set the test step number for failure reports. + * + * Call this function to display "step NNN" in addition to the line number + * and file name if a test fails. Typically the "step number" is the index + * of a for loop but it can be whatever you want. + * + * \param step The step number to report. + */ +void test_set_step( unsigned long step ) +{ + test_info.step = step; +} + void test_fail( const char *test, int line_no, const char* filename ) { test_info.result = TEST_RESULT_FAILED; diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function index 0f98d23aa6..24d9b97474 100644 --- a/tests/suites/host_test.function +++ b/tests/suites/host_test.function @@ -548,6 +548,7 @@ int execute_tests( int argc , const char ** argv ) { test_info.result = TEST_RESULT_SUCCESS; test_info.paramfail_test_state = PARAMFAIL_TESTSTATE_IDLE; + test_info.step = (unsigned long)( -1 ); #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) /* Suppress all output from the library unless we're verbose @@ -624,9 +625,15 @@ int execute_tests( int argc , const char ** argv ) { total_errors++; mbedtls_fprintf( stdout, "FAILED\n" ); - mbedtls_fprintf( stdout, " %s\n at line %d, %s\n", - test_info.test, test_info.line_no, - test_info.filename ); + mbedtls_fprintf( stdout, " %s\n at ", + test_info.test ); + if( test_info.step != (unsigned long)( -1 ) ) + { + mbedtls_fprintf( stdout, "step %lu, ", + test_info.step ); + } + mbedtls_fprintf( stdout, "line %d, %s", + test_info.line_no, test_info.filename ); } fflush( stdout ); } diff --git a/tests/suites/target_test.function b/tests/suites/target_test.function index 91f7198734..937e8dd720 100644 --- a/tests/suites/target_test.function +++ b/tests/suites/target_test.function @@ -375,6 +375,7 @@ int execute_tests( int args, const char ** argv ) { ret = 0; test_info.result = TEST_RESULT_SUCCESS; + test_info.step = (unsigned long)( -1 ); data_len = 0; data = receive_data( &data_len ); From bcbe1dfb23f05c5b3431c96dc187ea2b1cffbe03 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 17:57:43 +0100 Subject: [PATCH 02/17] Improve the documentation of ASN.1 parsing functions Document preconditions on parameters, values changed through pointers, and error codes. This commit leaves some issues regarding integers (especially negative integers) open, because we don't have a policy decision on how to handle them yet. --- include/mbedtls/asn1.h | 227 +++++++++++++++++++++++++++++------------ 1 file changed, 161 insertions(+), 66 deletions(-) diff --git a/include/mbedtls/asn1.h b/include/mbedtls/asn1.h index ab947ab7ef..6891bb9c31 100644 --- a/include/mbedtls/asn1.h +++ b/include/mbedtls/asn1.h @@ -176,119 +176,203 @@ mbedtls_asn1_named_data; * \brief Get the length of an ASN.1 element. * Updates the pointer to immediately behind the length. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param len The variable that will receive the value + * \param p On entry, \c *p points to the first byte of the length, + * i.e. immediately after the tag. + * On successful completion, \c *p points to the first byte + * after the length, i.e. the first byte of the content. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On successful completion, \c *len contains the lengtth + * read from the ASN.1 input. * - * \return 0 if successful, MBEDTLS_ERR_ASN1_OUT_OF_DATA on reaching - * end of data, MBEDTLS_ERR_ASN1_INVALID_LENGTH if length is - * unparseable. + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element + * would end beyond \p end. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparseable. */ int mbedtls_asn1_get_len( unsigned char **p, - const unsigned char *end, - size_t *len ); + const unsigned char *end, + size_t *len ); /** - * \brief Get the tag and length of the tag. Check for the requested tag. + * \brief Get the tag and length of the element. + * Check for the requested tag. * Updates the pointer to immediately behind the tag and length. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param len The variable that will receive the length - * \param tag The expected tag + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * after the length, i.e. the first byte of the content. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On successful completion, \c *len contains the lengtth + * read from the ASN.1 input. + * \param tag The expected tag. * - * \return 0 if successful, MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if tag did - * not match requested tag, or another specific ASN.1 error code. + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the data does not start + * with the requested tag. + * \return #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element + * would end beyond \p end. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparseable. */ int mbedtls_asn1_get_tag( unsigned char **p, - const unsigned char *end, - size_t *len, int tag ); + const unsigned char *end, + size_t *len, int tag ); /** * \brief Retrieve a boolean ASN.1 tag and its value. * Updates the pointer to immediately behind the full tag. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param val The variable that will receive the value + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value (\c 0 or \c 1). * - * \return 0 if successful or a specific ASN.1 error code. + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BOOLEAN. */ int mbedtls_asn1_get_bool( unsigned char **p, - const unsigned char *end, - int *val ); + const unsigned char *end, + int *val ); /** * \brief Retrieve an integer ASN.1 tag and its value. * Updates the pointer to immediately behind the full tag. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param val The variable that will receive the value + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value. * - * \return 0 if successful or a specific ASN.1 error code. + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. */ int mbedtls_asn1_get_int( unsigned char **p, - const unsigned char *end, - int *val ); + const unsigned char *end, + int *val ); /** * \brief Retrieve a bitstring ASN.1 tag and its value. * Updates the pointer to immediately behind the full tag. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param bs The variable that will receive the value + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p is equal to \p end. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param bs On success, ::mbedtls_asn1_bitstring information about + * the parsed value. * - * \return 0 if successful or a specific ASN.1 error code. + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains + * extra data after a valid BIT STRING. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. */ int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end, - mbedtls_asn1_bitstring *bs); + mbedtls_asn1_bitstring *bs ); /** * \brief Retrieve a bitstring ASN.1 tag without unused bits and its * value. * Updates the pointer to the beginning of the bit/octet string. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param len Length of the actual bit/octect string in bytes + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * of the content of the BIT STRING. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On success, \c *len is the length of the content in bytes. * - * \return 0 if successful or a specific ASN.1 error code. + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_INVALID_DATA if the input starts with + * a valid BIT STRING with a nonzero number of unused bits. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. */ -int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end, - size_t *len ); +int mbedtls_asn1_get_bitstring_null( unsigned char **p, + const unsigned char *end, + size_t *len ); /** * \brief Parses and splits an ASN.1 "SEQUENCE OF " * Updated the pointer to immediately behind the full sequence tag. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param cur First variable in the chain to fill - * \param tag Type of sequence + * \note On error, this function may return a partial list in \p cur. + * You must set `cur->next = NULL` before calling this function! + * Otherwise it is impossible to distinguish a previously non-null + * pointer from a pointer to an object allocated by this function. * - * \return 0 if successful or a specific ASN.1 error code. + * \note If the sequence is empty, this function does not modify + * \c *cur. If the sequence is valid and non-empty, this + * function sets `cur->buf.tag` to \p tag. This allows + * callers to distinguish between an empty sequence and + * a one-element sequence. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p is equal to \p end. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param cur A ::mbedtls_asn1_sequence which this function fills. + * When this function returns, \c *cur is the head of a linked + * list. Each node in this list is allocated with + * mbedtls_calloc() apart from \p cur itself, and should + * therefore be freed with mbedtls_free(). + * The list describes the content of the sequence. + * The head of the list (i.e. \c *cur itself) describes the + * first element, `*cur->next` describes the second element, etc. + * For each element, `buf.tag == tag`, `buf.len` is the length + * of the content of the content of the element, and `buf.p` + * points to the first byte of the content (i.e. immediately + * past the length of the element). + * Note that list elements may be allocated even on error. + * \param tag Each element of the sequence must have this tag. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains + * extra data after a valid SEQUENCE OF \p tag. + * \return #MBEDTLS_ERR_ASN1_ALLOC_FAILED if a memory allocation failed. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. */ int mbedtls_asn1_get_sequence_of( unsigned char **p, - const unsigned char *end, - mbedtls_asn1_sequence *cur, - int tag); + const unsigned char *end, + mbedtls_asn1_sequence *cur, + int tag ); #if defined(MBEDTLS_BIGNUM_C) /** - * \brief Retrieve a MPI value from an integer ASN.1 tag. + * \brief Retrieve an integer ASN.1 tag and its value. * Updates the pointer to immediately behind the full tag. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param X The MPI that will receive the value + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param X On success, the parsed value. * - * \return 0 if successful or a specific ASN.1 or MPI error code. + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + * \return An MPI error code if the parsed value is too large. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. */ int mbedtls_asn1_get_mpi( unsigned char **p, - const unsigned char *end, - mbedtls_mpi *X ); + const unsigned char *end, + mbedtls_mpi *X ); #endif /* MBEDTLS_BIGNUM_C */ /** @@ -296,10 +380,14 @@ int mbedtls_asn1_get_mpi( unsigned char **p, * Updates the pointer to immediately behind the full * AlgorithmIdentifier. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param alg The buffer to receive the OID - * \param params The buffer to receive the params (if any) + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the AlgorithmIdentifier element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param alg The buffer to receive the OID. + * \param params The buffer to receive the parameters. + * This is zeroized if there are no parameters. * * \return 0 if successful or a specific ASN.1 or MPI error code. */ @@ -313,9 +401,12 @@ int mbedtls_asn1_get_alg( unsigned char **p, * Updates the pointer to immediately behind the full * AlgorithmIdentifier. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param alg The buffer to receive the OID + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the AlgorithmIdentifier element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param alg The buffer to receive the OID. * * \return 0 if successful or a specific ASN.1 or MPI error code. */ @@ -339,15 +430,19 @@ mbedtls_asn1_named_data *mbedtls_asn1_find_named_data( mbedtls_asn1_named_data * /** * \brief Free a mbedtls_asn1_named_data entry * - * \param entry The named data entry to free + * \param entry The named data entry to free. + * This function calls mbedtls_free() on + * `entry->oid.p` and `entry->val.p`. */ void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *entry ); /** - * \brief Free all entries in a mbedtls_asn1_named_data list - * Head will be set to NULL + * \brief Free all entries in a mbedtls_asn1_named_data list. * - * \param head Pointer to the head of the list of named data entries to free + * \param head Pointer to the head of the list of named data entries to free. + * This function calls mbedtls_asn1_free_named_data() and + * mbedtls_free() on each list element and + * sets \c *head to \c NULL. */ void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head ); From 27d806fab41a11441d97017158fcb1356ef7e74f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 18:02:53 +0100 Subject: [PATCH 03/17] Add ASN.1 parsing tests Add self-contained ASN.1 parsing tests, so that ASN.1 parsing is not solely tested through X.509 and TLS. The tests cover every function and almost complete line coverage in asn1parse.c. A few test cases containing negative and edge case INTEGER values are deliberately deactivated because the historical library behavior is at odds with official specifications, but changing the behavior might break interoperability. Other than that, these tests revealed a couple of minor bugs which will be fixed in subsequent commits. --- tests/CMakeLists.txt | 1 + tests/suites/test_suite_asn1parse.data | 438 ++++++++++++++++ tests/suites/test_suite_asn1parse.function | 568 +++++++++++++++++++++ 3 files changed, 1007 insertions(+) create mode 100644 tests/suites/test_suite_asn1parse.data create mode 100644 tests/suites/test_suite_asn1parse.function diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7dcc98d0e4..bcf462f397 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -79,6 +79,7 @@ add_test_suite(aes aes.rest) add_test_suite(aes aes.xts) add_test_suite(arc4) add_test_suite(aria) +add_test_suite(asn1parse) add_test_suite(asn1write) add_test_suite(base64) add_test_suite(blowfish) diff --git a/tests/suites/test_suite_asn1parse.data b/tests/suites/test_suite_asn1parse.data new file mode 100644 index 0000000000..83319e3e5c --- /dev/null +++ b/tests/suites/test_suite_asn1parse.data @@ -0,0 +1,438 @@ +Empty length +parse_prefixes:"04":0:MBEDTLS_ERR_ASN1_INVALID_LENGTH + +Prefixes of OCTET STRING, length=0 +parse_prefixes:"04007e":2:0 + +Prefixes of OCTET STRING, length=0 (0 length bytes) +parse_prefixes:"04807e":2:MBEDTLS_ERR_ASN1_INVALID_LENGTH + +Prefixes of OCTET STRING, length=1 +parse_prefixes:"0401417e":3:0 + +Prefixes of OCTET STRING, length=2 +parse_prefixes:"040241427e":4:0 + +Prefixes of BOOLEAN, length=0 +parse_prefixes:"01007e":2:MBEDTLS_ERR_ASN1_INVALID_LENGTH + +Prefixes of BOOLEAN, length=1 +parse_prefixes:"0101007e":3:0 + +Prefixes of BOOLEAN, length=2 +parse_prefixes:"010200007e":4:MBEDTLS_ERR_ASN1_INVALID_LENGTH + +Prefixes of INTEGER, length=1 +parse_prefixes:"0201417e":3:0 + +Prefixes of INTEGER, length=2 +parse_prefixes:"020241427e":4:0 + +Prefixes of INTEGER, length=5 +parse_prefixes:"020541424344457e":7:0 + +Prefixes of empty BIT STRING +parse_prefixes:"03007e":2:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +Prefixes of BIT STRING, unused_bits=0, payload_length=0 +parse_prefixes:"030100":3:0 + +Prefixes of BIT STRING, unused_bits=0, payload_length=1 +parse_prefixes:"0302002a":4:0 + +Prefixes of BIT STRING, unused_bits=1, payload_length=1 +parse_prefixes:"0302012a":4:0 + +Prefixes of empty SEQUENCE +parse_prefixes:"30007e":2:0 + +Prefixes of SEQUENCE of BOOLEAN, INTEGER, INTEGER +parse_prefixes:"300b01010102012a02031234567e":13:0 + +Prefixes of SEQUENCE of (SEQUENCE of INTEGER, INTEGER), INTEGER +parse_prefixes:"300b30060201410201420201617e":13:0 + +length=0 (short form) +get_len:"00":0 + +length=0 (1 length byte) +get_len:"8100":0 + +length=0 (2 length bytes) +get_len:"820000":0 + +length=1 (short form) +get_len:"01":1 + +length=1 (1 length byte) +get_len:"8101":1 + +length=1 (2 length bytes) +get_len:"820001":1 + +length=1 (3 length bytes) +get_len:"83000001":1 + +length=1 (4 length bytes) +get_len:"8400000001":1 + +length=2 (short form) +get_len:"02":2 + +length=2 (1 length byte) +get_len:"8102":2 + +length=2 (2 length bytes) +get_len:"820002":2 + +length=2 (3 length bytes) +get_len:"83000002":2 + +length=2 (4 length bytes) +get_len:"8400000002":2 + +length=127 (short form) +get_len:"7f":127 + +length=128 (1 length byte) +get_len:"8180":128 + +length=128 (2 length bytes) +get_len:"820080":128 + +length=255 (1 length byte) +get_len:"81ff":255 + +length=255 (2 length bytes) +get_len:"8200ff":255 + +length=256 (2 length bytes) +get_len:"820100":256 + +length=256 (3 length bytes) +get_len:"83000100":256 + +length=258 (2 length bytes) +get_len:"820102":258 + +length=258 (3 length bytes) +get_len:"83000102":258 + +length=65535 (2 length bytes) +get_len:"82ffff":65535 + +length=65535 (3 length bytes) +get_len:"8300ffff":65535 + +length=65535 (4 length bytes) +get_len:"840000ffff":65535 + +length=65536 (3 length bytes) +get_len:"83010000":65536 + +length=65536 (4 length bytes) +get_len:"8400010000":65536 + +length=16777215 (3 length bytes) +get_len:"83ffffff":16777215 + +length=16777215 (4 length bytes) +get_len:"8400ffffff":16777215 + +length=16777216 (4 length bytes) +get_len:"8401000000":16777216 + +length=16909060 (4 length bytes) +get_len:"8401020304":16909060 + +BOOLEAN FALSE +get_boolean:"010100":0:0 + +BOOLEAN TRUE (1) +get_boolean:"010101":1:0 + +BOOLEAN TRUE (2) +get_boolean:"010101":1:0 + +BOOLEAN TRUE (128) +get_boolean:"010180":1:0 + +BOOLEAN TRUE (255) +get_boolean:"0101ff":1:0 + +Not BOOLEAN +get_boolean:"020101":0:MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +Empty INTEGER +depends_on:SUPPORT_NEGATIVE_INTEGERS +get_integer:"0200":"":MBEDTLS_ERR_ASN1_INVALID_LENGTH + +INTEGER 0 +get_integer:"020100":"0":0 + +INTEGER 0, extra leading 0 +get_integer:"02020000":"0":0 + +INTEGER -0 +depends_on:SUPPORT_NEGATIVE_INTEGERS +get_integer:"020180":"0":0 + +INTEGER 1 +get_integer:"020101":"1":0: + +INTEGER 1, extra leading 0 +get_integer:"02020001":"1":0: + +INTEGER -1 +depends_on:SUPPORT_NEGATIVE_INTEGERS +get_integer:"020181":"-1":0 + +INTEGER 0x7f +get_integer:"02017f":"7f":0 + +INTEGER -0x7f +depends_on:SUPPORT_NEGATIVE_INTEGERS +get_integer:"0201ff":"-7f":0 + +INTEGER 0x80 +get_integer:"02020080":"80":0 + +INTEGER 0x80, extra leading 0 +get_integer:"0203000080":"80":0 + +INTEGER 0xff +get_integer:"020200ff":"ff":0 + +INTEGER 0x7fff +get_integer:"02027fff":"7fff":0 + +INTEGER 0x12345678 +get_integer:"020412345678":"12345678":0 + +INTEGER 0x12345678, extra leading 0 +get_integer:"02050012345678":"12345678":0 + +INTEGER 0x123456789abcdef0 +get_integer:"0208123456789abcdef0":"123456789abcdef0":0 + +INTEGER with 127 value octets +get_integer:"027f0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd":0 + +INTEGER with 127 value octets (long length encoding) +get_integer:"02817f0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd":0 + +INTEGER with 128 value octets +get_integer:"0281800123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef":0 + +INTEGER with 128 value octets (leading 0 in length) +get_integer:"028200800123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef":0 + +Not INTEGER +get_integer:"010101":"":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +INTEGER too large for mpi +get_mpi_too_large: + +BIT STRING: empty +get_bitstring:"0300":0:0:MBEDTLS_ERR_ASN1_OUT_OF_DATA:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING: octets=0, unused_bits=0 +get_bitstring:"030100":0:0:0:0 + +BIT STRING: octets=0, unused_bits=7 +get_bitstring:"030107":0:7:0:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING: octets=0, unused_bits=8 +get_bitstring:"030108":0:0:MBEDTLS_ERR_ASN1_INVALID_LENGTH:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING: octets=1, unused_bits=0 +get_bitstring:"03020041":1:0:0:0 + +BIT STRING: octets=1, unused_bits=7 +get_bitstring:"03020741":1:7:0:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING: octets=1, unused_bits=8 +get_bitstring:"03020841":1:8:MBEDTLS_ERR_ASN1_INVALID_LENGTH:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING: octets=2, unused_bits=0 +get_bitstring:"0303004142":2:0:0:0 + +BIT STRING: octets=2, unused_bits=7 +get_bitstring:"0303074142":2:7:0:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING: octets=2, unused_bits=8 +get_bitstring:"0303084142":2:8:MBEDTLS_ERR_ASN1_INVALID_LENGTH:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING with trailing garbage, unused_bits=0 +get_bitstring:"030200417e":1:0:MBEDTLS_ERR_ASN1_LENGTH_MISMATCH:0 + +BIT STRING with trailing garbage, unused_bits=7 +get_bitstring:"030207417e":1:7:MBEDTLS_ERR_ASN1_LENGTH_MISMATCH:MBEDTLS_ERR_ASN1_INVALID_DATA + +BIT STRING with trailing garbage, unused_bits=8 +get_bitstring:"030208417e":1:8:MBEDTLS_ERR_ASN1_INVALID_LENGTH:MBEDTLS_ERR_ASN1_INVALID_DATA + +Not BIT STRING +get_bitstring:"04020100":0:0:MBEDTLS_ERR_ASN1_UNEXPECTED_TAG:MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +SEQUENCE OF 0 OCTET STRING +get_sequence_of:"3000":0x04:"":0 + +SEQUENCE OF 0 OCTET STRING plus trailing garbage +get_sequence_of:"30007e":0x04:"":MBEDTLS_ERR_ASN1_LENGTH_MISMATCH + +SEQUENCE of 1 OCTET STRING truncated after tag +get_sequence_of:"300104":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 1 OCTET STRING truncated in length #1 +get_sequence_of:"30020481":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 1 OCTET STRING truncated in length #2 +get_sequence_of:"3003048201":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 1 OCTET STRING truncated in content #1 +get_sequence_of:"30020401":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 1 OCTET STRING truncated in content #2 +get_sequence_of:"3003040241":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 1 OCTET STRING truncated in content #3 +get_sequence_of:"300404034142":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 1 OCTET STRING (0) +get_sequence_of:"30020400":0x04:"4,0":0 + +SEQUENCE of 1 OCTET STRING (1) +get_sequence_of:"3003040141":0x04:"4,1":0 + +SEQUENCE of 1 OCTET STRING (126) +get_sequence_of:"308180047e414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141":0x04:"5,126":0 + +SEQUENCE of 2 OCTET STRINGs, second truncated after tag +get_sequence_of:"30050402414104":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 2 OCTET STRINGs, second truncated in length #1 +get_sequence_of:"3006040241410481":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 2 OCTET STRINGs, second truncated in length #2 +get_sequence_of:"300704024141048201":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 2 OCTET STRINGs, second truncated in content #1 +get_sequence_of:"3006040241410401":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 2 OCTET STRINGs, second truncated in content #2 +get_sequence_of:"300704024141040241":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 2 OCTET STRINGs, second truncated in content #3 +get_sequence_of:"30080402414104034142":0x04:"":MBEDTLS_ERR_ASN1_OUT_OF_DATA + +SEQUENCE of 2 OCTET STRINGs (2, 0) +get_sequence_of:"3006040241410400":0x04:"4,2,8,0":0 + +SEQUENCE of 2 OCTET STRINGs (2, 1) +get_sequence_of:"300704024141040142":0x04:"4,2,8,1":0 + +SEQUENCE of 2 OCTET STRINGs (0, 2) +get_sequence_of:"3006040004024141":0x04:"4,0,6,2":0 + +SEQUENCE of 2 OCTET STRINGs (1, 2) +get_sequence_of:"300704014104024242":0x04:"4,1,7,2":0 + +Not a SEQUENCE (not CONSTRUCTED) +get_sequence_of:"1000":0x04:"":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +Not a SEQUENCE (not SEQUENCE) +get_sequence_of:"3100":0x04:"":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +AlgorithmIdentifier, no params +get_alg:"300506034f4944":4:3:0:0:0:7:0 + +AlgorithmIdentifier, no params, trailing garbage +get_alg:"300506034f49447e":4:3:0:0:0:7:0 + +AlgorithmIdentifier, null params +get_alg:"300706034f49440500":4:3:0x05:9:0:9:0 + +AlgorithmIdentifier, null params, trailing garbage +get_alg:"300706034f494405007e":4:3:0x05:9:0:9:0 + +AlgorithmIdentifier, OCTET STRING params +get_alg:"300c06034f494404056162636465":4:3:0x04:9:5:14:0 + +AlgorithmIdentifier, truncated before OID +get_alg:"3000":4:3:0:0:0:2:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +AlgorithmIdentifier, truncated in OID after tag +get_alg:"300106":0:0:0:0:0:3:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +AlgorithmIdentifier, truncated in OID after length +get_alg:"30020603":4:3:0:0:0:4:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +AlgorithmIdentifier, truncated inside OID content +get_alg:"300406034f49":4:3:0:0:0:6:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +AlgorithmIdentifier, truncated in params after tag +get_alg:"300606034f494404":4:3:0x04:0:0:8:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +AlgorithmIdentifier, truncated in params after length +get_alg:"300706034f49440405":4:3:0x04:9:0:9:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +AlgorithmIdentifier, truncated inside params content +get_alg:"300806034f4944040561":4:3:0x04:9:5:10:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +Not an AlgorithmIdentifier (not a SEQUENCE) +get_alg:"310506034f4944":0:0:0:0:0:0:MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +Not an AlgorithmIdentifier (empty SEQUENCE) +get_alg:"3000":0:0:0:0:0:0:MBEDTLS_ERR_ASN1_OUT_OF_DATA + +Not an AlgorithmIdentifier (not an OID) +get_alg:"3006050006034f4944":0:0:0:0:0:0:MBEDTLS_ERR_ASN1_UNEXPECTED_TAG + +Not an AlgorithmIdentifier (too many elements) +get_alg:"300f06034f494406034f494406034f4944":0:0:0:0:0:0:MBEDTLS_ERR_ASN1_LENGTH_MISMATCH + +Find named data: not found +find_named_data:"414141":"424242":"434343":"444444":"7f7f7f":0:4 + +Find named data: empty haystack +find_named_data:"414141":"424242":"434343":"444444":"7f7f7f":4:4 + +Find named data: first +find_named_data:"414141":"424242":"434343":"444444":"414141":0:0 + +Find named data: last +find_named_data:"414141":"424242":"434343":"444444":"444444":0:3 + +Find named data: skip suffix +find_named_data:"41414141":"414141":"434343":"444444":"414141":0:1 + +Find named data: skip prefix +find_named_data:"4141":"414141":"434343":"444444":"414141":0:1 + +Find named data: first match +find_named_data:"414141":"414141":"434343":"444444":"414141":0:0 + +Free named data: null pointer +free_named_data_null: + +Free named data: all null +free_named_data:0:0:0 + +Free named data: with oid +free_named_data:1:0:0 + +Free named data: with val +free_named_data:0:1:0 + +Free named data: with next +free_named_data:0:0:1 + +Free named data list (empty) +free_named_data_list:0 + +Free named data list (1) +free_named_data_list:0 + +Free named data list (2) +free_named_data_list:0 diff --git a/tests/suites/test_suite_asn1parse.function b/tests/suites/test_suite_asn1parse.function new file mode 100644 index 0000000000..9e9f509499 --- /dev/null +++ b/tests/suites/test_suite_asn1parse.function @@ -0,0 +1,568 @@ +/* BEGIN_HEADER */ +#include +#include +#include + +#include "mbedtls/bignum.h" +#include "mbedtls/asn1.h" +#if defined(MBEDTLS_ASN1_WRITE_C) +#include "mbedtls/asn1write.h" +#endif + +#define ERR_PARSE_INCONSISTENCY INT_MAX + +static int nested_parse( unsigned char **const p, + const unsigned char *const end ) +{ + int ret; + size_t len = 0; + size_t len2 = 0; + unsigned char *const start = *p; + unsigned char *content_start; + unsigned char tag; + + /* First get the length, skipping over the tag. */ + content_start = start + 1; + ret = mbedtls_asn1_get_len( &content_start, end, &len ); + TEST_ASSERT( content_start <= end ); + if( ret != 0 ) + return( ret ); + + /* Since we have a valid element start (tag and length), retrieve and + * check the tag. */ + tag = start[0]; + TEST_EQUAL( mbedtls_asn1_get_tag( p, end, &len2, tag ^ 1 ), + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); + *p = start; + TEST_EQUAL( mbedtls_asn1_get_tag( p, end, &len2, tag ), 0 ); + TEST_EQUAL( len, len2 ); + TEST_ASSERT( *p == content_start ); + *p = content_start; + + switch( tag & 0x1f ) + { + case MBEDTLS_ASN1_BOOLEAN: + { + int val = -257; + *p = start; + ret = mbedtls_asn1_get_bool( p, end, &val ); + if( ret == 0 ) + TEST_ASSERT( val == 0 || val == 1 ); + break; + } + + case MBEDTLS_ASN1_INTEGER: + { +#if defined(MBEDTLS_BIGNUM_C) + mbedtls_mpi mpi; + mbedtls_mpi_init( &mpi ); + *p = start; + ret = mbedtls_asn1_get_mpi( p, end, &mpi ); + mbedtls_mpi_free( &mpi ); +#endif + /* If we're sure that the number fits in an int, also + * call mbedtls_asn1_get_int(). */ + if( ret == 0 && len < sizeof( int ) ) + { + int val = -257; + unsigned char *q = start; + ret = mbedtls_asn1_get_int( &q, end, &val ); + TEST_ASSERT( *p == q ); + } + break; + } + + case MBEDTLS_ASN1_BIT_STRING: + { + mbedtls_asn1_bitstring bs; + *p = start; + ret = mbedtls_asn1_get_bitstring( p, end, &bs ); + break; + } + + case MBEDTLS_ASN1_SEQUENCE: + { + while( *p <= end && *p < content_start + len && ret == 0 ) + ret = nested_parse( p, content_start + len ); + break; + } + + case MBEDTLS_ASN1_OCTET_STRING: + case MBEDTLS_ASN1_NULL: + case MBEDTLS_ASN1_OID: + case MBEDTLS_ASN1_UTF8_STRING: + case MBEDTLS_ASN1_SET: + case MBEDTLS_ASN1_PRINTABLE_STRING: + case MBEDTLS_ASN1_T61_STRING: + case MBEDTLS_ASN1_IA5_STRING: + case MBEDTLS_ASN1_UTC_TIME: + case MBEDTLS_ASN1_GENERALIZED_TIME: + case MBEDTLS_ASN1_UNIVERSAL_STRING: + case MBEDTLS_ASN1_BMP_STRING: + default: + /* No further testing implemented for this tag. */ + *p += len; + return( 0 ); + } + + TEST_ASSERT( *p <= end ); + return( ret ); + +exit: + return( ERR_PARSE_INCONSISTENCY ); +} + +int get_len_step( const data_t *input, size_t buffer_size, + size_t actual_length ) +{ + unsigned char *buf = NULL; + unsigned char *p = NULL; + size_t parsed_length; + int ret; + + test_set_step( buffer_size ); + /* Allocate a new buffer of exactly the length to parse each time. + * This gives memory sanitizers a chance to catch buffer overreads. */ + if( buffer_size == 0 ) + { + ASSERT_ALLOC( buf, 1 ); + p = buf + 1; + } + else + { + ASSERT_ALLOC( buf, buffer_size ); + if( buffer_size > input->len ) + { + memcpy( buf, input->x, input->len ); + memset( buf + input->len, 'A', buffer_size - input->len ); + } + else + { + memcpy( buf, input->x, buffer_size ); + } + p = buf; + } + + ret = mbedtls_asn1_get_len( &p, buf + buffer_size, &parsed_length ); + + if( buffer_size >= input->len + actual_length ) + { + TEST_EQUAL( ret, 0 ); + TEST_ASSERT( p == buf + input->len ); + TEST_EQUAL( parsed_length, actual_length ); + } + else + { + TEST_EQUAL( ret, MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + } + mbedtls_free( buf ); + return( 1 ); + +exit: + /* It may be impossible to allocate large lengths on embedded platforms. + * Pass in this case (though it would be better to mark the test + * as skipped). */ + if( buf == NULL ) + return( 1 ); + + mbedtls_free( buf ); + return( 0 ); +} + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_ASN1_PARSE_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE */ +void parse_prefixes( const data_t *input, + int actual_length_arg, + int last_result ) +{ + size_t actual_length = actual_length_arg; + unsigned char *buf = NULL; + unsigned char *p = NULL; + size_t buffer_size; + int ret; + + for( buffer_size = 1; buffer_size <= input->len; buffer_size++ ) + { + test_set_step( buffer_size ); + /* Allocate a new buffer of exactly the length to parse each time. + * This gives memory sanitizers a chance to catch buffer overreads. */ + ASSERT_ALLOC( buf, buffer_size ); + memcpy( buf, input->x, buffer_size ); + p = buf; + ret = nested_parse( &p, buf + buffer_size ); + if( ret == ERR_PARSE_INCONSISTENCY ) + goto exit; + if( actual_length > 0 && buffer_size >= actual_length ) + { + TEST_EQUAL( ret, last_result ); + if( ret == 0 ) + TEST_ASSERT( p == buf + actual_length ); + } + else + { + TEST_EQUAL( ret, MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + } + mbedtls_free( buf ); + buf = NULL; + } + +exit: + mbedtls_free( buf ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void get_len( const data_t *input, int actual_length_arg ) +{ + size_t actual_length = actual_length_arg; + size_t buffer_size; + + for( buffer_size = 1; buffer_size <= input->len + 1; buffer_size++ ) + { + if( ! get_len_step( input, buffer_size, actual_length ) ) + goto exit; + } + if( ! get_len_step( input, input->len + actual_length - 1, actual_length ) ) + goto exit; + if( ! get_len_step( input, input->len + actual_length, actual_length ) ) + goto exit; +} +/* END_CASE */ + +/* BEGIN_CASE */ +void get_boolean( const data_t *input, + int expected_value, int expected_result ) +{ + unsigned char *p = input->x; + int val; + int ret; + ret = mbedtls_asn1_get_bool( &p, input->x + input->len, &val ); + TEST_EQUAL( ret, expected_result ); + if( expected_result == 0 ) + { + TEST_EQUAL( val, expected_value ); + TEST_ASSERT( p == input->x + input->len ); + } +} +/* END_CASE */ + +/* BEGIN_CASE */ +void get_integer( const data_t *input, + const char *expected_hex, int expected_result ) +{ + unsigned char *p; +#if defined(MBEDTLS_BIGNUM_C) + mbedtls_mpi expected_mpi; + mbedtls_mpi actual_mpi; +#endif + long expected_value; + int expected_result_for_int = expected_result; + int expected_result_for_mpi = expected_result; + int val; + int ret; + +#if defined(MBEDTLS_BIGNUM_C) + mbedtls_mpi_init( &expected_mpi ); + mbedtls_mpi_init( &actual_mpi ); +#endif + + errno = 0; + expected_value = strtol( expected_hex, NULL, 16 ); + if( expected_result == 0 && + ( errno == ERANGE +#if LONG_MAX > INT_MAX + || expected_value > INT_MAX || expected_value < INT_MIN +#endif + ) ) + { + expected_result_for_int = MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + p = input->x; + ret = mbedtls_asn1_get_int( &p, input->x + input->len, &val ); + TEST_EQUAL( ret, expected_result_for_int ); + if( ret == 0 ) + { + TEST_EQUAL( val, expected_value ); + TEST_ASSERT( p == input->x + input->len ); + } + +#if defined(MBEDTLS_BIGNUM_C) + ret = mbedtls_mpi_read_string( &expected_mpi, 16, expected_hex ); + TEST_ASSERT( ret == 0 || ret == MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + if( ret == MBEDTLS_ERR_MPI_BAD_INPUT_DATA ) + { + /* The data overflows the maximum MPI size. */ + expected_result_for_mpi = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + p = input->x; + ret = mbedtls_asn1_get_mpi( &p, input->x + input->len, &actual_mpi ); + TEST_EQUAL( ret, expected_result_for_mpi ); + if( ret == 0 ) + { + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &actual_mpi , &expected_mpi ) == 0 ); + TEST_ASSERT( p == input->x + input->len ); + } +#endif + +exit: +#if defined(MBEDTLS_BIGNUM_C) + mbedtls_mpi_free( &expected_mpi ); + mbedtls_mpi_free( &actual_mpi ); +#endif +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_BIGNUM_C */ +void get_mpi_too_large( ) +{ + unsigned char *buf = NULL; + unsigned char *p; + mbedtls_mpi actual_mpi; + size_t too_many_octets = + MBEDTLS_MPI_MAX_LIMBS * sizeof(mbedtls_mpi_uint) + 1; + size_t size = too_many_octets + 6; + + mbedtls_mpi_init( &actual_mpi ); + + ASSERT_ALLOC( buf, size ); + buf[0] = 0x02; /* tag: INTEGER */ + buf[1] = 0x84; /* 4-octet length */ + buf[2] = ( too_many_octets >> 24 ) & 0xff; + buf[3] = ( too_many_octets >> 16 ) & 0xff; + buf[4] = ( too_many_octets >> 8 ) & 0xff; + buf[5] = too_many_octets & 0xff; + buf[6] = 0x01; /* most significant octet */ + + p = buf; + TEST_EQUAL( mbedtls_asn1_get_mpi( &p, buf + size, &actual_mpi ), + MBEDTLS_ERR_MPI_ALLOC_FAILED ); + +exit: + mbedtls_mpi_free( &actual_mpi ); + mbedtls_free( buf ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void get_bitstring( const data_t *input, + int expected_length, int expected_unused_bits, + int expected_result, int expected_result_null ) +{ + mbedtls_asn1_bitstring bs = { 0xdead, 0x21, NULL }; + unsigned char *p = input->x; + + TEST_EQUAL( mbedtls_asn1_get_bitstring( &p, input->x + input->len, &bs ), + expected_result ); + if( expected_result == 0 ) + { + TEST_EQUAL( bs.len, (size_t) expected_length ); + TEST_EQUAL( bs.unused_bits, expected_unused_bits ); + TEST_ASSERT( bs.p != NULL ); + TEST_EQUAL( bs.p - input->x + bs.len, input->len ); + TEST_ASSERT( p == input->x + input->len ); + } + + p = input->x; + TEST_EQUAL( mbedtls_asn1_get_bitstring_null( &p, input->x + input->len, + &bs.len ), + expected_result_null ); + if( expected_result_null == 0 ) + { + TEST_EQUAL( bs.len, (size_t) expected_length ); + if( expected_result == 0 ) + TEST_ASSERT( p == input->x + input->len - bs.len ); + } +} +/* END_CASE */ + +/* BEGIN_CASE */ +void get_sequence_of( const data_t *input, int tag, + const char *description, + int expected_result ) +{ + mbedtls_asn1_sequence head = { { 0, 0, NULL }, NULL }; + mbedtls_asn1_sequence *cur, *next; + unsigned char *p = input->x; + const char *rest = description; + unsigned long n; + + TEST_EQUAL( mbedtls_asn1_get_sequence_of( &p, input->x + input->len, + &head, tag ), + expected_result ); + if( expected_result == 0 ) + { + TEST_ASSERT( p == input->x + input->len ); + + if( ! *rest ) + { + TEST_EQUAL( head.buf.tag, 0 ); + TEST_ASSERT( head.buf.p == NULL ); + TEST_EQUAL( head.buf.len, 0 ); + TEST_ASSERT( head.next == NULL ); + } + else + { + cur = &head; + while( *rest ) + { + ++test_info.step; + TEST_ASSERT( cur != NULL ); + TEST_EQUAL( cur->buf.tag, tag ); + n = strtoul( rest, (char **) &rest, 0 ); + TEST_EQUAL( n, (size_t)( cur->buf.p - input->x ) ); + ++rest; + n = strtoul( rest, (char **) &rest, 0 ); + TEST_EQUAL( n, cur->buf.len ); + if( *rest ) + ++rest; + cur = cur->next; + } + TEST_ASSERT( cur == NULL ); + } + } + +exit: + cur = head.next; + while( cur != NULL ) + { + next = cur->next; + mbedtls_free( cur ); + cur = next; + } +} +/* END_CASE */ + +/* BEGIN_CASE */ +void get_alg( const data_t *input, + int oid_offset, int oid_length, + int params_tag, int params_offset, int params_length, + int total_length, + int expected_result ) +{ + mbedtls_asn1_buf oid = { -1, 0, NULL }; + mbedtls_asn1_buf params = { -1, 0, NULL }; + unsigned char *p = input->x; + int ret; + + TEST_EQUAL( mbedtls_asn1_get_alg( &p, input->x + input->len, + &oid, ¶ms ), + expected_result ); + if( expected_result == 0 ) + { + TEST_EQUAL( oid.tag, MBEDTLS_ASN1_OID ); + TEST_EQUAL( oid.p - input->x, oid_offset ); + TEST_EQUAL( oid.len, (size_t) oid_length ); + TEST_EQUAL( params.tag, params_tag ); + if( params_offset != 0 ) + TEST_EQUAL( params.p - input->x, params_offset ); + else + TEST_ASSERT( params.p == NULL ); + TEST_EQUAL( params.len, (size_t) params_length ); + TEST_EQUAL( p - input->x, total_length ); + } + + ret = mbedtls_asn1_get_alg_null( &p, input->x + input->len, &oid ); + if( expected_result == 0 && params_offset == 0 ) + { + TEST_EQUAL( oid.tag, MBEDTLS_ASN1_OID ); + TEST_EQUAL( oid.p - input->x, oid_offset ); + TEST_EQUAL( oid.len, (size_t) oid_length ); + TEST_EQUAL( p - input->x, total_length ); + } + else + TEST_ASSERT( ret != 0 ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void find_named_data( data_t *oid0, data_t *oid1, data_t *oid2, data_t *oid3, + data_t *needle, int from, int position ) +{ + mbedtls_asn1_named_data nd[] ={ + { {0x06, oid0->len, oid0->x}, {0, 0, NULL}, NULL, 0 }, + { {0x06, oid1->len, oid1->x}, {0, 0, NULL}, NULL, 0 }, + { {0x06, oid2->len, oid2->x}, {0, 0, NULL}, NULL, 0 }, + { {0x06, oid3->len, oid3->x}, {0, 0, NULL}, NULL, 0 }, + }; + mbedtls_asn1_named_data *pointers[ARRAY_LENGTH( nd ) + 1]; + size_t i; + mbedtls_asn1_named_data *found; + + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + pointers[i] = &nd[i]; + pointers[ARRAY_LENGTH( nd )] = NULL; + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + nd[i].next = pointers[i+1]; + + found = mbedtls_asn1_find_named_data( pointers[from], + (const char *) needle->x, + needle->len ); + TEST_ASSERT( found == pointers[position] ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void free_named_data_null( ) +{ + mbedtls_asn1_free_named_data( NULL ); + goto exit; /* Silence unused label warning */ +} +/* END_CASE */ + +/* BEGIN_CASE */ +void free_named_data( int with_oid, int with_val, int with_next ) +{ + mbedtls_asn1_named_data next = + { {0x06, 0, NULL}, {0, 0xcafe, NULL}, NULL, 0 }; + mbedtls_asn1_named_data head = + { {0x06, 0, NULL}, {0, 0, NULL}, NULL, 0 }; + + if( with_oid ) + ASSERT_ALLOC( head.oid.p, 1 ); + if( with_val ) + ASSERT_ALLOC( head.val.p, 1 ); + if( with_next ) + head.next = &next; + + mbedtls_asn1_free_named_data( &head ); + TEST_ASSERT( head.oid.p == NULL ); + TEST_ASSERT( head.val.p == NULL ); + TEST_ASSERT( head.next == NULL ); + TEST_ASSERT( next.val.len == 0xcafe ); + +exit: + mbedtls_free( head.oid.p ); + mbedtls_free( head.val.p ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void free_named_data_list( int length ) +{ + mbedtls_asn1_named_data *head = NULL; + int i; + + for( i = 0; i < length; i++ ) + { + mbedtls_asn1_named_data *new = NULL; + ASSERT_ALLOC( new, sizeof( mbedtls_asn1_named_data ) ); + head->next = new; + head = new; + } + + mbedtls_asn1_free_named_data_list( &head ); + TEST_ASSERT( head == NULL ); + /* Most of the point of the test is that it doesn't leak memory. + * So this test is only really useful under a memory leak detection + * framework. */ +exit: + mbedtls_asn1_free_named_data_list( &head ); +} +/* END_CASE */ From f7d6acd4754b732084aea5f0a94ecdc6b4156185 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 18:06:08 +0100 Subject: [PATCH 04/17] mbedtls_asn1_get_int: allow leading zeros properly Allow any number of leading zeros, not just based on sizeof(int). --- library/asn1parse.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/library/asn1parse.c b/library/asn1parse.c index 171c340b8c..20e8177b6c 100644 --- a/library/asn1parse.c +++ b/library/asn1parse.c @@ -149,11 +149,18 @@ int mbedtls_asn1_get_int( unsigned char **p, if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_INTEGER ) ) != 0 ) return( ret ); - if( len == 0 || len > sizeof( int ) || ( **p & 0x80 ) != 0 ) + if( len == 0 || ( **p & 0x80 ) != 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + + while( len > 0 && **p == 0 ) + { + ++( *p ); + --len; + } + if( len > sizeof( int ) ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); *val = 0; - while( len-- > 0 ) { *val = ( *val << 8 ) | **p; From e40d1207ebb30dc9549e86c6be9180069a00fefa Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 18:08:35 +0100 Subject: [PATCH 05/17] mbedtls_asn1_get_bitstring_null: fix rejection of short inputs Fix improper rejection of bitstrings with length less than 2. --- library/asn1parse.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/asn1parse.c b/library/asn1parse.c index 20e8177b6c..4764ca4cbc 100644 --- a/library/asn1parse.c +++ b/library/asn1parse.c @@ -230,8 +230,13 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end if( ( ret = mbedtls_asn1_get_tag( p, end, len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 ) return( ret ); - if( (*len)-- < 2 || *(*p)++ != 0 ) + if( *len == 0 ) return( MBEDTLS_ERR_ASN1_INVALID_DATA ); + --( *len ); + + if( **p != 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_DATA ); + ++( *p ); return( 0 ); } From 3a032c36c1a0a6e8f7d62994287b5e9a31ae8598 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 18:13:36 +0100 Subject: [PATCH 06/17] Add test cases for BOOLEANs and INTEGERs Omit negative integers and MPIs that would result in values that look like negative INTEGERs, since the library doesn't respect the specifications there, but fixing it has a serious risk of breaking interoperability when ASN.1 is used in X.509 and other cryptography-related applications. --- tests/suites/test_suite_asn1write.data | 84 ++++++++++++++++ tests/suites/test_suite_asn1write.function | 112 +++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/tests/suites/test_suite_asn1write.data b/tests/suites/test_suite_asn1write.data index 9982d03a72..f98df4e25e 100644 --- a/tests/suites/test_suite_asn1write.data +++ b/tests/suites/test_suite_asn1write.data @@ -1,3 +1,87 @@ +ASN.1 Write BOOLEAN FALSE +mbedtls_asn1_write_bool:0:"010100" + +ASN.1 Write BOOLEAN TRUE +mbedtls_asn1_write_bool:1:"0101ff" + +ASN.1 Write int 0 +mbedtls_asn1_write_int:0:"020100" + +ASN.1 Write int 1 +mbedtls_asn1_write_int:1:"020101" + +ASN.1 Write int 127 +mbedtls_asn1_write_int:0x7f:"02017f" + +ASN.1 Write int 128 +mbedtls_asn1_write_int:0x80:"02020080" + +ASN.1 Write int 255 +mbedtls_asn1_write_int:0xff:"020200ff" + +ASN.1 Write int 256 +mbedtls_asn1_write_int:0x100:"02020100" + +ASN.1 Write int 32767 +mbedtls_asn1_write_int:0x7fff:"02027fff" + +ASN.1 Write int 32768 +mbedtls_asn1_write_int:0x8000:"0203008000" + +ASN.1 Write int 65535 +mbedtls_asn1_write_int:0xffff:"020300ffff" + +ASN.1 Write int 65536 +mbedtls_asn1_write_int:0x10000:"0203010000" + +ASN.1 Write int 8388607 +mbedtls_asn1_write_int:0x7fffff:"02037fffff" + +ASN.1 Write int 8388608 +mbedtls_asn1_write_int:0x800000:"020400800000" + +ASN.1 Write int 0x12345678 +mbedtls_asn1_write_int:0x12345678:"020412345678" + +ASN.1 Write int 2147483647 +mbedtls_asn1_write_int:0x7fffffff:"02047fffffff" + +#ASN.1 Write mpi 0 +#mbedtls_asn1_write_mpi:"00":"020100" + +ASN.1 Write mpi 1 +mbedtls_asn1_write_mpi:"01":"020101" + +ASN.1 Write mpi 0x7f +mbedtls_asn1_write_mpi:"7f":"02017f" + +#ASN.1 Write mpi 0x80 +#mbedtls_asn1_write_mpi:"7f":"02020080" + +#ASN.1 Write mpi 0xff +#mbedtls_asn1_write_mpi:"7f":"020200ff" + +ASN.1 Write mpi 0x100 +mbedtls_asn1_write_mpi:"0100":"02020100" + +ASN.1 Write mpi, 127*8-1 bits +mbedtls_asn1_write_mpi:"7f7b16e05c1537de7c41cef1a0985d6a3ced98aec28e091874cbad6b5e40a5c956258f18861c28bed8ba808259339ee34b2e509c4080149474d5d5b86093f90c475a6443fc87e1a293d4151be625d652f1c32a00a018bba10c8a2ae5b2b0ee4be64e053dce9d07ec7919526c9dfcf2ec9fc3db485caa8e5a68a2cd0a427de8":"027f7f7b16e05c1537de7c41cef1a0985d6a3ced98aec28e091874cbad6b5e40a5c956258f18861c28bed8ba808259339ee34b2e509c4080149474d5d5b86093f90c475a6443fc87e1a293d4151be625d652f1c32a00a018bba10c8a2ae5b2b0ee4be64e053dce9d07ec7919526c9dfcf2ec9fc3db485caa8e5a68a2cd0a427de8" + +#ASN.1 Write mpi, 127*8 bits +#mbedtls_asn1_write_mpi:"e77b16e05c1537de7c41cef1a0985d6a3ced98aec28e091874cbad6b5e40a5c956258f18861c28bed8ba808259339ee34b2e509c4080149474d5d5b86093f90c475a6443fc87e1a293d4151be625d652f1c32a00a018bba10c8a2ae5b2b0ee4be64e053dce9d07ec7919526c9dfcf2ec9fc3db485caa8e5a68a2cd0a427de8":"028180e77b16e05c1537de7c41cef1a0985d6a3ced98aec28e091874cbad6b5e40a5c956258f18861c28bed8ba808259339ee34b2e509c4080149474d5d5b86093f90c475a6443fc87e1a293d4151be625d652f1c32a00a018bba10c8a2ae5b2b0ee4be64e053dce9d07ec7919526c9dfcf2ec9fc3db485caa8e5a68a2cd0a427de8" + +ASN.1 Write mpi, 127*8+1 bits +mbedtls_asn1_write_mpi:"108446d68934cc1af23c4cd909884d4bd737a1890e12f5ef8bf3d807d72feffa63c0bf2633345f8b8418d144617c871a7a0277ac0150eed4b3db7f9dff21114cd0d7f282400f03c931cb00c367550e374a1ed3762a1801ca714cfc8d5aac69707ca81e0661400ed0014d97cba48f94d835dd681fc3053c51958afbf7583cf49c":"028180108446d68934cc1af23c4cd909884d4bd737a1890e12f5ef8bf3d807d72feffa63c0bf2633345f8b8418d144617c871a7a0277ac0150eed4b3db7f9dff21114cd0d7f282400f03c931cb00c367550e374a1ed3762a1801ca714cfc8d5aac69707ca81e0661400ed0014d97cba48f94d835dd681fc3053c51958afbf7583cf49c" + +ASN.1 Write mpi, 255*8-1 bits +mbedtls_asn1_write_mpi:"7bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c":"0281ff7bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c" + +#ASN.1 Write mpi, 255*8 bits +#mbedtls_asn1_write_mpi:"fbd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c":"0282010000fbd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c" + +ASN.1 Write mpi, 256*8-1 bits +mbedtls_asn1_write_mpi:"7bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c89":"028201007bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c89" + ASN.1 Write Octet String #0 (Empty string) mbedtls_asn1_write_octet_string:"":"0400":2:2 diff --git a/tests/suites/test_suite_asn1write.function b/tests/suites/test_suite_asn1write.function index e45583cbbd..7dfc16217e 100644 --- a/tests/suites/test_suite_asn1write.function +++ b/tests/suites/test_suite_asn1write.function @@ -3,6 +3,53 @@ #define GUARD_LEN 4 #define GUARD_VAL 0x2a + +typedef struct +{ + unsigned char *output; + unsigned char *start; + unsigned char *end; + unsigned char *p; + size_t size; +} generic_write_data_t; + +int generic_write_start_step( generic_write_data_t *data ) +{ + test_set_step( data->size ); + ASSERT_ALLOC( data->output, data->size == 0 ? 1 : data->size ); + data->end = data->output + data->size; + data->p = data->end; + data->start = data->end - data->size; + return( 1 ); +exit: + return( 0 ); +} + +int generic_write_finish_step( generic_write_data_t *data, + const data_t *expected, int ret ) +{ + int ok = 0; + + if( data->size < expected->len ) + { + TEST_EQUAL( ret, MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); + } + else + { + TEST_EQUAL( ret, data->end - data->p ); + TEST_ASSERT( data->p >= data->start ); + TEST_ASSERT( data->p <= data->end ); + ASSERT_COMPARE( data->p, (size_t)( data->end - data->p ), + expected->x, expected->len ); + } + ok = 1; + +exit: + mbedtls_free( data->output ); + data->output = NULL; + return( ok ); +} + /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -10,6 +57,71 @@ * END_DEPENDENCIES */ +/* BEGIN_CASE */ +void mbedtls_asn1_write_bool( int val, data_t *expected ) +{ + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; + int ret; + + for( data.size = 0; data.size < expected->len + 1; data.size++ ) + { + if( ! generic_write_start_step( &data ) ) + goto exit; + ret = mbedtls_asn1_write_bool( &data.p, data.start, val ); + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; + } + +exit: + mbedtls_free( data.output ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void mbedtls_asn1_write_int( int val, data_t *expected ) +{ + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; + int ret; + + for( data.size = 0; data.size < expected->len + 1; data.size++ ) + { + if( ! generic_write_start_step( &data ) ) + goto exit; + ret = mbedtls_asn1_write_int( &data.p, data.start, val ); + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; + } + +exit: + mbedtls_free( data.output ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_BIGNUM_C */ +void mbedtls_asn1_write_mpi( data_t *val, data_t *expected ) +{ + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; + mbedtls_mpi mpi; + int ret; + + mbedtls_mpi_init( &mpi ); + TEST_ASSERT( mbedtls_mpi_read_binary( &mpi, val->x, val->len ) == 0 ); + + for( data.size = 0; data.size < expected->len + 1; data.size++ ) + { + if( ! generic_write_start_step( &data ) ) + goto exit; + ret = mbedtls_asn1_write_mpi( &data.p, data.start, &mpi ); + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; + } + +exit: + mbedtls_mpi_free( &mpi ); + mbedtls_free( data.output ); +} +/* END_CASE */ + /* BEGIN_CASE */ void mbedtls_asn1_write_octet_string( data_t * str, data_t * asn1, int buf_len, int result ) From 1dbab67ce8e79457cceeeee8510b2f781fb950bf Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 18:15:18 +0100 Subject: [PATCH 07/17] Improve mbedtls_asn1_write_int to support values >255 mbedtls_asn1_write_int had an undocumented restriction to values that fit in a single octet. Fix this. Negative integers are still not supported. --- library/asn1write.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/asn1write.c b/library/asn1write.c index b54e26bd8a..98c6766725 100644 --- a/library/asn1write.c +++ b/library/asn1write.c @@ -236,17 +236,20 @@ int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val ) int ret; size_t len = 0; - if( *p - start < 1 ) - return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); - - len += 1; - *--(*p) = val; - - if( val > 0 && **p & 0x80 ) + do { if( *p - start < 1 ) return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); + len += 1; + *--(*p) = val & 0xff; + val >>= 8; + } + while( val > 0 ); + if( **p & 0x80 ) + { + if( *p - start < 1 ) + return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); *--(*p) = 0x00; len += 1; } From 105031b1e14e3f80a66a27e1745fc220292b0f4b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 19:28:41 +0100 Subject: [PATCH 08/17] asn1_write documentation: say that integers must be non-negative The documentation never said it explicitly, but the ASN.1 library doesn't support negative integers. Say it explicitly. Also fix a copypasta error. --- include/mbedtls/asn1write.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/mbedtls/asn1write.h b/include/mbedtls/asn1write.h index 336f2daf1b..8ecab4e2dd 100644 --- a/include/mbedtls/asn1write.h +++ b/include/mbedtls/asn1write.h @@ -100,6 +100,7 @@ int mbedtls_asn1_write_raw_buffer( unsigned char **p, unsigned char *start, * \param p The reference to the current position pointer. * \param start The start of the buffer, for bounds-checking. * \param X The MPI to write. + * It must be non-negative. * * \return The number of bytes written to \p p on success. * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. @@ -184,6 +185,7 @@ int mbedtls_asn1_write_bool( unsigned char **p, unsigned char *start, * \param p The reference to the current position pointer. * \param start The start of the buffer, for bounds-checking. * \param val The integer value to write. + * It must be non-negative. * * \return The number of bytes written to \p p on success. * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. @@ -232,7 +234,7 @@ int mbedtls_asn1_write_printable_string( unsigned char **p, /** * \brief Write a UTF8 string in ASN.1 format using the UTF8String - * string encoding tag (#MBEDTLS_ASN1_PRINTABLE_STRING). + * string encoding tag (#MBEDTLS_ASN1_UTF8_STRING). * * \note This function works backwards in data buffer. * From 3f37dca794d41f1fb077d3dbe483501b636877b6 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 19:30:20 +0100 Subject: [PATCH 09/17] asn1write: Test short and large buffers more systematically Use the test-many-sizes framework for string writes as well (previously, it was only used for booleans and integers). This way, more edge cases are tested with less test code. This commit removes buffer overwrite checks. Instead of these checks, run the test suite under a memory sanitizer (which we do in our CI). --- tests/suites/test_suite_asn1write.data | 122 ++++++++--------- tests/suites/test_suite_asn1write.function | 144 ++++++++------------- 2 files changed, 117 insertions(+), 149 deletions(-) diff --git a/tests/suites/test_suite_asn1write.data b/tests/suites/test_suite_asn1write.data index f98df4e25e..90654d75c3 100644 --- a/tests/suites/test_suite_asn1write.data +++ b/tests/suites/test_suite_asn1write.data @@ -82,56 +82,62 @@ mbedtls_asn1_write_mpi:"7bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7 ASN.1 Write mpi, 256*8-1 bits mbedtls_asn1_write_mpi:"7bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c89":"028201007bd1913fcfb652896209ad3e62f5d04a8dfc71eb1698543c52200bd7bbf3c11dd9ff57c299a2f4da172b3d5bd7e29affddf8859be7d50a45537a0df15b17af603d18803fd17134847cba78d83e64bf9fee58364d6124add0541da7bad331cd35fb48186a74bc502ddb967602401c0db02b19e5d38f09e8618fa7f6a1a3f738629baffdc63d9d70d396007d943fd64ae696e5b7e88f2c6d6ec322b461dbddd36efa91d990343b66419cf4832a22dc9ad13021185a1bf007989a50ba3bfd1152b8db899482d3ed498d1b9fae243a3cdae9530d8b29fdb684f70cdc0c9b8527265312603b405e67d59d4b1d654ddc3b7fd5515acb32440dc80903c8474a2c136c89" -ASN.1 Write Octet String #0 (Empty string) -mbedtls_asn1_write_octet_string:"":"0400":2:2 +ASN.1 Write OCTET STRING: length=0 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"":"0400" -ASN.1 Write Octet String #1 (Large buffer) -mbedtls_asn1_write_octet_string:"AABBCC":"0403AABBCC":10:5 +ASN.1 Write OCTET STRING: length=1 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"41":"040141" -ASN.1 Write Octet String #2 (Buffer just fits) -mbedtls_asn1_write_octet_string:"AABBCC":"0403AABBCC":5:5 +ASN.1 Write OCTET STRING: length=2 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"4142":"04024142" -ASN.1 Write Octet String #3 (Buffer too small for tag) -mbedtls_asn1_write_octet_string:"AABBCC":"0403AABBCC":4:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write OCTET STRING: length=127 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"99a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"047f99a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" -ASN.1 Write Octet String #4 (Buffer too small for len) -mbedtls_asn1_write_octet_string:"AABBCC":"0403AABBCC":3:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write OCTET STRING: length=128 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"0199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"0481800199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" -ASN.1 Write Octet String #5 (Buffer too small for string) -mbedtls_asn1_write_octet_string:"AABBCC":"0403AABBCC":2:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write OCTET STRING: length=255 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"633ed2cb0a2915dc4438a4c063017eb336cd9571d2a0585522c5073ca22a30ca7b8c9bd167d89ba1827bc6fb5d6ef6dcc52ee6eecc47e84ee0dd18fa3ebbdb6edfc679f037160d48d46a0d7e571335b24a28c8fd29b7f4a93d013b74e522bc1f5f605096bb99d438814b77b54d6dde608417b0a0ce9a8cb507fbeb95e9926b4bb6eec725599493d4b156ef3a5fd701426456029111c20f1d03c5d8999d2c042277ef91c5114a6c06218c1ba28d41ef08e4870d0cef260cba9de16d7d11ed5889b88fb93073746ebb158a4246cdb8a4ce403a5d1d598a0d11548f22070f833c1344d15e7a1445c133d19b8295b7c071bf2227178938031249d22d21c6f8e53d":"0481ff633ed2cb0a2915dc4438a4c063017eb336cd9571d2a0585522c5073ca22a30ca7b8c9bd167d89ba1827bc6fb5d6ef6dcc52ee6eecc47e84ee0dd18fa3ebbdb6edfc679f037160d48d46a0d7e571335b24a28c8fd29b7f4a93d013b74e522bc1f5f605096bb99d438814b77b54d6dde608417b0a0ce9a8cb507fbeb95e9926b4bb6eec725599493d4b156ef3a5fd701426456029111c20f1d03c5d8999d2c042277ef91c5114a6c06218c1ba28d41ef08e4870d0cef260cba9de16d7d11ed5889b88fb93073746ebb158a4246cdb8a4ce403a5d1d598a0d11548f22070f833c1344d15e7a1445c133d19b8295b7c071bf2227178938031249d22d21c6f8e53d" -ASN.1 Write Octet String #6 (l = 128, large buffer) -mbedtls_asn1_write_octet_string:"000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":"048180000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":140:131 +ASN.1 Write OCTET STRING: length=256 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OCTET_STRING:"5a633ed2cb0a2915dc4438a4c063017eb336cd9571d2a0585522c5073ca22a30ca7b8c9bd167d89ba1827bc6fb5d6ef6dcc52ee6eecc47e84ee0dd18fa3ebbdb6edfc679f037160d48d46a0d7e571335b24a28c8fd29b7f4a93d013b74e522bc1f5f605096bb99d438814b77b54d6dde608417b0a0ce9a8cb507fbeb95e9926b4bb6eec725599493d4b156ef3a5fd701426456029111c20f1d03c5d8999d2c042277ef91c5114a6c06218c1ba28d41ef08e4870d0cef260cba9de16d7d11ed5889b88fb93073746ebb158a4246cdb8a4ce403a5d1d598a0d11548f22070f833c1344d15e7a1445c133d19b8295b7c071bf2227178938031249d22d21c6f8e53d":"048201005a633ed2cb0a2915dc4438a4c063017eb336cd9571d2a0585522c5073ca22a30ca7b8c9bd167d89ba1827bc6fb5d6ef6dcc52ee6eecc47e84ee0dd18fa3ebbdb6edfc679f037160d48d46a0d7e571335b24a28c8fd29b7f4a93d013b74e522bc1f5f605096bb99d438814b77b54d6dde608417b0a0ce9a8cb507fbeb95e9926b4bb6eec725599493d4b156ef3a5fd701426456029111c20f1d03c5d8999d2c042277ef91c5114a6c06218c1ba28d41ef08e4870d0cef260cba9de16d7d11ed5889b88fb93073746ebb158a4246cdb8a4ce403a5d1d598a0d11548f22070f833c1344d15e7a1445c133d19b8295b7c071bf2227178938031249d22d21c6f8e53d" -ASN.1 Write Octet String #7 (l = 128, buffer just fits) -mbedtls_asn1_write_octet_string:"000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":"048180000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":131:131 +ASN.1 Write UTF8 STRING: length=0 +mbedtls_asn1_write_string:MBEDTLS_ASN1_UTF8_STRING:"":"0c00" -ASN.1 Write Octet String #8 (l = 128, buffer too small for tag) -mbedtls_asn1_write_octet_string:"000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":"":130:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write UTF8 STRING: length=1 +mbedtls_asn1_write_string:MBEDTLS_ASN1_UTF8_STRING:"41":"0c0141" -ASN.1 Write Octet String #9 (l = 128, buffer too small for len) -mbedtls_asn1_write_octet_string:"000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":"":129:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write UTF8 STRING: length=128 +mbedtls_asn1_write_string:MBEDTLS_ASN1_UTF8_STRING:"0199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"0c81800199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" -ASN.1 Write Octet String #9 (l = 128, buffer too small for string) -mbedtls_asn1_write_octet_string:"000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F":"":127:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write PRINTABLE STRING: length=0 +mbedtls_asn1_write_string:MBEDTLS_ASN1_PRINTABLE_STRING:"":"1300" -ASN.1 Write IA5 String #0 (Empty string) -mbedtls_asn1_write_ia5_string:"":"1600":2:2 +ASN.1 Write PRINTABLE STRING: length=1 +mbedtls_asn1_write_string:MBEDTLS_ASN1_PRINTABLE_STRING:"41":"130141" -ASN.1 Write IA5 String #1 (Large buffer) -mbedtls_asn1_write_ia5_string:"ABC":"1603414243":10:5 +ASN.1 Write PRINTABLE STRING: length=128 +mbedtls_asn1_write_string:MBEDTLS_ASN1_PRINTABLE_STRING:"0199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"1381800199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" -ASN.1 Write IA5 String #2 (Buffer just fits) -mbedtls_asn1_write_ia5_string:"ABC":"1603414243":5:5 +ASN.1 Write IA5 STRING: length=0 +mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING:"":"1600" -ASN.1 Write IA5 String #3 (Buffer too small for tag) -mbedtls_asn1_write_ia5_string:"ABC":"":4:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write IA5 STRING: length=1 +mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING:"41":"160141" -ASN.1 Write IA5 String #4 (Buffer too small for len) -mbedtls_asn1_write_ia5_string:"ABC":"":3:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write IA5 STRING: length=128 +mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING:"0199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"1681800199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" -ASN.1 Write IA5 String #5 (Buffer too small for string) -mbedtls_asn1_write_ia5_string:"ABC":"":2:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL +ASN.1 Write tagged string: length=0 +mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING | MBEDTLS_ASN1_CONTEXT_SPECIFIC:"":"9600" + +ASN.1 Write tagged string: length=1 +mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING | MBEDTLS_ASN1_CONTEXT_SPECIFIC:"41":"960141" + +ASN.1 Write tagged string: length=128 +mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING | MBEDTLS_ASN1_CONTEXT_SPECIFIC:"0199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"9681800199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" ASN.1 Write / Read Length #0 (Len = 0, short form) mbedtls_asn1_write_len:0:"00":1:1 @@ -176,73 +182,73 @@ ASN.1 Write / Read Length #12 (Len = 16909060, buffer too small) mbedtls_asn1_write_len:16909060:"8401020304":4:MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ASN.1 Write Named Bitstring / Unused bits #0 -test_asn1_write_bitstrings:"FF":8:"030200FF":4:1 +test_asn1_write_bitstrings:"FF":8:"030200FF":1 ASN.1 Write Named Bitstring / Unused bits #1 -test_asn1_write_bitstrings:"FE":8:"030201FE":4:1 +test_asn1_write_bitstrings:"FE":8:"030201FE":1 ASN.1 Write Named Bitstring / Unused bits #2 -test_asn1_write_bitstrings:"FC":7:"030202FC":4:1 +test_asn1_write_bitstrings:"FC":7:"030202FC":1 ASN.1 Write Named Bitstring / Unused bits #3 -test_asn1_write_bitstrings:"F8":8:"030203F8":4:1 +test_asn1_write_bitstrings:"F8":8:"030203F8":1 ASN.1 Write Named Bitstring / Unused bits #4 -test_asn1_write_bitstrings:"F0":6:"030204F0":4:1 +test_asn1_write_bitstrings:"F0":6:"030204F0":1 ASN.1 Write Named Bitstring / Unused bits #5 -test_asn1_write_bitstrings:"E0":6:"030205E0":4:1 +test_asn1_write_bitstrings:"E0":6:"030205E0":1 ASN.1 Write Named Bitstring / Unused bits #6 -test_asn1_write_bitstrings:"C0":8:"030206C0":4:1 +test_asn1_write_bitstrings:"C0":8:"030206C0":1 ASN.1 Write Named Bitstring / Unused bits #7 -test_asn1_write_bitstrings:"80":8:"03020780":4:1 +test_asn1_write_bitstrings:"80":8:"03020780":1 ASN.1 Write Named Bitstring / Empty bitstring -test_asn1_write_bitstrings:"00":7:"030100":3:1 +test_asn1_write_bitstrings:"00":7:"030100":1 ASN.1 Write Named Bitstring / Empty bitstring (bits = 16) -test_asn1_write_bitstrings:"0000":16:"030100":3:1 +test_asn1_write_bitstrings:"0000":16:"030100":1 ASN.1 Write Named Bitstring / Empty bitstring (bits = 24) -test_asn1_write_bitstrings:"FFFFFF":0:"030100":3:1 +test_asn1_write_bitstrings:"FFFFFF":0:"030100":1 ASN.1 Write Named Bitstring / 15 trailing bits all unset -test_asn1_write_bitstrings:"F88000":24:"030307F880":5:1 +test_asn1_write_bitstrings:"F88000":24:"030307F880":1 ASN.1 Write Named Bitstring / 15 trailing bits all set -test_asn1_write_bitstrings:"F8FFFF":9:"030307F880":5:1 +test_asn1_write_bitstrings:"F8FFFF":9:"030307F880":1 ASN.1 Write Bitstring / Unused bits #0 -test_asn1_write_bitstrings:"FF":8:"030200FF":4:0 +test_asn1_write_bitstrings:"FF":8:"030200FF":0 ASN.1 Write Bitstring / Unused bits #1 -test_asn1_write_bitstrings:"FF":7:"030201FE":4:0 +test_asn1_write_bitstrings:"FF":7:"030201FE":0 ASN.1 Write Bitstring / Unused bits #2 -test_asn1_write_bitstrings:"FF":6:"030202FC":4:0 +test_asn1_write_bitstrings:"FF":6:"030202FC":0 ASN.1 Write Bitstring / Unused bits #3 -test_asn1_write_bitstrings:"FF":5:"030203F8":4:0 +test_asn1_write_bitstrings:"FF":5:"030203F8":0 ASN.1 Write Bitstring / Unused bits #4 -test_asn1_write_bitstrings:"FF":4:"030204F0":4:0 +test_asn1_write_bitstrings:"FF":4:"030204F0":0 ASN.1 Write Bitstring / Unused bits #5 -test_asn1_write_bitstrings:"FF":3:"030205E0":4:0 +test_asn1_write_bitstrings:"FF":3:"030205E0":0 ASN.1 Write Bitstring / Unused bits #6 -test_asn1_write_bitstrings:"FF":2:"030206C0":4:0 +test_asn1_write_bitstrings:"FF":2:"030206C0":0 ASN.1 Write Bitstring / Unused bits #7 -test_asn1_write_bitstrings:"FF":1:"03020780":4:0 +test_asn1_write_bitstrings:"FF":1:"03020780":0 ASN.1 Write Bitstring / 1 trailing bit (bits 15) -test_asn1_write_bitstrings:"0003":15:"0303010002":5:0 +test_asn1_write_bitstrings:"0003":15:"0303010002":0 ASN.1 Write Bitstring / 0 bits -test_asn1_write_bitstrings:"":0:"030100":3:0 +test_asn1_write_bitstrings:"":0:"030100":0 ASN.1 Write Bitstring / long string all bits unset except trailing bits -test_asn1_write_bitstrings:"000000000007":45:"030703000000000000":9:0 +test_asn1_write_bitstrings:"000000000007":45:"030703000000000000":0 diff --git a/tests/suites/test_suite_asn1write.function b/tests/suites/test_suite_asn1write.function index 7dfc16217e..1669ca86d5 100644 --- a/tests/suites/test_suite_asn1write.function +++ b/tests/suites/test_suite_asn1write.function @@ -114,6 +114,8 @@ void mbedtls_asn1_write_mpi( data_t *val, data_t *expected ) ret = mbedtls_asn1_write_mpi( &data.p, data.start, &mpi ); if( ! generic_write_finish_step( &data, expected, ret ) ) goto exit; + if( expected->len > 10 && data.size == 8 ) + data.size = expected->len - 2; } exit: @@ -123,70 +125,49 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void mbedtls_asn1_write_octet_string( data_t * str, data_t * asn1, - int buf_len, int result ) +void mbedtls_asn1_write_string( int tag, data_t *content, data_t *expected ) { + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; int ret; - unsigned char buf[150]; - size_t i; - unsigned char *p; - memset( buf, GUARD_VAL, sizeof( buf ) ); - - - p = buf + GUARD_LEN + buf_len; - - ret = mbedtls_asn1_write_octet_string( &p, buf + GUARD_LEN, str->x, str->len ); - - /* Check for buffer overwrite on both sides */ - for( i = 0; i < GUARD_LEN; i++ ) + for( data.size = 0; data.size < expected->len + 1; data.size++ ) { - TEST_ASSERT( buf[i] == GUARD_VAL ); - TEST_ASSERT( buf[GUARD_LEN + buf_len + i] == GUARD_VAL ); + if( ! generic_write_start_step( &data ) ) + goto exit; + switch( tag ) + { + case MBEDTLS_ASN1_OCTET_STRING: + ret = mbedtls_asn1_write_octet_string( + &data.p, data.start, content->x, content->len ); + break; + case MBEDTLS_ASN1_UTF8_STRING: + ret = mbedtls_asn1_write_utf8_string( + &data.p, data.start, + (const char *) content->x, content->len ); + break; + case MBEDTLS_ASN1_PRINTABLE_STRING: + ret = mbedtls_asn1_write_printable_string( + &data.p, data.start, + (const char *) content->x, content->len ); + break; + case MBEDTLS_ASN1_IA5_STRING: + ret = mbedtls_asn1_write_ia5_string( + &data.p, data.start, + (const char *) content->x, content->len ); + break; + default: + ret = mbedtls_asn1_write_tagged_string( + &data.p, data.start, tag, + (const char *) content->x, content->len ); + } + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; + if( expected->len > 10 && data.size == 8 ) + data.size = expected->len - 2; } - if( result >= 0 ) - { - TEST_ASSERT( (size_t) ret == asn1->len ); - TEST_ASSERT( p + asn1->len == buf + GUARD_LEN + buf_len ); - - TEST_ASSERT( memcmp( p, asn1->x, asn1->len ) == 0 ); - } -} -/* END_CASE */ - -/* BEGIN_CASE */ -void mbedtls_asn1_write_ia5_string( char * str, data_t * asn1, - int buf_len, int result ) -{ - int ret; - unsigned char buf[150]; - size_t str_len; - size_t i; - unsigned char *p; - - memset( buf, GUARD_VAL, sizeof( buf ) ); - - str_len = strlen( str ); - - p = buf + GUARD_LEN + buf_len; - - ret = mbedtls_asn1_write_ia5_string( &p, buf + GUARD_LEN, str, str_len ); - - /* Check for buffer overwrite on both sides */ - for( i = 0; i < GUARD_LEN; i++ ) - { - TEST_ASSERT( buf[i] == GUARD_VAL ); - TEST_ASSERT( buf[GUARD_LEN + buf_len + i] == GUARD_VAL ); - } - - if( result >= 0 ) - { - TEST_ASSERT( (size_t) ret == asn1->len ); - TEST_ASSERT( p + asn1->len == buf + GUARD_LEN + buf_len ); - - TEST_ASSERT( memcmp( p, asn1->x, asn1->len ) == 0 ); - } +exit: + mbedtls_free( data.output ); } /* END_CASE */ @@ -243,44 +224,25 @@ void mbedtls_asn1_write_len( int len, data_t * asn1, int buf_len, /* BEGIN_CASE */ void test_asn1_write_bitstrings( data_t *bitstring, int bits, - data_t *expected_asn1, int result, - int is_named ) + data_t *expected, int is_named ) { + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; int ret; - size_t i; - unsigned char buf[150]; - unsigned char *p; + int ( *func )( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t bits ) = + ( is_named ? mbedtls_asn1_write_named_bitstring : + mbedtls_asn1_write_bitstring ); - memset( buf, GUARD_VAL, sizeof( buf ) ); - - p = buf + GUARD_LEN + expected_asn1->len; - - if ( is_named == 0 ) + for( data.size = 0; data.size < expected->len + 1; data.size++ ) { - ret = mbedtls_asn1_write_bitstring( &p, - buf, - (unsigned char *)bitstring->x, - (size_t) bits ); - } - else - { - ret = mbedtls_asn1_write_named_bitstring( &p, - buf, - (unsigned char *)bitstring->x, - (size_t) bits ); - } - TEST_ASSERT( ret == result ); - - /* Check for buffer overwrite on both sides */ - for( i = 0; i < GUARD_LEN; i++ ) - { - TEST_ASSERT( buf[i] == GUARD_VAL ); - TEST_ASSERT( buf[GUARD_LEN + expected_asn1->len + i] == GUARD_VAL ); + if( ! generic_write_start_step( &data ) ) + goto exit; + ret = ( *func )( &data.p, data.start, bitstring->x, bits ); + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; } - if ( result >= 0 ) - { - TEST_ASSERT( memcmp( p, expected_asn1->x, expected_asn1->len ) == 0 ); - } +exit: + mbedtls_free( data.output ); } /* END_CASE */ From 91d8d023c29bc18b8cbb1549649a8ad9c2c12e7c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 19:34:24 +0100 Subject: [PATCH 10/17] Fix typo that prevented mbedtls_asn1_write_len tests from running --- tests/suites/test_suite_asn1write.function | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/suites/test_suite_asn1write.function b/tests/suites/test_suite_asn1write.function index 1669ca86d5..4b1319de5f 100644 --- a/tests/suites/test_suite_asn1write.function +++ b/tests/suites/test_suite_asn1write.function @@ -171,7 +171,7 @@ exit: } /* END_CASE */ -/* BEGIN_CASE depends_on:MBEDTLS_ASN1PARSE_C */ +/* BEGIN_CASE depends_on:MBEDTLS_ASN1_PARSE_C */ void mbedtls_asn1_write_len( int len, data_t * asn1, int buf_len, int result ) { From 9311cf509364d5dec5bfbc3d850d3aba1931a707 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 20:05:05 +0100 Subject: [PATCH 11/17] asn1write: test NULL, OID and AlgorithmIdentifier --- tests/suites/test_suite_asn1write.data | 39 +++++++++++++++ tests/suites/test_suite_asn1write.function | 55 ++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/tests/suites/test_suite_asn1write.data b/tests/suites/test_suite_asn1write.data index 90654d75c3..9ee439b06b 100644 --- a/tests/suites/test_suite_asn1write.data +++ b/tests/suites/test_suite_asn1write.data @@ -1,3 +1,6 @@ +ASN.1 Write NULL +mbedtls_asn1_write_null:"0500" + ASN.1 Write BOOLEAN FALSE mbedtls_asn1_write_bool:0:"010100" @@ -139,6 +142,42 @@ mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING | MBEDTLS_ASN1_CONTEXT_SPECIFI ASN.1 Write tagged string: length=128 mbedtls_asn1_write_string:MBEDTLS_ASN1_IA5_STRING | MBEDTLS_ASN1_CONTEXT_SPECIFIC:"0199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38":"9681800199a66790856f7199641f55cadabb660aaed6aa0d9ef8cef4417118c6e8c6e15becbaa21c63faf48726e92357a38b3079a0b9d60be7457ec6552f900dd032577167c91e829927343c3a769b362db4de0ad2ffb8f13cc2eeca9e52dc557118baa88b857477595622bc301a1ae2150030d652c4a482cf88d0ded85d6731ff2d38" +ASN.1 Write OID: length=0 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OID:"":"0600" + +ASN.1 Write OID: length=1 +mbedtls_asn1_write_string:MBEDTLS_ASN1_OID:"41":"060141" + +ASN.1 Write AlgorithmIdentifier, null parameters +mbedtls_asn1_write_algorithm_identifier:"4f4944":8:"300d06034f4944" + +ASN.1 Write AlgorithmIdentifier, parameters (8 bytes) +mbedtls_asn1_write_algorithm_identifier:"4f4944":8:"300d06034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0x7f +mbedtls_asn1_write_algorithm_identifier:"4f4944":0x7a:"307f06034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0x80 +mbedtls_asn1_write_algorithm_identifier:"4f4944":0x7b:"30818006034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0xff +mbedtls_asn1_write_algorithm_identifier:"4f4944":0xfa:"3081ff06034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0x100 +mbedtls_asn1_write_algorithm_identifier:"4f4944":0xfb:"3082010006034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0xffff +mbedtls_asn1_write_algorithm_identifier:"4f4944":0xfffa:"3082ffff06034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0x10000 +mbedtls_asn1_write_algorithm_identifier:"4f4944":0xfffb:"308301000006034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0xffffff +mbedtls_asn1_write_algorithm_identifier:"4f4944":0xfffffa:"3083ffffff06034f4944" + +ASN.1 Write AlgorithmIdentifier, total length=0x1000000 +mbedtls_asn1_write_algorithm_identifier:"4f4944":0xfffffb:"30840100000006034f4944" + ASN.1 Write / Read Length #0 (Len = 0, short form) mbedtls_asn1_write_len:0:"00":1:1 diff --git a/tests/suites/test_suite_asn1write.function b/tests/suites/test_suite_asn1write.function index 4b1319de5f..3cbe072afa 100644 --- a/tests/suites/test_suite_asn1write.function +++ b/tests/suites/test_suite_asn1write.function @@ -57,6 +57,26 @@ exit: * END_DEPENDENCIES */ +/* BEGIN_CASE */ +void mbedtls_asn1_write_null( data_t *expected ) +{ + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; + int ret; + + for( data.size = 0; data.size < expected->len + 1; data.size++ ) + { + if( ! generic_write_start_step( &data ) ) + goto exit; + ret = mbedtls_asn1_write_null( &data.p, data.start ); + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; + } + +exit: + mbedtls_free( data.output ); +} +/* END_CASE */ + /* BEGIN_CASE */ void mbedtls_asn1_write_bool( int val, data_t *expected ) { @@ -140,6 +160,11 @@ void mbedtls_asn1_write_string( int tag, data_t *content, data_t *expected ) ret = mbedtls_asn1_write_octet_string( &data.p, data.start, content->x, content->len ); break; + case MBEDTLS_ASN1_OID: + ret = mbedtls_asn1_write_oid( + &data.p, data.start, + (const char *) content->x, content->len ); + break; case MBEDTLS_ASN1_UTF8_STRING: ret = mbedtls_asn1_write_utf8_string( &data.p, data.start, @@ -171,6 +196,36 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void mbedtls_asn1_write_algorithm_identifier( data_t *oid, + int par_len, + data_t *expected ) +{ + generic_write_data_t data = { NULL, NULL, NULL, NULL, 0 }; + int ret; + + for( data.size = 0; data.size < expected->len + 1; data.size++ ) + { + if( ! generic_write_start_step( &data ) ) + goto exit; + ret = mbedtls_asn1_write_algorithm_identifier( + &data.p, data.start, + (const char *) oid->x, oid->len, par_len ); + /* If params_len != 0, mbedtls_asn1_write_algorithm_identifier() + * assumes that the parameters are already present in the buffer + * and returns a length that accounts for this, but our test + * data omits the parameters. */ + if( ret >= 0 ) + ret -= par_len; + if( ! generic_write_finish_step( &data, expected, ret ) ) + goto exit; + } + +exit: + mbedtls_free( data.output ); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_ASN1_PARSE_C */ void mbedtls_asn1_write_len( int len, data_t * asn1, int buf_len, int result ) From a902303587513273299108dd6c3e88c5d1c586df Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 1 Mar 2019 23:26:05 +0100 Subject: [PATCH 12/17] Test mbedtls_asn1_store_named_data --- tests/suites/test_suite_asn1write.data | 48 +++++++ tests/suites/test_suite_asn1write.function | 149 +++++++++++++++++++++ 2 files changed, 197 insertions(+) diff --git a/tests/suites/test_suite_asn1write.data b/tests/suites/test_suite_asn1write.data index 9ee439b06b..fd589fb23e 100644 --- a/tests/suites/test_suite_asn1write.data +++ b/tests/suites/test_suite_asn1write.data @@ -291,3 +291,51 @@ test_asn1_write_bitstrings:"":0:"030100":0 ASN.1 Write Bitstring / long string all bits unset except trailing bits test_asn1_write_bitstrings:"000000000007":45:"030703000000000000":0 + +Store named data: not found +store_named_data_find:"414141":"424242":"434343":"444444":"7f7f7f":0:-1 + +Store named data: empty haystack +store_named_data_find:"414141":"424242":"434343":"444444":"7f7f7f":4:-1 + +Store named data: first +store_named_data_find:"414141":"424242":"434343":"444444":"414141":0:0 + +Store named data: last +store_named_data_find:"414141":"424242":"434343":"444444":"444444":0:3 + +Store named data: skip suffix +store_named_data_find:"41414141":"414141":"434343":"444444":"414141":0:1 + +Store named data: skip prefix +store_named_data_find:"4141":"414141":"434343":"444444":"414141":0:1 + +Store named data: first match +store_named_data_find:"414141":"414141":"434343":"444444":"414141":0:0 + +Store named data: found, null to zero +store_named_data_val_found:0:0 + +Store named data: found, null to data +store_named_data_val_found:0:9 + +Store named data: found, data to zero +store_named_data_val_found:9:0 + +Store named data: found, smaller data +store_named_data_val_found:9:2 + +Store named data: found, same-size data +store_named_data_val_found:9:9 + +Store named data: found, larger data +store_named_data_val_found:4:9 + +Store named data: new, val_len=0 +store_named_data_val_new:0 + +Store named data: new, val_len=4 +store_named_data_val_new:4 + +Store named data: new, val_len=4, val=NULL +store_named_data_val_new:-4 diff --git a/tests/suites/test_suite_asn1write.function b/tests/suites/test_suite_asn1write.function index 3cbe072afa..b69f6b5c3a 100644 --- a/tests/suites/test_suite_asn1write.function +++ b/tests/suites/test_suite_asn1write.function @@ -301,3 +301,152 @@ exit: mbedtls_free( data.output ); } /* END_CASE */ + +/* BEGIN_CASE */ +void store_named_data_find( data_t *oid0, data_t *oid1, + data_t *oid2, data_t *oid3, + data_t *needle, int from, int position ) +{ + data_t *oid[4] = {oid0, oid1, oid2, oid3}; + mbedtls_asn1_named_data nd[] ={ + { {0x06, 0, NULL}, {0, 0, NULL}, NULL, 0 }, + { {0x06, 0, NULL}, {0, 0, NULL}, NULL, 0 }, + { {0x06, 0, NULL}, {0, 0, NULL}, NULL, 0 }, + { {0x06, 0, NULL}, {0, 0, NULL}, NULL, 0 }, + }; + mbedtls_asn1_named_data *pointers[ARRAY_LENGTH( nd ) + 1]; + size_t i; + mbedtls_asn1_named_data *head = NULL; + mbedtls_asn1_named_data *found = NULL; + + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + pointers[i] = &nd[i]; + pointers[ARRAY_LENGTH( nd )] = NULL; + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + { + ASSERT_ALLOC( nd[i].oid.p, oid[i]->len ); + memcpy( nd[i].oid.p, oid[i]->x, oid[i]->len ); + nd[i].oid.len = oid[i]->len; + nd[i].next = pointers[i+1]; + } + + head = pointers[from]; + found = mbedtls_asn1_store_named_data( &head, + (const char *) needle->x, + needle->len, + NULL, 0 ); + + /* In any case, the existing list structure must be unchanged. */ + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + TEST_ASSERT( nd[i].next == pointers[i+1] ); + + if( position >= 0 ) + { + /* position should have been found and modified. */ + TEST_ASSERT( head == pointers[from] ); + TEST_ASSERT( found == pointers[position] ); + } + else + { + /* A new entry should have been created. */ + TEST_ASSERT( found == head ); + TEST_ASSERT( head->next == pointers[from] ); + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + TEST_ASSERT( found != &nd[i] ); + } + +exit: + if( found != NULL && found == head && found != pointers[from] ) + { + mbedtls_free( found->oid.p ); + mbedtls_free( found ); + } + for( i = 0; i < ARRAY_LENGTH( nd ); i++ ) + mbedtls_free( nd[i].oid.p ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void store_named_data_val_found( int old_len, int new_len ) +{ + mbedtls_asn1_named_data nd = + { {0x06, 3, (unsigned char *) "OID"}, {0, 0, NULL}, NULL, 0 }; + mbedtls_asn1_named_data *head = &nd; + mbedtls_asn1_named_data *found = NULL; + unsigned char *old_val = NULL; + unsigned char *new_val = (unsigned char *) "new value"; + + if( old_len != 0 ) + { + ASSERT_ALLOC( nd.val.p, (size_t) old_len ); + old_val = nd.val.p; + nd.val.len = old_len; + memset( old_val, 'x', old_len ); + } + if( new_len <= 0 ) + { + new_len = - new_len; + new_val = NULL; + } + + found = mbedtls_asn1_store_named_data( &head, "OID", 3, + new_val, new_len ); + TEST_ASSERT( head == &nd ); + TEST_ASSERT( found == head ); + + if( new_val != NULL) + ASSERT_COMPARE( found->val.p, found->val.len, + new_val, (size_t) new_len ); + if( new_len == 0) + TEST_ASSERT( found->val.p == NULL ); + else if( new_len == old_len ) + TEST_ASSERT( found->val.p == old_val ); + else + TEST_ASSERT( found->val.p != old_val ); + +exit: + mbedtls_free( nd.val.p ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void store_named_data_val_new( int new_len ) +{ + mbedtls_asn1_named_data *head = NULL; + mbedtls_asn1_named_data *found = NULL; + const unsigned char *oid = (unsigned char *) "OID"; + size_t oid_len = strlen( (const char *) oid ); + const unsigned char *new_val = (unsigned char *) "new value"; + + if( new_len <= 0 ) + new_val = NULL; + if( new_len < 0 ) + new_len = - new_len; + + found = mbedtls_asn1_store_named_data( &head, + (const char *) oid, oid_len, + new_val, (size_t) new_len ); + TEST_ASSERT( found != NULL ); + TEST_ASSERT( found == head ); + TEST_ASSERT( found->oid.p != oid ); + ASSERT_COMPARE( found->oid.p, found->oid.len, oid, oid_len ); + if( new_len == 0 ) + TEST_ASSERT( found->val.p == NULL ); + else if( new_val == NULL ) + TEST_ASSERT( found->val.p != NULL ); + else + { + TEST_ASSERT( found->val.p != new_val ); + ASSERT_COMPARE( found->val.p, found->val.len, + new_val, (size_t) new_len ); + } + +exit: + if( found != NULL ) + { + mbedtls_free( found->oid.p ); + mbedtls_free( found->val.p ); + } + mbedtls_free( found ); +} +/* END_CASE */ From 09c0a2364b9603936bf4e56e839ea7d10dcc120a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 4 Mar 2019 15:00:06 +0100 Subject: [PATCH 13/17] mbedtls_asn1_store_named_data: clarify val allocation behavior Document how mbedtls_asn1_store_named_data allocates val.p in the new or modified entry. Change the behavior to be more regular, always setting the new length to val_len. This does not affect the previous documented behavior since this aspect was not documented. This does not affect current usage in Mbed TLS's X.509 module where calls with the same OID always use the same size for the associated value. --- include/mbedtls/asn1write.h | 8 ++++++-- library/asn1write.c | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/mbedtls/asn1write.h b/include/mbedtls/asn1write.h index 8ecab4e2dd..982414626e 100644 --- a/include/mbedtls/asn1write.h +++ b/include/mbedtls/asn1write.h @@ -334,9 +334,13 @@ int mbedtls_asn1_write_octet_string( unsigned char **p, unsigned char *start, * through (will be updated in case of a new entry). * \param oid The OID to look for. * \param oid_len The size of the OID. - * \param val The data to store (can be \c NULL if you want to fill - * it by hand). + * \param val The associated data to store. If this is \c NULL, + * no data is copied to the new or existing buffer. * \param val_len The minimum length of the data buffer needed. + * If this is 0, do not allocate a buffer for the associated + * data. + * If the OID was already present, enlarge, shrink or free + * the existing buffer to fit \p val_len. * * \return A pointer to the new / existing entry on success. * \return \c NULL if if there was a memory allocation error. diff --git a/library/asn1write.c b/library/asn1write.c index 98c6766725..a138d0b75c 100644 --- a/library/asn1write.c +++ b/library/asn1write.c @@ -432,18 +432,26 @@ mbedtls_asn1_named_data *mbedtls_asn1_store_named_data( memcpy( cur->oid.p, oid, oid_len ); cur->val.len = val_len; - cur->val.p = mbedtls_calloc( 1, val_len ); - if( cur->val.p == NULL ) + if( val_len != 0 ) { - mbedtls_free( cur->oid.p ); - mbedtls_free( cur ); - return( NULL ); + cur->val.p = mbedtls_calloc( 1, val_len ); + if( cur->val.p == NULL ) + { + mbedtls_free( cur->oid.p ); + mbedtls_free( cur ); + return( NULL ); + } } cur->next = *head; *head = cur; } - else if( cur->val.len < val_len ) + else if( val_len == 0 ) + { + mbedtls_free( cur->val.p ); + cur->val.p = NULL; + } + else if( cur->val.len != val_len ) { /* * Enlarge existing value buffer if needed From 2cd8ecc08b9e73196daa12e22feb90fd446b3407 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 4 Mar 2019 17:13:43 +0100 Subject: [PATCH 14/17] New test helper macro ASSERT_ALLOC_WEAK The new macro ASSERT_ALLOC_WEAK does not fail the test case if the memory allocation fails. This is useful for tests that allocate a large amount of memory, but that aren't useful on platforms where allocating such a large amount is not possible. Ideally this macro should mark the test as skipped. We don't yet have a facility for that but we're working on it. Once we have a skip functionality, this macro should be changed to use it. --- tests/suites/helpers.function | 20 ++++++++++++++++++++ tests/suites/test_suite_asn1parse.function | 8 +------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function index d45fd4ea7d..00320bca37 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -158,6 +158,26 @@ typedef enum } \ while( 0 ) +/** Allocate memory dynamically. Exit the test if this fails, but do + * not mark the test as failed. + * + * This macro behaves like #ASSERT_ALLOC, except that if the allocation + * fails, it jumps to the \c exit label without calling test_fail(). + */ +#define ASSERT_ALLOC_WEAK( pointer, length ) \ + do \ + { \ + TEST_ASSERT( ( pointer ) == NULL ); \ + if( ( length ) != 0 ) \ + { \ + ( pointer ) = mbedtls_calloc( sizeof( *( pointer ) ), \ + ( length ) ); \ + if( ( pointer ) == NULL ) \ + goto exit; \ + } \ + } \ + while( 0 ) + /** Compare two buffers and fail the test case if they differ. * * This macro expands to an instruction, not an expression. diff --git a/tests/suites/test_suite_asn1parse.function b/tests/suites/test_suite_asn1parse.function index 9e9f509499..f5ecd5515b 100644 --- a/tests/suites/test_suite_asn1parse.function +++ b/tests/suites/test_suite_asn1parse.function @@ -130,7 +130,7 @@ int get_len_step( const data_t *input, size_t buffer_size, } else { - ASSERT_ALLOC( buf, buffer_size ); + ASSERT_ALLOC_WEAK( buf, buffer_size ); if( buffer_size > input->len ) { memcpy( buf, input->x, input->len ); @@ -159,12 +159,6 @@ int get_len_step( const data_t *input, size_t buffer_size, return( 1 ); exit: - /* It may be impossible to allocate large lengths on embedded platforms. - * Pass in this case (though it would be better to mark the test - * as skipped). */ - if( buf == NULL ) - return( 1 ); - mbedtls_free( buf ); return( 0 ); } From aac38533489dfec073a453f614be971798be0bdb Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 11 Sep 2019 16:52:01 +0200 Subject: [PATCH 15/17] Fix long-standing bug in error code description MBEDTLS_ERR_ASN1_INVALID_DATA is documented as "not used", but it has been used since the PolarSSL days. --- include/mbedtls/asn1.h | 2 +- library/error.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mbedtls/asn1.h b/include/mbedtls/asn1.h index 6891bb9c31..92f3bcbdd0 100644 --- a/include/mbedtls/asn1.h +++ b/include/mbedtls/asn1.h @@ -52,7 +52,7 @@ #define MBEDTLS_ERR_ASN1_UNEXPECTED_TAG -0x0062 /**< ASN1 tag was of an unexpected value. */ #define MBEDTLS_ERR_ASN1_INVALID_LENGTH -0x0064 /**< Error when trying to determine the length or invalid length. */ #define MBEDTLS_ERR_ASN1_LENGTH_MISMATCH -0x0066 /**< Actual length differs from expected length. */ -#define MBEDTLS_ERR_ASN1_INVALID_DATA -0x0068 /**< Data is invalid. (not used) */ +#define MBEDTLS_ERR_ASN1_INVALID_DATA -0x0068 /**< Data is invalid. */ #define MBEDTLS_ERR_ASN1_ALLOC_FAILED -0x006A /**< Memory allocation failed */ #define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL -0x006C /**< Buffer too small when writing ASN.1 data structure. */ diff --git a/library/error.c b/library/error.c index 7d7155ba09..649b3baa41 100644 --- a/library/error.c +++ b/library/error.c @@ -467,7 +467,7 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) if( use_ret == -(MBEDTLS_ERR_ASN1_LENGTH_MISMATCH) ) mbedtls_snprintf( buf, buflen, "ASN1 - Actual length differs from expected length" ); if( use_ret == -(MBEDTLS_ERR_ASN1_INVALID_DATA) ) - mbedtls_snprintf( buf, buflen, "ASN1 - Data is invalid. (not used)" ); + mbedtls_snprintf( buf, buflen, "ASN1 - Data is invalid" ); if( use_ret == -(MBEDTLS_ERR_ASN1_ALLOC_FAILED) ) mbedtls_snprintf( buf, buflen, "ASN1 - Memory allocation failed" ); if( use_ret == -(MBEDTLS_ERR_ASN1_BUF_TOO_SMALL) ) From dc2db4832dcb4aad1fb95ab3b12db37879080969 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 20 Sep 2019 21:06:01 +0200 Subject: [PATCH 16/17] Fix typos in documentation --- include/mbedtls/asn1.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mbedtls/asn1.h b/include/mbedtls/asn1.h index 92f3bcbdd0..1a76111684 100644 --- a/include/mbedtls/asn1.h +++ b/include/mbedtls/asn1.h @@ -182,7 +182,7 @@ mbedtls_asn1_named_data; * after the length, i.e. the first byte of the content. * On error, the value of \c *p is undefined. * \param end End of data. - * \param len On successful completion, \c *len contains the lengtth + * \param len On successful completion, \c *len contains the length * read from the ASN.1 input. * * \return 0 if successful. @@ -204,7 +204,7 @@ int mbedtls_asn1_get_len( unsigned char **p, * after the length, i.e. the first byte of the content. * On error, the value of \c *p is undefined. * \param end End of data. - * \param len On successful completion, \c *len contains the lengtth + * \param len On successful completion, \c *len contains the length * read from the ASN.1 input. * \param tag The expected tag. * @@ -304,8 +304,8 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p, size_t *len ); /** - * \brief Parses and splits an ASN.1 "SEQUENCE OF " - * Updated the pointer to immediately behind the full sequence tag. + * \brief Parses and splits an ASN.1 "SEQUENCE OF ". + * Updates the pointer to immediately behind the full sequence tag. * * \note On error, this function may return a partial list in \p cur. * You must set `cur->next = NULL` before calling this function! From 88f136f98b14144e86212ec5ebc994d71ceaee67 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 20 Sep 2019 21:06:27 +0200 Subject: [PATCH 17/17] Fix free_named_data_list tests Fix copypasta in test data and fix a switcho in test code. --- tests/suites/test_suite_asn1parse.data | 4 ++-- tests/suites/test_suite_asn1parse.function | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/suites/test_suite_asn1parse.data b/tests/suites/test_suite_asn1parse.data index 83319e3e5c..c5d9136b77 100644 --- a/tests/suites/test_suite_asn1parse.data +++ b/tests/suites/test_suite_asn1parse.data @@ -432,7 +432,7 @@ Free named data list (empty) free_named_data_list:0 Free named data list (1) -free_named_data_list:0 +free_named_data_list:1 Free named data list (2) -free_named_data_list:0 +free_named_data_list:2 diff --git a/tests/suites/test_suite_asn1parse.function b/tests/suites/test_suite_asn1parse.function index f5ecd5515b..3bfb1c7037 100644 --- a/tests/suites/test_suite_asn1parse.function +++ b/tests/suites/test_suite_asn1parse.function @@ -547,7 +547,7 @@ void free_named_data_list( int length ) { mbedtls_asn1_named_data *new = NULL; ASSERT_ALLOC( new, sizeof( mbedtls_asn1_named_data ) ); - head->next = new; + new->next = head; head = new; }