chachapoly: split crypt_and_mac() to match GCM API

In addition to making the APIs of the various AEAD modules more consistent
with each other, it's useful to have an auth_decrypt() function so that we can
safely check the tag ourselves, as the user might otherwise do it in an
insecure way (or even forget to do it altogether).
diff --git a/library/chachapoly.c b/library/chachapoly.c
index 35ae99e..0dba5ed 100644
--- a/library/chachapoly.c
+++ b/library/chachapoly.c
@@ -295,44 +295,70 @@
     return( 0 );
 }
 
-int mbedtls_chachapoly_crypt_and_mac ( const unsigned char key[32],
-                                       const unsigned char nonce[12],
-                                       mbedtls_chachapoly_mode_t mode,
-                                       size_t aad_len,
-                                       const unsigned char *aad,
-                                       size_t ilen,
-                                       const unsigned char *input,
-                                       unsigned char *output,
-                                       unsigned char mac[16] )
+int mbedtls_chachapoly_crypt_and_tag( mbedtls_chachapoly_context *ctx,
+                                      mbedtls_chachapoly_mode_t mode,
+                                      size_t length,
+                                      const unsigned char nonce[12],
+                                      const unsigned char *aad,
+                                      size_t aad_len,
+                                      const unsigned char *input,
+                                      unsigned char *output,
+                                      unsigned char tag[16] )
 {
-    mbedtls_chachapoly_context ctx;
     int result;
 
-    mbedtls_chachapoly_init( &ctx );
-
-    result = mbedtls_chachapoly_setkey( &ctx, key );
+    result = mbedtls_chachapoly_starts( ctx, nonce, mode );
     if ( result != 0 )
         goto cleanup;
 
-    result = mbedtls_chachapoly_starts( &ctx, nonce, mode );
-    if ( result != 0 )
-        goto cleanup;
-
-    result = mbedtls_chachapoly_update_aad( &ctx, aad_len, aad );
+    result = mbedtls_chachapoly_update_aad( ctx, aad_len, aad );
     if ( result != 0 )
             goto cleanup;
 
-    result = mbedtls_chachapoly_update( &ctx, ilen, input, output );
+    result = mbedtls_chachapoly_update( ctx, length, input, output );
     if ( result != 0 )
             goto cleanup;
 
-    result = mbedtls_chachapoly_finish( &ctx, mac );
+    result = mbedtls_chachapoly_finish( ctx, tag );
 
 cleanup:
-    mbedtls_chachapoly_free( &ctx );
     return( result );
 }
 
+int mbedtls_chachapoly_auth_decrypt( mbedtls_chachapoly_context *ctx,
+                                     size_t length,
+                                     const unsigned char nonce[12],
+                                     const unsigned char *aad,
+                                     size_t aad_len,
+                                     const unsigned char tag[16],
+                                     const unsigned char *input,
+                                     unsigned char *output )
+{
+    int ret;
+    unsigned char check_tag[16];
+    size_t i;
+    int diff;
+
+    if( ( ret = mbedtls_chachapoly_crypt_and_tag( ctx,
+                        MBEDTLS_CHACHAPOLY_DECRYPT, length, nonce,
+                        aad, aad_len, input, output, check_tag ) ) != 0 )
+    {
+        return( ret );
+    }
+
+    /* Check tag in "constant-time" */
+    for( diff = 0, i = 0; i < sizeof( check_tag ); i++ )
+        diff |= tag[i] ^ check_tag[i];
+
+    if( diff != 0 )
+    {
+        mbedtls_zeroize( output, length );
+        return( MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED );
+    }
+
+    return( 0 );
+}
+
 #endif /* MBEDTLS_CHACHAPOLY_ALT */
 
 #if defined(MBEDTLS_SELF_TEST)
@@ -425,6 +451,7 @@
 
 int mbedtls_chachapoly_self_test( int verbose )
 {
+    mbedtls_chachapoly_context ctx;
     unsigned i;
     int result;
     unsigned char output[200];
@@ -437,12 +464,24 @@
             mbedtls_printf( "  ChaCha20-Poly1305 test %u ", i );
         }
 
-        result = mbedtls_chachapoly_crypt_and_mac( test_key[i],
-                                                   test_nonce[i],
+        mbedtls_chachapoly_init( &ctx );
+
+        result = mbedtls_chachapoly_setkey( &ctx, test_key[i] );
+        if ( result != 0 )
+        {
+            if ( verbose != 0 )
+            {
+                mbedtls_printf( "setkey() error code: %i\n", result );
+            }
+            return( -1 );
+        }
+
+        result = mbedtls_chachapoly_crypt_and_tag( &ctx,
                                                    MBEDTLS_CHACHAPOLY_ENCRYPT,
-                                                   test_aad_len[i],
-                                                   test_aad[i],
                                                    test_input_len[i],
+                                                   test_nonce[i],
+                                                   test_aad[i],
+                                                   test_aad_len[i],
                                                    test_input[i],
                                                    output,
                                                    mac );
@@ -450,7 +489,7 @@
         {
             if ( verbose != 0 )
             {
-                mbedtls_printf( "error code: %i\n", result );
+                mbedtls_printf( "crypt_and_tag() error code: %i\n", result );
             }
             return( -1 );
         }
@@ -473,6 +512,8 @@
             return( -1 );
         }
 
+        mbedtls_chachapoly_free( &ctx );
+
         if ( verbose != 0 )
         {
             mbedtls_printf( "passed\n" );