From 2968d306e49e914cecc5d05840a307d1a60a9c5b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 25 Feb 2022 15:09:36 +0100 Subject: [PATCH] Implement mbedtls_ct_hmac() using PSA hash API Signed-off-by: Neil Armstrong --- library/constant_time.c | 119 +++++++++++++++++++++++++++ library/constant_time_internal.h | 12 +++ tests/suites/test_suite_ssl.function | 56 +++++++++++++ 3 files changed, 187 insertions(+) diff --git a/library/constant_time.c b/library/constant_time.c index 0f2e46f6de..c3f0773628 100644 --- a/library/constant_time.c +++ b/library/constant_time.c @@ -437,6 +437,124 @@ void mbedtls_ct_memcpy_offset( unsigned char *dest, } } +#if defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_ct_hmac( mbedtls_svc_key_id_t key, + psa_algorithm_t mac_alg, + const unsigned char *add_data, + size_t add_data_len, + const unsigned char *data, + size_t data_len_secret, + size_t min_data_len, + size_t max_data_len, + unsigned char *output ) +{ + /* + * This function breaks the HMAC abstraction and uses the psa_hash_clone() + * in order to get constant-flow behaviour. + * + * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means + * concatenation, and okey/ikey are the XOR of the key with some fixed bit + * patterns (see RFC 2104, sec. 2). + * + * We'll first compute ikey/okey, then inner_hash = HASH(ikey + msg) by + * hashing up to minlen, then cloning the context, and for each byte up + * to maxlen finishing up the hash computation, keeping only the + * correct result. + * + * Then we only need to compute HASH(okey + inner_hash) and we're done. + */ + /* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5, + * all of which have the same block size except SHA-384. */ + psa_algorithm_t hash_alg = PSA_ALG_HMAC_GET_HASH( mac_alg ); + const size_t block_size = PSA_HASH_BLOCK_LENGTH( hash_alg ); + unsigned char ikey[MBEDTLS_MD_MAX_BLOCK_SIZE]; + unsigned char okey[MBEDTLS_MD_MAX_BLOCK_SIZE]; + const size_t hash_size = PSA_HASH_LENGTH( hash_alg ); + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + size_t hash_length; + + unsigned char aux_out[MBEDTLS_MD_MAX_SIZE]; + psa_hash_operation_t aux_operation = PSA_HASH_OPERATION_INIT; + size_t offset; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + unsigned char mac_key[MBEDTLS_MD_MAX_BLOCK_SIZE]; + size_t mac_key_length; + size_t i; + +#define PSA_CHK( func_call ) \ + do { \ + status = (func_call); \ + if( status != PSA_SUCCESS ) \ + goto cleanup; \ + } while( 0 ) + + /* Export MAC key */ + PSA_CHK( psa_export_key( key, mac_key, + MBEDTLS_MD_MAX_BLOCK_SIZE, + &mac_key_length ) ); + + if( mac_key_length > block_size ) + { + PSA_CHK( psa_hash_setup( &operation, hash_alg ) ); + PSA_CHK( psa_hash_update( &operation, mac_key, mac_key_length ) ); + PSA_CHK( psa_hash_finish( &operation, mac_key, + MBEDTLS_MD_MAX_BLOCK_SIZE, &mac_key_length ) ); + } + + /* Calculate ikey/okey */ + memset( ikey, 0x36, block_size ); + memset( okey, 0x5C, block_size ); + + for( i = 0; i < mac_key_length; i++ ) + { + ikey[i] = (unsigned char)( ikey[i] ^ mac_key[i] ); + okey[i] = (unsigned char)( okey[i] ^ mac_key[i] ); + } + + mbedtls_platform_zeroize( mac_key, MBEDTLS_MD_MAX_BLOCK_SIZE ); + + PSA_CHK( psa_hash_setup( &operation, hash_alg ) ); + + /* Now compute inner_hash = HASH(ikey + msg) */ + PSA_CHK( psa_hash_update( &operation, ikey, block_size ) ); + PSA_CHK( psa_hash_update( &operation, add_data, add_data_len ) ); + PSA_CHK( psa_hash_update( &operation, data, min_data_len ) ); + + /* For each possible length, compute the hash up to that point */ + for( offset = min_data_len; offset <= max_data_len; offset++ ) + { + PSA_CHK( psa_hash_clone( &operation, &aux_operation ) ); + PSA_CHK( psa_hash_finish( &aux_operation, aux_out, + MBEDTLS_MD_MAX_SIZE, &hash_length ) ); + /* Keep only the correct inner_hash in the output buffer */ + mbedtls_ct_memcpy_if_eq( output, aux_out, hash_size, + offset, data_len_secret ); + + if( offset < max_data_len ) + PSA_CHK( psa_hash_update( &operation, data + offset, 1 ) ); + } + + /* The context needs to finish() before it starts() again */ + PSA_CHK( psa_hash_abort( &operation ) ); + + /* Now compute HASH(okey + inner_hash) */ + PSA_CHK( psa_hash_setup( &operation, hash_alg ) ); + PSA_CHK( psa_hash_update( &operation, okey, block_size ) ); + PSA_CHK( psa_hash_update( &operation, output, hash_size ) ); + PSA_CHK( psa_hash_finish( &operation, output, hash_size, &hash_length ) ); + +#undef PSA_CHK + +cleanup: + mbedtls_platform_zeroize( mac_key, MBEDTLS_MD_MAX_BLOCK_SIZE ); + mbedtls_platform_zeroize( ikey, MBEDTLS_MD_MAX_BLOCK_SIZE ); + mbedtls_platform_zeroize( okey, MBEDTLS_MD_MAX_BLOCK_SIZE ); + psa_hash_abort( &operation ); + psa_hash_abort( &aux_operation ); + return( status == PSA_SUCCESS ? 0 : MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED ); +} +#else int mbedtls_ct_hmac( mbedtls_md_context_t *ctx, const unsigned char *add_data, size_t add_data_len, @@ -520,6 +638,7 @@ cleanup: mbedtls_md_free( &aux ); return( ret ); } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ diff --git a/library/constant_time_internal.h b/library/constant_time_internal.h index 053cf123cc..4838d05e9c 100644 --- a/library/constant_time_internal.h +++ b/library/constant_time_internal.h @@ -276,6 +276,17 @@ void mbedtls_ct_memcpy_offset( unsigned char *dest, * \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED * The hardware accelerator failed. */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_ct_hmac( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const unsigned char *add_data, + size_t add_data_len, + const unsigned char *data, + size_t data_len_secret, + size_t min_data_len, + size_t max_data_len, + unsigned char *output ); +#else int mbedtls_ct_hmac( mbedtls_md_context_t *ctx, const unsigned char *add_data, size_t add_data_len, @@ -284,6 +295,7 @@ int mbedtls_ct_hmac( mbedtls_md_context_t *ctx, size_t min_data_len, size_t max_data_len, unsigned char *output ); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 0ec8b95b56..e02fc1cbb0 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5020,8 +5020,15 @@ void ssl_cf_hmac( int hash ) * Test the function mbedtls_ct_hmac() against a reference * implementation. */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t alg; + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; +#else mbedtls_md_context_t ctx, ref_ctx; const mbedtls_md_info_t *md_info; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ size_t out_len, block_size; size_t min_in_len, in_len, max_in_len, i; /* TLS additional data is 13 bytes (hence the "lucky 13" name) */ @@ -5031,6 +5038,20 @@ void ssl_cf_hmac( int hash ) unsigned char *out = NULL; unsigned char rec_num = 0; + USE_PSA_INIT( ); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + alg = PSA_ALG_HMAC( mbedtls_psa_translate_md( hash ) ); + + out_len = PSA_HASH_LENGTH( alg ); + block_size = PSA_HASH_BLOCK_LENGTH( alg ); + + /* mbedtls_ct_hmac() requires the key to be exportable */ + psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_VERIFY_HASH ); + psa_set_key_algorithm( &attributes, PSA_ALG_HMAC( alg ) ); + psa_set_key_type( &attributes, PSA_KEY_TYPE_HMAC ); +#else mbedtls_md_init( &ctx ); mbedtls_md_init( &ref_ctx ); @@ -5039,10 +5060,18 @@ void ssl_cf_hmac( int hash ) out_len = mbedtls_md_get_size( md_info ); TEST_ASSERT( out_len != 0 ); block_size = hash == MBEDTLS_MD_SHA384 ? 128 : 64; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ /* Use allocated out buffer to catch overwrites */ ASSERT_ALLOC( out, out_len ); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + /* Set up dummy key */ + memset( ref_out, 42, sizeof( ref_out ) ); + TEST_EQUAL( PSA_SUCCESS, psa_import_key( &attributes, + ref_out, out_len, + &key ) ); +#else /* Set up contexts with the given hash and a dummy key */ TEST_EQUAL( 0, mbedtls_md_setup( &ctx, md_info, 1 ) ); TEST_EQUAL( 0, mbedtls_md_setup( &ref_ctx, md_info, 1 ) ); @@ -5050,6 +5079,7 @@ void ssl_cf_hmac( int hash ) TEST_EQUAL( 0, mbedtls_md_hmac_starts( &ctx, ref_out, out_len ) ); TEST_EQUAL( 0, mbedtls_md_hmac_starts( &ref_ctx, ref_out, out_len ) ); memset( ref_out, 0, sizeof( ref_out ) ); +#endif /* * Test all possible lengths up to a point. The difference between @@ -5076,13 +5106,31 @@ void ssl_cf_hmac( int hash ) /* Get the function's result */ TEST_CF_SECRET( &in_len, sizeof( in_len ) ); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + TEST_EQUAL( 0, mbedtls_ct_hmac( key, PSA_ALG_HMAC( alg ), + add_data, sizeof( add_data ), + data, in_len, + min_in_len, max_in_len, + out ) ); +#else TEST_EQUAL( 0, mbedtls_ct_hmac( &ctx, add_data, sizeof( add_data ), data, in_len, min_in_len, max_in_len, out ) ); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ TEST_CF_PUBLIC( &in_len, sizeof( in_len ) ); TEST_CF_PUBLIC( out, out_len ); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + TEST_EQUAL( PSA_SUCCESS, psa_mac_verify_setup( &operation, + key, alg ) ); + TEST_EQUAL( PSA_SUCCESS, psa_mac_update( &operation, add_data, + sizeof( add_data ) ) ); + TEST_EQUAL( PSA_SUCCESS, psa_mac_update( &operation, + data, in_len ) ); + TEST_EQUAL( PSA_SUCCESS, psa_mac_verify_finish( &operation, + out, out_len ) ); +#else /* Compute the reference result */ TEST_EQUAL( 0, mbedtls_md_hmac_update( &ref_ctx, add_data, sizeof( add_data ) ) ); @@ -5092,6 +5140,7 @@ void ssl_cf_hmac( int hash ) /* Compare */ ASSERT_COMPARE( out, out_len, ref_out, out_len ); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ } mbedtls_free( data ); @@ -5099,11 +5148,18 @@ void ssl_cf_hmac( int hash ) } exit: +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_mac_abort( &operation ); + psa_destroy_key( key ); +#else mbedtls_md_free( &ref_ctx ); mbedtls_md_free( &ctx ); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ mbedtls_free( data ); mbedtls_free( out ); + + USE_PSA_DONE( ); } /* END_CASE */