Split mbedtls_gcm_update_ad out of mbedtls_gcm_starts

The GCM interface now has separate functions to start the operation
and to pass the associated data.

This is in preparation for allowing the associated data to be passed
in chunks with repeatated calls to mbedtls_gcm_update_ad().

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/ChangeLog.d/gcm-update.txt b/ChangeLog.d/gcm-update.txt
index 10d53ef..0fffd09 100644
--- a/ChangeLog.d/gcm-update.txt
+++ b/ChangeLog.d/gcm-update.txt
@@ -6,7 +6,9 @@
      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().
+     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
diff --git a/include/mbedtls/gcm.h b/include/mbedtls/gcm.h
index 0bd6e1e..7218081 100644
--- a/include/mbedtls/gcm.h
+++ b/include/mbedtls/gcm.h
@@ -231,6 +231,26 @@
  * \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 starts a GCM encryption or decryption
+ *                  operation.
+ *
+ * \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,
@@ -238,12 +258,9 @@
  *
  * \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
diff --git a/library/cipher.c b/library/cipher.c
index 7e6d0e0..e09130a 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
 
diff --git a/library/gcm.c b/library/gcm.c
index 13e7296..ee10093 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 )
@@ -546,7 +558,10 @@
     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_ad( ctx, add, add_len ) ) != 0 )
         return( ret );
 
     if( ( ret = mbedtls_gcm_update( ctx, input, length,
@@ -961,10 +976,14 @@
                 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;
 
@@ -1031,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 )
diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function
index da6aea8..9733eb2 100644
--- a/tests/suites/test_suite_gcm.function
+++ b/tests/suites/test_suite_gcm.function
@@ -23,8 +23,8 @@
     TEST_EQUAL( input->len, expected_output->len );
 
     TEST_EQUAL( 0, mbedtls_gcm_starts( ctx, mode,
-                                         iv->x, iv->len,
-                                         add->x, add->len ) );
+                                         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
@@ -300,19 +300,17 @@
     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() */