Move MAC default length checking into psa_key_policy_permits

Signed-off-by: Steven Cooreman <steven.cooreman@silabs.com>
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index 4f67ddf..f843e66 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -545,6 +545,68 @@
     return( slot->attr.bits );
 }
 
+/** Return the output MAC length of a MAC algorithm, in bytes
+ *
+ * \param[in] algorithm     The specific MAC algorithm
+ * \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
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p algorithm is a MAC algorithm, but ubsupported by this PSA core.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \p algorithm is not a valid, specific MAC algorithm or \p key_type
+ *         describes a key incompatible with the specified \p algorithm.
+ */
+static psa_status_t psa_get_mac_output_length( psa_algorithm_t algorithm,
+                                               psa_key_type_t key_type,
+                                               size_t *length )
+{
+    if( !PSA_ALG_IS_MAC( algorithm ) || PSA_ALG_IS_WILDCARD( algorithm ) )
+        return( PSA_ERROR_INVALID_ARGUMENT );
+
+    size_t default_length = 0;
+
+    if( PSA_ALG_FULL_LENGTH_MAC( algorithm ) == PSA_ALG_CMAC ||
+        PSA_ALG_FULL_LENGTH_MAC( algorithm ) == PSA_ALG_CBC_MAC )
+    {
+        default_length = PSA_BLOCK_CIPHER_BLOCK_LENGTH( key_type );
+        /* CMAC and CBC-MAC are only defined on block ciphers */
+        if( default_length == 0 )
+            return( PSA_ERROR_INVALID_ARGUMENT );
+    }
+    else if( PSA_ALG_IS_HMAC( algorithm ) )
+    {
+        /* HMAC output length is dictated by the underlying hash operation */
+        psa_algorithm_t hash_alg = PSA_ALG_HMAC_GET_HASH( algorithm );
+        default_length = PSA_HASH_LENGTH( hash_alg );
+
+        if( hash_alg == 0 || default_length == 0 )
+            return( PSA_ERROR_INVALID_ARGUMENT );
+    }
+    else
+        return( PSA_ERROR_NOT_SUPPORTED );
+
+    /* Output the expected (potentially truncated) length as long as it can
+     * actually be output by the algorithm */
+    if( PSA_ALG_FULL_LENGTH_MAC( algorithm ) == algorithm )
+    {
+        *length = default_length;
+        return( PSA_SUCCESS );
+    }
+    else if( PSA_MAC_TRUNCATED_LENGTH( algorithm ) <= default_length )
+    {
+        *length = PSA_MAC_TRUNCATED_LENGTH( algorithm );
+        return( PSA_SUCCESS );
+    }
+    else
+        return( PSA_ERROR_INVALID_ARGUMENT );
+}
+
 /** Try to allocate a buffer to an empty key slot.
  *
  * \param[in,out] slot          Key slot to attach buffer to.
@@ -741,7 +803,8 @@
     return( 0 );
 }
 
-static int psa_key_algorithm_permits( psa_algorithm_t policy_alg,
+static int psa_key_algorithm_permits( psa_key_type_t key_type,
+                                      psa_algorithm_t policy_alg,
                                       psa_algorithm_t requested_alg )
 {
     /* Common case: the policy only allows requested_alg. */
@@ -768,22 +831,49 @@
         return( PSA_ALG_AEAD_GET_TAG_LENGTH( policy_alg ) <=
                 PSA_ALG_AEAD_GET_TAG_LENGTH( requested_alg ) );
     }
-    /* If policy_alg is a wildcard MAC algorithm of the same base as
-     * the requested algorithm, check the requested tag length to be
-     * equal-length or longer than the wildcard-specified length. */
     if( PSA_ALG_IS_MAC( policy_alg ) &&
         PSA_ALG_IS_MAC( requested_alg ) &&
         ( PSA_ALG_FULL_LENGTH_MAC( policy_alg ) ==
-          PSA_ALG_FULL_LENGTH_MAC( requested_alg ) ) &&
-        ( ( policy_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG ) != 0 ) )
+          PSA_ALG_FULL_LENGTH_MAC( requested_alg ) ) )
     {
-        /* Special case: full-length MAC is encoded with 0-length.
-         * A minimum-length policy will always allow a full-length MAC. */
-        if( PSA_ALG_FULL_LENGTH_MAC( requested_alg ) == requested_alg )
-            return( 1 );
+        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 ) )
+        {
+            return( 0 );
+        }
+        if( PSA_SUCCESS != psa_get_mac_output_length(
+                            PSA_ALG_FULL_LENGTH_MAC( requested_alg ),
+                            key_type,
+                            &default_output_length ) )
+        {
+            return( 0 );
+        }
 
-        return( PSA_MAC_TRUNCATED_LENGTH( policy_alg ) <=
-                PSA_MAC_TRUNCATED_LENGTH( 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 );
+
+        /* If the requested algorithm is default-length, allow it if the policy
+         * is exactly the default length. */
+        if( PSA_MAC_TRUNCATED_LENGTH( requested_alg ) == 0 &&
+            PSA_MAC_TRUNCATED_LENGTH( policy_alg ) == default_output_length )
+        {
+            return( 1 );
+        }
+
+        /* If policy_alg is a wildcard MAC algorithm of the same base as
+         * the requested algorithm, check the requested tag length to be
+         * equal-length or longer than the wildcard-specified length. */
+        if( ( policy_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG ) != 0 )
+        {
+            return( PSA_MAC_TRUNCATED_LENGTH( policy_alg ) <=
+                    actual_output_length );
+        }
     }
     /* If policy_alg is a generic key agreement operation, then using it for
      * a key derivation with that key agreement should also be allowed. This
@@ -809,6 +899,7 @@
  *                                      the \p policy does not allow it.
  */
 static psa_status_t psa_key_policy_permits( const psa_key_policy_t *policy,
+                                            psa_key_type_t key_type,
                                             psa_algorithm_t alg )
 {
     /* '0' is not a valid algorithm */
@@ -819,8 +910,8 @@
     if( PSA_ALG_IS_WILDCARD( alg ) )
         return( PSA_ERROR_INVALID_ARGUMENT );
 
-    if( psa_key_algorithm_permits( policy->alg, alg ) ||
-        psa_key_algorithm_permits( policy->alg2, alg ) )
+    if( psa_key_algorithm_permits( key_type, policy->alg, alg ) ||
+        psa_key_algorithm_permits( key_type, policy->alg2, alg ) )
         return( PSA_SUCCESS );
     else
         return( PSA_ERROR_NOT_PERMITTED );
@@ -899,7 +990,9 @@
     /* Enforce that the usage policy permits the requested algortihm. */
     if( alg != 0 )
     {
-        status = psa_key_policy_permits( &slot->attr.policy, alg );
+        status = psa_key_policy_permits( &slot->attr.policy,
+                                         slot->attr.type,
+                                         alg );
         if( status != PSA_SUCCESS )
             goto error;
     }
@@ -2934,36 +3027,7 @@
 
     if( truncated == 0 )
     {
-        /* The "normal" case: untruncated algorithm. Re-validate the
-         * key policy with explicit MAC length set in the algorithm
-         * when the algorithm policy is at-least-this-length to catch
-         * a corner case due to the default MAC length being unknown
-         * at key loading time. */
-        if( PSA_ALG_IS_MAC( slot->attr.policy.alg ) &&
-            ( PSA_ALG_FULL_LENGTH_MAC( slot->attr.policy.alg ) ==
-                full_length_alg ) &&
-            ( slot->attr.policy.alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG ) )
-        {
-            /* validate policy length */
-            if( PSA_MAC_TRUNCATED_LENGTH( slot->attr.policy.alg ) >
-                    operation->mac_size )
-            {
-                status = PSA_ERROR_NOT_PERMITTED;
-            }
-        }
-
-        if( PSA_ALG_IS_MAC( slot->attr.policy.alg2 ) &&
-            ( PSA_ALG_FULL_LENGTH_MAC( slot->attr.policy.alg2 ) ==
-                full_length_alg ) &&
-            ( slot->attr.policy.alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG ) )
-        {
-            /* validate policy length */
-            if( PSA_MAC_TRUNCATED_LENGTH( slot->attr.policy.alg2 ) >
-                    operation->mac_size )
-            {
-                status = PSA_ERROR_NOT_PERMITTED;
-            }
-        }
+        /* The "normal" case: untruncated algorithm. Nothing to do. */
     }
     else if( truncated < 4 )
     {
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index b542938..84267ad 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -1085,8 +1085,12 @@
 depends_on:MBEDTLS_AES_C:MBEDTLS_CMAC_C
 mac_setup:PSA_KEY_TYPE_AES:"000102030405060708090a0b0c0d0e0f":PSA_ALG_CMAC:PSA_SUCCESS
 
-PSA MAC setup: bad algorithm (unknown MAC algorithm)
-mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(0):PSA_ERROR_NOT_SUPPORTED
+PSA MAC setup: bad algorithm (HMAC without specified hash)
+mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(0):PSA_ERROR_INVALID_ARGUMENT
+
+PSA MAC setup: bad algorithm (unsupported HMAC hash algorithm)
+depends_on:!PSA_WANT_ALG_MD2
+mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(PSA_ALG_MD2):PSA_ERROR_NOT_SUPPORTED
 
 PSA MAC setup: bad algorithm (not a MAC algorithm)
 depends_on:MBEDTLS_AES_C:MBEDTLS_CIPHER_MODE_CBC
@@ -1107,7 +1111,7 @@
 PSA MAC setup: incompatible key HMAC for CMAC
 depends_on:MBEDTLS_CMAC_C
 # Either INVALID_ARGUMENT or NOT_SUPPORTED would be reasonable here
-mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f":PSA_ALG_CMAC:PSA_ERROR_NOT_SUPPORTED
+mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f":PSA_ALG_CMAC:PSA_ERROR_INVALID_ARGUMENT
 
 PSA MAC setup: algorithm known but not supported, long key
 depends_on:!MBEDTLS_MD5_C