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/include/mbedtls/chachapoly.h b/include/mbedtls/chachapoly.h
index 810675d..e7413b3 100644
--- a/include/mbedtls/chachapoly.h
+++ b/include/mbedtls/chachapoly.h
@@ -31,6 +31,8 @@
#define MBEDTLS_ERR_CHACHAPOLY_BAD_INPUT_DATA -0x00047 /**< Invalid input parameter(s). */
#define MBEDTLS_ERR_CHACHAPOLY_BAD_STATE -0x00049 /**< The requested operation is not permitted in the current state */
+#define MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED -0x00049 /**< Authenticated decryption failed: data was not authentic. */
+
#ifdef __cplusplus
extern "C" {
@@ -192,37 +194,64 @@
unsigned char mac[16] );
/**
- * \brief Encrypt or decrypt data, and produce a MAC with ChaCha20-Poly1305.
+ * \brief Encrypt or decrypt data, and produce a MAC (tag) with ChaCha20-Poly1305.
*
- * \param key The 256-bit (32 bytes) encryption key to use.
- * \param nonce The 96-bit (12 bytes) nonce/IV to use.
+ * \param ctx The ChachaPoly context.
* \param mode Specifies whether the data in the \p input buffer is to
* be encrypted or decrypted. If there is no data to encrypt
* or decrypt (i.e. \p ilen is 0) then the value of this
* parameter does not matter.
- * \param aad_len The length (in bytes) of the AAD data to process.
+ * \param length The length (in bytes) of the data to encrypt or decrypt.
+ * \param nonce The 96-bit (12 bytes) nonce/IV to use.
* \param aad Buffer containing the additional authenticated data (AAD).
* This pointer can be NULL if aad_len == 0.
- * \param ilen The length (in bytes) of the data to encrypt or decrypt.
+ * \param aad_len The length (in bytes) of the AAD data to process.
* \param input Buffer containing the data to encrypt or decrypt.
* This pointer can be NULL if ilen == 0.
* \param output Buffer to where the encrypted or decrypted data is written.
* This pointer can be NULL if ilen == 0.
- * \param mac Buffer to where the computed 128-bit (16 bytes) MAC is written.
+ * \param tag Buffer to where the computed 128-bit (16 bytes) MAC is written.
*
* \return MBEDTLS_ERR_CHACHAPOLY_BAD_INPUT_DATA is returned
* if one or more of the required parameters are NULL.
* Otherwise, 0 is returned to indicate success.
*/
-int mbedtls_chachapoly_crypt_and_mac( const unsigned char key[32],
- const unsigned char nonce[12],
+int mbedtls_chachapoly_crypt_and_tag( mbedtls_chachapoly_context *ctx,
mbedtls_chachapoly_mode_t mode,
- size_t aad_len,
+ size_t length,
+ const unsigned char nonce[12],
const unsigned char *aad,
- size_t ilen,
+ size_t aad_len,
const unsigned char *input,
unsigned char *output,
- unsigned char mac[16] );
+ unsigned char tag[16] );
+
+/**
+ * \brief Decrypt data and check a MAC (tag) with ChaCha20-Poly1305.
+ *
+ * \param ctx The ChachaPoly context.
+ * \param length The length of the input and output data.
+ * \param nonce The nonce / initialization vector.
+ * \param aad The buffer holding the additional authenticated data.
+ * \param aad_len The length of the additional authenticated data.
+ * \param tag The buffer holding the tag.
+ * \param input The buffer holding the input data.
+ * \param output The buffer for holding the output data.
+ *
+ * \return MBEDTLS_ERR_CHACHAPOLY_BAD_INPUT_DATA is returned
+ * if one or more of the required parameters are NULL.
+ * MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED if the tag does not
+ * match.
+ * Otherwise, 0 is returned to indicate success.
+ */
+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 );
/**
* \brief Checkup routine
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" );
diff --git a/tests/suites/test_suite_chachapoly.function b/tests/suites/test_suite_chachapoly.function
index fb1a738..b205c4c 100644
--- a/tests/suites/test_suite_chachapoly.function
+++ b/tests/suites/test_suite_chachapoly.function
@@ -24,6 +24,7 @@
size_t key_len;
size_t nonce_len;
size_t mac_len;
+ mbedtls_chachapoly_context ctx;
memset( key_str, 0x00, 32 );
memset( nonce_str, 0x00, 12 );
@@ -43,14 +44,21 @@
TEST_ASSERT( nonce_len == 12 );
TEST_ASSERT( mac_len == 16 );
- mbedtls_chachapoly_crypt_and_mac( key_str, nonce_str,
+ mbedtls_chachapoly_init( &ctx );
+
+ mbedtls_chachapoly_setkey( &ctx, key_str );
+
+ mbedtls_chachapoly_crypt_and_tag( &ctx,
MBEDTLS_CHACHAPOLY_ENCRYPT,
- aad_len, aad_str,
- input_len, input_str, output,
- mac );
+ input_len, nonce_str,
+ aad_str, aad_len,
+ input_str, output, mac );
TEST_ASSERT( memcmp( output_str, output, output_len ) == 0 );
TEST_ASSERT( memcmp( mac_str, mac, 16U ) == 0 );
+
+exit:
+ mbedtls_chachapoly_free( &ctx );
}
/* END_CASE */
@@ -64,13 +72,14 @@
unsigned char output_str[10000];
unsigned char mac_str[16];
unsigned char output[10000];
- unsigned char mac[16];
size_t input_len;
size_t output_len;
size_t aad_len;
size_t key_len;
size_t nonce_len;
size_t mac_len;
+ int ret;
+ mbedtls_chachapoly_context ctx;
memset( key_str, 0x00, 32 );
memset( nonce_str, 0x00, 12 );
@@ -90,14 +99,20 @@
TEST_ASSERT( nonce_len == 12 );
TEST_ASSERT( mac_len == 16 );
- mbedtls_chachapoly_crypt_and_mac( key_str, nonce_str,
- MBEDTLS_CHACHAPOLY_DECRYPT,
- aad_len, aad_str,
- input_len, input_str, output,
- mac );
+ mbedtls_chachapoly_init( &ctx );
+ mbedtls_chachapoly_setkey( &ctx, key_str );
+
+ ret = mbedtls_chachapoly_auth_decrypt( &ctx,
+ input_len, nonce_str,
+ aad_str, aad_len,
+ mac_str, input_str, output );
+
+ TEST_ASSERT( ret == 0 );
TEST_ASSERT( memcmp( output_str, output, output_len ) == 0 );
- TEST_ASSERT( memcmp( mac_str, mac, 16U ) == 0 );
+
+exit:
+ mbedtls_chachapoly_free( &ctx );
}
/* END_CASE */