Add output length parameters to mbedtls_gcm_update

Alternative implementations of GCM may delay the output of partial
blocks from mbedtls_gcm_update(). Add an output length parameter to
mbedtls_gcm_update() to allow such implementations to delay the output
of partial blocks. With the software implementation, there is no such
delay.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
Gilles Peskine 2021-04-15 17:22:35 +02:00
parent 9461e45a17
commit a56c448636
5 changed files with 112 additions and 55 deletions

View file

@ -2,6 +2,7 @@ API changes
* The interface of the GCM module has changed to remove restrictions on * The interface of the GCM module has changed to remove restrictions on
how the input to multipart operations is broken down. mbedtls_gcm_finish() how the input to multipart operations is broken down. mbedtls_gcm_finish()
now takes an extra output parameter for the last partial output block. 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 The software implementation always produces the full output at each
call to mbedtls_gcm_update(), but alternative implementations activated call to mbedtls_gcm_update(), but alternative implementations activated
by MBEDTLS_GCM_ALT may delay partial blocks to the next call to by MBEDTLS_GCM_ALT may delay partial blocks to the next call to

View file

@ -253,22 +253,42 @@ int mbedtls_gcm_starts( mbedtls_gcm_context *ctx,
* input buffer. If the buffers overlap, the output buffer * input buffer. If the buffers overlap, the output buffer
* must trail at least 8 Bytes behind the input buffer. * must trail at least 8 Bytes behind the input buffer.
* *
* \param ctx The GCM context. This must be initialized. * \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 input_length
* \param input The buffer holding the input data. If \p length is greater * is greater than zero, this must be a readable buffer
* than zero, this must be a readable buffer of at least that * of at least \p input_length bytes.
* size in Bytes. * \param input_length The length of the input data in bytes.
* \param output The buffer for holding the output data. If \p length is * \param output The buffer for the output data. If \p output_length
* greater than zero, this must be a writable buffer of at * is greater than zero, this must be a writable buffer of
* least that size in Bytes. * 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 \c 0 on success.
* \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure. * \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
*/ */
int mbedtls_gcm_update( mbedtls_gcm_context *ctx, int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
size_t length, const unsigned char *input, size_t input_length,
const unsigned char *input, unsigned char *output, size_t output_size,
unsigned char *output ); size_t *output_length );
/** /**
* \brief This function finishes the GCM operation and generates * \brief This function finishes the GCM operation and generates

View file

@ -545,9 +545,9 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i
#if defined(MBEDTLS_GCM_C) #if defined(MBEDTLS_GCM_C)
if( ctx->cipher_info->mode == MBEDTLS_MODE_GCM ) if( ctx->cipher_info->mode == MBEDTLS_MODE_GCM )
{ {
*olen = ilen; return( mbedtls_gcm_update( (mbedtls_gcm_context *) ctx->cipher_ctx,
return( mbedtls_gcm_update( (mbedtls_gcm_context *) ctx->cipher_ctx, ilen, input, input, ilen,
output ) ); output, ilen, olen ) );
} }
#endif #endif

View file

@ -395,9 +395,9 @@ static int gcm_mask( mbedtls_gcm_context *ctx,
} }
int mbedtls_gcm_update( mbedtls_gcm_context *ctx, int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
size_t length, const unsigned char *input, size_t input_length,
const unsigned char *input, unsigned char *output, size_t output_size,
unsigned char *output ) size_t *output_length )
{ {
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
const unsigned char *p = input; const unsigned char *p = input;
@ -405,22 +405,27 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
size_t offset; size_t offset;
unsigned char ectr[16]; unsigned char ectr[16];
/* Exit early if length==0 so that we don't do any pointer arithmetic on if( output_size < input_length )
* a potentially null pointer. */ return( MBEDTLS_ERR_GCM_BAD_INPUT );
if( length == 0 ) 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 ); return( 0 );
GCM_VALIDATE_RET( ctx != NULL ); GCM_VALIDATE_RET( ctx != NULL );
GCM_VALIDATE_RET( input != NULL ); GCM_VALIDATE_RET( input != NULL );
GCM_VALIDATE_RET( output != 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 ); return( MBEDTLS_ERR_GCM_BAD_INPUT );
/* Total length is restricted to 2^39 - 256 bits, ie 2^36 - 2^5 bytes /* Total length is restricted to 2^39 - 256 bits, ie 2^36 - 2^5 bytes
* Also check for possible overflow */ * Also check for possible overflow */
if( ctx->len + length < ctx->len || if( ctx->len + input_length < ctx->len ||
(uint64_t) ctx->len + length > 0xFFFFFFFE0ull ) (uint64_t) ctx->len + input_length > 0xFFFFFFFE0ull )
{ {
return( MBEDTLS_ERR_GCM_BAD_INPUT ); return( MBEDTLS_ERR_GCM_BAD_INPUT );
} }
@ -429,8 +434,8 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
if( offset != 0 ) if( offset != 0 )
{ {
size_t use_len = 16 - offset; size_t use_len = 16 - offset;
if( use_len > length ) if( use_len > input_length )
use_len = length; use_len = input_length;
if( ( ret = gcm_mask( ctx, ectr, offset, use_len, p, out_p ) ) != 0 ) if( ( ret = gcm_mask( ctx, ectr, offset, use_len, p, out_p ) ) != 0 )
return( ret ); return( ret );
@ -439,14 +444,14 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
gcm_mult( ctx, ctx->buf, ctx->buf ); gcm_mult( ctx, ctx->buf, ctx->buf );
ctx->len += use_len; ctx->len += use_len;
length -= use_len; input_length -= use_len;
p += use_len; p += use_len;
out_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 ); gcm_incr( ctx->y );
if( ( ret = gcm_mask( ctx, ectr, 0, 16, p, out_p ) ) != 0 ) 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 ); gcm_mult( ctx, ctx->buf, ctx->buf );
length -= 16; input_length -= 16;
p += 16; p += 16;
out_p += 16; out_p += 16;
} }
if( length > 0 ) if( input_length > 0 )
{ {
gcm_incr( ctx->y ); 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 ); return( ret );
} }
@ -532,6 +537,7 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx,
unsigned char *tag ) unsigned char *tag )
{ {
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t olen;
GCM_VALIDATE_RET( ctx != NULL ); GCM_VALIDATE_RET( ctx != NULL );
GCM_VALIDATE_RET( iv != 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 ) if( ( ret = mbedtls_gcm_starts( ctx, mode, iv, iv_len, add, add_len ) ) != 0 )
return( ret ); 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 ); return( ret );
if( ( ret = mbedtls_gcm_finish( ctx, NULL, 0, tag, tag_len ) ) != 0 ) 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]; unsigned char tag_buf[16];
int i, j, ret; int i, j, ret;
mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES; mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
size_t olen;
for( j = 0; j < 3; j++ ) for( j = 0; j < 3; j++ )
{ {
@ -963,25 +971,34 @@ int mbedtls_gcm_self_test( int verbose )
if( pt_len_test_data[i] > 32 ) if( pt_len_test_data[i] > 32 )
{ {
size_t rest_len = 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]], pt_test_data[pt_index_test_data[i]],
buf ); 32,
buf, sizeof( buf ), &olen );
if( ret != 0 ) if( ret != 0 )
goto exit; goto exit;
if( olen != 32 )
goto exit;
ret = mbedtls_gcm_update( &ctx, rest_len, ret = mbedtls_gcm_update( &ctx,
pt_test_data[pt_index_test_data[i]] + 32, pt_test_data[pt_index_test_data[i]] + 32,
buf + 32 ); rest_len,
buf + 32, sizeof( buf ) - 32, &olen );
if( ret != 0 ) if( ret != 0 )
goto exit; goto exit;
if( olen != rest_len )
goto exit;
} }
else else
{ {
ret = mbedtls_gcm_update( &ctx, pt_len_test_data[i], ret = mbedtls_gcm_update( &ctx,
pt_test_data[pt_index_test_data[i]], pt_test_data[pt_index_test_data[i]],
buf ); pt_len_test_data[i],
buf, sizeof( buf ), &olen );
if( ret != 0 ) if( ret != 0 )
goto exit; goto exit;
if( olen != pt_len_test_data[i] )
goto exit;
} }
ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 ); 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 ) if( pt_len_test_data[i] > 32 )
{ {
size_t rest_len = 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], ret = mbedtls_gcm_update( &ctx,
buf ); ct_test_data[j * 6 + i], 32,
buf, sizeof( buf ), &olen );
if( ret != 0 ) if( ret != 0 )
goto exit; 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, ct_test_data[j * 6 + i] + 32,
buf + 32 ); rest_len,
buf + 32, sizeof( buf ) - 32, &olen );
if( ret != 0 ) if( ret != 0 )
goto exit; goto exit;
if( olen != rest_len )
goto exit;
} }
else else
{ {
ret = mbedtls_gcm_update( &ctx, pt_len_test_data[i], ret = mbedtls_gcm_update( &ctx,
ct_test_data[j * 6 + i], ct_test_data[j * 6 + i],
buf ); pt_len_test_data[i],
buf, sizeof( buf ), &olen );
if( ret != 0 ) if( ret != 0 )
goto exit; goto exit;
if( olen != pt_len_test_data[i] )
goto exit;
} }
ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 ); ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 );

View file

@ -16,6 +16,7 @@ static int check_multipart( mbedtls_gcm_context *ctx,
int ok = 0; int ok = 0;
uint8_t *output = NULL; uint8_t *output = NULL;
size_t n2 = input->len - n1; size_t n2 = input->len - n1;
size_t olen;
/* Sanity checks on the test data */ /* Sanity checks on the test data */
TEST_ASSERT( n1 <= input->len ); 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 * tries to write beyond the advertised required buffer size, this will
* count as an overflow for memory sanitizers and static checkers. */ * count as an overflow for memory sanitizers and static checkers. */
ASSERT_ALLOC( output, n1 ); ASSERT_ALLOC( output, n1 );
TEST_EQUAL( 0, mbedtls_gcm_update( ctx, n1, input->x, output ) ); olen = 0xdeadbeef;
ASSERT_COMPARE( output, n1, expected_output->x, n1 ); 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 ); mbedtls_free( output );
output = NULL; output = NULL;
ASSERT_ALLOC( output, n2 ); ASSERT_ALLOC( output, n2 );
TEST_EQUAL( 0, mbedtls_gcm_update( ctx, n2, input->x + n1, output ) ); olen = 0xdeadbeef;
ASSERT_COMPARE( output, n2, expected_output->x + n1, n2 ); 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 ); mbedtls_free( output );
output = NULL; output = NULL;
@ -185,6 +190,7 @@ void gcm_invalid_param( )
int valid_mode = MBEDTLS_GCM_ENCRYPT; int valid_mode = MBEDTLS_GCM_ENCRYPT;
int valid_len = sizeof(valid_buffer); int valid_len = sizeof(valid_buffer);
int valid_bitlen = 128, invalid_bitlen = 1; int valid_bitlen = 128, invalid_bitlen = 1;
size_t olen;
mbedtls_gcm_init( &ctx ); mbedtls_gcm_init( &ctx );
@ -312,16 +318,20 @@ void gcm_invalid_param( )
/* mbedtls_gcm_update() */ /* mbedtls_gcm_update() */
TEST_INVALID_PARAM_RET( TEST_INVALID_PARAM_RET(
MBEDTLS_ERR_GCM_BAD_INPUT, MBEDTLS_ERR_GCM_BAD_INPUT,
mbedtls_gcm_update( NULL, valid_len, mbedtls_gcm_update( NULL, valid_buffer, valid_len,
valid_buffer, valid_buffer ) ); valid_buffer, valid_len, &olen ) );
TEST_INVALID_PARAM_RET( TEST_INVALID_PARAM_RET(
MBEDTLS_ERR_GCM_BAD_INPUT, MBEDTLS_ERR_GCM_BAD_INPUT,
mbedtls_gcm_update( &ctx, valid_len, mbedtls_gcm_update( &ctx, NULL, valid_len,
NULL, valid_buffer ) ); valid_buffer, valid_len, &olen ) );
TEST_INVALID_PARAM_RET( TEST_INVALID_PARAM_RET(
MBEDTLS_ERR_GCM_BAD_INPUT, MBEDTLS_ERR_GCM_BAD_INPUT,
mbedtls_gcm_update( &ctx, valid_len, mbedtls_gcm_update( &ctx, valid_buffer, valid_len,
valid_buffer, NULL ) ); 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() */ /* mbedtls_gcm_finish() */
TEST_INVALID_PARAM_RET( TEST_INVALID_PARAM_RET(