diff --git a/.gitignore b/.gitignore index 9542e25..3b6fb23 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,8 @@ test/psa_devid_cryptocb_test test/build-probe test/build-cryptocb test/build-cryptocb-asan +test/build-enroll +test/build-enroll-asan test/psa_store_commit_test test/psa_store_dir_validation_test test/psa_store_read_open_test @@ -80,6 +82,9 @@ test/psa_kdf_psk_to_ms_size_test test/psa_kdf_zeroize_output_test test/psa_import_zero_length_test test/psa_copy_key_cross_lifetime_test -build-test/out/ -test/psa_zero_capacity_buffer_test test/psa_tf_psa_extra_test +test/psa_zero_capacity_buffer_test +test/psa_enrollment_alg_test +build-test/out/ +.cache/ +compile_commands.json diff --git a/Makefile b/Makefile index 2e09f21..eecf736 100644 --- a/Makefile +++ b/Makefile @@ -104,6 +104,10 @@ endif ifeq ($(AES_FAST),1) CPPFLAGS += -DWOLFPSA_AES_FAST endif +# ENROLLMENT_ALG=1 allows the Mbed TLS enrollment algorithm on volatile keys +ifeq ($(ENROLLMENT_ALG),1) +CPPFLAGS += -DWOLFPSA_ENROLLMENT_ALG +endif CFLAGS += $(DEBUG_FLAGS) $(SANITIZE_FLAGS) LDFLAGS += $(SANITIZE_FLAGS) @@ -204,6 +208,7 @@ UNIT_TESTS := psa_api_test \ psa_store_read_open_test \ psa_store_dir_validation_test \ psa_devid_cryptocb_test \ + psa_enrollment_alg_test \ psa_kdf_zeroize_output_test \ psa_import_zero_length_test \ psa_copy_key_cross_lifetime_test \ diff --git a/src/psa_aead.c b/src/psa_aead.c index 0459860..1ea76ed 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -152,6 +152,33 @@ static psa_status_t wolfpsa_aead_gcm_check_tag_size(size_t tag_length) } #endif +/* Whether permitted algorithm key_alg allows AEAD alg and its tag length */ +static int wolfpsa_aead_alg_permitted(psa_algorithm_t key_alg, + psa_algorithm_t alg) +{ + size_t key_tag_len = wolfpsa_aead_tag_length(key_alg); + size_t req_tag_len = wolfpsa_aead_tag_length(alg); + + if (key_tag_len == 0 || + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(key_alg) != + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg)) { + return 0; + } + if ((key_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return req_tag_len >= key_tag_len; + } + return req_tag_len == key_tag_len; +} + +/* Whether the permitted or enrollment algorithm allows AEAD alg */ +static int wolfpsa_aead_key_permits_alg(const psa_key_attributes_t *attributes, + psa_algorithm_t alg) +{ + return wolfpsa_aead_alg_permitted(psa_get_key_algorithm(attributes), alg) || + wolfpsa_aead_alg_permitted( + psa_get_key_enrollment_algorithm(attributes), alg); +} + static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, psa_key_usage_t usage, psa_algorithm_t alg, @@ -161,11 +188,6 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; - psa_algorithm_t key_base; - psa_algorithm_t req_base; - size_t key_tag_len; - size_t req_tag_len; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -207,43 +229,14 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - key_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(key_alg); - req_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg); - key_tag_len = wolfpsa_aead_tag_length(key_alg); - req_tag_len = wolfpsa_aead_tag_length(alg); - - if (key_tag_len == 0 || req_tag_len == 0) { + if (wolfpsa_aead_tag_length(alg) == 0) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_INVALID_ARGUMENT; } - if (key_base != req_base) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - if ((key_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) { - if (req_tag_len < key_tag_len) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (req_tag_len != key_tag_len) { + if (!wolfpsa_aead_key_permits_alg(attributes, alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; @@ -1525,7 +1518,6 @@ static psa_status_t wolfpsa_xchacha_oneshot_encrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; int ret; /* Encrypt produces plaintext_length bytes of ciphertext plus 16-byte tag. */ size_t out_len; @@ -1569,12 +1561,7 @@ static psa_status_t wolfpsa_xchacha_oneshot_encrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_XCHACHA20_POLY1305)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1611,7 +1598,6 @@ static psa_status_t wolfpsa_xchacha_oneshot_decrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; int ret; size_t pt_len; @@ -1652,12 +1638,7 @@ static psa_status_t wolfpsa_xchacha_oneshot_decrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_XCHACHA20_POLY1305)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1701,7 +1682,6 @@ static psa_status_t wolfpsa_ascon_oneshot_encrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; wc_AsconAEAD128 ascon; int ret; size_t out_len; @@ -1749,12 +1729,7 @@ static psa_status_t wolfpsa_ascon_oneshot_encrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_ASCON_AEAD128)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1807,7 +1782,6 @@ static psa_status_t wolfpsa_ascon_oneshot_decrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; wc_AsconAEAD128 ascon; int ret; size_t ct_len; /* ciphertext body length (without tag) */ @@ -1854,12 +1828,7 @@ static psa_status_t wolfpsa_ascon_oneshot_decrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_ASCON_AEAD128)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index b241b02..b26a380 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -275,6 +275,19 @@ static int wolfpsa_sign_alg_permitted(psa_algorithm_t key_alg, return 0; } +static int wolfpsa_asymmetric_alg_permitted(psa_algorithm_t key_alg, + psa_algorithm_t alg, + psa_key_usage_t usage) +{ + if (key_alg == PSA_ALG_NONE) { + return 0; + } + if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { + return wolfpsa_key_agreement_alg_permitted(key_alg, alg); + } + return wolfpsa_sign_alg_permitted(key_alg, alg, usage); +} + static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, psa_key_usage_t usage, psa_algorithm_t alg, @@ -284,7 +297,6 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -299,24 +311,10 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { - if (!wolfpsa_key_agreement_alg_permitted(key_alg, alg)) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (!wolfpsa_sign_alg_permitted(key_alg, alg, usage)) { + if (!wolfpsa_asymmetric_alg_permitted(psa_get_key_algorithm(attributes), + alg, usage) && + !wolfpsa_asymmetric_alg_permitted( + psa_get_key_enrollment_algorithm(attributes), alg, usage)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; diff --git a/src/psa_cipher.c b/src/psa_cipher.c index f64112c..08ea6b4 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -161,7 +161,6 @@ static psa_status_t wolfpsa_cipher_check_key( { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -228,8 +227,7 @@ static psa_status_t wolfpsa_cipher_check_key( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE || key_alg != alg) { + if (!wolfpsa_key_permits_alg(attributes, alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; diff --git a/src/psa_kem.c b/src/psa_kem.c index 5f99f62..2cfb517 100644 --- a/src/psa_kem.c +++ b/src/psa_kem.c @@ -88,7 +88,6 @@ psa_status_t psa_encapsulate(psa_key_id_t key, psa_key_type_t key_type; size_t key_bits; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; psa_status_t status; uint8_t ss[WOLFPSA_MLKEM_SS_SIZE]; /* Staging buffer: ciphertext is written here before the output key is @@ -129,7 +128,6 @@ psa_status_t psa_encapsulate(psa_key_id_t key, key_type = psa_get_key_type(&key_attr); key_bits = psa_get_key_bits(&key_attr); key_usage = psa_get_key_usage_flags(&key_attr); - key_alg = psa_get_key_algorithm(&key_attr); if (key_type != PSA_KEY_TYPE_ML_KEM_KEY_PAIR && key_type != PSA_KEY_TYPE_ML_KEM_PUBLIC_KEY) { @@ -142,11 +140,7 @@ psa_status_t psa_encapsulate(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - if (key_alg != alg && key_alg != PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (key_alg == PSA_ALG_NONE) { + if (!wolfpsa_key_permits_alg(&key_attr, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -207,7 +201,6 @@ psa_status_t psa_decapsulate(psa_key_id_t key, psa_key_type_t key_type; size_t key_bits; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; psa_status_t status; uint8_t ss[WOLFPSA_MLKEM_SS_SIZE]; @@ -240,7 +233,6 @@ psa_status_t psa_decapsulate(psa_key_id_t key, key_type = psa_get_key_type(&key_attr); key_bits = psa_get_key_bits(&key_attr); key_usage = psa_get_key_usage_flags(&key_attr); - key_alg = psa_get_key_algorithm(&key_attr); /* Decapsulate requires the key pair (private key). */ if (key_type != PSA_KEY_TYPE_ML_KEM_KEY_PAIR) { @@ -253,11 +245,7 @@ psa_status_t psa_decapsulate(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - if (key_alg != alg && key_alg != PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (key_alg == PSA_ALG_NONE) { + if (!wolfpsa_key_permits_alg(&key_attr, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 60f3faf..16de2e9 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -843,6 +843,20 @@ psa_status_t psa_key_derivation_input_integer(psa_key_derivation_operation_t * wolfpsa_kdf_input_integer(operation, step, value)); } +/* Whether a key with permitted algorithm key_alg can be input to ctx */ +static int wolfpsa_kdf_key_alg_permitted(const wolfpsa_kdf_ctx_t *ctx, + psa_algorithm_t key_alg) +{ + if (key_alg == PSA_ALG_NONE) { + return 0; + } + if (ctx->is_key_agreement) { + return PSA_ALG_IS_KEY_AGREEMENT(key_alg) && + PSA_ALG_KEY_AGREEMENT_GET_KDF(key_alg) == ctx->alg; + } + return key_alg == ctx->alg; +} + static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * operation, psa_key_derivation_step_t step, @@ -852,7 +866,6 @@ static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; uint8_t *key_data = NULL; size_t key_data_length = 0; - psa_algorithm_t key_alg; psa_status_t status; if (ctx == NULL) { @@ -914,21 +927,10 @@ static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * return PSA_ERROR_INVALID_ARGUMENT; } - key_alg = psa_get_key_algorithm(&attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - if (ctx->is_key_agreement) { - if (!PSA_ALG_IS_KEY_AGREEMENT(key_alg) || - PSA_ALG_KEY_AGREEMENT_GET_KDF(key_alg) != ctx->alg) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (key_alg != ctx->alg) { + if (!wolfpsa_kdf_key_alg_permitted(ctx, + psa_get_key_algorithm(&attributes)) && + !wolfpsa_kdf_key_alg_permitted( + ctx, psa_get_key_enrollment_algorithm(&attributes))) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 141b3c2..c118ab3 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -992,6 +992,8 @@ static psa_status_t psa_key_attributes_deserialize( XMEMCPY(&attributes->policy.alg, buffer, sizeof(psa_algorithm_t)); buffer += sizeof(psa_algorithm_t); + /* Persistent keys have no enrollment algorithm */ + attributes->policy.alg2 = PSA_ALG_NONE; XMEMCPY(&attributes->lifetime, buffer, sizeof(psa_key_lifetime_t)); @@ -1112,6 +1114,7 @@ psa_status_t psa_import_key( size_t buffer_size; size_t attr_length; int ret = 0; + int alg2_ok = 0; void* store = NULL; psa_key_attributes_t attr; @@ -1157,8 +1160,12 @@ psa_status_t psa_import_key( return PSA_ERROR_NOT_SUPPORTED; } - if (attr.policy.alg2 != PSA_ALG_NONE) { - wolfpsa_debug_import_reason("unsupported secondary algorithm", &attr, +#ifdef WOLFPSA_ENROLLMENT_ALG + /* The persistent key format has no field for the enrollment algorithm */ + alg2_ok = PSA_KEY_LIFETIME_IS_VOLATILE(attr.lifetime); +#endif /* WOLFPSA_ENROLLMENT_ALG */ + if (attr.policy.alg2 != PSA_ALG_NONE && !alg2_ok) { + wolfpsa_debug_import_reason("unsupported enrollment algorithm", &attr, data_length); return PSA_ERROR_NOT_SUPPORTED; } @@ -2565,6 +2572,18 @@ static int wolfpsa_alg_intersect(psa_algorithm_t src_alg, return ok; } +/* As wolfpsa_alg_intersect(), but alg2 unset on either side is dropped */ +static int wolfpsa_alg2_intersect(psa_algorithm_t src_alg, + psa_algorithm_t dst_alg, + psa_algorithm_t* alg) +{ + if (src_alg == PSA_ALG_NONE || dst_alg == PSA_ALG_NONE) { + *alg = PSA_ALG_NONE; + return 1; + } + return wolfpsa_alg_intersect(src_alg, dst_alg, alg); +} + /* Copy a key in the PSA key storage */ psa_status_t psa_copy_key( psa_key_id_t source_key, @@ -2583,6 +2602,7 @@ psa_status_t psa_copy_key( psa_key_attributes_t src_attr = PSA_KEY_ATTRIBUTES_INIT; psa_key_attributes_t dst_attr; psa_algorithm_t copy_alg = PSA_ALG_NONE; + psa_algorithm_t copy_alg2 = PSA_ALG_NONE; /* Check parameters */ if (attributes == NULL || target_key == NULL) { @@ -2626,7 +2646,10 @@ psa_status_t psa_copy_key( } if (!wolfpsa_alg_intersect(psa_get_key_algorithm(&vol_attr), - attributes->policy.alg, ©_alg)) { + attributes->policy.alg, ©_alg) || + !wolfpsa_alg2_intersect(vol_attr.policy.alg2, + attributes->policy.alg2, + ©_alg2)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_INVALID_ARGUMENT; } @@ -2642,6 +2665,7 @@ psa_status_t psa_copy_key( dst_attr.policy.usage = psa_get_key_usage_flags(&vol_attr) & psa_get_key_usage_flags(&dst_attr); dst_attr.policy.alg = copy_alg; + dst_attr.policy.alg2 = copy_alg2; status = psa_import_key(&dst_attr, key_data, key_data_length, target_key); @@ -2697,7 +2721,9 @@ psa_status_t psa_copy_key( } if (!wolfpsa_alg_intersect(psa_get_key_algorithm(&src_attr), - attributes->policy.alg, ©_alg)) { + attributes->policy.alg, ©_alg) || + !wolfpsa_alg2_intersect(src_attr.policy.alg2, attributes->policy.alg2, + ©_alg2)) { wolfPSA_Store_Close(store); return PSA_ERROR_INVALID_ARGUMENT; } @@ -2713,6 +2739,7 @@ psa_status_t psa_copy_key( dst_attr.policy.usage = psa_get_key_usage_flags(&src_attr) & psa_get_key_usage_flags(&dst_attr); dst_attr.policy.alg = copy_alg; + dst_attr.policy.alg2 = copy_alg2; XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); status = wolfpsa_validate_stored_key_data_length(key_data_length); @@ -2742,6 +2769,32 @@ psa_status_t psa_copy_key( return status; } +/* Exact match, or the ANY_HASH wildcard of a hash-and-sign family */ +static int wolfpsa_policy_alg_permits(psa_algorithm_t key_alg, + psa_algorithm_t alg) +{ + if (key_alg == PSA_ALG_NONE) { + return 0; + } + if (key_alg == alg) { + return 1; + } + return PSA_ALG_IS_SIGN_HASH(alg) && + PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH && + PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_ANY_HASH && + (key_alg & ~PSA_ALG_HASH_MASK) == (alg & ~PSA_ALG_HASH_MASK); +} + +/* Whether the permitted algorithm or the enrollment algorithm allows alg */ +int wolfpsa_key_permits_alg(const psa_key_attributes_t* attributes, + psa_algorithm_t alg) +{ + return wolfpsa_policy_alg_permits(psa_get_key_algorithm(attributes), + alg) || + wolfpsa_policy_alg_permits( + psa_get_key_enrollment_algorithm(attributes), alg); +} + /* Check whether a key supports a given algorithm and usage combination. * * PSA Crypto API 1.4 semantics: @@ -2759,7 +2812,6 @@ psa_status_t psa_check_key_usage(psa_key_id_t key, psa_status_t status; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; wolfpsa_trace("psa_check_key_usage(key=%u alg=0x%08x usage=0x%08x)", (unsigned)key, (unsigned)alg, (unsigned)usage); @@ -2775,35 +2827,9 @@ psa_status_t psa_check_key_usage(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - if (alg != PSA_ALG_NONE) { - key_alg = psa_get_key_algorithm(&attributes); - - /* A key with no permitted algorithm cannot be used for any - * cryptographic operation. */ - if (key_alg == PSA_ALG_NONE) { - psa_reset_key_attributes(&attributes); - return PSA_ERROR_NOT_PERMITTED; - } - - /* Exact match is always accepted. Otherwise apply the ANY_HASH - * wildcard rule: a sign-hash policy using PSA_ALG_ANY_HASH authorises - * any concrete hash-and-sign algorithm of the same base family. */ - if (key_alg != alg) { - int alg_ok = 0; - - if (PSA_ALG_IS_SIGN_HASH(alg) && - PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH && - PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_ANY_HASH && - ((key_alg & ~PSA_ALG_HASH_MASK) == (alg & ~PSA_ALG_HASH_MASK))) - { - alg_ok = 1; - } - - if (!alg_ok) { - psa_reset_key_attributes(&attributes); - return PSA_ERROR_NOT_PERMITTED; - } - } + if (alg != PSA_ALG_NONE && !wolfpsa_key_permits_alg(&attributes, alg)) { + psa_reset_key_attributes(&attributes); + return PSA_ERROR_NOT_PERMITTED; } psa_reset_key_attributes(&attributes); diff --git a/src/psa_key_wrap.c b/src/psa_key_wrap.c index 3a66794..e3687c7 100644 --- a/src/psa_key_wrap.c +++ b/src/psa_key_wrap.c @@ -54,7 +54,6 @@ psa_status_t psa_wrap_key(psa_key_id_t wrapping_key, psa_key_attributes_t target_attr; psa_key_type_t wrap_type; psa_key_usage_t wrap_usage; - psa_algorithm_t wrap_alg; uint8_t *kek = NULL; size_t kek_len = 0; uint8_t *plaintext = NULL; @@ -105,8 +104,7 @@ psa_status_t psa_wrap_key(psa_key_id_t wrapping_key, return PSA_ERROR_NOT_PERMITTED; } - wrap_alg = psa_get_key_algorithm(&wrap_attr); - if (wrap_alg == PSA_ALG_NONE || wrap_alg != alg) { + if (!wolfpsa_key_permits_alg(&wrap_attr, alg)) { wolfpsa_forcezero_free_key_data(kek, kek_len); return PSA_ERROR_NOT_PERMITTED; } @@ -206,7 +204,6 @@ psa_status_t psa_unwrap_key(const psa_key_attributes_t *attributes, psa_key_attributes_t wrap_attr; psa_key_type_t wrap_type; psa_key_usage_t wrap_usage; - psa_algorithm_t wrap_alg; uint8_t *kek = NULL; size_t kek_len = 0; uint8_t *plaintext = NULL; @@ -266,8 +263,7 @@ psa_status_t psa_unwrap_key(const psa_key_attributes_t *attributes, return PSA_ERROR_NOT_PERMITTED; } - wrap_alg = psa_get_key_algorithm(&wrap_attr); - if (wrap_alg == PSA_ALG_NONE || wrap_alg != alg) { + if (!wolfpsa_key_permits_alg(&wrap_attr, alg)) { wolfpsa_forcezero_free_key_data(kek, kek_len); return PSA_ERROR_NOT_PERMITTED; } diff --git a/src/psa_mac.c b/src/psa_mac.c index 712a532..d4112be 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -145,6 +145,32 @@ static int wolfpsa_hash_type_from_alg(psa_algorithm_t alg) } } +/* Whether permitted algorithm key_alg allows MAC alg and its length */ +static int wolfpsa_mac_alg_permitted(const psa_key_attributes_t *attributes, + psa_algorithm_t key_alg, + psa_algorithm_t alg) +{ + size_t req_length; + size_t min_length; + + if (key_alg == PSA_ALG_NONE || + PSA_ALG_FULL_LENGTH_MAC(key_alg) != PSA_ALG_FULL_LENGTH_MAC(alg)) { + return 0; + } + + req_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, alg); + min_length = PSA_MAC_TRUNCATED_LENGTH(key_alg); + if (min_length == 0) { + min_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, + PSA_ALG_FULL_LENGTH_MAC(key_alg)); + } + + if ((key_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return req_length >= min_length; + } + return req_length == min_length; +} + static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, psa_key_usage_t usage, psa_algorithm_t alg, @@ -154,12 +180,6 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; - psa_algorithm_t key_alg_full; - psa_algorithm_t req_alg_full; - size_t req_mac_length; - size_t key_full_length; - size_t key_min_length; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -203,57 +223,17 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - key_alg_full = PSA_ALG_FULL_LENGTH_MAC(key_alg); - req_alg_full = PSA_ALG_FULL_LENGTH_MAC(alg); - - if (key_alg_full != req_alg_full) { + if (!wolfpsa_mac_alg_permitted(attributes, + psa_get_key_algorithm(attributes), alg) && + !wolfpsa_mac_alg_permitted(attributes, + psa_get_key_enrollment_algorithm(attributes), + alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_NOT_PERMITTED; } - req_mac_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, alg); - key_full_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, key_alg_full); - key_min_length = PSA_MAC_TRUNCATED_LENGTH(key_alg); - if (key_min_length == 0) { - key_min_length = key_full_length; - } - - if ((key_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { - if (req_mac_length < key_min_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else { - if ((key_alg & PSA_ALG_MAC_TRUNCATION_MASK) != 0) { - if (req_mac_length != key_min_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (req_mac_length != key_full_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - return PSA_SUCCESS; } diff --git a/test/Makefile b/test/Makefile index 5877f3a..08c76fe 100644 --- a/test/Makefile +++ b/test/Makefile @@ -51,6 +51,10 @@ USER_SETTINGS_PATH ?= $(WOLFPSA_PATH) WOLFSSL_CPPFLAGS ?= -DWOLFSSL_USER_SETTINGS CPPFLAGS += -DHAVE_CONFIG_H -DWOLFSSL_HAVE_PSA -DHAVE_PK_CALLBACKS +# Mirrors the library's ENROLLMENT_ALG=1, which psa_api_test checks +ifeq ($(ENROLLMENT_ALG),1) +CPPFLAGS += -DWOLFPSA_ENROLLMENT_ALG +endif CPPFLAGS += -I$(WOLFSSL_PATH) -I$(WOLFSSL_PATH)/wolfssl ifneq ($(WOLFSSL_BUILD_DIR),) CPPFLAGS += -I$(WOLFSSL_BUILD_DIR) @@ -209,21 +213,26 @@ psa_tls_client: # user_settings.h with that knob enabled instead of the shared libwolfpsa. CRYPTOCB_BUILD_DIR := $(abspath .)/build-cryptocb$(if $(SANITIZE_FLAGS),-asan) CRYPTOCB_LIB := $(CRYPTOCB_BUILD_DIR)/libwolfpsa-cryptocb.a -CRYPTOCB_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DWOLF_CRYPTO_CB \ - -I$(abspath $(WOLFSSL_PATH)) -I$(WOLFPSA_ABS) -I$(WOLFPSA_ABS)/wolfpsa \ - -I$(WOLFPSA_ABS)/src +VARIANT_INC := -I$(abspath $(WOLFSSL_PATH)) -I$(WOLFPSA_ABS) \ + -I$(WOLFPSA_ABS)/wolfpsa -I$(WOLFPSA_ABS)/src +CRYPTOCB_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DWOLF_CRYPTO_CB $(VARIANT_INC) # FORCE, because only the sub-make knows whether a library source changed. # Without it make treats the existing archive as permanently up to date and # the test relinks against a stale copy of the code it is meant to guard. $(CRYPTOCB_LIB): FORCE + $(call variant_lib,$(CRYPTOCB_BUILD_DIR),-DWOLF_CRYPTO_CB) + +# Build a library variant: $(1) is its build dir, $(2) its extra defines +define variant_lib $(MAKE) -C $(WOLFPSA_PATH) $@ \ LIBNAME=$@ \ - BUILD_DIR=$(CRYPTOCB_BUILD_DIR) \ + BUILD_DIR=$(1) \ WOLFSSL_PATH=$(abspath $(WOLFSSL_PATH)) \ - WOLFSSL_CPPFLAGS="-DWOLFSSL_USER_SETTINGS -DWOLF_CRYPTO_CB" \ + WOLFSSL_CPPFLAGS="-DWOLFSSL_USER_SETTINGS $(2)" \ CFLAGS="$(CFLAGS)" \ LDFLAGS="$(SANITIZE_FLAGS)" +endef FORCE: @@ -239,6 +248,20 @@ psa_kdf_zeroize_output_test: psa_server/psa_kdf_zeroize_output_test.c $(CRYPTOCB $(CC) $(CRYPTOCB_CPPFLAGS) $(CFLAGS) -o $@ $< $(CRYPTOCB_LIB) \ $(SANITIZE_FLAGS) -lpthread -lm +# The enrollment algorithm is opt-in, so its test has a library built with it +ENROLL_BUILD_DIR := $(abspath .)/build-enroll$(if $(SANITIZE_FLAGS),-asan) +ENROLL_LIB := $(ENROLL_BUILD_DIR)/libwolfpsa-enroll.a +ENROLL_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DWOLFPSA_ENROLLMENT_ALG \ + $(VARIANT_INC) + +$(ENROLL_LIB): FORCE + $(call variant_lib,$(ENROLL_BUILD_DIR),-DWOLFPSA_ENROLLMENT_ALG) + +psa_enrollment_alg_test: psa_server/psa_enrollment_alg_test.c $(ENROLL_LIB) \ + FORCE + $(CC) $(ENROLL_CPPFLAGS) $(CFLAGS) -o $@ $< $(ENROLL_LIB) \ + $(SANITIZE_FLAGS) -lpthread -lm + # psa_import_key_probe_test ships its own wolfPSA_Store_* backend: the POSIX # store cannot fail a read probe with an I/O error, only succeed or report # "not available", so the probe-failure path needs a mock. It therefore links @@ -315,5 +338,7 @@ clean: rm -f psa_server/*.o rm -rf $(abspath .)/build-cryptocb $(abspath .)/build-cryptocb-asan rm -rf $(abspath .)/build-probe $(abspath .)/build-probe-asan + rm -rf $(abspath .)/build-enroll $(abspath .)/build-enroll-asan rm -f $(BINARIES) psa_devid_cryptocb_test psa_kdf_zeroize_output_test *.o *.log + rm -f psa_enrollment_alg_test rm -f psa_server/*.gcda psa_server/*.gcno diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index c0b6789..dd92af1 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -805,12 +805,18 @@ static int test_algorithm_none_rejects_key_usage(void) return ret; } +/* alg2 needs WOLFPSA_ENROLLMENT_ALG and a volatile key */ static int test_secondary_algorithm_is_not_supported(void) { static const uint8_t aes_key[16] = { 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c }; +#ifdef WOLFPSA_ENROLLMENT_ALG + const psa_status_t volatile_st = PSA_SUCCESS; +#else + const psa_status_t volatile_st = PSA_ERROR_NOT_SUPPORTED; +#endif /* WOLFPSA_ENROLLMENT_ALG */ psa_key_attributes_t attrs = psa_key_attributes_init(); psa_key_id_t key_id = PSA_KEY_ID_NULL; psa_status_t st; @@ -819,32 +825,34 @@ static int test_secondary_algorithm_is_not_supported(void) psa_set_key_bits(&attrs, 128); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); psa_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); - attrs.policy.alg2 = PSA_ALG_CTR; + psa_set_key_enrollment_algorithm(&attrs, PSA_ALG_CTR); + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_true(st == volatile_st, "psa_import_key volatile alg2") != + TEST_OK) { + return TEST_FAIL; + } + (void)psa_destroy_key(key_id); + key_id = PSA_KEY_ID_NULL; + psa_set_key_id(&attrs, PSA_KEY_ID_USER_MIN + 2407u); st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); if (check_true(st == PSA_ERROR_NOT_SUPPORTED, - "psa_import_key rejects unsupported secondary algorithm policy") != TEST_OK) { + "psa_import_key rejects persistent alg2") != TEST_OK) { return TEST_FAIL; } if (check_true(key_id == PSA_KEY_ID_NULL, - "psa_import_key leaves key id null when alg2 is unsupported") != TEST_OK) { + "import leaves id null (persistent alg2)") != TEST_OK) { return TEST_FAIL; } - psa_reset_key_attributes(&attrs); - psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attrs, 128); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); - psa_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); - attrs.policy.alg2 = PSA_ALG_CTR; - st = psa_generate_key(&attrs, &key_id); if (check_true(st == PSA_ERROR_NOT_SUPPORTED, - "psa_generate_key rejects unsupported secondary algorithm policy") != TEST_OK) { + "psa_generate_key rejects persistent alg2") != TEST_OK) { return TEST_FAIL; } if (check_true(key_id == PSA_KEY_ID_NULL, - "psa_generate_key leaves key id null when alg2 is unsupported") != TEST_OK) { + "generate leaves id null (persistent alg2)") != TEST_OK) { return TEST_FAIL; } diff --git a/test/psa_server/psa_enrollment_alg_test.c b/test/psa_server/psa_enrollment_alg_test.c new file mode 100644 index 0000000..1b73605 --- /dev/null +++ b/test/psa_server/psa_enrollment_alg_test.c @@ -0,0 +1,361 @@ +/* psa_enrollment_alg_test.c + * + * With WOLFPSA_ENROLLMENT_ALG, a volatile key's enrollment algorithm is a + * second permitted algorithm: each operation accepts either one, and + * psa_copy_key() intersects it. Links a library built with that option. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +static int failures; + +static void check(const char *what, const char *step, psa_status_t got, + psa_status_t want) +{ + if (got != want) { + printf("FAIL: %s, %s: got %d want %d\n", what, step, (int)got, + (int)want); + failures++; + } +} + +/* Generate a volatile key with an enrollment algorithm */ +static psa_key_id_t make_key(psa_key_type_t type, size_t bits, + psa_key_usage_t usage, psa_algorithm_t alg, + psa_algorithm_t alg2) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attrs, type); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, usage); + psa_set_key_algorithm(&attrs, alg); + psa_set_key_enrollment_algorithm(&attrs, alg2); + status = psa_generate_key(&attrs, &key); + check("make key", "generate", status, PSA_SUCCESS); + if (status == PSA_SUCCESS) { + check("make key", "get attributes", psa_get_key_attributes(key, &got), + PSA_SUCCESS); + if (psa_get_key_enrollment_algorithm(&got) != alg2) { + printf("FAIL: enrollment algorithm 0x%08x want 0x%08x\n", + (unsigned)psa_get_key_enrollment_algorithm(&got), + (unsigned)alg2); + failures++; + } + } + return key; +} + +static psa_status_t cipher(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t in[16] = {0}; + uint8_t out[32]; + size_t out_len = 0; + + return psa_cipher_encrypt(key, alg, in, sizeof(in), out, sizeof(out), + &out_len); +} + +static psa_status_t mac(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t out[PSA_MAC_MAX_SIZE]; + size_t out_len = 0; + + return psa_mac_compute(key, alg, (const uint8_t *)"abc", 3, out, + sizeof(out), &out_len); +} + +static psa_status_t aead(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + uint8_t nonce[PSA_AEAD_NONCE_MAX_SIZE] = {0}; + size_t nonce_len = 0; + uint8_t ct[32]; + uint8_t pt[32]; + size_t ct_len = 0; + size_t pt_len = 0; + psa_status_t status; + + status = psa_get_key_attributes(key, &attrs); + if (status == PSA_SUCCESS) { + nonce_len = PSA_AEAD_NONCE_LENGTH(psa_get_key_type(&attrs), alg); + status = psa_aead_encrypt(key, alg, nonce, nonce_len, NULL, 0, + (const uint8_t *)"abc", 3, ct, sizeof(ct), + &ct_len); + } + if (status == PSA_SUCCESS) { + status = psa_aead_decrypt(key, alg, nonce, nonce_len, NULL, 0, ct, + ct_len, pt, sizeof(pt), &pt_len); + } + return status; +} + +static psa_status_t sign(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t hash[32] = {0}; + uint8_t sig[PSA_SIGNATURE_MAX_SIZE]; + size_t sig_len = 0; + + return psa_sign_hash(key, alg, hash, sizeof(hash), sig, sizeof(sig), + &sig_len); +} + +static psa_status_t agree(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t pub[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(256)]; + uint8_t secret[PSA_BITS_TO_BYTES(256)]; + size_t pub_len = 0; + size_t secret_len = 0; + psa_status_t status; + + status = psa_export_public_key(key, pub, sizeof(pub), &pub_len); + if (status == PSA_SUCCESS) { + status = psa_raw_key_agreement(alg, key, pub, pub_len, secret, + sizeof(secret), &secret_len); + } + return status; +} + +static psa_status_t derive(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_status_t status; + + status = psa_key_derivation_setup(&op, alg); + if (status == PSA_SUCCESS) { + status = psa_key_derivation_input_key( + &op, PSA_KEY_DERIVATION_INPUT_SECRET, key); + } + psa_key_derivation_abort(&op); + return status; +} + +/* Wrap the key with itself, then unwrap it */ +static psa_status_t wrap(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + uint8_t data[32]; + size_t data_len = 0; + psa_key_id_t out = PSA_KEY_ID_NULL; + psa_status_t status; + + status = psa_wrap_key(key, alg, key, data, sizeof(data), &data_len); + if (status == PSA_SUCCESS) { + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + status = psa_unwrap_key(&attrs, key, alg, data, data_len, &out); + psa_destroy_key(out); + } + return status; +} + +static psa_status_t kem(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + uint8_t ct[PSA_ENCAPSULATE_CIPHERTEXT_MAX_SIZE]; + size_t ct_len = 0; + psa_key_id_t out = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + status = psa_encapsulate(key, alg, &attrs, &out, ct, sizeof(ct), &ct_len); + psa_destroy_key(out); + out = PSA_KEY_ID_NULL; + if (status == PSA_SUCCESS) { + status = psa_decapsulate(key, alg, ct, ct_len, &attrs, &out); + psa_destroy_key(out); + } + return status; +} + +/* One key per row: alg2 is permitted as well as alg, other (if set) is not */ +static void check_operations(void) +{ + static const struct { + const char *what; + psa_key_type_t type; + size_t bits; + psa_key_usage_t usage; + psa_algorithm_t alg; + psa_algorithm_t alg2; + psa_algorithm_t other; + psa_status_t (*use)(psa_key_id_t key, psa_algorithm_t alg); + } cases[] = { + { "cipher", PSA_KEY_TYPE_AES, 128, PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CTR, PSA_ALG_CBC_NO_PADDING, PSA_ALG_ECB_NO_PADDING, + cipher }, + { "MAC", PSA_KEY_TYPE_HMAC, 256, PSA_KEY_USAGE_SIGN_MESSAGE, + PSA_ALG_HMAC(PSA_ALG_SHA_256), PSA_ALG_HMAC(PSA_ALG_SHA_384), + PSA_ALG_HMAC(PSA_ALG_SHA_512), mac }, + { "AEAD", PSA_KEY_TYPE_AES, 128, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, + PSA_ALG_GCM, PSA_ALG_CCM, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 12), aead }, + { "XChaCha20-Poly1305", PSA_KEY_TYPE_XCHACHA20, 256, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_NONE, + PSA_ALG_XCHACHA20_POLY1305, PSA_ALG_NONE, aead }, + { "Ascon-AEAD128", PSA_KEY_TYPE_ASCON, 128, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_NONE, + PSA_ALG_ASCON_AEAD128, PSA_ALG_NONE, aead }, + { "key wrap", PSA_KEY_TYPE_AES, 128, + PSA_KEY_USAGE_WRAP | PSA_KEY_USAGE_UNWRAP | PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_KW, PSA_ALG_NONE, wrap }, + { "ML-KEM", PSA_KEY_TYPE_ML_KEM_KEY_PAIR, 768, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_NONE, + PSA_ALG_ML_KEM, PSA_ALG_NONE, kem }, + { "ECDSA", PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1), 256, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_DERIVE, PSA_ALG_ECDH, + PSA_ALG_ECDSA(PSA_ALG_ANY_HASH), + PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), sign }, + { "ECDH", PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1), 256, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_DERIVE, + PSA_ALG_ECDSA(PSA_ALG_ANY_HASH), PSA_ALG_ECDH, PSA_ALG_NONE, + agree }, + { "KDF", PSA_KEY_TYPE_DERIVE, 256, PSA_KEY_USAGE_DERIVE, + PSA_ALG_HKDF(PSA_ALG_SHA_256), PSA_ALG_HKDF(PSA_ALG_SHA_384), + PSA_ALG_HKDF(PSA_ALG_SHA_512), derive }, + }; + psa_key_id_t key; + psa_algorithm_t use_alg; + size_t i; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + key = make_key(cases[i].type, cases[i].bits, cases[i].usage, + cases[i].alg, cases[i].alg2); + if (key == PSA_KEY_ID_NULL) { + continue; + } + use_alg = cases[i].alg2; + if (use_alg == PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)) { + use_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); + } + check(cases[i].what, "enrollment algorithm", + cases[i].use(key, use_alg), PSA_SUCCESS); + if (cases[i].other != PSA_ALG_NONE) { + check(cases[i].what, "other algorithm", + cases[i].use(key, cases[i].other), PSA_ERROR_NOT_PERMITTED); + } + check(cases[i].what, "psa_check_key_usage", + psa_check_key_usage(key, use_alg, 0), PSA_SUCCESS); + psa_destroy_key(key); + } +} + +/* psa_copy_key() intersects alg2, or drops it if either side has none */ +static void check_copy(void) +{ + static const struct { + const char *what; + psa_algorithm_t target_alg2; + psa_status_t want; + psa_algorithm_t want_alg2; + } cases[] = { + { "copy, same alg2", PSA_ALG_CBC_NO_PADDING, PSA_SUCCESS, + PSA_ALG_CBC_NO_PADDING }, + { "copy, no alg2", PSA_ALG_NONE, PSA_SUCCESS, PSA_ALG_NONE }, + { "copy, other alg2", PSA_ALG_ECB_NO_PADDING, + PSA_ERROR_INVALID_ARGUMENT, PSA_ALG_NONE }, + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t source; + psa_key_id_t copy; + psa_status_t status; + size_t i; + + source = make_key(PSA_KEY_TYPE_AES, 128, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_COPY, + PSA_ALG_CTR, PSA_ALG_CBC_NO_PADDING); + if (source == PSA_KEY_ID_NULL) { + return; + } + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + copy = PSA_KEY_ID_NULL; + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + psa_set_key_enrollment_algorithm(&attrs, cases[i].target_alg2); + status = psa_copy_key(source, &attrs, ©); + check(cases[i].what, "psa_copy_key", status, cases[i].want); + if (status == PSA_SUCCESS && + (psa_get_key_attributes(copy, &got) != PSA_SUCCESS || + psa_get_key_enrollment_algorithm(&got) != cases[i].want_alg2)) { + printf("FAIL: %s: enrollment algorithm 0x%08x\n", cases[i].what, + (unsigned)psa_get_key_enrollment_algorithm(&got)); + failures++; + } + psa_destroy_key(copy); + } + psa_destroy_key(source); +} + +/* Reading a persistent key clears a stale enrollment algorithm */ +static void check_persistent(void) +{ + static const uint8_t data[16] = {0}; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + psa_set_key_id(&attrs, PSA_KEY_ID_USER_MIN + 2408u); + check("persistent", "import", + psa_import_key(&attrs, data, sizeof(data), &key), PSA_SUCCESS); + + psa_set_key_enrollment_algorithm(&got, PSA_ALG_CBC_NO_PADDING); + check("persistent", "get attributes", psa_get_key_attributes(key, &got), + PSA_SUCCESS); + if (psa_get_key_enrollment_algorithm(&got) != PSA_ALG_NONE) { + printf("FAIL: persistent: enrollment algorithm 0x%08x\n", + (unsigned)psa_get_key_enrollment_algorithm(&got)); + failures++; + } + check("persistent", "enrollment algorithm", + cipher(key, PSA_ALG_CBC_NO_PADDING), PSA_ERROR_NOT_PERMITTED); + psa_destroy_key(key); +} + +int main(void) +{ + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + + check_operations(); + check_copy(); + check_persistent(); + + if (failures == 0) { + printf("Enrollment algorithm tests: all passed\n"); + return 0; + } + printf("Enrollment algorithm tests: %d failure(s)\n", failures); + return 1; +} diff --git a/wolfpsa/psa/crypto_struct.h b/wolfpsa/psa/crypto_struct.h index 510dbf4..b684aac 100644 --- a/wolfpsa/psa/crypto_struct.h +++ b/wolfpsa/psa/crypto_struct.h @@ -282,6 +282,19 @@ static inline psa_algorithm_t psa_get_key_algorithm( return attributes->policy.alg; } +/* Mbed TLS second permitted algorithm, honored with WOLFPSA_ENROLLMENT_ALG */ +static inline void psa_set_key_enrollment_algorithm( + psa_key_attributes_t *attributes, psa_algorithm_t alg2) +{ + attributes->policy.alg2 = alg2; +} + +static inline psa_algorithm_t psa_get_key_enrollment_algorithm( + const psa_key_attributes_t *attributes) +{ + return attributes->policy.alg2; +} + static inline void psa_set_key_type(psa_key_attributes_t *attributes, psa_key_type_t type) { diff --git a/wolfpsa/psa_key_storage.h b/wolfpsa/psa_key_storage.h index 036b31d..4caf1df 100644 --- a/wolfpsa/psa_key_storage.h +++ b/wolfpsa/psa_key_storage.h @@ -100,6 +100,8 @@ WOLFSSL_API psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, size_t* key_data_length); WOLFSSL_API void wolfpsa_forcezero_free_key_data(uint8_t* key_data, size_t key_data_length); +WOLFSSL_LOCAL int wolfpsa_key_permits_alg( + const psa_key_attributes_t* attributes, psa_algorithm_t alg); #ifdef __cplusplus } diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 37426e1..acd4dc5 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -124,6 +124,9 @@ if(CONFIG_WOLFPSA) # Image-wide: a settings file picking wolfCrypt's AES core may read it too. zephyr_compile_definitions_ifdef(CONFIG_WOLFPSA_AES_FAST WOLFPSA_AES_FAST) + zephyr_compile_definitions_ifdef(CONFIG_WOLFPSA_ENROLLMENT_ALG + WOLFPSA_ENROLLMENT_ALG) + # Inherit WOLFSSL_USER_SETTINGS + WOLFSSL_ZEPHYR and the wolfcrypt include # dirs from the wolfSSL module's interface library. zephyr_library_link_libraries(wolfSSL) diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f607c6f..5ad44d5 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -76,6 +76,14 @@ config WOLFPSA_AES_FAST channel is out of scope - a part with no data cache, or an attacker with no way to measure it. +config WOLFPSA_ENROLLMENT_ALG + bool "Mbed TLS enrollment algorithm on volatile keys" + help + Define WOLFPSA_ENROLLMENT_ALG: a volatile key may have a second + permitted algorithm, set with psa_set_key_enrollment_algorithm(), as + in Mbed TLS. Using one key with two algorithms is not recommended, so + enable this only for code that sets one, such as hostap. + config WOLFPSA_THREAD_SAFE bool "Serialize wolfPSA shared state with a global mutex" default y