Merge pull request #4704 from mpg/issue-3990-fix_psa_verify_with_alt-2.x
[Backport 2.x] Fix PSA RSA PSS verify with ALT implementations
diff --git a/.travis.yml b/.travis.yml
index 92090fb..e859cbb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,16 +30,7 @@
- name: Windows
os: windows
- before_install:
- - choco install python --version=3.5.4
- env:
- # Add the directory where the Choco package goes
- - PATH=/c/Python35:/c/Python35/Scripts:$PATH
script:
- - type python; python --version
- - python scripts/generate_psa_constants.py
- # Logs appear out of sequence on Windows. Give time to catch up.
- - sleep 5
- scripts/windows_msbuild.bat v141 # Visual Studio 2017
after_failure:
diff --git a/ChangeLog.d/one-shot-mac.txt b/ChangeLog.d/one-shot-mac.txt
new file mode 100644
index 0000000..112891d
--- /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 e36c82f..775923a 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -2300,6 +2300,46 @@
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,
@@ -2313,9 +2353,6 @@
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,
@@ -2328,39 +2365,12 @@
.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 )
{
@@ -2453,24 +2463,22 @@
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 );
@@ -2504,7 +2512,116 @@
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 20c56a0..135fa35 100644
--- a/library/psa_crypto_mac.c
+++ b/library/psa_crypto_mac.c
@@ -359,30 +359,6 @@
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,
@@ -497,6 +473,44 @@
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 5e32a89..678cb77 100644
--- a/tests/suites/test_suite_psa_crypto.function
+++ b/tests/suites/test_suite_psa_crypto.function
@@ -2048,7 +2048,21 @@
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 ) );
@@ -2100,7 +2114,11 @@
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 ) );
@@ -2108,7 +2126,14 @@
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 ) );
@@ -2117,9 +2142,15 @@
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 ) );
@@ -2133,6 +2164,12 @@
{
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 e86309b..3a9eff9 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 @@
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 @@
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 );