Add output parameter to mbedtls_gcm_finish
Alternative implementations of GCM may delay the output of partial
blocks from mbedtls_gcm_update(). Add an output parameter to
mbedtls_gcm_finish() to allow such implementations to pass the final
partial block back to the caller. With the software implementation,
this final output is always empty.
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/ChangeLog.d/gcm-update.txt b/ChangeLog.d/gcm-update.txt
index 4511fec..d384551 100644
--- a/ChangeLog.d/gcm-update.txt
+++ b/ChangeLog.d/gcm-update.txt
@@ -1,3 +1,13 @@
+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.
+ 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().
+ 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
diff --git a/include/mbedtls/gcm.h b/include/mbedtls/gcm.h
index 8a26ebb..951ee00 100644
--- a/include/mbedtls/gcm.h
+++ b/include/mbedtls/gcm.h
@@ -282,13 +282,23 @@
* 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.
+ * This must be a writable buffer of at least \p output_len
+ * bytes.
+ * With the built-in implementation, there is no final
+ * output and this can be \p NULL.
+ * Alternative implementations may return a partial block
+ * of output.
+ * \param output_len The size of the \p output buffer in bytes.
+ * With the built-in implementation, this can be \c 0.
+ * Alternative implementations may require a 15-byte buffer.
*
* \return \c 0 on success.
* \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
*/
int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
- unsigned char *tag,
- size_t tag_len );
+ unsigned char *output, size_t output_len,
+ unsigned char *tag, size_t tag_len );
/**
* \brief This function clears a GCM context and the underlying
diff --git a/library/cipher.c b/library/cipher.c
index c88d666..63eaba8 100644
--- a/library/cipher.c
+++ b/library/cipher.c
@@ -1101,6 +1101,7 @@
#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 +1154,7 @@
if( 0 != ( ret = mbedtls_gcm_finish(
(mbedtls_gcm_context *) ctx->cipher_ctx,
+ NULL, 0,
check_tag, tag_len ) ) )
{
return( ret );
diff --git a/library/gcm.c b/library/gcm.c
index b3105d8..de766bc 100644
--- a/library/gcm.c
+++ b/library/gcm.c
@@ -471,8 +471,8 @@
}
int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
- unsigned char *tag,
- size_t tag_len )
+ unsigned char *output, size_t output_len,
+ unsigned char *tag, size_t tag_len )
{
unsigned char work_buf[16];
size_t i;
@@ -482,6 +482,11 @@
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_len;
+
orig_len = ctx->len * 8;
orig_add_len = ctx->add_len * 8;
@@ -541,7 +546,7 @@
if( ( ret = mbedtls_gcm_update( ctx, length, input, output ) ) != 0 )
return( ret );
- if( ( ret = mbedtls_gcm_finish( ctx, tag, tag_len ) ) != 0 )
+ if( ( ret = mbedtls_gcm_finish( ctx, NULL, 0, tag, tag_len ) ) != 0 )
return( ret );
return( 0 );
@@ -979,7 +984,7 @@
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;
@@ -1039,7 +1044,7 @@
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;
diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function
index d4dce93..965d154 100644
--- a/tests/suites/test_suite_gcm.function
+++ b/tests/suites/test_suite_gcm.function
@@ -41,7 +41,7 @@
output = NULL;
ASSERT_ALLOC( output, tag->len );
- TEST_EQUAL( 0, mbedtls_gcm_finish( ctx, 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;
@@ -326,10 +326,10 @@
/* 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 );