Remove alignment requirement for mbedtls_gcm_update: implementation
mbedtls_gcm_update now accepts inputs of arbitrary size. There is no
longer a requirement that all calls except the last one pass a
multiple of 16 bytes.
This commit updates the library code and adjusts the GCM tests to
exercise arbitrarily aligned input sizes.
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/library/gcm.c b/library/gcm.c
index 300521e..b3105d8 100644
--- a/library/gcm.c
+++ b/library/gcm.c
@@ -355,21 +355,64 @@
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 @@
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];
- }
+ if( offset + use_len == 16 )
+ gcm_mult( ctx, ctx->buf, ctx->buf );
- 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 @@
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 c2f3534..d4dce93 100644
--- a/tests/suites/test_suite_gcm.function
+++ b/tests/suites/test_suite_gcm.function
@@ -111,7 +111,7 @@
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 @@
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,