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.
diff --git a/ChangeLog.d/gcm-update.txt b/ChangeLog.d/gcm-update.txt
new file mode 100644
index 0000000..0fffd09
--- /dev/null
+++ b/ChangeLog.d/gcm-update.txt
@@ -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.
diff --git a/docs/3.0-migration-guide.d/gcm-alt.md b/docs/3.0-migration-guide.d/gcm-alt.md
new file mode 100644
index 0000000..388e2be
--- /dev/null
+++ b/docs/3.0-migration-guide.d/gcm-alt.md
@@ -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.
diff --git a/docs/3.0-migration-guide.d/gcm-multipart.md b/docs/3.0-migration-guide.d/gcm-multipart.md
new file mode 100644
index 0000000..98e9fad
--- /dev/null
+++ b/docs/3.0-migration-guide.d/gcm-multipart.md
@@ -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.
diff --git a/include/mbedtls/cipher.h b/include/mbedtls/cipher.h
index aacceda..ffb20a6 100644
--- a/include/mbedtls/cipher.h
+++ b/include/mbedtls/cipher.h
@@ -724,11 +724,6 @@
  *                      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
diff --git a/include/mbedtls/gcm.h b/include/mbedtls/gcm.h
index 7636639..a2fc2ab 100644
--- a/include/mbedtls/gcm.h
+++ b/include/mbedtls/gcm.h
@@ -227,6 +227,32 @@
  * \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 @@
  *
  * \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 @@
  *                  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
diff --git a/library/cipher.c b/library/cipher.c
index 18ab710..4f56b52 100644
--- a/library/cipher.c
+++ b/library/cipher.c
@@ -415,6 +415,15 @@
     }
 #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 @@
 #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 @@
 #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 @@
 #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 @@
 
         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 300521e..2bd9071 100644
--- a/library/gcm.c
+++ b/library/gcm.c
@@ -269,11 +269,8 @@
 }
 
 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 @@
 
     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 @@
         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 @@
     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 @@
     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 @@
                        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 @@
     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 @@
     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 @@
                 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 @@
                 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 @@
             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;
 
diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function
index 9b7b0ee..9733eb2 100644
--- a/tests/suites/test_suite_gcm.function
+++ b/tests/suites/test_suite_gcm.function
@@ -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 @@
     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 @@
     {
         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 @@
     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 @@
         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 @@
     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 @@
     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 );