mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-02-01 23:54:01 +00:00
Merge pull request #4342 from gilles-peskine-arm/gcm-update-any-length
GCM: allow arbitrary lengths for update Only the ABI-API-checking job failed and this is expected thus good to go.
This commit is contained in:
commit
49fef37ebf
17
ChangeLog.d/gcm-update.txt
Normal file
17
ChangeLog.d/gcm-update.txt
Normal file
@ -0,0 +1,17 @@
|
||||
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
|
||||
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
|
||||
* The multi-part GCM interface (mbedtls_gcm_update() or
|
||||
mbedtls_cipher_update()) no longer requires the size of partial inputs to
|
||||
be a multiple of 16.
|
10
docs/3.0-migration-guide.d/gcm-alt.md
Normal file
10
docs/3.0-migration-guide.d/gcm-alt.md
Normal file
@ -0,0 +1,10 @@
|
||||
GCM interface changes: impact for alternative implementations
|
||||
-------------------------------------------------------------
|
||||
|
||||
The GCM multipart interface has changed as described in [“GCM multipart interface: application changes”](#gcm-multipart-interface:-application-changes). The consequences for an alternative implementation of GCM (`MBEDTLS_GCM_ALT`) are as follows:
|
||||
|
||||
* `mbedtls_gcm_starts()` now only sets the mode and the nonce (IV). The new function `mbedtls_gcm_update_ad()` receives the associated data. It may be called multiple times.
|
||||
* `mbedtls_gcm_update()` now allows arbitrary-length inputs, takes an extra parameter to indicate the actual output length. Alternative implementations may choose between two modes:
|
||||
* Always return the partial output immediately, even if it does not consist of a whole number of blocks.
|
||||
* Buffer the data for the last partial block, to be returned in the next call to `mbedtls_gcm_update()` or `mbedtls_gcm_finish()`.
|
||||
* `mbedtls_gcm_finish()` now takes an extra output buffer for the last partial block if needed.
|
13
docs/3.0-migration-guide.d/gcm-multipart.md
Normal file
13
docs/3.0-migration-guide.d/gcm-multipart.md
Normal file
@ -0,0 +1,13 @@
|
||||
GCM multipart interface: application changes
|
||||
--------------------------------------------
|
||||
|
||||
The GCM module now supports arbitrary chunked input in the multipart interface.
|
||||
This changes the interface for applications using the GCM module directly for multipart operations.
|
||||
Applications using one-shot GCM or using GCM via the `mbedtls_cipher_xxx` or `psa_aead_xxx` interfaces do not require any changes.
|
||||
|
||||
* `mbedtls_gcm_starts()` now only sets the mode and the nonce (IV). Call the new function `mbedtls_gcm_update_ad()` to pass the associated data.
|
||||
* The current implementation has a limitation that `mbedtls_gcm_update_ad()` may only be called once. This limitation will be lifted shortly; watch https://github.com/ARMmbed/mbedtls/issues/4351 for updates.
|
||||
* `mbedtls_gcm_update()` now takes an extra parameter to indicate the actual output length. In Mbed TLS 2.x, applications had to pass inputs consisting of whole 16-byte blocks except for the last block (this limitation has been lifted). In this case:
|
||||
* As long as the input remains block-aligned, the output length is exactly the input length, as before.
|
||||
* If the length of the last input is not a multiple of 16, alternative implementations may return the last partial block in the call to `mbedtls_gcm_finish()` instead of returning it in the last call to `mbedtls_gcm_update()`.
|
||||
* `mbedtls_gcm_finish()` now takes an extra output buffer for the last partial block. This is needed for alternative implementations that can only process a whole block at a time.
|
@ -724,11 +724,6 @@ int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx,
|
||||
* Exception: For MBEDTLS_MODE_ECB, expects a single block
|
||||
* in size. For example, 16 Bytes for AES.
|
||||
*
|
||||
* \note If the underlying cipher is used in GCM mode, all calls
|
||||
* to this function, except for the last one before
|
||||
* mbedtls_cipher_finish(), must have \p ilen as a
|
||||
* multiple of the block size of the cipher.
|
||||
*
|
||||
* \param ctx The generic cipher context. This must be initialized and
|
||||
* bound to a key.
|
||||
* \param input The buffer holding the input data. This must be a
|
||||
|
@ -227,6 +227,32 @@ 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 feeds an input buffer as associated data
|
||||
* (authenticated but not encrypted data) in a GCM
|
||||
* encryption or decryption operation.
|
||||
*
|
||||
* Call this function after mbedtls_gcm_starts() to pass
|
||||
* the associated data. If the associated data is empty,
|
||||
* you do not need to call this function. You may not
|
||||
* call this function after calling mbedtls_cipher_update().
|
||||
*
|
||||
* \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,
|
||||
@ -234,42 +260,65 @@ 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
|
||||
* encryption or decryption operation.
|
||||
*
|
||||
* ` The function expects input to be a multiple of 16
|
||||
* Bytes. Only the last call before calling
|
||||
* mbedtls_gcm_finish() can be less than 16 Bytes.
|
||||
* You may call this function zero, one or more times
|
||||
* to pass successive parts of the input: the plaintext to
|
||||
* encrypt, or the ciphertext (not including the tag) to
|
||||
* decrypt. After the last part of the input, call
|
||||
* mbedtls_gcm_finish().
|
||||
*
|
||||
* This function may produce output in one of the following
|
||||
* ways:
|
||||
* - Immediate output: the output length is always equal
|
||||
* to the input length.
|
||||
* - Buffered output: the output consists of a whole number
|
||||
* of 16-byte blocks. If the total input length so far
|
||||
* (not including associated data) is 16 \* *B* + *A*
|
||||
* with *A* < 16 then the total output length is 16 \* *B*.
|
||||
*
|
||||
* In particular:
|
||||
* - It is always correct to call this function with
|
||||
* \p output_size >= \p input_length + 15.
|
||||
* - If \p input_length is a multiple of 16 for all the calls
|
||||
* to this function during an operation, then it is
|
||||
* correct to use \p output_size = \p input_length.
|
||||
*
|
||||
* \note For decryption, the output buffer cannot be the same as
|
||||
* 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. This must be a multiple of
|
||||
* 16 except in the last call before mbedtls_gcm_finish().
|
||||
* \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_size
|
||||
* is greater than zero, this must be a writable buffer of
|
||||
* of at least \p output_size bytes.
|
||||
* \param output_size The size of the output buffer in bytes.
|
||||
* See the function description regarding the output size.
|
||||
* \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.
|
||||
* \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure:
|
||||
* total input length too long,
|
||||
* unsupported input/output buffer overlap detected,
|
||||
* or \p output_size too small.
|
||||
*/
|
||||
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
|
||||
@ -283,13 +332,26 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
|
||||
* buffer of at least \p tag_len Bytes.
|
||||
* \param tag_len The length of the tag to generate. This must be at least
|
||||
* four.
|
||||
* \param output The buffer for the final output.
|
||||
* If \p output_size is nonzero, this must be a writable
|
||||
* buffer of at least \p output_size bytes.
|
||||
* \param output_size The size of the \p output buffer in bytes.
|
||||
* This must be large enough for the output that
|
||||
* mbedtls_gcm_update() has not produced. In particular:
|
||||
* - If mbedtls_gcm_update() produces immediate output,
|
||||
* or if the total input size is a multiple of \c 16,
|
||||
* then mbedtls_gcm_finish() never produces any output,
|
||||
* so \p output_size can be \c 0.
|
||||
* - \p output_size never needs to be more than \c 15.
|
||||
*
|
||||
* \return \c 0 on success.
|
||||
* \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
|
||||
* \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure:
|
||||
* invalid value of \p tag_len,
|
||||
* or \p output_size too small.
|
||||
*/
|
||||
int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
|
||||
unsigned char *tag,
|
||||
size_t tag_len );
|
||||
unsigned char *output, size_t output_size,
|
||||
unsigned char *tag, size_t tag_len );
|
||||
|
||||
/**
|
||||
* \brief This function clears a GCM context and the underlying
|
||||
|
@ -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
|
||||
|
||||
@ -545,9 +554,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
|
||||
|
||||
@ -1101,6 +1110,7 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx,
|
||||
#if defined(MBEDTLS_GCM_C)
|
||||
if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
|
||||
return( mbedtls_gcm_finish( (mbedtls_gcm_context *) ctx->cipher_ctx,
|
||||
NULL, 0,
|
||||
tag, tag_len ) );
|
||||
#endif
|
||||
|
||||
@ -1153,6 +1163,7 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
|
||||
|
||||
if( 0 != ( ret = mbedtls_gcm_finish(
|
||||
(mbedtls_gcm_context *) ctx->cipher_ctx,
|
||||
NULL, 0,
|
||||
check_tag, tag_len ) ) )
|
||||
{
|
||||
return( ret );
|
||||
|
256
library/gcm.c
256
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 )
|
||||
@ -355,72 +367,129 @@ int mbedtls_gcm_starts( mbedtls_gcm_context *ctx,
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Increment the counter. */
|
||||
static void gcm_incr( unsigned char y[16] )
|
||||
{
|
||||
size_t i;
|
||||
for( i = 16; i > 12; i-- )
|
||||
if( ++y[i - 1] != 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate and apply the encryption mask. Process use_len bytes of data,
|
||||
* starting at position offset in the mask block. */
|
||||
static int gcm_mask( mbedtls_gcm_context *ctx,
|
||||
unsigned char ectr[16],
|
||||
size_t offset, size_t use_len,
|
||||
const unsigned char *input,
|
||||
unsigned char *output )
|
||||
{
|
||||
size_t i;
|
||||
size_t olen = 0;
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
|
||||
if( ( ret = mbedtls_cipher_update( &ctx->cipher_ctx, ctx->y, 16, ectr,
|
||||
&olen ) ) != 0 )
|
||||
{
|
||||
mbedtls_platform_zeroize( ectr, 16 );
|
||||
return( ret );
|
||||
}
|
||||
|
||||
for( i = 0; i < use_len; i++ )
|
||||
{
|
||||
if( ctx->mode == MBEDTLS_GCM_DECRYPT )
|
||||
ctx->buf[offset + i] ^= input[i];
|
||||
output[i] = ectr[offset + i] ^ input[i];
|
||||
if( ctx->mode == MBEDTLS_GCM_ENCRYPT )
|
||||
ctx->buf[offset + i] ^= output[i];
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char ectr[16];
|
||||
size_t i;
|
||||
const unsigned char *p;
|
||||
const unsigned char *p = input;
|
||||
unsigned char *out_p = output;
|
||||
size_t use_len, olen = 0;
|
||||
size_t offset;
|
||||
unsigned char ectr[16];
|
||||
|
||||
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( length == 0 || input != NULL );
|
||||
GCM_VALIDATE_RET( length == 0 || output != 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 );
|
||||
}
|
||||
|
||||
ctx->len += length;
|
||||
|
||||
p = input;
|
||||
while( length > 0 )
|
||||
offset = ctx->len % 16;
|
||||
if( offset != 0 )
|
||||
{
|
||||
use_len = ( length < 16 ) ? length : 16;
|
||||
size_t use_len = 16 - offset;
|
||||
if( use_len > input_length )
|
||||
use_len = input_length;
|
||||
|
||||
for( i = 16; i > 12; i-- )
|
||||
if( ++ctx->y[i - 1] != 0 )
|
||||
break;
|
||||
|
||||
if( ( ret = mbedtls_cipher_update( &ctx->cipher_ctx, ctx->y, 16, ectr,
|
||||
&olen ) ) != 0 )
|
||||
{
|
||||
if( ( ret = gcm_mask( ctx, ectr, offset, use_len, p, out_p ) ) != 0 )
|
||||
return( ret );
|
||||
}
|
||||
|
||||
for( i = 0; i < use_len; i++ )
|
||||
{
|
||||
if( ctx->mode == MBEDTLS_GCM_DECRYPT )
|
||||
ctx->buf[i] ^= p[i];
|
||||
out_p[i] = ectr[i] ^ p[i];
|
||||
if( ctx->mode == MBEDTLS_GCM_ENCRYPT )
|
||||
ctx->buf[i] ^= out_p[i];
|
||||
}
|
||||
if( offset + use_len == 16 )
|
||||
gcm_mult( ctx, ctx->buf, ctx->buf );
|
||||
|
||||
gcm_mult( ctx, ctx->buf, ctx->buf );
|
||||
|
||||
length -= use_len;
|
||||
ctx->len += use_len;
|
||||
input_length -= use_len;
|
||||
p += use_len;
|
||||
out_p += use_len;
|
||||
}
|
||||
|
||||
ctx->len += input_length;
|
||||
|
||||
while( input_length >= 16 )
|
||||
{
|
||||
gcm_incr( ctx->y );
|
||||
if( ( ret = gcm_mask( ctx, ectr, 0, 16, p, out_p ) ) != 0 )
|
||||
return( ret );
|
||||
|
||||
gcm_mult( ctx, ctx->buf, ctx->buf );
|
||||
|
||||
input_length -= 16;
|
||||
p += 16;
|
||||
out_p += 16;
|
||||
}
|
||||
|
||||
if( input_length > 0 )
|
||||
{
|
||||
gcm_incr( ctx->y );
|
||||
if( ( ret = gcm_mask( ctx, ectr, 0, input_length, p, out_p ) ) != 0 )
|
||||
return( ret );
|
||||
}
|
||||
|
||||
mbedtls_platform_zeroize( ectr, sizeof( ectr ) );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
|
||||
unsigned char *tag,
|
||||
size_t tag_len )
|
||||
unsigned char *output, size_t output_size,
|
||||
unsigned char *tag, size_t tag_len )
|
||||
{
|
||||
unsigned char work_buf[16];
|
||||
size_t i;
|
||||
@ -430,12 +499,20 @@ int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
|
||||
GCM_VALIDATE_RET( ctx != NULL );
|
||||
GCM_VALIDATE_RET( tag != NULL );
|
||||
|
||||
/* We never pass any output in finish(). The output parameter exists only
|
||||
* for the sake of alternative implementations. */
|
||||
(void) output;
|
||||
(void) output_size;
|
||||
|
||||
orig_len = ctx->len * 8;
|
||||
orig_add_len = ctx->add_len * 8;
|
||||
|
||||
if( tag_len > 16 || tag_len < 4 )
|
||||
return( MBEDTLS_ERR_GCM_BAD_INPUT );
|
||||
|
||||
if( ctx->len % 16 != 0 )
|
||||
gcm_mult( ctx, ctx->buf, ctx->buf );
|
||||
|
||||
memcpy( tag, ctx->base_ectr, tag_len );
|
||||
|
||||
if( orig_len || orig_add_len )
|
||||
@ -472,6 +549,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 );
|
||||
@ -480,13 +558,17 @@ 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( ctx, length, input, output ) ) != 0 )
|
||||
if( ( ret = mbedtls_gcm_update_ad( ctx, add, add_len ) ) != 0 )
|
||||
return( ret );
|
||||
|
||||
if( ( ret = mbedtls_gcm_finish( ctx, tag, tag_len ) ) != 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 )
|
||||
return( ret );
|
||||
|
||||
return( 0 );
|
||||
@ -780,6 +862,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++ )
|
||||
{
|
||||
@ -893,38 +976,51 @@ 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;
|
||||
|
||||
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, tag_buf, 16 );
|
||||
ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 );
|
||||
if( ret != 0 )
|
||||
goto exit;
|
||||
|
||||
@ -954,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 )
|
||||
@ -964,27 +1063,36 @@ 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, tag_buf, 16 );
|
||||
ret = mbedtls_gcm_finish( &ctx, NULL, 0, tag_buf, 16 );
|
||||
if( ret != 0 )
|
||||
goto exit;
|
||||
|
||||
|
@ -1,5 +1,62 @@
|
||||
/* BEGIN_HEADER */
|
||||
#include "mbedtls/gcm.h"
|
||||
|
||||
/* Use the multipart interface to process the encrypted data in two parts
|
||||
* and check that the output matches the expected output.
|
||||
* The context must have been set up with the key. */
|
||||
static int check_multipart( mbedtls_gcm_context *ctx,
|
||||
int mode,
|
||||
const data_t *iv,
|
||||
const data_t *add,
|
||||
const data_t *input,
|
||||
const data_t *expected_output,
|
||||
const data_t *tag,
|
||||
size_t n1 )
|
||||
{
|
||||
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 );
|
||||
TEST_EQUAL( input->len, expected_output->len );
|
||||
|
||||
TEST_EQUAL( 0, mbedtls_gcm_starts( ctx, mode,
|
||||
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
|
||||
* count as an overflow for memory sanitizers and static checkers. */
|
||||
ASSERT_ALLOC( output, 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 );
|
||||
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;
|
||||
|
||||
ASSERT_ALLOC( output, tag->len );
|
||||
TEST_EQUAL( 0, mbedtls_gcm_finish( ctx, NULL, 0, output, tag->len ) );
|
||||
ASSERT_COMPARE( output, tag->len, tag->x, tag->len );
|
||||
mbedtls_free( output );
|
||||
output = NULL;
|
||||
|
||||
ok = 1;
|
||||
exit:
|
||||
mbedtls_free( output );
|
||||
return( ok );
|
||||
}
|
||||
|
||||
/* END_HEADER */
|
||||
|
||||
/* BEGIN_DEPENDENCIES
|
||||
@ -43,6 +100,7 @@ void gcm_encrypt_and_tag( int cipher_id, data_t * key_str,
|
||||
unsigned char tag_output[16];
|
||||
mbedtls_gcm_context ctx;
|
||||
size_t tag_len = tag_len_bits / 8;
|
||||
size_t n1;
|
||||
|
||||
mbedtls_gcm_init( &ctx );
|
||||
|
||||
@ -55,10 +113,18 @@ void gcm_encrypt_and_tag( int cipher_id, data_t * key_str,
|
||||
{
|
||||
TEST_ASSERT( mbedtls_gcm_crypt_and_tag( &ctx, MBEDTLS_GCM_ENCRYPT, src_str->len, iv_str->x, iv_str->len, add_str->x, add_str->len, src_str->x, output, tag_len, tag_output ) == 0 );
|
||||
|
||||
TEST_ASSERT( mbedtls_test_hexcmp( output, dst->x,
|
||||
src_str->len, dst->len ) == 0 );
|
||||
TEST_ASSERT( mbedtls_test_hexcmp( tag_output, tag->x,
|
||||
tag_len, tag->len ) == 0 );
|
||||
ASSERT_COMPARE( output, src_str->len, dst->x, dst->len );
|
||||
ASSERT_COMPARE( tag_output, tag_len, tag->x, tag->len );
|
||||
|
||||
for( n1 = 0; n1 <= src_str->len; n1 += 1 )
|
||||
{
|
||||
mbedtls_test_set_step( n1 );
|
||||
if( !check_multipart( &ctx, MBEDTLS_GCM_ENCRYPT,
|
||||
iv_str, add_str, src_str,
|
||||
dst, tag,
|
||||
n1 ) )
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
@ -77,6 +143,7 @@ void gcm_decrypt_and_verify( int cipher_id, data_t * key_str,
|
||||
mbedtls_gcm_context ctx;
|
||||
int ret;
|
||||
size_t tag_len = tag_len_bits / 8;
|
||||
size_t n1;
|
||||
|
||||
mbedtls_gcm_init( &ctx );
|
||||
|
||||
@ -95,10 +162,17 @@ void gcm_decrypt_and_verify( int cipher_id, data_t * key_str,
|
||||
else
|
||||
{
|
||||
TEST_ASSERT( ret == 0 );
|
||||
ASSERT_COMPARE( output, src_str->len, pt_result->x, pt_result->len );
|
||||
|
||||
TEST_ASSERT( mbedtls_test_hexcmp( output, pt_result->x,
|
||||
src_str->len,
|
||||
pt_result->len ) == 0 );
|
||||
for( n1 = 0; n1 <= src_str->len; n1 += 1 )
|
||||
{
|
||||
mbedtls_test_set_step( n1 );
|
||||
if( !check_multipart( &ctx, MBEDTLS_GCM_DECRYPT,
|
||||
iv_str, add_str, src_str,
|
||||
pt_result, tag_str,
|
||||
n1 ) )
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,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 );
|
||||
|
||||
@ -225,42 +300,44 @@ 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() */
|
||||
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(
|
||||
MBEDTLS_ERR_GCM_BAD_INPUT,
|
||||
mbedtls_gcm_finish( NULL, valid_buffer, valid_len ) );
|
||||
mbedtls_gcm_finish( NULL, NULL, 0, valid_buffer, valid_len ) );
|
||||
TEST_INVALID_PARAM_RET(
|
||||
MBEDTLS_ERR_GCM_BAD_INPUT,
|
||||
mbedtls_gcm_finish( &ctx, NULL, valid_len ) );
|
||||
mbedtls_gcm_finish( &ctx, NULL, 0, NULL, valid_len ) );
|
||||
|
||||
exit:
|
||||
mbedtls_gcm_free( &ctx );
|
||||
|
Loading…
x
Reference in New Issue
Block a user