diff --git a/ChangeLog.d/gcm-update.txt b/ChangeLog.d/gcm-update.txt index d38455102..10d53efd7 100644 --- a/ChangeLog.d/gcm-update.txt +++ b/ChangeLog.d/gcm-update.txt @@ -2,6 +2,7 @@ API changes * The interface of the GCM module has changed to remove restrictions on how the input to multipart operations is broken down. mbedtls_gcm_finish() now takes an extra output parameter for the last partial output block. + mbedtls_gcm_update() now takes extra parameters for the output length. 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 diff --git a/include/mbedtls/gcm.h b/include/mbedtls/gcm.h index 951ee00c4..0bd6e1e0f 100644 --- a/include/mbedtls/gcm.h +++ b/include/mbedtls/gcm.h @@ -253,22 +253,42 @@ int mbedtls_gcm_starts( mbedtls_gcm_context *ctx, * input buffer. If the buffers overlap, the output buffer * must trail at least 8 Bytes behind the input buffer. * - * \param ctx The GCM context. This must be initialized. - * \param length The length of the input data. - * \param input The buffer holding the input data. If \p length is greater - * than zero, this must be a readable buffer of at least that - * size in Bytes. - * \param output The buffer for holding the output data. If \p length is - * greater than zero, this must be a writable buffer of at - * least that size in Bytes. + * \param ctx The GCM context. This must be initialized. + * \param input The buffer holding the input data. If \p input_length + * is greater than zero, this must be a readable buffer + * of at least \p input_length bytes. + * \param input_length The length of the input data in bytes. + * \param output The buffer for the output data. If \p output_length + * is greater than zero, this must be a writable buffer of + * of at least \p output_size bytes. + * This function may withhold the end of the output if + * it is a partial block for the underlying block cipher. + * That is, if the cumulated input passed to + * mbedtls_gcm_update() so far (including the current call) + * is 16 *n* + *p* with *p* < 16, this function may + * withhold the last *p* bytes, which will be output by + * a subsequent call to mbedtls_gcm_update() or + * mbedtls_gcm_finish(). + * \param output_size The size of the output buffer in bytes. + * This must be at least \p input_length plus the length + * of the input withheld by the previous call to + * mbedtls_gcm_update(). Therefore: + * - With arbitrary inputs, \p output_size may need to + * be as large as `input_length + 15`. + * - If all input lengths are a multiple of 16, then + * \p output_size = \p input_length is sufficient. + * \param output_length On success, \p *output_length contains the actual + * length of the output written in \p output. + * On failure, the content of \p *output_length is + * unspecified. * * \return \c 0 on success. * \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure. */ int mbedtls_gcm_update( mbedtls_gcm_context *ctx, - size_t length, - const unsigned char *input, - unsigned char *output ); + const unsigned char *input, size_t input_length, + unsigned char *output, size_t output_size, + size_t *output_length ); /** * \brief This function finishes the GCM operation and generates diff --git a/library/cipher.c b/library/cipher.c index 63eaba88f..7e6d0e02c 100644 --- a/library/cipher.c +++ b/library/cipher.c @@ -545,9 +545,9 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i #if defined(MBEDTLS_GCM_C) if( ctx->cipher_info->mode == MBEDTLS_MODE_GCM ) { - *olen = ilen; - return( mbedtls_gcm_update( (mbedtls_gcm_context *) ctx->cipher_ctx, ilen, input, - output ) ); + return( mbedtls_gcm_update( (mbedtls_gcm_context *) ctx->cipher_ctx, + input, ilen, + output, ilen, olen ) ); } #endif diff --git a/library/gcm.c b/library/gcm.c index de766bc76..13e729643 100644 --- a/library/gcm.c +++ b/library/gcm.c @@ -395,9 +395,9 @@ static int gcm_mask( mbedtls_gcm_context *ctx, } int mbedtls_gcm_update( mbedtls_gcm_context *ctx, - size_t length, - const unsigned char *input, - unsigned char *output ) + const unsigned char *input, size_t input_length, + unsigned char *output, size_t output_size, + size_t *output_length ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; const unsigned char *p = input; @@ -405,22 +405,27 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx, size_t offset; unsigned char ectr[16]; - /* Exit early if length==0 so that we don't do any pointer arithmetic on - * a potentially null pointer. */ - if( length == 0 ) + if( output_size < input_length ) + return( MBEDTLS_ERR_GCM_BAD_INPUT ); + GCM_VALIDATE_RET( output_length != NULL ); + *output_length = input_length; + + /* Exit early if input_length==0 so that we don't do any pointer arithmetic + * on a potentially null pointer. */ + if( input_length == 0 ) return( 0 ); GCM_VALIDATE_RET( ctx != NULL ); GCM_VALIDATE_RET( input != NULL ); GCM_VALIDATE_RET( output != NULL ); - if( output > input && (size_t) ( output - input ) < length ) + if( output > input && (size_t) ( output - input ) < input_length ) return( MBEDTLS_ERR_GCM_BAD_INPUT ); /* Total length is restricted to 2^39 - 256 bits, ie 2^36 - 2^5 bytes * Also check for possible overflow */ - if( ctx->len + length < ctx->len || - (uint64_t) ctx->len + length > 0xFFFFFFFE0ull ) + if( ctx->len + input_length < ctx->len || + (uint64_t) ctx->len + input_length > 0xFFFFFFFE0ull ) { return( MBEDTLS_ERR_GCM_BAD_INPUT ); } @@ -429,8 +434,8 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx, if( offset != 0 ) { size_t use_len = 16 - offset; - if( use_len > length ) - use_len = length; + if( use_len > input_length ) + use_len = input_length; if( ( ret = gcm_mask( ctx, ectr, offset, use_len, p, out_p ) ) != 0 ) return( ret ); @@ -439,14 +444,14 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx, gcm_mult( ctx, ctx->buf, ctx->buf ); ctx->len += use_len; - length -= use_len; + input_length -= use_len; p += use_len; out_p += use_len; } - ctx->len += length; + ctx->len += input_length; - while( length >= 16 ) + while( input_length >= 16 ) { gcm_incr( ctx->y ); if( ( ret = gcm_mask( ctx, ectr, 0, 16, p, out_p ) ) != 0 ) @@ -454,15 +459,15 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx, gcm_mult( ctx, ctx->buf, ctx->buf ); - length -= 16; + input_length -= 16; p += 16; out_p += 16; } - if( length > 0 ) + if( input_length > 0 ) { gcm_incr( ctx->y ); - if( ( ret = gcm_mask( ctx, ectr, 0, length, p, out_p ) ) != 0 ) + if( ( ret = gcm_mask( ctx, ectr, 0, input_length, p, out_p ) ) != 0 ) return( ret ); } @@ -532,6 +537,7 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx, unsigned char *tag ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; GCM_VALIDATE_RET( ctx != NULL ); GCM_VALIDATE_RET( iv != NULL ); @@ -543,7 +549,8 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx, if( ( ret = mbedtls_gcm_starts( ctx, mode, iv, iv_len, add, add_len ) ) != 0 ) return( ret ); - if( ( ret = mbedtls_gcm_update( ctx, length, input, output ) ) != 0 ) + if( ( ret = mbedtls_gcm_update( ctx, input, length, + output, length, &olen ) ) != 0 ) return( ret ); if( ( ret = mbedtls_gcm_finish( ctx, NULL, 0, tag, tag_len ) ) != 0 ) @@ -840,6 +847,7 @@ int mbedtls_gcm_self_test( int verbose ) unsigned char tag_buf[16]; int i, j, ret; mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES; + size_t olen; for( j = 0; j < 3; j++ ) { @@ -963,25 +971,34 @@ int mbedtls_gcm_self_test( int verbose ) if( pt_len_test_data[i] > 32 ) { size_t rest_len = pt_len_test_data[i] - 32; - ret = mbedtls_gcm_update( &ctx, 32, + ret = mbedtls_gcm_update( &ctx, pt_test_data[pt_index_test_data[i]], - buf ); + 32, + buf, sizeof( buf ), &olen ); if( ret != 0 ) goto exit; + if( olen != 32 ) + goto exit; - ret = mbedtls_gcm_update( &ctx, rest_len, - pt_test_data[pt_index_test_data[i]] + 32, - buf + 32 ); + ret = mbedtls_gcm_update( &ctx, + pt_test_data[pt_index_test_data[i]] + 32, + rest_len, + buf + 32, sizeof( buf ) - 32, &olen ); if( ret != 0 ) goto exit; + if( olen != rest_len ) + goto exit; } else { - ret = mbedtls_gcm_update( &ctx, pt_len_test_data[i], + ret = mbedtls_gcm_update( &ctx, pt_test_data[pt_index_test_data[i]], - buf ); + pt_len_test_data[i], + buf, sizeof( buf ), &olen ); if( ret != 0 ) goto exit; + if( olen != pt_len_test_data[i] ) + goto exit; } ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 ); @@ -1024,24 +1041,33 @@ int mbedtls_gcm_self_test( int verbose ) if( pt_len_test_data[i] > 32 ) { size_t rest_len = pt_len_test_data[i] - 32; - ret = mbedtls_gcm_update( &ctx, 32, ct_test_data[j * 6 + i], - buf ); + ret = mbedtls_gcm_update( &ctx, + ct_test_data[j * 6 + i], 32, + buf, sizeof( buf ), &olen ); if( ret != 0 ) goto exit; + if( olen != 32 ) + goto exit; - ret = mbedtls_gcm_update( &ctx, rest_len, + ret = mbedtls_gcm_update( &ctx, ct_test_data[j * 6 + i] + 32, - buf + 32 ); + rest_len, + buf + 32, sizeof( buf ) - 32, &olen ); if( ret != 0 ) goto exit; + if( olen != rest_len ) + goto exit; } else { - ret = mbedtls_gcm_update( &ctx, pt_len_test_data[i], + ret = mbedtls_gcm_update( &ctx, ct_test_data[j * 6 + i], - buf ); + pt_len_test_data[i], + buf, sizeof( buf ), &olen ); if( ret != 0 ) goto exit; + if( olen != pt_len_test_data[i] ) + goto exit; } ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 ); diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function index 965d15482..da6aea899 100644 --- a/tests/suites/test_suite_gcm.function +++ b/tests/suites/test_suite_gcm.function @@ -16,6 +16,7 @@ static int check_multipart( mbedtls_gcm_context *ctx, int ok = 0; uint8_t *output = NULL; size_t n2 = input->len - n1; + size_t olen; /* Sanity checks on the test data */ TEST_ASSERT( n1 <= input->len ); @@ -29,14 +30,18 @@ static int check_multipart( mbedtls_gcm_context *ctx, * tries to write beyond the advertised required buffer size, this will * count as an overflow for memory sanitizers and static checkers. */ ASSERT_ALLOC( output, n1 ); - TEST_EQUAL( 0, mbedtls_gcm_update( ctx, n1, input->x, output ) ); - ASSERT_COMPARE( output, n1, expected_output->x, n1 ); + olen = 0xdeadbeef; + TEST_EQUAL( 0, mbedtls_gcm_update( ctx, input->x, n1, output, n1, &olen ) ); + TEST_EQUAL( n1, olen ); + ASSERT_COMPARE( output, olen, expected_output->x, n1 ); mbedtls_free( output ); output = NULL; ASSERT_ALLOC( output, n2 ); - TEST_EQUAL( 0, mbedtls_gcm_update( ctx, n2, input->x + n1, output ) ); - ASSERT_COMPARE( output, n2, expected_output->x + n1, n2 ); + olen = 0xdeadbeef; + TEST_EQUAL( 0, mbedtls_gcm_update( ctx, input->x + n1, n2, output, n2, &olen ) ); + TEST_EQUAL( n2, olen ); + ASSERT_COMPARE( output, olen, expected_output->x + n1, n2 ); mbedtls_free( output ); output = NULL; @@ -185,6 +190,7 @@ void gcm_invalid_param( ) int valid_mode = MBEDTLS_GCM_ENCRYPT; int valid_len = sizeof(valid_buffer); int valid_bitlen = 128, invalid_bitlen = 1; + size_t olen; mbedtls_gcm_init( &ctx ); @@ -312,16 +318,20 @@ void gcm_invalid_param( ) /* mbedtls_gcm_update() */ TEST_INVALID_PARAM_RET( MBEDTLS_ERR_GCM_BAD_INPUT, - mbedtls_gcm_update( NULL, valid_len, - valid_buffer, valid_buffer ) ); + mbedtls_gcm_update( NULL, valid_buffer, valid_len, + valid_buffer, valid_len, &olen ) ); TEST_INVALID_PARAM_RET( MBEDTLS_ERR_GCM_BAD_INPUT, - mbedtls_gcm_update( &ctx, valid_len, - NULL, valid_buffer ) ); + mbedtls_gcm_update( &ctx, NULL, valid_len, + valid_buffer, valid_len, &olen ) ); TEST_INVALID_PARAM_RET( MBEDTLS_ERR_GCM_BAD_INPUT, - mbedtls_gcm_update( &ctx, valid_len, - valid_buffer, NULL ) ); + mbedtls_gcm_update( &ctx, valid_buffer, valid_len, + NULL, valid_len, &olen ) ); + TEST_INVALID_PARAM_RET( + MBEDTLS_ERR_GCM_BAD_INPUT, + mbedtls_gcm_update( &ctx, valid_buffer, valid_len, + valid_buffer, valid_len, NULL ) ); /* mbedtls_gcm_finish() */ TEST_INVALID_PARAM_RET(