diff --git a/include/polarssl/cipher.h b/include/polarssl/cipher.h index 67ca28ccf..ef037be5d 100644 --- a/include/polarssl/cipher.h +++ b/include/polarssl/cipher.h @@ -320,7 +320,7 @@ static inline cipher_mode_t cipher_get_cipher_mode( const cipher_context_t *ctx * \param ctx cipher's context. Must have been initialised. * * \return size of the cipher's IV, or 0 if ctx has not been - * initialised. + * initialised or accepts IV of various sizes. */ static inline int cipher_get_iv_size( const cipher_context_t *ctx ) { @@ -432,11 +432,18 @@ int cipher_set_padding_mode( cipher_context_t *ctx, cipher_padding_t mode ); * * \param ctx generic cipher context * \param iv IV to use or NONCE_COUNTER in the case of a CTR-mode cipher + * \param iv_len IV length for ciphers with variable-size IV, + * Discared by ciphers with fixed-size IV. + * \param ad Additional data for AEAD ciphers, or discarded. + * May be NULL only if ad_len is 0. + * \param ad_len Length of ad for AEAD ciphers, or discarded. * * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA * if parameter verification fails. */ -int cipher_reset( cipher_context_t *ctx, const unsigned char *iv ); +int cipher_reset( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len ); /** * \brief Generic cipher update function. Encrypts/decrypts @@ -471,8 +478,13 @@ int cipher_update( cipher_context_t *ctx, const unsigned char *input, size_t ile * the last block, and written to the output buffer. * * \param ctx Generic cipher context - * \param output buffer to write data to. Needs block_size data available. + * \param output buffer to write data to. Needs block_size available. * \param olen length of the data written to the output buffer. + * \param tag Ignore by non-AEAD ciphers. For AEAD ciphers: + * - on encryption: buffer to write the tag; + * - on decryption: tag to verify. + * May be NULL if tag_len is zero. + * \param tag_len Length of the tag to write/check for AEAD ciphers. * * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if * parameter verification fails, @@ -481,7 +493,9 @@ int cipher_update( cipher_context_t *ctx, const unsigned char *input, size_t ile * POLARSSL_ERR_CIPHER_INVALID_PADDING on invalid padding * while decrypting or a cipher specific error code. */ -int cipher_finish( cipher_context_t *ctx, unsigned char *output, size_t *olen); +int cipher_finish( cipher_context_t *ctx, + unsigned char *output, size_t *olen, + unsigned char *tag, size_t tag_len ); /** * \brief Checkup routine diff --git a/include/polarssl/gcm.h b/include/polarssl/gcm.h index 2bed34288..dc058dcd7 100644 --- a/include/polarssl/gcm.h +++ b/include/polarssl/gcm.h @@ -146,7 +146,7 @@ int gcm_auth_decrypt( gcm_context *ctx, * \param mode GCM_ENCRYPT or GCM_DECRYPT * \param iv initialization vector * \param iv_len length of IV - * \param add additional data + * \param add additional data (or NULL if length is 0) * \param add_len length of additional data * * \return 0 if successful @@ -182,14 +182,14 @@ int gcm_update( gcm_context *ctx, /** * \brief Generic GCM finalisation function. Wraps up the GCM stream - * and generated the tag. The tag can have a maximum length of + * and generates the tag. The tag can have a maximum length of * 16 bytes. * * \param ctx GCM context - * \param tag buffer for holding the tag + * \param tag buffer for holding the tag (may be NULL if tag_len is 0) * \param tag_len length of the tag to generate * - * \return 0 if successful or POLARSSL_ERR_GCM_BAD_INPUT + * \return 0 if successful or POLARSSL_ERR_GCM_BAD_INPUT */ int gcm_finish( gcm_context *ctx, unsigned char *tag, diff --git a/library/cipher.c b/library/cipher.c index 733f6e5f9..d7cac05f0 100644 --- a/library/cipher.c +++ b/library/cipher.c @@ -396,7 +396,9 @@ int cipher_setkey( cipher_context_t *ctx, const unsigned char *key, return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; } -int cipher_reset( cipher_context_t *ctx, const unsigned char *iv ) +int cipher_reset( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len ) { if( NULL == ctx || NULL == ctx->cipher_info || NULL == iv ) return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; @@ -406,11 +408,13 @@ int cipher_reset( cipher_context_t *ctx, const unsigned char *iv ) #if defined(POLARSSL_GCM_C) if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) { - // TODO: allow other IV length - // TODO: allow additional data return gcm_starts( ctx->cipher_ctx, ctx->operation, - iv, 12, (unsigned char *) "", 0 ); + iv, iv_len, ad, ad_len ); } +#else + ((void) ad); + ((void) ad_len); + ((void) iv_len); #endif memcpy( ctx->iv, iv, cipher_get_iv_size( ctx ) ); @@ -742,7 +746,9 @@ static int get_no_padding( unsigned char *input, size_t input_len, return 0; } -int cipher_finish( cipher_context_t *ctx, unsigned char *output, size_t *olen) +int cipher_finish( cipher_context_t *ctx, + unsigned char *output, size_t *olen, + unsigned char *tag, size_t tag_len ) { int ret = 0; @@ -761,8 +767,9 @@ int cipher_finish( cipher_context_t *ctx, unsigned char *output, size_t *olen) #if defined(POLARSSL_GCM_C) if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) { - size_t tag_len = 0; // TODO - unsigned char tag[16]; + unsigned char check_tag[16]; + size_t i; + int diff; if( 0 != ( ret = gcm_update( ctx->cipher_ctx, ctx->unprocessed_len, ctx->unprocessed_data, @@ -773,11 +780,29 @@ int cipher_finish( cipher_context_t *ctx, unsigned char *output, size_t *olen) *olen += ctx->unprocessed_len; - if( 0 != ( ret = gcm_finish( ctx->cipher_ctx, tag, tag_len ) ) ) + if( 0 != ( ret = gcm_finish( ctx->cipher_ctx, check_tag, tag_len ) ) ) return( ret ); + /* On encryption, write the tag */ + if( POLARSSL_ENCRYPT == ctx->operation ) + { + if( tag_len != 0 ) + memcpy( tag, check_tag, tag_len ); + return( 0 ); + } + + /* On decryption, check the tag (in "constant-time") */ + for( diff = 0, i = 0; i < tag_len; i++ ) + diff |= tag[i] ^ check_tag[i]; + + if( diff != 0 ) + return( POLARSSL_ERR_GCM_AUTH_FAILED ); + return( 0 ); } +#else + ((void) tag); + ((void) tag_len); #endif if( POLARSSL_MODE_CBC == ctx->cipher_info->mode ) diff --git a/library/cipher_wrap.c b/library/cipher_wrap.c index c8eee5432..5c9810056 100644 --- a/library/cipher_wrap.c +++ b/library/cipher_wrap.c @@ -271,7 +271,7 @@ const cipher_info_t aes_128_gcm_info = { POLARSSL_MODE_GCM, 128, "AES-128-GCM", - 16, + 0, 16, &gcm_aes_info }; @@ -281,7 +281,7 @@ const cipher_info_t aes_256_gcm_info = { POLARSSL_MODE_GCM, 256, "AES-256-GCM", - 16, + 0, 16, &gcm_aes_info }; diff --git a/library/gcm.c b/library/gcm.c index 104fda3a4..99036a05c 100644 --- a/library/gcm.c +++ b/library/gcm.c @@ -296,7 +296,8 @@ int gcm_finish( gcm_context *ctx, if( tag_len > 16 ) return( POLARSSL_ERR_GCM_BAD_INPUT ); - memcpy( tag, ctx->base_ectr, tag_len ); + if( tag_len != 0 ) + memcpy( tag, ctx->base_ectr, tag_len ); if( orig_len || orig_add_len ) { diff --git a/library/pkcs12.c b/library/pkcs12.c index e0d7207ce..9ccb60ab7 100644 --- a/library/pkcs12.c +++ b/library/pkcs12.c @@ -184,7 +184,7 @@ int pkcs12_pbe( asn1_buf *pbe_params, int mode, if( ( ret = cipher_setkey( &cipher_ctx, key, keylen, mode ) ) != 0 ) goto exit; - if( ( ret = cipher_reset( &cipher_ctx, iv ) ) != 0 ) + if( ( ret = cipher_reset( &cipher_ctx, iv, 0, NULL, 0 ) ) != 0 ) goto exit; if( ( ret = cipher_update( &cipher_ctx, data, len, @@ -193,8 +193,11 @@ int pkcs12_pbe( asn1_buf *pbe_params, int mode, goto exit; } - if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen ) ) != 0 ) + if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen, NULL, 0 ) ) + != 0 ) + { ret = POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH; + } exit: cipher_free_ctx( &cipher_ctx ); diff --git a/library/pkcs5.c b/library/pkcs5.c index 9e3ce9332..2b6a75a92 100644 --- a/library/pkcs5.c +++ b/library/pkcs5.c @@ -187,7 +187,7 @@ int pkcs5_pbes2( asn1_buf *pbe_params, int mode, if( ( ret = cipher_setkey( &cipher_ctx, key, keylen, mode ) ) != 0 ) goto exit; - if( ( ret = cipher_reset( &cipher_ctx, iv ) ) != 0 ) + if( ( ret = cipher_reset( &cipher_ctx, iv, 0, NULL, 0 ) ) != 0 ) goto exit; if( ( ret = cipher_update( &cipher_ctx, data, datalen, @@ -196,8 +196,11 @@ int pkcs5_pbes2( asn1_buf *pbe_params, int mode, goto exit; } - if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen ) ) != 0 ) + if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen, NULL, 0 ) ) + != 0 ) + { ret = POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH; + } exit: md_free_ctx( &md_ctx ); diff --git a/programs/aes/crypt_and_hash.c b/programs/aes/crypt_and_hash.c index 47b17d523..a9b862e7a 100644 --- a/programs/aes/crypt_and_hash.c +++ b/programs/aes/crypt_and_hash.c @@ -306,7 +306,7 @@ int main( int argc, char *argv[] ) fprintf( stderr, "cipher_setkey() returned error\n"); goto exit; } - if( cipher_reset( &cipher_ctx, IV ) != 0 ) + if( cipher_reset( &cipher_ctx, IV, 16, NULL, 0 ) != 0 ) { fprintf( stderr, "cipher_reset() returned error\n"); goto exit; @@ -338,7 +338,7 @@ int main( int argc, char *argv[] ) } } - if( cipher_finish( &cipher_ctx, output, &olen ) != 0 ) + if( cipher_finish( &cipher_ctx, output, &olen, NULL, 0 ) != 0 ) { fprintf( stderr, "cipher_finish() returned error\n" ); goto exit; @@ -424,7 +424,7 @@ int main( int argc, char *argv[] ) cipher_setkey( &cipher_ctx, digest, cipher_info->key_length, POLARSSL_DECRYPT ); - cipher_reset( &cipher_ctx, IV); + cipher_reset( &cipher_ctx, IV, 16, NULL, 0 ); md_hmac_starts( &md_ctx, digest, 32 ); @@ -455,7 +455,7 @@ int main( int argc, char *argv[] ) /* * Write the final block of data */ - cipher_finish( &cipher_ctx, output, &olen ); + cipher_finish( &cipher_ctx, output, &olen, NULL, 0 ); if( fwrite( output, 1, olen, fout ) != olen ) { diff --git a/tests/suites/test_suite_cipher.function b/tests/suites/test_suite_cipher.function index 537720872..b1814fab0 100644 --- a/tests/suites/test_suite_cipher.function +++ b/tests/suites/test_suite_cipher.function @@ -14,6 +14,8 @@ void enc_dec_buf( int cipher_id, char *cipher_string, int key_len, size_t length = length_val; unsigned char key[32]; unsigned char iv[16]; + unsigned char ad[13]; + unsigned char tag[16]; const cipher_info_t *cipher_info; cipher_context_t ctx_dec; @@ -35,6 +37,8 @@ void enc_dec_buf( int cipher_id, char *cipher_string, int key_len, memset( inbuf, 5, 64 ); memset( encbuf, 0, 64 ); memset( decbuf, 0, 64 ); + memset( tag, 0, 16 ); + memset( ad, 0x2a, 13 ); /* Check and get info structures */ cipher_info = cipher_info_from_type( cipher_id ); @@ -54,8 +58,8 @@ void enc_dec_buf( int cipher_id, char *cipher_string, int key_len, TEST_ASSERT( 0 == cipher_set_padding_mode( &ctx_enc, pad_mode ) ); } - TEST_ASSERT( 0 == cipher_reset( &ctx_dec, iv ) ); - TEST_ASSERT( 0 == cipher_reset( &ctx_enc, iv ) ); + TEST_ASSERT( 0 == cipher_reset( &ctx_dec, iv, 16, ad, 13 ) ); + TEST_ASSERT( 0 == cipher_reset( &ctx_enc, iv, 16, ad, 13 ) ); /* encode length number of bytes from inbuf */ TEST_ASSERT( 0 == cipher_update( &ctx_enc, inbuf, length, encbuf, &outlen ) ); @@ -66,7 +70,8 @@ void enc_dec_buf( int cipher_id, char *cipher_string, int key_len, total_len < length && total_len + cipher_get_block_size( &ctx_enc ) > length ) ); - TEST_ASSERT( 0 == cipher_finish( &ctx_enc, encbuf + outlen, &outlen ) ); + TEST_ASSERT( 0 == cipher_finish( &ctx_enc, encbuf + outlen, &outlen, + tag, 16 ) ); total_len += outlen; TEST_ASSERT( total_len == length || @@ -83,7 +88,8 @@ void enc_dec_buf( int cipher_id, char *cipher_string, int key_len, total_len < length && total_len + cipher_get_block_size( &ctx_dec ) >= length ) ); - TEST_ASSERT( 0 == cipher_finish( &ctx_dec, decbuf + outlen, &outlen ) ); + TEST_ASSERT( 0 == cipher_finish( &ctx_dec, decbuf + outlen, &outlen, + tag, 16 ) ); total_len += outlen; TEST_ASSERT( total_len == length ); @@ -127,11 +133,11 @@ void enc_fail( int cipher_id, int pad_mode, int key_len, TEST_ASSERT( 0 == cipher_init_ctx( &ctx, cipher_info ) ); TEST_ASSERT( 0 == cipher_setkey( &ctx, key, key_len, POLARSSL_ENCRYPT ) ); TEST_ASSERT( 0 == cipher_set_padding_mode( &ctx, pad_mode ) ); - TEST_ASSERT( 0 == cipher_reset( &ctx, iv ) ); + TEST_ASSERT( 0 == cipher_reset( &ctx, iv, 16, NULL, 0 ) ); /* encode length number of bytes from inbuf */ TEST_ASSERT( 0 == cipher_update( &ctx, inbuf, length, encbuf, &outlen ) ); - TEST_ASSERT( ret == cipher_finish( &ctx, encbuf + outlen, &outlen ) ); + TEST_ASSERT( ret == cipher_finish( &ctx, encbuf + outlen, &outlen, NULL, 0 ) ); /* done */ TEST_ASSERT( 0 == cipher_free_ctx( &ctx ) ); @@ -168,12 +174,13 @@ void dec_empty_buf() TEST_ASSERT( 0 == cipher_setkey( &ctx_dec, key, 128, POLARSSL_DECRYPT ) ); - TEST_ASSERT( 0 == cipher_reset( &ctx_dec, iv ) ); + TEST_ASSERT( 0 == cipher_reset( &ctx_dec, iv, 16, NULL, 0 ) ); /* decode 0-byte string */ TEST_ASSERT( 0 == cipher_update( &ctx_dec, encbuf, 0, decbuf, &outlen ) ); TEST_ASSERT( 0 == outlen ); - TEST_ASSERT( POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED == cipher_finish( &ctx_dec, decbuf + outlen, &outlen ) ); + TEST_ASSERT( POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED == cipher_finish( + &ctx_dec, decbuf + outlen, &outlen, NULL, 0 ) ); TEST_ASSERT( 0 == outlen ); TEST_ASSERT( 0 == cipher_free_ctx( &ctx_dec ) ); @@ -221,8 +228,8 @@ void enc_dec_buf_multipart( int cipher_id, int key_len, int first_length_val, TEST_ASSERT( 0 == cipher_setkey( &ctx_dec, key, key_len, POLARSSL_DECRYPT ) ); TEST_ASSERT( 0 == cipher_setkey( &ctx_enc, key, key_len, POLARSSL_ENCRYPT ) ); - TEST_ASSERT( 0 == cipher_reset( &ctx_dec, iv ) ); - TEST_ASSERT( 0 == cipher_reset( &ctx_enc, iv ) ); + TEST_ASSERT( 0 == cipher_reset( &ctx_dec, iv, 16, NULL, 0 ) ); + TEST_ASSERT( 0 == cipher_reset( &ctx_enc, iv, 16, NULL, 0 ) ); /* encode length number of bytes from inbuf */ TEST_ASSERT( 0 == cipher_update( &ctx_enc, inbuf, first_length, encbuf, &outlen ) ); @@ -234,7 +241,8 @@ void enc_dec_buf_multipart( int cipher_id, int key_len, int first_length_val, totaloutlen < length && totaloutlen + cipher_get_block_size( &ctx_enc ) > length ) ); - TEST_ASSERT( 0 == cipher_finish( &ctx_enc, encbuf + totaloutlen, &outlen ) ); + TEST_ASSERT( 0 == cipher_finish( &ctx_enc, encbuf + totaloutlen, &outlen, + NULL, 0 ) ); totaloutlen += outlen; TEST_ASSERT( totaloutlen == length || ( totaloutlen % cipher_get_block_size( &ctx_enc ) == 0 && @@ -250,7 +258,8 @@ void enc_dec_buf_multipart( int cipher_id, int key_len, int first_length_val, totaloutlen < length && totaloutlen + cipher_get_block_size( &ctx_dec ) >= length ) ); - TEST_ASSERT( 0 == cipher_finish( &ctx_dec, decbuf + outlen, &outlen ) ); + TEST_ASSERT( 0 == cipher_finish( &ctx_dec, decbuf + outlen, &outlen, + NULL, 0 ) ); totaloutlen += outlen; TEST_ASSERT( totaloutlen == length );