Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement mbedtls_pk_import_into_psa #8807

Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
48b87eb
Choose a curve for tests at compile time
gilles-peskine-arm Feb 7, 2024
05ee3fb
mbedtls_pk_import_into_psa: documentation
gilles-peskine-arm Feb 7, 2024
4781bd9
exercise_key: allow SIGN_MESSAGE/VERIFY_MESSAGE with PSA_ALG_ANY_HASH
gilles-peskine-arm Feb 9, 2024
fdb809e
exercise_key: fix asymmetric encrypt/decrypt with >2028-bit RSA
gilles-peskine-arm Feb 9, 2024
fc3d866
mbedtls_pk_import_into_psa: implement and test
gilles-peskine-arm Feb 9, 2024
10e9c41
mbedtls_pk_import_into_psa: negative tests for different ECC curve
gilles-peskine-arm Feb 9, 2024
157679c
mbedtls_pk_import_into_psa: positive tests with pkparse output
gilles-peskine-arm Feb 9, 2024
35cb319
depends.py: set unique configuration names in outcome file
gilles-peskine-arm Feb 12, 2024
74860dd
Don't define pk_sign_verify in configurations where it's unused
gilles-peskine-arm Feb 12, 2024
1d33876
Fix some preprocessor guards
gilles-peskine-arm Feb 12, 2024
3495567
Don't exercise if the algorithm is not supported
gilles-peskine-arm Feb 12, 2024
465e4ed
Prioritize SHA2 over MD5 for KNOWN_SUPPORTED_HASH_ALG
gilles-peskine-arm Feb 12, 2024
cbd2cbb
Rename identifier for consistency
gilles-peskine-arm Feb 15, 2024
f50cd59
Fix encrypt/decrypt confusion
gilles-peskine-arm Feb 15, 2024
68a287d
Use named constants FROM_PAIR/FROM_PUBLIC for readability
gilles-peskine-arm Feb 15, 2024
d6fc350
Test mbedtls_pk_import_into_psa with different bits
gilles-peskine-arm Feb 15, 2024
4da1f01
Fix copypasta
gilles-peskine-arm Feb 15, 2024
6fe8a06
New test helper: mbedtls_test_key_consistency_psa_pk
gilles-peskine-arm Feb 15, 2024
2ec141a
After pk_import_into_psa, test that the keys match
gilles-peskine-arm Feb 15, 2024
83b8baf
mbedtls_pk_import_into_psa: fix Montgomery keys in the legacy case
gilles-peskine-arm Feb 15, 2024
dd49c73
Merge remote-tracking branch 'development' into pk_import_into_psa-im…
gilles-peskine-arm Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.d/mbedtls_pk_import_into_psa.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Features
* The new functions mbedtls_pk_get_psa_attributes() and
mbedtls_pk_import_into_psa() provide a uniform way to create a PSA
key from a PK key.
48 changes: 48 additions & 0 deletions include/mbedtls/pk.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,54 @@ int mbedtls_pk_can_do_ext(const mbedtls_pk_context *ctx, psa_algorithm_t alg,
int mbedtls_pk_get_psa_attributes(const mbedtls_pk_context *pk,
psa_key_usage_t usage,
psa_key_attributes_t *attributes);

/**
* \brief Import a key into the PSA key store.
*
* This function is equivalent to calling psa_import_key()
* with the key material from \p pk.
*
* The typical way to use this function is:
* -# Call mbedtls_pk_get_psa_attributes() to obtain
* attributes for the given key.
* -# If desired, modify the attributes, for example:
* - To create a persistent key, call
* psa_set_key_identifier() and optionally
* psa_set_key_lifetime().
* - To import only the public part of a key pair:
* ```
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doxygen doesn't recognize the backticks as meaning a code block here. I don't know what its rules are. Fixed in #8887.

* psa_set_key_type(&attributes,
* PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(
* psa_get_key_type(&attributes)));
* ```
* - Restrict the key usage if desired.
* -# Call mbedtls_pk_import_into_psa().
*
* \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[in] attributes
* The attributes to use for the new key. They must be
* compatible with \p pk. In particular, the key type
* must match the content of \p pk.
* If \p pk contains a key pair, the key type in
* attributes can be either the key pair type or the
* corresponding public key type (to import only the
* public part).
* \param[out] key_id
* On success, the identifier of the newly created key.
* On error, this is #MBEDTLS_SVC_KEY_ID_INIT.
*
* \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_import_into_psa(const mbedtls_pk_context *pk,
const psa_key_attributes_t *attributes,
mbedtls_svc_key_id_t *key_id);
#endif /* MBEDTLS_PSA_CRYPTO_C */

/**
Expand Down
2 changes: 1 addition & 1 deletion include/psa/crypto_values.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@
((type) | PSA_KEY_TYPE_CATEGORY_FLAG_PAIR)
/** The public key type corresponding to a key pair type.
*
* You may also pass a key pair type as \p type, it will be left unchanged.
* You may also pass a public key type as \p type, it will be left unchanged.
*
* \param type A public key type or key pair type.
*
Expand Down
281 changes: 279 additions & 2 deletions library/pk.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@

#if defined(MBEDTLS_RSA_C)
#include "mbedtls/rsa.h"
#if defined(MBEDTLS_PKCS1_V21) && !defined(MBEDTLS_USE_PSA_CRYPTO)
#include "rsa_internal.h"
#endif
#endif
#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
#include "mbedtls/ecp.h"
#endif
Expand Down Expand Up @@ -579,6 +577,285 @@ int mbedtls_pk_get_psa_attributes(const mbedtls_pk_context *pk,

return 0;
}

#if defined(MBEDTLS_PK_USE_PSA_EC_DATA) || defined(MBEDTLS_USE_PSA_CRYPTO)
mpg marked this conversation as resolved.
Show resolved Hide resolved
static psa_status_t export_import_into_psa(mbedtls_svc_key_id_t old_key_id,
const psa_key_attributes_t *attributes,
mbedtls_svc_key_id_t *new_key_id)
{
unsigned char key_buffer[PSA_EXPORT_KEY_PAIR_MAX_SIZE];
size_t key_length = 0;
psa_status_t status = psa_export_key(old_key_id,
key_buffer, sizeof(key_buffer),
&key_length);
if (status != PSA_SUCCESS) {
return status;
}
status = psa_import_key(attributes, key_buffer, key_length, new_key_id);
mbedtls_platform_zeroize(key_buffer, key_length);
return status;
}

static int copy_into_psa(mbedtls_svc_key_id_t old_key_id,
const psa_key_attributes_t *attributes,
mbedtls_svc_key_id_t *new_key_id)
{
/* Normally, we prefer copying: it's more efficient and works even
* for non-exportable keys. */
psa_status_t status = psa_copy_key(old_key_id, attributes, new_key_id);
if (status == PSA_ERROR_NOT_PERMITTED /*missing COPY usage*/ ||
status == PSA_ERROR_INVALID_ARGUMENT /*incompatible policy*/) {
/* There are edge cases where copying won't work, but export+import
* might:
* - If the old key does not allow PSA_KEY_USAGE_COPY.
* - If the old key's usage does not allow what attributes wants.
* Because the key was intended for use in the pk module, and may
* have had a policy chosen solely for what pk needs rather than
* based on a detailed understanding of PSA policies, we are a bit
* more liberal than psa_copy_key() here.
*/
/* Here we need to check that the types match, otherwise we risk
* importing nonsensical data. */
psa_key_attributes_t old_attributes = PSA_KEY_ATTRIBUTES_INIT;
status = psa_get_key_attributes(old_key_id, &old_attributes);
if (status != PSA_SUCCESS) {
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
}
psa_key_type_t old_type = psa_get_key_type(&old_attributes);
psa_reset_key_attributes(&old_attributes);
if (old_type != psa_get_key_type(attributes)) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
status = export_import_into_psa(old_key_id, attributes, new_key_id);
}
return PSA_PK_TO_MBEDTLS_ERR(status);
}
#endif /* MBEDTLS_PK_USE_PSA_EC_DATA || MBEDTLS_USE_PSA_CRYPTO */

static int import_pair_into_psa(const mbedtls_pk_context *pk,
const psa_key_attributes_t *attributes,
mbedtls_svc_key_id_t *key_id)
{
switch (mbedtls_pk_get_type(pk)) {
#if defined(MBEDTLS_RSA_C)
case MBEDTLS_PK_RSA:
{
if (psa_get_key_type(attributes) != PSA_KEY_TYPE_RSA_KEY_PAIR) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
unsigned char key_buffer[
PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS)];
unsigned char *const key_end = key_buffer + sizeof(key_buffer);
unsigned char *key_data = key_end;
int ret = mbedtls_rsa_write_key(mbedtls_pk_rsa(*pk),
key_buffer, &key_data);
if (ret < 0) {
return ret;
}
size_t key_length = key_end - key_data;
ret = PSA_PK_TO_MBEDTLS_ERR(psa_import_key(attributes,
key_data, key_length,
key_id));
mbedtls_platform_zeroize(key_data, key_length);
return ret;
}
#endif /* MBEDTLS_RSA_C */

#if defined(MBEDTLS_PK_HAVE_ECC_KEYS)
case MBEDTLS_PK_ECKEY:
case MBEDTLS_PK_ECKEY_DH:
case MBEDTLS_PK_ECDSA:
{
/* We need to check the curve family, otherwise the import could
* succeed with nonsensical data.
* We don't check the bit-size: it's optional in attributes,
* and if it's specified, psa_import_key() will know from the key
* data length and will check that the bit-size matches. */
psa_key_type_t to_type = psa_get_key_type(attributes);
#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
psa_ecc_family_t from_family = pk->ec_family;
#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
/* We're only reading the key, but mbedtls_ecp_write_key()
* is missing a const annotation on its key parameter, so
* we need the non-const accessor here. */
mpg marked this conversation as resolved.
Show resolved Hide resolved
mbedtls_ecp_keypair *ec = mbedtls_pk_ec_rw(*pk);
size_t from_bits = 0;
psa_ecc_family_t from_family = mbedtls_ecc_group_to_psa(ec->grp.id,
&from_bits);
#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
if (to_type != PSA_KEY_TYPE_ECC_KEY_PAIR(from_family)) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}

#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
if (mbedtls_svc_key_id_is_null(pk->priv_id)) {
/* We have a public key and want a key pair. */
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
return copy_into_psa(pk->priv_id, attributes, key_id);
#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
if (ec->d.n == 0) {
/* Private key not set. Assume the input is a public key only.
* (The other possibility is that it's an incomplete object
* where the group is set but neither the public key nor
* the private key. This is not possible through ecp.h
* functions, so we don't bother reporting a more suitable
* error in that case.) */
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
unsigned char key_buffer[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
/* Make sure to pass the exact key length to
* mbedtls_ecp_write_key(), because it writes Montgomery keys
* at the start of the buffer but Weierstrass keys at the
* end of the buffer. */
size_t key_length = PSA_BITS_TO_BYTES(ec->grp.nbits);
int ret = mbedtls_ecp_write_key(ec, key_buffer, key_length);
if (ret < 0) {
return ret;
}
ret = PSA_PK_TO_MBEDTLS_ERR(psa_import_key(attributes,
key_buffer, key_length,
key_id));
mbedtls_platform_zeroize(key_buffer, key_length);
return ret;
#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
}
#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */

#if defined(MBEDTLS_USE_PSA_CRYPTO)
case MBEDTLS_PK_OPAQUE:
return copy_into_psa(pk->priv_id, attributes, key_id);
#endif /* MBEDTLS_USE_PSA_CRYPTO */

default:
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
}
}

static int import_public_into_psa(const mbedtls_pk_context *pk,
const psa_key_attributes_t *attributes,
mbedtls_svc_key_id_t *key_id)
{
psa_key_type_t psa_type = psa_get_key_type(attributes);

#if defined(MBEDTLS_RSA_C) || \
(defined(MBEDTLS_PK_HAVE_ECC_KEYS) && !defined(MBEDTLS_PK_USE_PSA_EC_DATA)) || \
defined(MBEDTLS_USE_PSA_CRYPTO)
unsigned char key_buffer[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
#endif
unsigned char *key_data = NULL;
mpg marked this conversation as resolved.
Show resolved Hide resolved
size_t key_length = 0;

switch (mbedtls_pk_get_type(pk)) {
#if defined(MBEDTLS_RSA_C)
case MBEDTLS_PK_RSA:
{
if (psa_type != PSA_KEY_TYPE_RSA_PUBLIC_KEY) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
unsigned char *const key_end = key_buffer + sizeof(key_buffer);
key_data = key_end;
int ret = mbedtls_rsa_write_pubkey(mbedtls_pk_rsa(*pk),
key_buffer, &key_data);
if (ret < 0) {
return ret;
}
key_length = (size_t) ret;
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:
{
/* We need to check the curve family, otherwise the import could
* succeed with nonsensical data.
* We don't check the bit-size: it's optional in attributes,
* and if it's specified, psa_import_key() will know from the key
* data length and will check that the bit-size matches. */
#if defined(MBEDTLS_PK_USE_PSA_EC_DATA)
if (psa_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(pk->ec_family)) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
key_data = (unsigned char *) pk->pub_raw;
key_length = pk->pub_raw_len;
#else /* MBEDTLS_PK_USE_PSA_EC_DATA */
const mbedtls_ecp_keypair *ec = mbedtls_pk_ec_ro(*pk);
size_t from_bits = 0;
psa_ecc_family_t from_family = mbedtls_ecc_group_to_psa(ec->grp.id,
&from_bits);
if (psa_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(from_family)) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
int ret = mbedtls_ecp_write_public_key(
ec, MBEDTLS_ECP_PF_UNCOMPRESSED,
&key_length, key_buffer, sizeof(key_buffer));
if (ret < 0) {
return ret;
}
key_data = key_buffer;
#endif /* MBEDTLS_PK_USE_PSA_EC_DATA */
break;
}
#endif /* MBEDTLS_PK_HAVE_ECC_KEYS */

#if defined(MBEDTLS_USE_PSA_CRYPTO)
case MBEDTLS_PK_OPAQUE:
{
psa_key_attributes_t old_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status =
psa_get_key_attributes(pk->priv_id, &old_attributes);
if (status != PSA_SUCCESS) {
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
}
psa_key_type_t old_type = psa_get_key_type(&old_attributes);
psa_reset_key_attributes(&old_attributes);
if (psa_type != PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(old_type)) {
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
status = psa_export_public_key(pk->priv_id,
key_buffer, sizeof(key_buffer),
&key_length);
if (status != PSA_SUCCESS) {
return PSA_PK_TO_MBEDTLS_ERR(status);
}
key_data = key_buffer;
break;
}
#endif /* MBEDTLS_USE_PSA_CRYPTO */

default:
return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
}

return PSA_PK_TO_MBEDTLS_ERR(psa_import_key(attributes,
key_data, key_length,
key_id));
}

int mbedtls_pk_import_into_psa(const mbedtls_pk_context *pk,
const psa_key_attributes_t *attributes,
mbedtls_svc_key_id_t *key_id)
{
/* Set the output immediately so that it won't contain garbage even
* if we error out before calling psa_import_key(). */
*key_id = MBEDTLS_SVC_KEY_ID_INIT;

#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_RSA_ALT) {
return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
}
#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */

int want_public = PSA_KEY_TYPE_IS_PUBLIC_KEY(psa_get_key_type(attributes));
if (want_public) {
return import_public_into_psa(pk, attributes, key_id);
} else {
return import_pair_into_psa(pk, attributes, key_id);
}
}
#endif /* MBEDTLS_PSA_CRYPTO_C */

/*
Expand Down
4 changes: 2 additions & 2 deletions library/pk_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#include "mbedtls/ecp.h"
#endif

#if defined(MBEDTLS_USE_PSA_CRYPTO)
#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
#include "psa/crypto.h"

#include "psa_util_internal.h"
Expand All @@ -28,7 +28,7 @@
#define PSA_PK_ECDSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \
psa_to_pk_ecdsa_errors, \
psa_pk_status_to_mbedtls)
#endif /* MBEDTLS_USE_PSA_CRYPTO */
#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */

/* Headers/footers for PEM files */
#define PEM_BEGIN_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----"
Expand Down
2 changes: 2 additions & 0 deletions library/psa_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ int psa_pk_status_to_mbedtls(psa_status_t status)
return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
case PSA_ERROR_INVALID_ARGUMENT:
return MBEDTLS_ERR_PK_INVALID_ALG;
case PSA_ERROR_NOT_PERMITTED:
return MBEDTLS_ERR_PK_TYPE_MISMATCH;
case PSA_ERROR_INSUFFICIENT_MEMORY:
return MBEDTLS_ERR_PK_ALLOC_FAILED;
case PSA_ERROR_BAD_STATE:
Expand Down
Loading