Test the multipart GCM interface

The existing GCM test suite only exercises the one-shot API. Also test
the multipart interface: systematically run it on the same test data,
with the input (plaintext or ciphertext) split in two parts.

Given the current limitations of the GCM API, the associated data is
always passed in a single shot to mbedtls_gcm_starts(), and the first
part of the input is a nonzero multiple of 16.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/tests/suites/test_suite_gcm.function b/tests/suites/test_suite_gcm.function
index 9b7b0ee..c2f3534 100644
--- a/tests/suites/test_suite_gcm.function
+++ b/tests/suites/test_suite_gcm.function
@@ -1,5 +1,57 @@
 /* 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;
+
+    /* 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,
+                                         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 );
+    TEST_EQUAL( 0, mbedtls_gcm_update( ctx, n1, input->x, output ) );
+    ASSERT_COMPARE( output, n1, expected_output->x, n1 );
+    mbedtls_free( output );
+    output = NULL;
+
+    ASSERT_ALLOC( output, n2 );
+    TEST_EQUAL( 0, mbedtls_gcm_update( ctx, n2, input->x + n1, output ) );
+    ASSERT_COMPARE( output, n2, expected_output->x + n1, n2 );
+    mbedtls_free( output );
+    output = NULL;
+
+    ASSERT_ALLOC( output, tag->len );
+    TEST_EQUAL( 0, mbedtls_gcm_finish( ctx, 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 +95,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 +108,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 = 16; n1 < src_str->len; n1 += 16 )
+        {
+            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 +138,7 @@
     mbedtls_gcm_context ctx;
     int ret;
     size_t tag_len = tag_len_bits / 8;
+    size_t n1;
 
     mbedtls_gcm_init( &ctx );
 
@@ -95,10 +157,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 = 16; n1 < src_str->len; n1 += 16 )
+            {
+                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;
+            }
         }
     }