diff --git a/library/gcm.c b/library/gcm.c index 300521ecdd..b3105d80d6 100644 --- a/library/gcm.c +++ b/library/gcm.c @@ -355,21 +355,64 @@ 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 ) { 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]; + + /* Exit early if length==0 so that we don't do any pointer arithmetic on + * a potentially null pointer. */ + if( 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 ) return( MBEDTLS_ERR_GCM_BAD_INPUT ); @@ -382,39 +425,48 @@ int mbedtls_gcm_update( mbedtls_gcm_context *ctx, 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 > length ) + use_len = 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]; - } - - gcm_mult( ctx, ctx->buf, ctx->buf ); + if( offset + use_len == 16 ) + gcm_mult( ctx, ctx->buf, ctx->buf ); + ctx->len += use_len; length -= use_len; p += use_len; out_p += use_len; } + ctx->len += length; + + while( 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 ); + + length -= 16; + p += 16; + out_p += 16; + } + + if( length > 0 ) + { + gcm_incr( ctx->y ); + if( ( ret = gcm_mask( ctx, ectr, 0, length, p, out_p ) ) != 0 ) + return( ret ); + } + + mbedtls_platform_zeroize( ectr, sizeof( ectr ) ); return( 0 ); } @@ -436,6 +488,9 @@ int mbedtls_gcm_finish( mbedtls_gcm_context *ctx, 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 ) diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function index c2f353496e..d4dce9398c 100644 --- a/tests/suites/test_suite_gcm.function +++ b/tests/suites/test_suite_gcm.function @@ -111,7 +111,7 @@ void gcm_encrypt_and_tag( int cipher_id, data_t * key_str, ASSERT_COMPARE( output, src_str->len, dst->x, dst->len ); ASSERT_COMPARE( tag_output, tag_len, tag->x, tag->len ); - for( n1 = 16; n1 < src_str->len; n1 += 16 ) + for( n1 = 0; n1 <= src_str->len; n1 += 1 ) { mbedtls_test_set_step( n1 ); if( !check_multipart( &ctx, MBEDTLS_GCM_ENCRYPT, @@ -159,7 +159,7 @@ void gcm_decrypt_and_verify( int cipher_id, data_t * key_str, TEST_ASSERT( ret == 0 ); ASSERT_COMPARE( output, src_str->len, pt_result->x, pt_result->len ); - for( n1 = 16; n1 < src_str->len; n1 += 16 ) + for( n1 = 0; n1 <= src_str->len; n1 += 1 ) { mbedtls_test_set_step( n1 ); if( !check_multipart( &ctx, MBEDTLS_GCM_DECRYPT,