From f60c815dc745fed23125cab302bf9702fbeee09c Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Fri, 27 Apr 2018 14:45:49 +0100 Subject: [PATCH 1/4] CCM*: Add public API Interface for CCM* (described in IEEE Std 802.15.4.). --- include/mbedtls/ccm.h | 194 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/include/mbedtls/ccm.h b/include/mbedtls/ccm.h index 5a34f3a0a..7d117d9a9 100644 --- a/include/mbedtls/ccm.h +++ b/include/mbedtls/ccm.h @@ -60,6 +60,55 @@ typedef struct { } mbedtls_ccm_context; +/** + * \brief The CCM* callback for encrypting with variable tag length. + * The function pointer is passed to the APIs called. This + * function calculates the nonce and returns it in a buffer. + * + * \warning This function must not return the same nonce more than once + * in the lifetime of the key! + * + * \note To prevent attacks taking advantage of the variable tag + * length CCM* encodes the tag length in the nonce. The method + * of encoding may vary. Standards might mandate encoding other + * information in the nonce (e.g. address and frame counter) + * too. + * + * \param app_ctx A pointer to structure containing the application context + * if it is necessary for calculating the initialisation vector + * (nonce). + * \param tag_len Length of the tag in bytes. + * \nonce Output variable, points to the buffer capable of holding the + * calculated nonce. Must be at least \p nonce_len bytes long. + * \nonce_len The length of the nonce in bytes. + * + * \return \c 0 on success. + * \return MBEDTLS_ERR_CCM_BAD_INPUT error code on failure. + */ +typedef int (*mbedtls_ccm_star_get_nonce_t)( void *app_ctx, size_t tag_len, + unsigned char *nonce, + size_t nonce_len ); + +/** + * \brief The CCM* callback for decrypting with variable tag length. + * The function pointer is passed to the APIs called. This + * function calculates and returns the length of the tag in the + * output parameter. + * + * \param app_ctx A pointer to structure containing the application context + * if it is necessary for decoding the tag length or validating + * the initialisation vector (nonce). + * \param tag_len Output variable for holding the tag length in bytes. + * \nonce A buffer containing the nonce. + * \nonce_len The length of the nonce in bytes. + * + * \return \c 0 on success. + * \return MBEDTLS_ERR_CCM_BAD_INPUT error code on failure. + */ +typedef int (*mbedtls_ccm_star_get_tag_len_t)( void *app_ctx, size_t* tag_len, + const unsigned char *nonce, + size_t nonce_len ); + /** * \brief This function initializes the specified CCM context, * to make references valid, and prepare the context @@ -97,7 +146,6 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx ); /** * \brief This function encrypts a buffer using CCM. * - * * \note The tag is written to a separate buffer. To concatenate * the \p tag with the \p output, as done in RFC-3610: * Counter with CBC-MAC (CCM), use @@ -127,6 +175,82 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, const unsigned char *input, unsigned char *output, unsigned char *tag, size_t tag_len ); +/** + * \brief This function encrypts a buffer using CCM* with fixed tag + * length. + * + * \note The tag is written to a separate buffer. To concatenate + * the \p tag with the \p output, as done in RFC-3610: + * Counter with CBC-MAC (CCM), use + * \p tag = \p output + \p length, and make sure that the + * output buffer is at least \p length + \p tag_len wide. + * + * \param ctx The CCM context to use for encryption. + * \param length The length of the input data in Bytes. + * \param iv Initialization vector (nonce). + * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13. + * \param add The additional data field. + * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. + * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. + * \param tag The buffer holding the tag. + * \param tag_len The length of the tag to generate in Bytes: + * 0, 4, 6, 8, 10, 12, 14 or 16. + * + * \warning Passing 0 as \p tag_len means that the message is no + * longer authenticated. + * + * \return \c 0 on success. + * \return A CCM or cipher-specific error code on failure. + */ +int mbedtls_ccm_sfix_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ); + +/** + * \brief This function encrypts a buffer using CCM* with variable + * tag length. + * + * \note The tag is written to a separate buffer. To concatenate + * the \p tag with the \p output, as done in RFC-3610: + * Counter with CBC-MAC (CCM), use + * \p tag = \p output + \p length, and make sure that the + * output buffer is at least \p length + \p tag_len wide. + * + * \param ctx The CCM context to use for encryption. + * \param length The length of the input data in Bytes. + * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. + * \param add The additional data field. + * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. + * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. + * \param tag The buffer holding the tag. + * \param tag_len The length of the tag to generate in Bytes: + * 0, 4, 6, 8, 10, 12, 14 or 16. + * \param get_iv A callback function returning the IV (nonce) with the + * tag length encoded in it. + * \param get_iv_ctx Context passed to the \p get_iv callback. + * + * \warning Passing 0 as \p tag_len means that the message is no + * longer authenticated. + * + * \return \c 0 on success. + * \return A CCM or cipher-specific error code on failure. + */ +int mbedtls_ccm_svar_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, + size_t iv_len, const unsigned char *add, + size_t add_len, const unsigned char *input, + unsigned char *output, unsigned char *tag, + size_t tag_len, mbedtls_ccm_star_get_nonce_t get_iv, + void *get_iv_ctx ); + /** * \brief This function performs a CCM authenticated decryption of a * buffer. @@ -155,6 +279,74 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, const unsigned char *input, unsigned char *output, const unsigned char *tag, size_t tag_len ); +/** + * \brief This function performs a CCM* authenticated decryption of a + * buffer with fixed tag length. + * + * \param ctx The CCM context to use for decryption. + * \param length The length of the input data in Bytes. + * \param iv Initialization vector. + * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13. + * \param add The additional data field. + * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. + * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. + * \param tag The buffer holding the tag. + * \param tag_len The length of the tag in Bytes. + * 0, 4, 6, 8, 10, 12, 14 or 16. + * + * \warning Passing 0 as \p tag_len means that the message is no + * longer authenticated. + * + * \return \c 0 on success. This indicates that the message is + * authentic. + * \return #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match. + * \return A cipher-specific error code on calculation failure. + */ +int mbedtls_ccm_sfix_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len ); + +/** + * \brief This function performs a CCM* authenticated decryption + * of a buffer with variable tag length. + * + * \param ctx The CCM context to use for decryption. + * \param length The length of the input data in Bytes. + * \param iv Initialization vector. + * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. + * \param add The additional data field. + * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. Unlike the \p input + * parameters of other Mbed TLS CCM functions, this buffer + * holds the concatenation of the encrypted data and the + * authentication tag. + * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. + * \param output_len The length of the decrypted data. + * \param get_tag_len A callback function returning the tag length. + * \param get_tlen_ctx Context passed to the \p get_tag_len callback. + * + * + * \return \c 0 on success. This indicates that the message is + * authentic. + * \return #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match. + * \return A cipher-specific error code on calculation failure. + */ +int mbedtls_ccm_svar_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + size_t* output_len, + mbedtls_ccm_star_get_tag_len_t get_tag_len, + void *get_tlen_ctx ); + #ifdef __cplusplus } #endif From e068d6f8850e606a0f42aa2d6ca08a305562e5b5 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Mon, 14 May 2018 14:31:49 +0100 Subject: [PATCH 2/4] CCM*: Add implementation for fixed tag length --- library/ccm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/library/ccm.c b/library/ccm.c index 9101e5f7c..9461c3db5 100644 --- a/library/ccm.c +++ b/library/ccm.c @@ -158,7 +158,13 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, * 'length' checked later (when writing it to the first block) */ if( tag_len < 4 || tag_len > 16 || tag_len % 2 != 0 ) - return( MBEDTLS_ERR_CCM_BAD_INPUT ); + { + /* + * Loosen the requirements to enable support for CCM* (IEEE 802.15.4) + */ + if( tag_len != 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + } /* Also implies q is within bounds */ if( iv_len < 7 || iv_len > 13 ) @@ -306,7 +312,7 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, /* * Authenticated encryption */ -int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, +int mbedtls_ccm_sfix_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, const unsigned char *iv, size_t iv_len, const unsigned char *add, size_t add_len, const unsigned char *input, unsigned char *output, @@ -316,10 +322,23 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, add, add_len, input, output, tag, tag_len ) ); } +int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ) +{ + if( tag_len == 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + + return( mbedtls_ccm_sfix_encrypt_and_tag( ctx, length, iv, iv_len, add, + add_len, input, output, tag, tag_len ) ); +} + /* * Authenticated decryption */ -int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, +int mbedtls_ccm_sfix_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, const unsigned char *iv, size_t iv_len, const unsigned char *add, size_t add_len, const unsigned char *input, unsigned char *output, @@ -350,6 +369,18 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, return( 0 ); } +int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len ) +{ + if( tag_len == 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + + return( mbedtls_ccm_sfix_auth_decrypt( ctx, length, iv, iv_len, add, + add_len, input, output, tag, tag_len ) ); +} #endif /* !MBEDTLS_CCM_ALT */ #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) From c4e14e1ff593b426fed3fb1a67baed06df20302d Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Mon, 14 May 2018 14:32:41 +0100 Subject: [PATCH 3/4] CCM*: Add minimal test for fixed tag length --- tests/suites/test_suite_ccm.data | 9 ++++++ tests/suites/test_suite_ccm.function | 41 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/tests/suites/test_suite_ccm.data b/tests/suites/test_suite_ccm.data index 90ba42d83..a55ece108 100644 --- a/tests/suites/test_suite_ccm.data +++ b/tests/suites/test_suite_ccm.data @@ -41,6 +41,15 @@ ccm_lengths:5:10:65281:8:MBEDTLS_ERR_CCM_BAD_INPUT CCM lengths #8 msg too long for this IV length (2^16, q = 2) ccm_lengths:65536:13:5:8:MBEDTLS_ERR_CCM_BAD_INPUT +CCM lengths #9 tag length 0 +ccm_lengths:5:10:5:0:MBEDTLS_ERR_CCM_BAD_INPUT + +CCM* fixed tag lengths #1 all OK +ccm_sfix_lengths:5:10:5:8:0 + +CCM* fixed tag lengths #2 all OK - tag length 0 +ccm_sfix_lengths:5:10:5:0:0 + CCM encrypt and tag RFC 3610 #1 depends_on:MBEDTLS_AES_C mbedtls_ccm_encrypt_and_tag:MBEDTLS_CIPHER_ID_AES:"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF":"08090A0B0C0D0E0F101112131415161718191A1B1C1D1E":"00000003020100A0A1A2A3A4A5":"0001020304050607":"588C979A61C663D2F066D0C2C0F989806D5F6B61DAC38417E8D12CFDF926E0" diff --git a/tests/suites/test_suite_ccm.function b/tests/suites/test_suite_ccm.function index 2f5c77c2c..d64b69795 100644 --- a/tests/suites/test_suite_ccm.function +++ b/tests/suites/test_suite_ccm.function @@ -74,6 +74,47 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_AES_C */ +void ccm_sfix_lengths( int msg_len, int iv_len, int add_len, int tag_len, + int res ) +{ + mbedtls_ccm_context ctx; + unsigned char key[16]; + unsigned char msg[10]; + unsigned char iv[14]; + unsigned char add[10]; + unsigned char out[10]; + unsigned char tag[18]; + int decrypt_ret; + + mbedtls_ccm_init( &ctx ); + + memset( key, 0, sizeof( key ) ); + memset( msg, 0, sizeof( msg ) ); + memset( iv, 0, sizeof( iv ) ); + memset( add, 0, sizeof( add ) ); + memset( out, 0, sizeof( out ) ); + memset( tag, 0, sizeof( tag ) ); + + TEST_ASSERT( mbedtls_ccm_setkey( &ctx, MBEDTLS_CIPHER_ID_AES, + key, 8 * sizeof( key ) ) == 0 ); + + TEST_ASSERT( mbedtls_ccm_sfix_encrypt_and_tag( &ctx, msg_len, iv, iv_len, + add, add_len, msg, out, tag, tag_len ) == res ); + + decrypt_ret = mbedtls_ccm_sfix_auth_decrypt( &ctx, msg_len, iv, iv_len, add, + add_len, msg, out, tag, tag_len ); + + if( res == 0 && tag_len != 0 ) + TEST_ASSERT( decrypt_ret == MBEDTLS_ERR_CCM_AUTH_FAILED ); + else + TEST_ASSERT( decrypt_ret == res ); + +exit: + mbedtls_ccm_free( &ctx ); +} +/* END_CASE */ + /* BEGIN_CASE */ void mbedtls_ccm_encrypt_and_tag( int cipher_id, char *key_hex, char *msg_hex, From 40235354252718de3fa5d5ae4d04d7735bdb4511 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Mon, 14 May 2018 16:40:28 +0100 Subject: [PATCH 4/4] CCM*: Implement variable tag length --- library/ccm.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/ccm.c b/library/ccm.c index 9461c3db5..6b944bfb2 100644 --- a/library/ccm.c +++ b/library/ccm.c @@ -335,6 +335,24 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, add_len, input, output, tag, tag_len ) ); } +#define CCM_MAX_IV_LEN 13 + +int mbedtls_ccm_svar_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, + size_t iv_len, const unsigned char *add, + size_t add_len, const unsigned char *input, + unsigned char *output, unsigned char *tag, + size_t tag_len, mbedtls_ccm_star_get_iv_t get_iv, + void *get_iv_ctx ) +{ + unsigned char iv[CCM_MAX_IV_LEN]; + + if( get_iv( get_iv_ctx, tag_len, iv, iv_len ) != 0 ) + return MBEDTLS_ERR_CCM_BAD_INPUT; + + return( mbedtls_ccm_sfix_encrypt_and_tag( ctx, length, iv, iv_len, add, + add_len, input, output, tag, tag_len ) ); +} + /* * Authenticated decryption */ @@ -381,6 +399,26 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, return( mbedtls_ccm_sfix_auth_decrypt( ctx, length, iv, iv_len, add, add_len, input, output, tag, tag_len ) ); } + + +int mbedtls_ccm_svar_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + size_t* output_len, + mbedtls_ccm_star_get_tag_len_t get_tag_len, + void *get_tlen_ctx ) +{ + size_t tag_len = 0; + + if( get_tag_len( get_tlen_ctx, &tag_len, iv, iv_len ) != 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + + *output_len = length - tag_len; + + return( mbedtls_ccm_sfix_auth_decrypt( ctx, length, iv, iv_len, add, + add_len, input, output, input + length, tag_len ) ); +} #endif /* !MBEDTLS_CCM_ALT */ #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C)