Keys may allow a second algorithm

Add a second permitted algorithm to key policies.

This commit includes smoke tests that do not cover psa_copy_key.
diff --git a/include/psa/crypto_extra.h b/include/psa/crypto_extra.h
index c89c55d..a1a6589 100644
--- a/include/psa/crypto_extra.h
+++ b/include/psa/crypto_extra.h
@@ -62,6 +62,45 @@
     MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( PSA_ERROR_INSUFFICIENT_DATA )
 #endif
 
+/** \addtogroup policy
+ * @{
+ */
+
+/** \brief Set the enrollment algorithm in a key policy.
+ *
+ * An operation on a key may indifferently use the algorithm set with
+ * psa_key_policy_set_usage() or with this function.
+ *
+ * \param[in,out] policy The key policy to modify. It must have been
+ *                       initialized as per the documentation for
+ *                       #psa_key_policy_t.
+ * \param alg2           A second algorithm that the key may be used for,
+ *                       in addition to the algorithm set with
+ *                       psa_key_policy_set_usage().
+ *
+ * \warning Setting an enrollment algorithm is not recommended, because
+ *          using the same key with different algorithms can allow some
+ *          attacks based on arithmetic relations between different
+ *          computations made with the same key, or can escalate harmless
+ *          side channels into exploitable ones. Use this function only
+ *          if it is necessary to support a protocol for which is has been
+ *          verified that the usage of the key with multiple algorithms
+ *          is safe.
+ */
+void psa_key_policy_set_enrollment_algorithm(psa_key_policy_t *policy,
+                                             psa_algorithm_t alg2);
+
+/** \brief Retrieve the enrollment algorithm field of a policy structure.
+ *
+ * \param[in] policy    The policy object to query.
+ *
+ * \return The enrollment algorithm for a key with this policy.
+ */
+psa_algorithm_t psa_key_policy_get_enrollment_algorithm(
+    const psa_key_policy_t *policy);
+
+/**@}*/
+
 /**
  * \brief Library deinitialization.
  *
diff --git a/include/psa/crypto_struct.h b/include/psa/crypto_struct.h
index ee3ecd7..8850357 100644
--- a/include/psa/crypto_struct.h
+++ b/include/psa/crypto_struct.h
@@ -228,9 +228,10 @@
 {
     psa_key_usage_t usage;
     psa_algorithm_t alg;
+    psa_algorithm_t alg2;
 };
 
-#define PSA_KEY_POLICY_INIT {0, 0}
+#define PSA_KEY_POLICY_INIT {0, 0, 0}
 static inline struct psa_key_policy_s psa_key_policy_init( void )
 {
     const struct psa_key_policy_s v = PSA_KEY_POLICY_INIT;
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index 3b9c78f..17f2c22 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -763,6 +763,25 @@
     return( 0 );
 }
 
+static int psa_key_algorithm_permits( psa_algorithm_t policy_alg,
+                                      psa_algorithm_t requested_alg )
+{
+    /* Common case: the policy only allows alg. */
+    if( requested_alg == policy_alg )
+        return( 1 );
+    /* If policy_alg is a hash-and-sign with a wildcard for the hash,
+     * and alg is the same hash-and-sign family with any hash,
+     * then alg is compliant with policy_alg. */
+    if( PSA_ALG_IS_HASH_AND_SIGN( requested_alg ) &&
+        PSA_ALG_SIGN_GET_HASH( policy_alg ) == PSA_ALG_ANY_HASH )
+    {
+        return( ( policy_alg & ~PSA_ALG_HASH_MASK ) ==
+                ( requested_alg & ~PSA_ALG_HASH_MASK ) );
+    }
+    /* If it isn't permitted, it's forbidden. */
+    return( 0 );
+}
+
 /** Test whether a policy permits an algorithm.
  *
  * The caller must test usage flags separately.
@@ -770,20 +789,8 @@
 static int psa_key_policy_permits( const psa_key_policy_t *policy,
                                    psa_algorithm_t alg )
 {
-    /* Common case: the policy only allows alg. */
-    if( alg == policy->alg )
-        return( 1 );
-    /* If policy->alg is a hash-and-sign with a wildcard for the hash,
-     * and alg is the same hash-and-sign family with any hash,
-     * then alg is compliant with policy->alg. */
-    if( PSA_ALG_IS_HASH_AND_SIGN( alg ) &&
-        PSA_ALG_SIGN_GET_HASH( policy->alg ) == PSA_ALG_ANY_HASH )
-    {
-        return( ( policy->alg & ~PSA_ALG_HASH_MASK ) ==
-                (         alg & ~PSA_ALG_HASH_MASK ) );
-    }
-    /* If it isn't permitted, it's forbidden. */
-    return( 0 );
+    return( psa_key_algorithm_permits( policy->alg, alg ) ||
+            psa_key_algorithm_permits( policy->alg2, alg ) );
 }
 
 /** Restrict a key policy based on a constraint.
@@ -804,10 +811,15 @@
 {
     psa_algorithm_t intersection_alg =
         psa_key_policy_algorithm_intersection( policy->alg, constraint->alg );
+    psa_algorithm_t intersection_alg2 =
+        psa_key_policy_algorithm_intersection( policy->alg2, constraint->alg2 );
     if( intersection_alg == 0 && policy->alg != 0 && constraint->alg != 0 )
         return( PSA_ERROR_INVALID_ARGUMENT );
+    if( intersection_alg2 == 0 && policy->alg2 != 0 && constraint->alg2 != 0 )
+        return( PSA_ERROR_INVALID_ARGUMENT );
     policy->usage &= constraint->usage;
     policy->alg = intersection_alg;
+    policy->alg2 = intersection_alg2;
     return( PSA_SUCCESS );
 }
 
@@ -3218,6 +3230,18 @@
 {
     return( policy->alg );
 }
+
+void psa_key_policy_set_enrollment_algorithm( psa_key_policy_t *policy,
+                                              psa_algorithm_t alg2 )
+{
+    policy->alg2 = alg2;
+}
+
+psa_algorithm_t psa_key_policy_get_enrollment_algorithm(
+    const psa_key_policy_t *policy )
+{
+    return( policy->alg2 );
+}
 #endif /* !defined(MBEDTLS_PSA_CRYPTO_SPM) */
 
 psa_status_t psa_set_key_policy( psa_key_handle_t handle,
diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data
index 65ac6d7..e93bc15 100644
--- a/tests/suites/test_suite_psa_crypto.data
+++ b/tests/suites/test_suite_psa_crypto.data
@@ -492,6 +492,14 @@
 depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECDH_C
 agreement_key_policy:PSA_KEY_USAGE_DERIVE:PSA_ALG_ECDH(PSA_ALG_SELECT_RAW):PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_SECP256R1):"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_ALG_FFDH(PSA_ALG_SELECT_RAW)
 
+PSA key policy algorithm2: CTR, CBC
+depends_on:MBEDTLS_AES_C:MBEDTLS_CIPHER_MODE_CTR:MBEDTLS_CIPHER_MODE_CBC_NOPAD
+key_policy_alg2:PSA_KEY_TYPE_AES:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_KEY_USAGE_ENCRYPT:PSA_ALG_CTR:PSA_ALG_CBC_NO_PADDING
+
+PSA key policy algorithm2: ECDH, ECDSA
+depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECDH_C:MBEDTLS_ECDSA_C
+key_policy_alg2:PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_SECP256R1):"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_SIGN | PSA_KEY_USAGE_VERIFY:PSA_ALG_ECDH(PSA_ALG_SELECT_RAW):PSA_ALG_ECDSA_ANY
+
 Copy key: raw, 0 bytes
 copy_key_policy:0:0:PSA_KEY_TYPE_RAW_DATA:"":0:0:-1:-1:0:0
 
diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function
index 4cec118..a79b738 100644
--- a/tests/suites/test_suite_psa_crypto.function
+++ b/tests/suites/test_suite_psa_crypto.function
@@ -1933,6 +1933,43 @@
 /* END_CASE */
 
 /* BEGIN_CASE */
+void key_policy_alg2( int key_type_arg, data_t *key_data,
+                      int usage_arg, int alg_arg, int alg2_arg )
+{
+    psa_key_handle_t handle = 0;
+    psa_key_type_t key_type = key_type_arg;
+    psa_key_policy_t policy = PSA_KEY_POLICY_INIT;
+    psa_key_policy_t got_policy = PSA_KEY_POLICY_INIT;
+    psa_key_usage_t usage = usage_arg;
+    psa_algorithm_t alg = alg_arg;
+    psa_algorithm_t alg2 = alg2_arg;
+
+    PSA_ASSERT( psa_crypto_init( ) );
+
+    PSA_ASSERT( psa_allocate_key( &handle ) );
+    psa_key_policy_set_usage( &policy, usage, alg );
+    psa_key_policy_set_enrollment_algorithm( &policy, alg2 );
+    PSA_ASSERT( psa_set_key_policy( handle, &policy ) );
+    PSA_ASSERT( psa_import_key( handle, key_type,
+                                key_data->x, key_data->len ) );
+
+    PSA_ASSERT( psa_get_key_policy( handle, &got_policy ) );
+    TEST_EQUAL( psa_key_policy_get_usage( &got_policy ), usage );
+    TEST_EQUAL( psa_key_policy_get_algorithm( &got_policy ), alg );
+    TEST_EQUAL( psa_key_policy_get_enrollment_algorithm( &got_policy ), alg2 );
+
+    if( ! exercise_key( handle, usage, alg ) )
+        goto exit;
+    if( ! exercise_key( handle, usage, alg2 ) )
+        goto exit;
+
+exit:
+    psa_destroy_key( handle );
+    mbedtls_psa_crypto_free( );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
 void copy_key_policy( int source_usage_arg, int source_alg_arg,
                       int type_arg, data_t *material,
                       int target_usage_arg, int target_alg_arg,