mbedtls_pk_get_psa_attributes: ECC support

Add code and unit tests for MBEDTLS_PK_ECxxx in
mbedtls_pk_get_psa_attributes().

This commit only supports built-in ECC (MBEDTLS_ECP_C). A subsequent commit
will handle driver-only ECC.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/library/pk.c b/library/pk.c
index 1485bd7..432fbcf 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -406,22 +406,28 @@
 {
     mbedtls_pk_type_t pk_type = mbedtls_pk_get_type(pk);
 
+    psa_key_usage_t more_usage = usage;
+    if (usage == PSA_KEY_USAGE_SIGN_MESSAGE) {
+        more_usage |= PSA_KEY_USAGE_VERIFY_MESSAGE;
+    } else if (usage == PSA_KEY_USAGE_SIGN_HASH) {
+        more_usage |= PSA_KEY_USAGE_VERIFY_HASH;
+    } else if (usage == PSA_KEY_USAGE_DECRYPT) {
+        more_usage |= PSA_KEY_USAGE_ENCRYPT;
+    }
+    more_usage |= PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY;
+
     switch (pk_type) {
 #if defined(MBEDTLS_RSA_C)
         case MBEDTLS_PK_RSA:
+        {
             int want_crypt = 0;
             int want_private = 0;
             switch (usage) {
                 case PSA_KEY_USAGE_SIGN_MESSAGE:
-                    usage |= PSA_KEY_USAGE_VERIFY_MESSAGE;
-                    want_private = 1;
-                    break;
                 case PSA_KEY_USAGE_SIGN_HASH:
-                    usage |= PSA_KEY_USAGE_VERIFY_HASH;
                     want_private = 1;
                     break;
                 case PSA_KEY_USAGE_DECRYPT:
-                    usage |= PSA_KEY_USAGE_ENCRYPT;
                     want_private = 1;
                     want_crypt = 1;
                     break;
@@ -448,8 +454,61 @@
             psa_set_key_algorithm(attributes,
                                   psa_algorithm_for_rsa(rsa, want_crypt));
             break;
+        }
 #endif /* MBEDTLS_RSA_C */
 
+#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
+        case MBEDTLS_PK_ECKEY:
+        case MBEDTLS_PK_ECKEY_DH:
+        case MBEDTLS_PK_ECDSA:
+        {
+            int sign_ok = (pk_type != MBEDTLS_PK_ECKEY_DH);
+            int derive_ok = (pk_type != MBEDTLS_PK_ECDSA);
+            mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*pk);
+            int has_private = (ec->d.n != 0);
+            size_t bits = 0;
+            psa_ecc_family_t family =
+                mbedtls_ecc_group_to_psa(ec->grp.id, &bits);
+            int want_private = 0;
+            psa_algorithm_t alg = 0;
+            switch (usage) {
+                case PSA_KEY_USAGE_SIGN_MESSAGE:
+                case PSA_KEY_USAGE_SIGN_HASH:
+                    want_private = 1;
+                /* FALLTHROUGH */
+                case PSA_KEY_USAGE_VERIFY_MESSAGE:
+                case PSA_KEY_USAGE_VERIFY_HASH:
+                    if (!sign_ok) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
+                    alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_ANY_HASH);
+#else
+                    alg = PSA_ALG_ECDSA(PSA_ALG_ANY_HASH);
+#endif
+                    break;
+                case PSA_KEY_USAGE_DERIVE:
+                    want_private = 1;
+                    alg = PSA_ALG_ECDH;
+                    if (!derive_ok) {
+                        return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+                    }
+                    break;
+                default:
+                    return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            if (want_private && !has_private) {
+                return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+            }
+            psa_set_key_type(attributes, (want_private ?
+                                          PSA_KEY_TYPE_ECC_KEY_PAIR(family) :
+                                          PSA_KEY_TYPE_ECC_PUBLIC_KEY(family)));
+            psa_set_key_bits(attributes, bits);
+            psa_set_key_algorithm(attributes, alg);
+            break;
+        }
+#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
+
 #if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
         case MBEDTLS_PK_RSA_ALT:
             return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
@@ -459,8 +518,7 @@
             return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
     }
 
-    usage |= PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY;
-    psa_set_key_usage_flags(attributes, usage);
+    psa_set_key_usage_flags(attributes, more_usage);
 #if defined(MBEDTLS_PSA_CRYPTO_C)
     /* Assume that we have all Mbed TLS attributes. When
      * MBEDTLS_PSA_CRYPTO_CLIENT is enabled but not MBEDTLS_PSA_CRYPTO_C,
diff --git a/tests/suites/test_suite_pk.data b/tests/suites/test_suite_pk.data
index bc0de71..f0903e3 100644
--- a/tests/suites/test_suite_pk.data
+++ b/tests/suites/test_suite_pk.data
@@ -789,3 +789,171 @@
 PSA attributes for pk: RSA v15 public DERIVE (bad)
 depends_on:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15
 pk_get_psa_attributes_fail:MBEDTLS_PK_RSA:0:PSA_KEY_USAGE_DERIVE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY pair DECRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_DECRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH pair DECRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_DECRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA pair DECRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_DECRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY public DECRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_DECRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public DECRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_DECRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA public DECRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_DECRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY pair ENCRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_ENCRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH pair ENCRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_ENCRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA pair ENCRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_ENCRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY public ENCRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_ENCRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public ENCRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_ENCRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA public ENCRYPT (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_ENCRYPT:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY pair DERIVE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_DERIVE:1:PSA_ALG_ECDH
+
+PSA attributes for pk: ECKEY_DH pair DERIVE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_DERIVE:1:PSA_ALG_ECDH
+
+PSA attributes for pk: ECDSA pair DERIVE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_DERIVE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY public DERIVE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_DERIVE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public DERIVE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_DERIVE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA public DERIVE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_DERIVE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY pair SIGN_MESSAGE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_SIGN_MESSAGE:1:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECDSA pair SIGN_MESSAGE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_SIGN_MESSAGE:1:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECKEY pair SIGN_HASH
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_SIGN_HASH:1:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECDSA pair SIGN_HASH
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_SIGN_HASH:1:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECKEY pair->public VERIFY_MESSAGE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_VERIFY_MESSAGE:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECDSA pair->public VERIFY_MESSAGE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_VERIFY_MESSAGE:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECKEY pair->public VERIFY_HASH
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:1:PSA_KEY_USAGE_VERIFY_HASH:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECDSA pair->public VERIFY_HASH
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECDSA:1:PSA_KEY_USAGE_VERIFY_HASH:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECKEY public VERIFY_MESSAGE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_VERIFY_MESSAGE:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECDSA public VERIFY_MESSAGE
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_VERIFY_MESSAGE:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECKEY public VERIFY_HASH
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_VERIFY_HASH:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECDSA public VERIFY_HASH
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_VERIFY_HASH:0:PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)
+
+PSA attributes for pk: ECKEY public SIGN_MESSAGE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_SIGN_MESSAGE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA public SIGN_MESSAGE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_SIGN_MESSAGE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY public SIGN_HASH (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY:0:PSA_KEY_USAGE_SIGN_HASH:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECDSA public SIGN_HASH (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECDSA:0:PSA_KEY_USAGE_SIGN_HASH:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH pair SIGN_MESSAGE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_SIGN_MESSAGE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH pair SIGN_HASH (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_SIGN_HASH:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH pair VERIFY_MESSAGE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_VERIFY_MESSAGE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH pair VERIFY_HASH (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:1:PSA_KEY_USAGE_VERIFY_HASH:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public SIGN_MESSAGE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_SIGN_MESSAGE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public SIGN_HASH (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_SIGN_HASH:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public VERIFY_MESSAGE (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_VERIFY_MESSAGE:MBEDTLS_ERR_PK_TYPE_MISMATCH
+
+PSA attributes for pk: ECKEY_DH public VERIFY_HASH (bad)
+depends_on:MBEDTLS_PK_HAVE_ECC_KEYS
+pk_get_psa_attributes_fail:MBEDTLS_PK_ECKEY_DH:0:PSA_KEY_USAGE_VERIFY_HASH:MBEDTLS_ERR_PK_TYPE_MISMATCH
diff --git a/tests/suites/test_suite_pk.function b/tests/suites/test_suite_pk.function
index d6902b4..90c5044 100644
--- a/tests/suites/test_suite_pk.function
+++ b/tests/suites/test_suite_pk.function
@@ -187,7 +187,7 @@
             } else {
                 unsigned char N[PSA_BITS_TO_BYTES(MBEDTLS_RSA_GEN_KEY_MIN_BITS)] = { 0xff };
                 N[sizeof(N) - 1] = 0x03;
-                const unsigned char E[1] = {0x03};
+                const unsigned char E[1] = { 0x03 };
                 TEST_EQUAL(mbedtls_rsa_import_raw(rsa,
                                                   N, sizeof(N),
                                                   NULL, 0, NULL, 0, NULL, 0,
@@ -198,6 +198,23 @@
         }
 #endif /* MBEDTLS_RSA_C */
 
+#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
+        case MBEDTLS_PK_ECKEY:
+        case MBEDTLS_PK_ECKEY_DH:
+        case MBEDTLS_PK_ECDSA:
+        {
+            mbedtls_ecp_group_id grp_id = mbedtls_ecp_grp_id_list()[0];
+            size_t bits;
+            *psa_type = PSA_KEY_TYPE_ECC_KEY_PAIR(mbedtls_ecc_group_to_psa(grp_id, &bits));
+            TEST_EQUAL(pk_genkey(pk, grp_id), 0);
+            if (!want_pair) {
+                mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*pk);
+                mbedtls_mpi_free(&ec->d);
+            }
+            break;
+        }
+#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */
+
         default:
             TEST_FAIL("Unknown PK type in test data");
             break;
@@ -1674,6 +1691,17 @@
     }
     expected_usage |= PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY;
 
+#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
+    /* When the resulting algorithm is ECDSA, the compile-time configuration
+     * can cause it to be either deterministic or randomized ECDSA.
+     * Rather than have two near-identical sets of test data depending on
+     * the configuration, always use randomized in the test data and
+     * tweak the expected result here. */
+    if (expected_alg == PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)) {
+        expected_alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_ANY_HASH);
+    }
+#endif
+
     TEST_EQUAL(mbedtls_pk_get_psa_attributes(&pk, usage, &attributes), 0);
 
     TEST_EQUAL(psa_get_key_lifetime(&attributes), lifetime);