diff --git a/ChangeLog.d/one-shot-mac.txt b/ChangeLog.d/one-shot-mac.txt new file mode 100644 index 000000000..112891dec --- /dev/null +++ b/ChangeLog.d/one-shot-mac.txt @@ -0,0 +1,3 @@ +Features + * Implement psa_mac_compute() and psa_mac_verify() as defined in the + PSA Cryptograpy API 1.0.0 specification. diff --git a/library/psa_crypto.c b/library/psa_crypto.c index a9caa186b..a0acc3fd9 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -2220,6 +2220,46 @@ psa_status_t psa_mac_abort( psa_mac_operation_t *operation ) return( status ); } +static psa_status_t psa_mac_finalize_alg_and_key_validation( + psa_algorithm_t alg, + const psa_key_attributes_t *attributes, + uint8_t *mac_size ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_type_t key_type = psa_get_key_type( attributes ); + size_t key_bits = psa_get_key_bits( attributes ); + + if( ! PSA_ALG_IS_MAC( alg ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + /* Validate the combination of key type and algorithm */ + status = psa_mac_key_can_do( alg, key_type ); + if( status != PSA_SUCCESS ) + return( status ); + + /* Get the output length for the algorithm and key combination */ + *mac_size = PSA_MAC_LENGTH( key_type, key_bits, alg ); + + if( *mac_size < 4 ) + { + /* A very short MAC is too short for security since it can be + * brute-forced. Ancient protocols with 32-bit MACs do exist, + * so we make this our minimum, even though 32 bits is still + * too small for security. */ + return( PSA_ERROR_NOT_SUPPORTED ); + } + + if( *mac_size > PSA_MAC_LENGTH( key_type, key_bits, + PSA_ALG_FULL_LENGTH_MAC( alg ) ) ) + { + /* It's impossible to "truncate" to a larger length than the full length + * of the algorithm. */ + return( PSA_ERROR_INVALID_ARGUMENT ); + } + + return( PSA_SUCCESS ); +} + static psa_status_t psa_mac_setup( psa_mac_operation_t *operation, mbedtls_svc_key_id_t key, psa_algorithm_t alg, @@ -2233,9 +2273,6 @@ static psa_status_t psa_mac_setup( psa_mac_operation_t *operation, if( operation->id != 0 ) return( PSA_ERROR_BAD_STATE ); - if( ! PSA_ALG_IS_MAC( alg ) ) - return( PSA_ERROR_INVALID_ARGUMENT ); - status = psa_get_and_lock_key_slot_with_policy( key, &slot, @@ -2248,39 +2285,12 @@ static psa_status_t psa_mac_setup( psa_mac_operation_t *operation, .core = slot->attr }; - /* Validate the combination of key type and algorithm */ - status = psa_mac_key_can_do( alg, psa_get_key_type( &attributes ) ); + status = psa_mac_finalize_alg_and_key_validation( alg, &attributes, + &operation->mac_size ); if( status != PSA_SUCCESS ) goto exit; operation->is_sign = is_sign; - - /* Get the output length for the algorithm and key combination */ - operation->mac_size = PSA_MAC_LENGTH( - psa_get_key_type( &attributes ), - psa_get_key_bits( &attributes ), - alg ); - - if( operation->mac_size < 4 ) - { - /* A very short MAC is too short for security since it can be - * brute-forced. Ancient protocols with 32-bit MACs do exist, - * so we make this our minimum, even though 32 bits is still - * too small for security. */ - status = PSA_ERROR_NOT_SUPPORTED; - goto exit; - } - - if( operation->mac_size > PSA_MAC_LENGTH( psa_get_key_type( &attributes ), - psa_get_key_bits( &attributes ), - PSA_ALG_FULL_LENGTH_MAC( alg ) ) ) - { - /* It's impossible to "truncate" to a larger length than the full length - * of the algorithm. */ - status = PSA_ERROR_INVALID_ARGUMENT; - goto exit; - } - /* Dispatch the MAC setup call with validated input */ if( is_sign ) { @@ -2373,24 +2383,22 @@ psa_status_t psa_mac_sign_finish( psa_mac_operation_t *operation, mac, operation->mac_size, mac_length ); - if( status == PSA_SUCCESS ) + /* In case of success, set the potential excess room in the output buffer + * to an invalid value, to avoid potentially leaking a longer MAC. + * In case of error, set the output length and content to a safe default, + * such that in case the caller misses an error check, the output would be + * an unachievable MAC. + */ + if( status != PSA_SUCCESS ) { - /* Set the excess room in the output buffer to an invalid value, to - * avoid potentially leaking a longer MAC. */ - if( mac_size > operation->mac_size ) - memset( &mac[operation->mac_size], - '!', - mac_size - operation->mac_size ); - } - else - { - /* Set the output length and content to a safe default, such that in - * case the caller misses an error check, the output would be an - * unachievable MAC. */ *mac_length = mac_size; - memset( mac, '!', mac_size ); + operation->mac_size = 0; } + if( mac_size > operation->mac_size ) + memset( &mac[operation->mac_size], '!', + mac_size - operation->mac_size ); + abort_status = psa_mac_abort( operation ); return( status == PSA_SUCCESS ? abort_status : status ); @@ -2424,7 +2432,116 @@ cleanup: return( status == PSA_SUCCESS ? abort_status : status ); } +static psa_status_t psa_mac_compute_internal( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length, + int is_sign ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + uint8_t operation_mac_size = 0; + status = psa_get_and_lock_key_slot_with_policy( + key, &slot, + is_sign ? PSA_KEY_USAGE_SIGN_HASH : PSA_KEY_USAGE_VERIFY_HASH, + alg ); + if( status != PSA_SUCCESS ) + goto exit; + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_mac_finalize_alg_and_key_validation( alg, &attributes, + &operation_mac_size ); + if( status != PSA_SUCCESS ) + goto exit; + + if( mac_size < operation_mac_size ) + { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_driver_wrapper_mac_compute( + &attributes, + slot->key.data, slot->key.bytes, + alg, + input, input_length, + mac, operation_mac_size, mac_length ); + +exit: + /* In case of success, set the potential excess room in the output buffer + * to an invalid value, to avoid potentially leaking a longer MAC. + * In case of error, set the output length and content to a safe default, + * such that in case the caller misses an error check, the output would be + * an unachievable MAC. + */ + if( status != PSA_SUCCESS ) + { + *mac_length = mac_size; + operation_mac_size = 0; + } + if( mac_size > operation_mac_size ) + memset( &mac[operation_mac_size], '!', mac_size - operation_mac_size ); + + unlock_status = psa_unlock_key_slot( slot ); + + return( ( status == PSA_SUCCESS ) ? unlock_status : status ); +} + +psa_status_t psa_mac_compute( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length) +{ + return( psa_mac_compute_internal( key, alg, + input, input_length, + mac, mac_size, mac_length, 1 ) ); +} + +psa_status_t psa_mac_verify( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *mac, + size_t mac_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t actual_mac[PSA_MAC_MAX_SIZE]; + size_t actual_mac_length; + + status = psa_mac_compute_internal( key, alg, + input, input_length, + actual_mac, sizeof( actual_mac ), + &actual_mac_length, 0 ); + if( status != PSA_SUCCESS ) + goto exit; + + if( mac_length != actual_mac_length ) + { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + if( mbedtls_psa_safer_memcmp( mac, actual_mac, actual_mac_length ) != 0 ) + { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + +exit: + mbedtls_platform_zeroize( actual_mac, sizeof( actual_mac ) ); + + return ( status ); +} /****************************************************************/ /* Asymmetric cryptography */ diff --git a/library/psa_crypto_mac.c b/library/psa_crypto_mac.c index adcc191e8..2c079d434 100644 --- a/library/psa_crypto_mac.c +++ b/library/psa_crypto_mac.c @@ -355,30 +355,6 @@ static psa_status_t mac_setup( mbedtls_psa_mac_operation_t *operation, return( status ); } -static psa_status_t mac_compute( - const psa_key_attributes_t *attributes, - const uint8_t *key_buffer, - size_t key_buffer_size, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *mac, - size_t mac_size, - size_t *mac_length ) -{ - /* One-shot MAC has not been implemented in this PSA implementation yet. */ - (void) attributes; - (void) key_buffer; - (void) key_buffer_size; - (void) alg; - (void) input; - (void) input_length; - (void) mac; - (void) mac_size; - (void) mac_length; - return( PSA_ERROR_NOT_SUPPORTED ); -} - static psa_status_t mac_update( mbedtls_psa_mac_operation_t *operation, const uint8_t *input, @@ -493,6 +469,44 @@ cleanup: return( status ); } + +static psa_status_t mac_compute( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_psa_mac_operation_t operation = MBEDTLS_PSA_MAC_OPERATION_INIT; + + status = mac_setup( &operation, + attributes, key_buffer, key_buffer_size, + alg ); + if( status != PSA_SUCCESS ) + goto exit; + + if( input_length > 0 ) + { + status = mac_update( &operation, input, input_length ); + if( status != PSA_SUCCESS ) + goto exit; + } + + status = mac_finish_internal( &operation, mac, mac_size ); + if( status == PSA_SUCCESS ) + *mac_length = mac_size; + +exit: + mac_abort( &operation ); + + return( status ); +} + #endif /* BUILTIN_ALG_HMAC || BUILTIN_ALG_CMAC */ #if defined(MBEDTLS_PSA_BUILTIN_MAC) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 7a774e51e..33e7c9532 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -1987,7 +1987,21 @@ void mac_sign( int key_type_arg, mbedtls_test_set_step( output_size ); ASSERT_ALLOC( actual_mac, output_size ); - /* Calculate the MAC. */ + /* Calculate the MAC, one-shot case. */ + TEST_EQUAL( psa_mac_compute( key, alg, + input->x, input->len, + actual_mac, output_size, &mac_length ), + expected_status ); + if( expected_status == PSA_SUCCESS ) + { + ASSERT_COMPARE( expected_mac->x, expected_mac->len, + actual_mac, mac_length ); + } + + if( output_size > 0 ) + memset( actual_mac, 0, output_size ); + + /* Calculate the MAC, multi-part case. */ PSA_ASSERT( psa_mac_sign_setup( &operation, key, alg ) ); PSA_ASSERT( psa_mac_update( &operation, input->x, input->len ) ); @@ -2039,7 +2053,11 @@ void mac_verify( int key_type_arg, PSA_ASSERT( psa_import_key( &attributes, key_data->x, key_data->len, &key ) ); - /* Test the correct MAC. */ + /* Verify correct MAC, one-shot case. */ + PSA_ASSERT( psa_mac_verify( key, alg, input->x, input->len, + expected_mac->x, expected_mac->len ) ); + + /* Verify correct MAC, multi-part case. */ PSA_ASSERT( psa_mac_verify_setup( &operation, key, alg ) ); PSA_ASSERT( psa_mac_update( &operation, input->x, input->len ) ); @@ -2047,7 +2065,14 @@ void mac_verify( int key_type_arg, expected_mac->x, expected_mac->len ) ); - /* Test a MAC that's too short. */ + /* Test a MAC that's too short, one-shot case. */ + TEST_EQUAL( psa_mac_verify( key, alg, + input->x, input->len, + expected_mac->x, + expected_mac->len - 1 ), + PSA_ERROR_INVALID_SIGNATURE ); + + /* Test a MAC that's too short, multi-part case. */ PSA_ASSERT( psa_mac_verify_setup( &operation, key, alg ) ); PSA_ASSERT( psa_mac_update( &operation, input->x, input->len ) ); @@ -2056,9 +2081,15 @@ void mac_verify( int key_type_arg, expected_mac->len - 1 ), PSA_ERROR_INVALID_SIGNATURE ); - /* Test a MAC that's too long. */ + /* Test a MAC that's too long, one-shot case. */ ASSERT_ALLOC( perturbed_mac, expected_mac->len + 1 ); memcpy( perturbed_mac, expected_mac->x, expected_mac->len ); + TEST_EQUAL( psa_mac_verify( key, alg, + input->x, input->len, + perturbed_mac, expected_mac->len + 1 ), + PSA_ERROR_INVALID_SIGNATURE ); + + /* Test a MAC that's too long, multi-part case. */ PSA_ASSERT( psa_mac_verify_setup( &operation, key, alg ) ); PSA_ASSERT( psa_mac_update( &operation, input->x, input->len ) ); @@ -2072,6 +2103,12 @@ void mac_verify( int key_type_arg, { mbedtls_test_set_step( i ); perturbed_mac[i] ^= 1; + + TEST_EQUAL( psa_mac_verify( key, alg, + input->x, input->len, + perturbed_mac, expected_mac->len ), + PSA_ERROR_INVALID_SIGNATURE ); + PSA_ASSERT( psa_mac_verify_setup( &operation, key, alg ) ); PSA_ASSERT( psa_mac_update( &operation, input->x, input->len ) ); diff --git a/tests/suites/test_suite_psa_crypto_driver_wrappers.function b/tests/suites/test_suite_psa_crypto_driver_wrappers.function index e86309b06..3a9eff955 100644 --- a/tests/suites/test_suite_psa_crypto_driver_wrappers.function +++ b/tests/suites/test_suite_psa_crypto_driver_wrappers.function @@ -1118,7 +1118,31 @@ void mac_sign( int key_type_arg, ASSERT_ALLOC( actual_mac, mac_buffer_size ); mbedtls_test_driver_mac_hooks.forced_status = forced_status; - /* Calculate the MAC. */ + /* + * Calculate the MAC, one-shot case. + */ + status = psa_mac_compute( key, alg, + input->x, input->len, + actual_mac, mac_buffer_size, + &mac_length ); + + TEST_EQUAL( mbedtls_test_driver_mac_hooks.hits, 1 ); + if( forced_status == PSA_SUCCESS || + forced_status == PSA_ERROR_NOT_SUPPORTED ) + { + PSA_ASSERT( status ); + } + else + TEST_EQUAL( forced_status, status ); + + if( mac_buffer_size > 0 ) + memset( actual_mac, 0, mac_buffer_size ); + mbedtls_test_driver_mac_hooks = mbedtls_test_driver_mac_hooks_init(); + mbedtls_test_driver_mac_hooks.forced_status = forced_status; + + /* + * Calculate the MAC, multipart case. + */ status = psa_mac_sign_setup( &operation, key, alg ); TEST_EQUAL( mbedtls_test_driver_mac_hooks.hits, 1 ); @@ -1214,7 +1238,27 @@ void mac_verify( int key_type_arg, mbedtls_test_driver_mac_hooks.forced_status = forced_status; - /* Test the correct MAC. */ + /* + * Verify the MAC, one-shot case. + */ + status = psa_mac_verify( key, alg, + input->x, input->len, + expected_mac->x, expected_mac->len ); + TEST_EQUAL( mbedtls_test_driver_mac_hooks.hits, 1 ); + if( forced_status == PSA_SUCCESS || + forced_status == PSA_ERROR_NOT_SUPPORTED ) + { + PSA_ASSERT( status ); + } + else + TEST_EQUAL( forced_status, status ); + + mbedtls_test_driver_mac_hooks = mbedtls_test_driver_mac_hooks_init(); + mbedtls_test_driver_mac_hooks.forced_status = forced_status; + + /* + * Verify the MAC, multi-part case. + */ status = psa_mac_verify_setup( &operation, key, alg ); TEST_EQUAL( mbedtls_test_driver_mac_hooks.hits, 1 );