From 295fc13ef33c9ce610be812f83ed00b366be1c39 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 15 Apr 2021 18:32:23 +0200 Subject: [PATCH] Split mbedtls_gcm_update_ad out of mbedtls_gcm_starts The GCM interface now has separate functions to start the operation and to pass the associated data. This is in preparation for allowing the associated data to be passed in chunks with repeatated calls to mbedtls_gcm_update_ad(). Signed-off-by: Gilles Peskine --- ChangeLog.d/gcm-update.txt | 4 +- include/mbedtls/gcm.h | 29 +++++++++++--- library/cipher.c | 13 +++++- library/gcm.c | 60 +++++++++++++++++++--------- tests/suites/test_suite_gcm.function | 12 +++--- 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/ChangeLog.d/gcm-update.txt b/ChangeLog.d/gcm-update.txt index 10d53efd7..0fffd094d 100644 --- a/ChangeLog.d/gcm-update.txt +++ b/ChangeLog.d/gcm-update.txt @@ -6,7 +6,9 @@ API changes The software implementation always produces the full output at each call to mbedtls_gcm_update(), but alternative implementations activated by MBEDTLS_GCM_ALT may delay partial blocks to the next call to - mbedtls_gcm_update() or mbedtls_gcm_finish(). + mbedtls_gcm_update() or mbedtls_gcm_finish(). Furthermore, applications + no longer pass the associated data to mbedtls_gcm_starts(), but to the + new function mbedtls_gcm_update_ad(). These changes are backward compatible for users of the cipher API. Features diff --git a/include/mbedtls/gcm.h b/include/mbedtls/gcm.h index 0bd6e1e0f..7218081e3 100644 --- a/include/mbedtls/gcm.h +++ b/include/mbedtls/gcm.h @@ -231,6 +231,26 @@ int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx, * \param iv The initialization vector. This must be a readable buffer of * at least \p iv_len Bytes. * \param iv_len The length of the IV. + * + * \return \c 0 on success. + */ +int mbedtls_gcm_starts( mbedtls_gcm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len ); + +/** + * \brief This function starts a GCM encryption or decryption + * operation. + * + * \note This function may only be called once per operation: + * you must pass the whole associated data in a single + * call. This limitation will be lifted in a future version + * of Mbed TLS. + * + * \param ctx The GCM context. This must have been started with + * mbedtls_gcm_starts() and must not have yet received + * any input with mbedtls_gcm_update(). * \param add The buffer holding the additional data, or \c NULL * if \p add_len is \c 0. * \param add_len The length of the additional data. If \c 0, @@ -238,12 +258,9 @@ int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx, * * \return \c 0 on success. */ -int mbedtls_gcm_starts( mbedtls_gcm_context *ctx, - int mode, - const unsigned char *iv, - size_t iv_len, - const unsigned char *add, - size_t add_len ); +int mbedtls_gcm_update_ad( mbedtls_gcm_context *ctx, + const unsigned char *add, + size_t add_len ); /** * \brief This function feeds an input buffer into an ongoing GCM diff --git a/library/cipher.c b/library/cipher.c index 7e6d0e02c..e09130ac5 100644 --- a/library/cipher.c +++ b/library/cipher.c @@ -415,6 +415,15 @@ int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx, } #endif +#if defined(MBEDTLS_GCM_C) + if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode ) + { + return( mbedtls_gcm_starts( (mbedtls_gcm_context *) ctx->cipher_ctx, + ctx->operation, + iv, iv_len ) ); + } +#endif + if ( actual_iv_size != 0 ) { memcpy( ctx->iv, iv, actual_iv_size ); @@ -466,8 +475,8 @@ int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx, #if defined(MBEDTLS_GCM_C) if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode ) { - return( mbedtls_gcm_starts( (mbedtls_gcm_context *) ctx->cipher_ctx, ctx->operation, - ctx->iv, ctx->iv_size, ad, ad_len ) ); + return( mbedtls_gcm_update_ad( (mbedtls_gcm_context *) ctx->cipher_ctx, + ad, ad_len ) ); } #endif diff --git a/library/gcm.c b/library/gcm.c index 13e729643..ee10093c0 100644 --- a/library/gcm.c +++ b/library/gcm.c @@ -269,11 +269,8 @@ static void gcm_mult( mbedtls_gcm_context *ctx, const unsigned char x[16], } int mbedtls_gcm_starts( mbedtls_gcm_context *ctx, - int mode, - const unsigned char *iv, - size_t iv_len, - const unsigned char *add, - size_t add_len ) + int mode, + const unsigned char *iv, size_t iv_len ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; unsigned char work_buf[16]; @@ -283,16 +280,11 @@ int mbedtls_gcm_starts( mbedtls_gcm_context *ctx, GCM_VALIDATE_RET( ctx != NULL ); GCM_VALIDATE_RET( iv != NULL ); - GCM_VALIDATE_RET( add_len == 0 || add != NULL ); - /* IV and AD are limited to 2^64 bits, so 2^61 bytes */ + /* IV is are limited to 2^64 bits, so 2^61 bytes */ /* IV is not allowed to be zero length */ - if( iv_len == 0 || - ( (uint64_t) iv_len ) >> 61 != 0 || - ( (uint64_t) add_len ) >> 61 != 0 ) - { + if( iv_len == 0 || (uint64_t) iv_len >> 61 != 0 ) return( MBEDTLS_ERR_GCM_BAD_INPUT ); - } memset( ctx->y, 0x00, sizeof(ctx->y) ); memset( ctx->buf, 0x00, sizeof(ctx->buf) ); @@ -337,6 +329,26 @@ int mbedtls_gcm_starts( mbedtls_gcm_context *ctx, return( ret ); } + return( 0 ); +} + + +int mbedtls_gcm_update_ad( mbedtls_gcm_context *ctx, + const unsigned char *add, size_t add_len ) +{ + const unsigned char *p; + size_t use_len, i; + + GCM_VALIDATE_RET( add_len == 0 || add != NULL ); + + /* IV is are limited to 2^64 bits, so 2^61 bytes */ + if( (uint64_t) add_len >> 61 != 0 ) + return( MBEDTLS_ERR_GCM_BAD_INPUT ); + + /* Calling update_ad multiple times is not yet supported */ + if( ctx->add_len != 0 ) + return( MBEDTLS_ERR_GCM_BAD_INPUT ); + ctx->add_len = add_len; p = add; while( add_len > 0 ) @@ -546,7 +558,10 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx, GCM_VALIDATE_RET( length == 0 || output != NULL ); GCM_VALIDATE_RET( tag != NULL ); - if( ( ret = mbedtls_gcm_starts( ctx, mode, iv, iv_len, add, add_len ) ) != 0 ) + if( ( ret = mbedtls_gcm_starts( ctx, mode, iv, iv_len ) ) != 0 ) + return( ret ); + + if( ( ret = mbedtls_gcm_update_ad( ctx, add, add_len ) ) != 0 ) return( ret ); if( ( ret = mbedtls_gcm_update( ctx, input, length, @@ -961,10 +976,14 @@ int mbedtls_gcm_self_test( int verbose ) goto exit; ret = mbedtls_gcm_starts( &ctx, MBEDTLS_GCM_ENCRYPT, - iv_test_data[iv_index_test_data[i]], - iv_len_test_data[i], - additional_test_data[add_index_test_data[i]], - add_len_test_data[i] ); + iv_test_data[iv_index_test_data[i]], + iv_len_test_data[i] ); + if( ret != 0 ) + goto exit; + + ret = mbedtls_gcm_update_ad( &ctx, + additional_test_data[add_index_test_data[i]], + add_len_test_data[i] ); if( ret != 0 ) goto exit; @@ -1031,8 +1050,11 @@ int mbedtls_gcm_self_test( int verbose ) goto exit; ret = mbedtls_gcm_starts( &ctx, MBEDTLS_GCM_DECRYPT, - iv_test_data[iv_index_test_data[i]], - iv_len_test_data[i], + iv_test_data[iv_index_test_data[i]], + iv_len_test_data[i] ); + if( ret != 0 ) + goto exit; + ret = mbedtls_gcm_update_ad( &ctx, additional_test_data[add_index_test_data[i]], add_len_test_data[i] ); if( ret != 0 ) diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function index da6aea899..9733eb235 100644 --- a/tests/suites/test_suite_gcm.function +++ b/tests/suites/test_suite_gcm.function @@ -23,8 +23,8 @@ static int check_multipart( mbedtls_gcm_context *ctx, TEST_EQUAL( input->len, expected_output->len ); TEST_EQUAL( 0, mbedtls_gcm_starts( ctx, mode, - iv->x, iv->len, - add->x, add->len ) ); + iv->x, iv->len ) ); + TEST_EQUAL( 0, mbedtls_gcm_update_ad( ctx, add->x, add->len ) ); /* Allocate a tight buffer for each update call. This way, if the function * tries to write beyond the advertised required buffer size, this will @@ -300,19 +300,17 @@ void gcm_invalid_param( ) TEST_INVALID_PARAM_RET( MBEDTLS_ERR_GCM_BAD_INPUT, mbedtls_gcm_starts( NULL, valid_mode, - valid_buffer, valid_len, valid_buffer, valid_len ) ); TEST_INVALID_PARAM_RET( MBEDTLS_ERR_GCM_BAD_INPUT, mbedtls_gcm_starts( &ctx, valid_mode, - NULL, valid_len, - valid_buffer, valid_len ) ); + NULL, valid_len ) ); + /* mbedtls_gcm_update_ad() */ TEST_INVALID_PARAM_RET( MBEDTLS_ERR_GCM_BAD_INPUT, - mbedtls_gcm_starts( &ctx, valid_mode, - valid_buffer, valid_len, + mbedtls_gcm_update_ad( &ctx, NULL, valid_len ) ); /* mbedtls_gcm_update() */