Rework MAC algorithm / key type validation
Reworked the validation of MAC algorithm with the used key type by
introducing psa_mac_key_can_do, which guarantees that PSA_MAC_LENGTH can
be called successfully after validation of the algorithm and key type.
This means psa_get_mac_output_length is no longer required.
Signed-off-by: Steven Cooreman <steven.cooreman@silabs.com>
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index 13a0835..db4b387 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -545,58 +545,44 @@
return( slot->attr.bits );
}
-/** Return the output MAC length of a MAC algorithm, in bytes
+/** Check whether a given key type is valid for use with a given MAC algorithm
*
- * \param[in] algorithm The specific (non-wildcard) MAC algorithm.
+ * Upon successful return of this function, the behavioud of #PSA_MAC_LENGTH
+ * will be defined when called with the validated \p algorithm and \p key_type
+ *
+ * \param[in] algorithm The specific MAC algorithm (can be wildcard).
* \param[in] key_type The key type of the key to be used with the
* \p algorithm.
- * \param[out] length The calculated output length of the given MAC
- * \p algorithm when used with a key corresponding to
- * the given \p key_type
*
* \retval #PSA_SUCCESS
- * The \p length has been successfully calculated
+ * The \p key_type is valid for use with the \p algorithm
* \retval #PSA_ERROR_INVALID_ARGUMENT
- * \p algorithm is not a valid, specific MAC algorithm recognized and
- * supported by this core, or \p key_type describes a key which is
- * inconsistent with the specified \p algorithm.
- * \retval #PSA_ERROR_INVALID_ARGUMENT
- * \p algorithm tries to truncate the MAC to a size which would be
- * larger than the underlying algorithm's maximum output length.
+ * The \p key_type is not valid for use with the \p algorithm
*/
-MBEDTLS_STATIC_TESTABLE psa_status_t psa_get_mac_output_length(
+MBEDTLS_STATIC_TESTABLE psa_status_t psa_mac_key_can_do(
psa_algorithm_t algorithm,
- psa_key_type_t key_type,
- size_t *length )
+ psa_key_type_t key_type )
{
- /* Get the default length for the algorithm and key combination. None of the
- * currently supported algorithms have a default output length dependent on
- * key size, so setting it to a bogus value is OK. */
- size_t default_length = PSA_MAC_LENGTH( key_type, 0,
- PSA_ALG_FULL_LENGTH_MAC( algorithm ) );
-
- /* PSA_MAC_LENGTH, when called on a full-length algorithm identifier, can
- * currently return either 0 (unknown algorithm) or 1 (cipher-MAC with
- * stream cipher) in cases where the key type / algorithm combination would
- * be invalid. */
- if( default_length == 0 || default_length == 1 )
- return( PSA_ERROR_INVALID_ARGUMENT );
-
- /* Output the expected (potentially truncated) length as long as it can
- * actually be output by the algorithm. Truncation length of '0' means
- * default output length of the keytype-algorithm combination. */
- if( PSA_MAC_TRUNCATED_LENGTH( algorithm ) == 0 )
- {
- *length = default_length;
- return( PSA_SUCCESS );
+ if( PSA_ALG_IS_HMAC( algorithm ) ) {
+ if( key_type == PSA_KEY_TYPE_HMAC )
+ return( PSA_SUCCESS );
}
- else if( PSA_MAC_TRUNCATED_LENGTH( algorithm ) <= default_length )
+
+ if( PSA_ALG_IS_BLOCK_CIPHER_MAC( algorithm ) )
{
- *length = PSA_MAC_TRUNCATED_LENGTH( algorithm );
- return( PSA_SUCCESS );
+ /* Check that we're calling PSA_BLOCK_CIPHER_BLOCK_LENGTH with a cipher
+ * key. */
+ if( ( key_type & PSA_KEY_TYPE_CATEGORY_MASK ) ==
+ PSA_KEY_TYPE_CATEGORY_SYMMETRIC )
+ {
+ /* PSA_BLOCK_CIPHER_BLOCK_LENGTH returns 1 for stream ciphers and
+ * the block length (larger than 1) for block ciphers. */
+ if( PSA_BLOCK_CIPHER_BLOCK_LENGTH( key_type ) > 1 )
+ return( PSA_SUCCESS );
+ }
}
- else
- return( PSA_ERROR_INVALID_ARGUMENT );
+
+ return( PSA_ERROR_INVALID_ARGUMENT );
}
/** Try to allocate a buffer to an empty key slot.
@@ -765,28 +751,19 @@
( PSA_ALG_FULL_LENGTH_MAC( alg1 ) ==
PSA_ALG_FULL_LENGTH_MAC( alg2 ) ) )
{
- /* Calculate the actual requested output length for both sides. In case
- * of at-least-this-length wildcard algorithms, the requested output
- * length is the shortest allowed length. */
- size_t alg1_len = 0;
- size_t alg2_len = 0;
- if( PSA_SUCCESS != psa_get_mac_output_length(
- PSA_ALG_TRUNCATED_MAC( alg1,
- PSA_MAC_TRUNCATED_LENGTH( alg1 ) ),
- key_type,
- &alg1_len ) )
- {
+ /* Validate the combination of key type and algorithm. Since the base
+ * algorithm of alg1 and alg2 are the same, we only need this once. */
+ if( PSA_SUCCESS != psa_mac_key_can_do( alg1, key_type ) )
return( 0 );
- }
- if( PSA_SUCCESS != psa_get_mac_output_length(
- PSA_ALG_TRUNCATED_MAC( alg2,
- PSA_MAC_TRUNCATED_LENGTH( alg2 ) ),
- key_type,
- &alg2_len ) )
- {
- return( 0 );
- }
+ /* Get the output length for the algorithm and key combination. None of
+ * the currently supported algorithms have an output length dependent on
+ * actual key size, so setting it to a bogus value is currently OK.
+ * Note that for at-least-this-length wildcard algorithms, the output
+ * length is set to the shortest allowed length, which allows us to
+ * calculate the most restrictive tag length for the intersection. */
+ size_t alg1_len = PSA_MAC_LENGTH( key_type, 0, alg1 );
+ size_t alg2_len = PSA_MAC_LENGTH( key_type, 0, alg2 );
size_t max_len = alg1_len > alg2_len ? alg1_len : alg2_len;
/* If both are wildcards, return most restrictive wildcard */
@@ -810,9 +787,10 @@
else
return( 0 );
}
- /* If none of them are wildcards, check whether we can match
- * default-length with exact-length, and return exact-length in that
- * case. */
+ /* If none of them are wildcards, check whether this is a case of one
+ * specifying the default length and the other a specific length. If the
+ * specific length equals the default length for this key type, the
+ * intersection would be the specific-length algorithm. */
if( alg1_len == alg2_len )
return( PSA_ALG_TRUNCATED_MAC( alg1, alg1_len ) );
}
@@ -853,27 +831,25 @@
( PSA_ALG_FULL_LENGTH_MAC( policy_alg ) ==
PSA_ALG_FULL_LENGTH_MAC( requested_alg ) ) )
{
- size_t actual_output_length;
- size_t default_output_length;
- if( PSA_SUCCESS != psa_get_mac_output_length(
- requested_alg,
- key_type,
- &actual_output_length ) )
- {
+ /* Validate the combination of key type and algorithm. Since the policy
+ * and requested algorithms are the same, we only need this once. */
+ if( PSA_SUCCESS != psa_mac_key_can_do( policy_alg, key_type ) )
return( 0 );
- }
- if( PSA_SUCCESS != psa_get_mac_output_length(
- PSA_ALG_FULL_LENGTH_MAC( requested_alg ),
- key_type,
- &default_output_length ) )
- {
- return( 0 );
- }
+
+ /* Get both the requested and the default output length for this
+ * algorithm and key combination. None of the currently supported
+ * algorithms have an output length dependent on actual key size, so
+ * setting it to a bogus value is currently OK. */
+ size_t requested_output_length = PSA_MAC_LENGTH(
+ key_type, 0, requested_alg );
+ size_t default_output_length = PSA_MAC_LENGTH(
+ key_type, 0,
+ PSA_ALG_FULL_LENGTH_MAC( requested_alg ) );
/* If the policy is default-length, only allow an algorithm with
* a declared exact-length matching the default. */
if( PSA_MAC_TRUNCATED_LENGTH( policy_alg ) == 0 )
- return( actual_output_length == default_output_length );
+ return( requested_output_length == default_output_length );
/* If the requested algorithm is default-length, allow it if the policy
* is exactly the default length. */
@@ -889,7 +865,7 @@
if( ( policy_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG ) != 0 )
{
return( PSA_MAC_TRUNCATED_LENGTH( policy_alg ) <=
- actual_output_length );
+ requested_output_length );
}
}
/* If policy_alg is a generic key agreement operation, then using it for
@@ -2981,7 +2957,6 @@
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
psa_key_slot_t *slot;
- size_t output_length = 0;
psa_key_usage_t usage =
is_sign ? PSA_KEY_USAGE_SIGN_HASH : PSA_KEY_USAGE_VERIFY_HASH;
@@ -3002,12 +2977,15 @@
if( status != PSA_SUCCESS )
goto exit;
- status = psa_get_mac_output_length( alg, slot->attr.type,
- &output_length );
+ /* Validate the combination of key type and algorithm */
+ status = psa_mac_key_can_do( alg, slot->attr.type );
if( status != PSA_SUCCESS )
goto exit;
- operation->mac_size = (uint8_t) output_length;
+ /* Get the output length for the algorithm and key combination. None of the
+ * currently supported algorithms have an output length dependent on actual
+ * key size, so setting it to a bogus value is currently OK. */
+ operation->mac_size = PSA_MAC_LENGTH( slot->attr.type, 0, alg );
if( operation->mac_size < 4 )
{
@@ -3019,6 +2997,15 @@
goto exit;
}
+ if( operation->mac_size >
+ PSA_MAC_LENGTH( slot->attr.type, 0, 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;
+ }
+
#if defined(MBEDTLS_CMAC_C)
if( PSA_ALG_FULL_LENGTH_MAC( alg ) == PSA_ALG_CMAC )
{
diff --git a/library/psa_crypto_invasive.h b/library/psa_crypto_invasive.h
index 99156de..1e5a407 100644
--- a/library/psa_crypto_invasive.h
+++ b/library/psa_crypto_invasive.h
@@ -78,9 +78,9 @@
#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C)
-psa_status_t psa_get_mac_output_length( psa_algorithm_t algorithm,
- psa_key_type_t key_type,
- size_t *length );
+psa_status_t psa_mac_key_can_do(
+ psa_algorithm_t algorithm,
+ psa_key_type_t key_type );
#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C */
#endif /* PSA_CRYPTO_INVASIVE_H */
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index 84267ad..3824fa0 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -1086,7 +1086,8 @@
mac_setup:PSA_KEY_TYPE_AES:"000102030405060708090a0b0c0d0e0f":PSA_ALG_CMAC:PSA_SUCCESS
PSA MAC setup: bad algorithm (HMAC without specified hash)
-mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(0):PSA_ERROR_INVALID_ARGUMENT
+# Either INVALID_ARGUMENT or NOT_SUPPORTED would be reasonable here
+mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(0):PSA_ERROR_NOT_SUPPORTED
PSA MAC setup: bad algorithm (unsupported HMAC hash algorithm)
depends_on:!PSA_WANT_ALG_MD2
diff --git a/tests/suites/test_suite_psa_crypto_metadata.function b/tests/suites/test_suite_psa_crypto_metadata.function
index 5839611..4bf5635 100644
--- a/tests/suites/test_suite_psa_crypto_metadata.function
+++ b/tests/suites/test_suite_psa_crypto_metadata.function
@@ -160,9 +160,7 @@
TEST_EQUAL( length, PSA_MAC_LENGTH( key_type, key_bits, alg ) );
#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C)
- size_t output_length = 0;
- PSA_ASSERT( psa_get_mac_output_length( alg, key_type, &output_length ) );
- TEST_EQUAL( length, output_length );
+ PSA_ASSERT( psa_mac_key_can_do( alg, key_type ) );
#endif
exit: ;