diff --git a/include/mbedtls/asn1.h b/include/mbedtls/asn1.h index 1a76111684..1c6683f63e 100644 --- a/include/mbedtls/asn1.h +++ b/include/mbedtls/asn1.h @@ -75,6 +75,7 @@ #define MBEDTLS_ASN1_OCTET_STRING 0x04 #define MBEDTLS_ASN1_NULL 0x05 #define MBEDTLS_ASN1_OID 0x06 +#define MBEDTLS_ASN1_ENUMERATED 0x0A #define MBEDTLS_ASN1_UTF8_STRING 0x0C #define MBEDTLS_ASN1_SEQUENCE 0x10 #define MBEDTLS_ASN1_SET 0x11 @@ -254,13 +255,32 @@ int mbedtls_asn1_get_bool( unsigned char **p, * 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 ); +/** + * \brief Retrieve an enumerated ASN.1 tag and its value. + * Updates the pointer to immediately behind the full 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 + * 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. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 ENUMERATED. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + */ +int mbedtls_asn1_get_enum( unsigned char **p, + 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. @@ -367,8 +387,6 @@ int mbedtls_asn1_get_sequence_of( unsigned char **p, * \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, diff --git a/include/mbedtls/asn1write.h b/include/mbedtls/asn1write.h index 982414626e..0bce28ed13 100644 --- a/include/mbedtls/asn1write.h +++ b/include/mbedtls/asn1write.h @@ -192,6 +192,21 @@ int mbedtls_asn1_write_bool( unsigned char **p, unsigned char *start, */ int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val ); +/** + * \brief Write an enum tag (#MBEDTLS_ASN1_ENUMERATED) and value + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \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. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_enum( unsigned char **p, unsigned char *start, int val ); + /** * \brief Write a string in ASN.1 format using a specific * string encoding tag. diff --git a/library/asn1parse.c b/library/asn1parse.c index 412259e358..87e7aa989a 100644 --- a/library/asn1parse.c +++ b/library/asn1parse.c @@ -139,17 +139,20 @@ int mbedtls_asn1_get_bool( unsigned char **p, return( 0 ); } -int mbedtls_asn1_get_int( unsigned char **p, - const unsigned char *end, - int *val ) +static int asn1_get_tagged_int( unsigned char **p, + const unsigned char *end, + int tag, int *val ) { int ret; size_t len; - if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_INTEGER ) ) != 0 ) + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, tag ) ) != 0 ) return( ret ); - /* len==0 is malformed (0 must be represented as 020100). */ + /* + * len==0 is malformed (0 must be represented as 020100 for INTEGER, + * or 0A0100 for ENUMERATED tags + */ if( len == 0 ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); /* This is a cryptography library. Reject negative integers. */ @@ -180,6 +183,20 @@ int mbedtls_asn1_get_int( unsigned char **p, return( 0 ); } +int mbedtls_asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ) +{ + return( asn1_get_tagged_int( p, end, MBEDTLS_ASN1_INTEGER, val) ); +} + +int mbedtls_asn1_get_enum( unsigned char **p, + const unsigned char *end, + int *val ) +{ + return( asn1_get_tagged_int( p, end, MBEDTLS_ASN1_ENUMERATED, val) ); +} + #if defined(MBEDTLS_BIGNUM_C) int mbedtls_asn1_get_mpi( unsigned char **p, const unsigned char *end, diff --git a/library/asn1write.c b/library/asn1write.c index a138d0b75c..b3a3ad5084 100644 --- a/library/asn1write.c +++ b/library/asn1write.c @@ -231,7 +231,7 @@ int mbedtls_asn1_write_bool( unsigned char **p, unsigned char *start, int boolea return( (int) len ); } -int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val ) +static int asn1_write_tagged_int( unsigned char **p, unsigned char *start, int val, int tag ) { int ret; size_t len = 0; @@ -255,11 +255,21 @@ int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val ) } MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) ); - MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_INTEGER ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, tag ) ); return( (int) len ); } +int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val ) +{ + return( asn1_write_tagged_int( p, start, val, MBEDTLS_ASN1_INTEGER ) ); +} + +int mbedtls_asn1_write_enum( unsigned char **p, unsigned char *start, int val ) +{ + return( asn1_write_tagged_int( p, start, val, MBEDTLS_ASN1_ENUMERATED ) ); +} + int mbedtls_asn1_write_tagged_string( unsigned char **p, unsigned char *start, int tag, const char *text, size_t text_len ) { diff --git a/tests/suites/test_suite_asn1parse.data b/tests/suites/test_suite_asn1parse.data index 4abae0bb4c..e26f93af7a 100644 --- a/tests/suites/test_suite_asn1parse.data +++ b/tests/suites/test_suite_asn1parse.data @@ -286,6 +286,90 @@ get_integer:"010101":"":MBEDTLS_ERR_ASN1_UNEXPECTED_TAG INTEGER too large for mpi get_mpi_too_large: +ENUMERATED 0 +get_enum:"0A0100":"0":0 + +ENUMERATED 0, extra leading 0 +get_enum:"0A020000":"0":0 + +ENUMERATED 1 +get_enum:"0A0101":"1":0 + +ENUMERATED 1, extra leading 0 +get_enum:"0A020001":"1":0 + +ENUMERATED 0x7f +get_enum:"0A017f":"7f":0 + +ENUMERATED 0x80 +get_enum:"0A020080":"80":0 + +ENUMERATED 0x80, extra leading 0 +get_enum:"0A03000080":"80":0 + +ENUMERATED 0xff +get_enum:"0A0200ff":"ff":0 + +ENUMERATED 0x7fff +get_enum:"0A027fff":"7fff":0 + +ENUMERATED 0x12345678 +get_enum:"0A0412345678":"12345678":0 + +ENUMERATED 0x12345678, extra leading 0 +get_enum:"0A050012345678":"12345678":0 + +ENUMERATED 0x7fffffff +get_enum:"0A047fffffff":"7fffffff":0 + +ENUMERATED 0x7fffffff, extra leading 0 +get_enum:"0A05007fffffff":"7fffffff":0 + +ENUMERATED 0x80000000 +get_enum:"0A050080000000":"80000000":0 + +ENUMERATED 0xffffffff +get_enum:"0A0500ffffffff":"ffffffff":0 + +ENUMERATED 0x100000000 +get_enum:"0A050100000000":"0100000000":0 + +ENUMERATED -1 +get_enum:"0A01ff":"-1":0 + +ENUMERATED -1, extra leading ff +get_enum:"0A02ffff":"-1":0 + +ENUMERATED -0x7f +get_enum:"0A0181":"-7f":0 + +ENUMERATED -0x80 +get_enum:"0A0180":"-80":0 + +ENUMERATED -0x81 +get_enum:"0A02ff7f":"-81":0 + +ENUMERATED -0xff +get_enum:"0A02ff01":"-ff":0 + +ENUMERATED -0x100 +get_enum:"0A02ff00":"-100":0 + +ENUMERATED -0x7fffffff +get_enum:"0A0480000001":"-7fffffff":0 + +ENUMERATED -0x80000000 +get_enum:"0A0480000000":"-80000000":0 + +ENUMERATED -0x80000001 +get_enum:"0A05ff7fffffff":"-80000001":0 + +ENUMERATED -0xffffffff +get_enum:"0A05ff00000001":"-ffffffff":0 + +ENUMERATED -0x100000000 +get_enum:"0A05ff00000000":"-100000000":0 + BIT STRING: empty get_bitstring:"0300":0:0:MBEDTLS_ERR_ASN1_OUT_OF_DATA:MBEDTLS_ERR_ASN1_INVALID_DATA diff --git a/tests/suites/test_suite_asn1parse.function b/tests/suites/test_suite_asn1parse.function index defbd01bb6..d747cc254f 100644 --- a/tests/suites/test_suite_asn1parse.function +++ b/tests/suites/test_suite_asn1parse.function @@ -393,6 +393,49 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void get_enum( const data_t *input, + const char *expected_hex, int expected_result ) +{ + unsigned char *p; + long expected_value; + int expected_result_for_enum = expected_result; + int val; + int ret; + + 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 + ) ) + { + /* The library returns the dubious error code INVALID_LENGTH + * for integers that are out of range. */ + expected_result_for_enum = MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + if( expected_result == 0 && expected_value < 0 ) + { + /* The library does not support negative INTEGERs and + * returns the dubious error code INVALID_LENGTH. + * Test that we preserve the historical behavior. If we + * decide to change the behavior, we'll also change this test. */ + expected_result_for_enum = MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + p = input->x; + ret = mbedtls_asn1_get_enum( &p, input->x + input->len, &val ); + TEST_EQUAL( ret, expected_result_for_enum ); + if( ret == 0 ) + { + TEST_EQUAL( val, expected_value ); + TEST_ASSERT( p == input->x + input->len ); + } +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_BIGNUM_C */ void get_mpi_too_large( ) { diff --git a/tests/suites/test_suite_asn1write.data b/tests/suites/test_suite_asn1write.data index fd589fb23e..7f5f5360e2 100644 --- a/tests/suites/test_suite_asn1write.data +++ b/tests/suites/test_suite_asn1write.data @@ -49,6 +49,48 @@ mbedtls_asn1_write_int:0x12345678:"020412345678" ASN.1 Write int 2147483647 mbedtls_asn1_write_int:0x7fffffff:"02047fffffff" +ASN.1 Write enum 0 +mbedtls_asn1_write_enum:0:"0A0100" + +ASN.1 Write enum 1 +mbedtls_asn1_write_enum:1:"0A0101" + +ASN.1 Write enum 127 +mbedtls_asn1_write_enum:0x7f:"0A017f" + +ASN.1 Write enum 128 +mbedtls_asn1_write_enum:0x80:"0A020080" + +ASN.1 Write enum 255 +mbedtls_asn1_write_enum:0xff:"0A0200ff" + +ASN.1 Write enum 256 +mbedtls_asn1_write_enum:0x100:"0A020100" + +ASN.1 Write enum 32767 +mbedtls_asn1_write_enum:0x7fff:"0A027fff" + +ASN.1 Write enum 32768 +mbedtls_asn1_write_enum:0x8000:"0A03008000" + +ASN.1 Write enum 65535 +mbedtls_asn1_write_enum:0xffff:"0A0300ffff" + +ASN.1 Write enum 65536 +mbedtls_asn1_write_enum:0x10000:"0A03010000" + +ASN.1 Write enum 8388607 +mbedtls_asn1_write_enum:0x7fffff:"0A037fffff" + +ASN.1 Write enum 8388608 +mbedtls_asn1_write_enum:0x800000:"0A0400800000" + +ASN.1 Write enum 0x12345678 +mbedtls_asn1_write_enum:0x12345678:"0A0412345678" + +ASN.1 Write enum 2147483647 +mbedtls_asn1_write_enum:0x7fffffff:"0A047fffffff" + #ASN.1 Write mpi 0 #mbedtls_asn1_write_mpi:"00":"020100" diff --git a/tests/suites/test_suite_asn1write.function b/tests/suites/test_suite_asn1write.function index b69f6b5c3a..21465c756f 100644 --- a/tests/suites/test_suite_asn1write.function +++ b/tests/suites/test_suite_asn1write.function @@ -117,6 +117,27 @@ exit: } /* END_CASE */ + +/* BEGIN_CASE */ +void mbedtls_asn1_write_enum( 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_enum( &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 ) {