Introduce mbedtls_pk_get_psa_attributes
Follow the specification in https://github.com/Mbed-TLS/mbedtls/pull/8657
as of dd77343381161e09a63b4694001da3957e27d3a7, i.e.
https://github.com/Mbed-TLS/mbedtls/blob/dd77343381161e09a63b4694001da3957e27d3a7/docs/architecture/psa-migration/psa-legacy-bridges.md#api-to-create-a-psa-key-from-a-pk-context
This commit introduces the function declaration, its documentation, the
definition without the interesting parts and a negative unit test function.
Subsequent commits will add RSA, ECC and PK_OPAQUE support.
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/include/mbedtls/pk.h b/include/mbedtls/pk.h
index 27768bd..a43b949 100644
--- a/include/mbedtls/pk.h
+++ b/include/mbedtls/pk.h
@@ -28,7 +28,7 @@
#include "mbedtls/ecdsa.h"
#endif
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
#include "psa/crypto.h"
#endif
@@ -484,6 +484,120 @@
psa_key_usage_t usage);
#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+/**
+ * \brief Determine valid PSA attributes that can be used to
+ * import a key into PSA.
+ *
+ * The attributes determined by this function are suitable
+ * for calling mbedtls_pk_import_into_psa() to create
+ * a PSA key with the same key material.
+ *
+ * The typical flow of operations involving this function is
+ * ```
+ * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ * int ret = mbedtls_pk_get_psa_attributes(pk, &attributes);
+ * if (ret != 0) ...; // error handling omitted
+ * // Tweak attributes if desired
+ * psa_key_id_t key_id = 0;
+ * ret = mbedtls_pk_import_into_psa(pk, &attributes, &key_id);
+ * if (ret != 0) ...; // error handling omitted
+ * ```
+ *
+ * \note This function does not support RSA-alt contexts
+ * (set up with mbedtls_pk_setup_rsa_alt()).
+ *
+ * \param[in] pk The PK context to use. It must have been set up.
+ * It can either contain a key pair or just a public key.
+ * \param usage A single `PSA_KEY_USAGE_xxx` flag among the following:
+ * - #PSA_KEY_USAGE_DECRYPT: \p pk must contain a
+ * key pair. The output \p attributes will contain a
+ * key pair type, and the usage policy will allow
+ * #PSA_KEY_USAGE_ENCRYPT as well as
+ * #PSA_KEY_USAGE_DECRYPT.
+ * - #PSA_KEY_USAGE_DERIVE: \p pk must contain a
+ * key pair. The output \p attributes will contain a
+ * key pair type.
+ * - #PSA_KEY_USAGE_ENCRYPT: The output
+ * \p attributes will contain a public key type.
+ * - #PSA_KEY_USAGE_SIGN_HASH: \p pk must contain a
+ * key pair. The output \p attributes will contain a
+ * key pair type, and the usage policy will allow
+ * #PSA_KEY_USAGE_VERIFY_HASH as well as
+ * #PSA_KEY_USAGE_SIGN_HASH.
+ * - #PSA_KEY_USAGE_SIGN_MESSAGE: \p pk must contain a
+ * key pair. The output \p attributes will contain a
+ * key pair type, and the usage policy will allow
+ * #PSA_KEY_USAGE_VERIFY_MESSAGE as well as
+ * #PSA_KEY_USAGE_SIGN_MESSAGE.
+ * - #PSA_KEY_USAGE_VERIFY_HASH: The output
+ * \p attributes will contain a public key type.
+ * - #PSA_KEY_USAGE_VERIFY_MESSAGE: The output
+ * \p attributes will contain a public key type.
+ * \param[out] attributes
+ * On success, valid attributes to import the key into PSA.
+ * - The lifetime and key identifier are unchanged. If the
+ * attribute structure was initialized or reset before
+ * calling this function, this will result in a volatile
+ * key. Call psa_set_key_identifier() before or after this
+ * function if you wish to create a persistent key. Call
+ * psa_set_key_lifetime() before or after this function if
+ * you wish to import the key in a secure element.
+ * - The key type and bit-size are determined by the contents
+ * of the PK context. If the PK context contains a key
+ * pair, the key type can be either a key pair type or
+ * the corresponding public key type, depending on
+ * \p usage. If the PK context contains a public key,
+ * the key type is a public key type.
+ * - The key's policy is determined by the key type and
+ * the \p usage parameter. The usage always allows
+ * \p usage, exporting and copying the key, and
+ * possibly other permissions as documented for the
+ * \p usage parameter.
+ * The permitted algorithm is determined as follows
+ * based on the #mbedtls_pk_type_t type of \p pk,
+ * the chosen \p usage and other factors:
+ * - #MBEDTLS_PK_RSA with whose underlying
+ * #mbedtls_rsa_context has the padding mode
+ * #MBEDTLS_RSA_PKCS_V15:
+ * #PSA_ALG_RSA_PKCS1V15_SIGN(#PSA_ALG_ANY_HASH)
+ * if \p usage is SIGN/VERIFY, and
+ * #PSA_ALG_RSA_PKCS1V15_CRYPT
+ * if \p usage is ENCRYPT/DECRYPT.
+ * - #MBEDTLS_PK_RSA with whose underlying
+ * #mbedtls_rsa_context has the padding mode
+ * #MBEDTLS_RSA_PKCS_V21 and the digest type
+ * corresponding to the PSA algorithm \c hash:
+ * #PSA_ALG_RSA_PSS_ANY_SALT(#PSA_ALG_ANY_HASH)
+ * if \p usage is SIGN/VERIFY, and
+ * #PSA_ALG_RSA_OAEP(\c hash)
+ * if \p usage is ENCRYPT/DECRYPT.
+ * - #MBEDTLS_PK_RSA_ALT: not supported.
+ * - #MBEDTLS_PK_ECDSA or #MBEDTLS_PK_ECKEY
+ * if \p usage is SIGN/VERIFY:
+ * #PSA_ALG_DETERMINISTIC_ECDSA(#PSA_ALG_ANY_HASH)
+ * if #MBEDTLS_ECDSA_DETERMINISTIC is enabled,
+ * otherwise #PSA_ALG_ECDSA(#PSA_ALG_ANY_HASH).
+ * - #MBEDTLS_PK_ECKEY_DH or #MBEDTLS_PK_ECKEY
+ * if \p usage is DERIVE:
+ * #PSA_ALG_ECDH.
+ * - #MBEDTLS_PK_OPAQUE: same as the algorithm policy
+ * set for the underlying PSA key, except that
+ * sign/decrypt flags are removed if the type is
+ * set to a public key type.
+ * Note that the enrollment algorithm set with
+ * psa_set_key_enrollment_algorithm() is not copied.
+ *
+ * \return 0 on success.
+ * #MBEDTLS_ERR_PK_TYPE_MISMATCH if \p pk does not contain
+ * a key of the type identified in \p attributes.
+ * Another error code on other failures.
+ */
+int mbedtls_pk_get_psa_attributes(const mbedtls_pk_context *pk,
+ psa_key_usage_t usage,
+ psa_key_attributes_t *attributes);
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
+
/**
* \brief Verify signature (including padding if relevant).
*
diff --git a/library/pk.c b/library/pk.c
index 61ac0df..bde561a 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -29,7 +29,7 @@
#include "mbedtls/ecdsa.h"
#endif
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
#include "psa_util_internal.h"
#include "md_psa.h"
#endif
@@ -378,6 +378,30 @@
}
#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+int mbedtls_pk_get_psa_attributes(const mbedtls_pk_context *pk,
+ psa_key_usage_t usage,
+ psa_key_attributes_t *attributes)
+{
+ mbedtls_pk_type_t pk_type = mbedtls_pk_get_type(pk);
+
+ switch (pk_type) {
+#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
+ case MBEDTLS_PK_RSA_ALT:
+ return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
+#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */
+
+ default:
+ return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+ }
+
+ usage |= PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY;
+ psa_set_key_usage_flags(attributes, usage);
+
+ return 0;
+}
+#endif
+
/*
* Helper for mbedtls_pk_sign and mbedtls_pk_verify
*/
diff --git a/tests/suites/test_suite_pk.data b/tests/suites/test_suite_pk.data
index af1e20c..e8ffff4 100644
--- a/tests/suites/test_suite_pk.data
+++ b/tests/suites/test_suite_pk.data
@@ -680,3 +680,8 @@
PSA wrapped sign ext: RSA2048, PK_RSASSA_PSS, MD_SHA512
depends_on:MBEDTLS_PKCS1_V21:MBEDTLS_MD_CAN_SHA512:MBEDTLS_RSA_C
pk_psa_wrap_sign_ext:MBEDTLS_PK_RSA:2048:MBEDTLS_PK_RSASSA_PSS:MBEDTLS_MD_SHA512
+
+PSA attributes for pk: NONE (bad)
+pk_get_psa_attributes_fail:MBEDTLS_PK_NONE:0:PSA_KEY_USAGE_SIGN_MESSAGE:MBEDTLS_ERR_PK_BAD_INPUT_DATA
+
+# There is a (negative) test for pk_type=MBEDTLS_PK_RSA_ALT in pk_rsa_alt().
diff --git a/tests/suites/test_suite_pk.function b/tests/suites/test_suite_pk.function
index 226598c..0ac84a2 100644
--- a/tests/suites/test_suite_pk.function
+++ b/tests/suites/test_suite_pk.function
@@ -162,6 +162,33 @@
}
#endif /* MBEDTLS_RSA_C */
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+static int pk_setup_for_type(mbedtls_pk_type_t pk_type, int want_pair,
+ mbedtls_pk_context *pk, psa_key_type_t *psa_type)
+{
+ int ok = 0;
+
+ if (pk_type == MBEDTLS_PK_NONE) {
+ return 1;
+ }
+ TEST_EQUAL(mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(pk_type)), 0);
+
+ switch (pk_type) {
+ default:
+ TEST_FAIL("Unknown PK type in test data");
+ break;
+ }
+
+ if (!want_pair) {
+ *psa_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(*psa_type);
+ }
+ ok = 1;
+
+exit:
+ return ok;
+}
+#endif
+
#if defined(MBEDTLS_USE_PSA_CRYPTO)
/*
@@ -1263,6 +1290,14 @@
TEST_ASSERT(mbedtls_pk_get_type(&alt) == MBEDTLS_PK_RSA_ALT);
TEST_ASSERT(strcmp(mbedtls_pk_get_name(&alt), "RSA-alt") == 0);
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ TEST_EQUAL(mbedtls_pk_get_psa_attributes(&alt,
+ PSA_KEY_USAGE_ENCRYPT,
+ &attributes),
+ MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE);
+#endif /* MBEDTLS_PSA_CRYPTO_C */
+
/* Test signature */
#if SIZE_MAX > UINT_MAX
TEST_ASSERT(mbedtls_pk_sign(&alt, MBEDTLS_MD_NONE, hash, SIZE_MAX,
@@ -1569,3 +1604,30 @@
PSA_DONE();
}
/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_C */
+void pk_get_psa_attributes_fail(int pk_type, int from_pair,
+ int usage_arg,
+ int expected_ret)
+{
+ mbedtls_pk_context pk;
+ mbedtls_pk_init(&pk);
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_key_usage_t usage = usage_arg;
+
+ MD_OR_USE_PSA_INIT();
+
+ psa_key_type_t expected_psa_type;
+ if (!pk_setup_for_type(pk_type, from_pair, &pk, &expected_psa_type)) {
+ goto exit;
+ }
+
+ TEST_EQUAL(mbedtls_pk_get_psa_attributes(&pk, usage, &attributes),
+ expected_ret);
+
+exit:
+ mbedtls_pk_free(&pk);
+ psa_reset_key_attributes(&attributes);
+ MD_OR_USE_PSA_DONE();
+}
+/* END_CASE */