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:
parent
9461e45a17
commit
a56c448636
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user