diff --git a/.gitignore b/.gitignore index 08803db..7d49aca 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ test/psa_tls_server test/psa_des3_stack_scrub_test test/psa_random_size_test test/psa_rsa_pss_interop_test +test/psa_crypto_init_test psa_server/tls_client/psa_tls_client psa_tls_client wolfcrypt-benchmark/wolfcrypt-psa-benchmark diff --git a/src/misc_impl.c b/src/misc_impl.c index b3c64e7..b6156d6 100644 --- a/src/misc_impl.c +++ b/src/misc_impl.c @@ -1,6 +1,22 @@ /* misc_impl.c * - * Provide misc.c helper implementations for NO_INLINE builds. + * 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 */ #ifdef HAVE_CONFIG_H diff --git a/src/psa_aead.c b/src/psa_aead.c index 0ba7e74..60e0221 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -78,6 +78,18 @@ static psa_status_t wolfpsa_aead_append(uint8_t **buf, size_t *len, return PSA_SUCCESS; } +static const uint8_t* wolfpsa_aead_nonnull_data(const uint8_t *data, + size_t data_length) +{ + static const uint8_t empty = 0; + + if (data == NULL && data_length == 0) { + return ∅ + } + + return data; +} + static size_t wolfpsa_aead_tag_length(psa_algorithm_t alg) { return PSA_ALG_AEAD_GET_TAG_LENGTH(alg); @@ -134,7 +146,14 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, } key_alg = psa_get_key_algorithm(attributes); - if (key_alg != PSA_ALG_NONE) { + 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; + } + + { psa_algorithm_t key_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(key_alg); psa_algorithm_t req_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg); key_tag_len = wolfpsa_aead_tag_length(key_alg); @@ -452,6 +471,9 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, size_t *tag_length) { int ret; + size_t chacha_ciphertext_size = 0; + const uint8_t *input; + const uint8_t *aad; if (ciphertext == NULL || ciphertext_length == NULL || tag == NULL || tag_length == NULL) { @@ -468,6 +490,13 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CHACHA20_POLY1305)) { + if (ctx->input_length > SIZE_MAX - ctx->tag_length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + chacha_ciphertext_size = ctx->input_length + ctx->tag_length; + } + if (ciphertext_size < ctx->input_length) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -481,6 +510,9 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + input = wolfpsa_aead_nonnull_data(ctx->input, ctx->input_length); + aad = wolfpsa_aead_nonnull_data(ctx->aad, ctx->aad_length); + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { Aes aes; ret = wc_AesInit(&aes, NULL, INVALID_DEVID); @@ -488,13 +520,14 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesGcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesGcmEncrypt(&aes, ciphertext, ctx->input, + ret = wc_AesGcmEncrypt(&aes, ciphertext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)ctx->tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret != 0) { return wc_error_to_psa_status(ret); } @@ -509,31 +542,45 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesCcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesCcmEncrypt(&aes, ciphertext, ctx->input, + ret = wc_AesCcmEncrypt(&aes, ciphertext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)ctx->tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret != 0) { return wc_error_to_psa_status(ret); } } else if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CHACHA20_POLY1305)) { size_t out_len = 0; + uint8_t *tmp = (uint8_t *)XMALLOC(chacha_ciphertext_size, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (tmp == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } ret = psa_chacha20_poly1305_encrypt(ctx->key, ctx->key_length, ctx->alg, ctx->nonce, ctx->nonce_length, - ctx->aad, ctx->aad_length, - ctx->input, ctx->input_length, - ciphertext, ciphertext_size, &out_len); + aad, ctx->aad_length, + input, ctx->input_length, + tmp, chacha_ciphertext_size, + &out_len); if (ret != 0) { + wc_ForceZero(tmp, chacha_ciphertext_size); + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); return (psa_status_t)ret; } if (out_len < ctx->tag_length) { + wc_ForceZero(tmp, chacha_ciphertext_size); + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_GENERIC_ERROR; } - XMEMCPY(tag, ciphertext + ctx->input_length, ctx->tag_length); + XMEMCPY(ciphertext, tmp, ctx->input_length); + XMEMCPY(tag, tmp + ctx->input_length, ctx->tag_length); + wc_ForceZero(tmp, chacha_ciphertext_size); + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); } else { return PSA_ERROR_NOT_SUPPORTED; @@ -552,6 +599,8 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, size_t tag_length) { int ret; + const uint8_t *input; + const uint8_t *aad; if (plaintext == NULL || plaintext_length == NULL || tag == NULL) { return PSA_ERROR_INVALID_ARGUMENT; @@ -586,6 +635,9 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + input = wolfpsa_aead_nonnull_data(ctx->input, ctx->input_length); + aad = wolfpsa_aead_nonnull_data(ctx->aad, ctx->aad_length); + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { Aes aes; ret = wc_AesInit(&aes, NULL, INVALID_DEVID); @@ -593,13 +645,14 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesGcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesGcmDecrypt(&aes, plaintext, ctx->input, + ret = wc_AesGcmDecrypt(&aes, plaintext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret == AES_GCM_AUTH_E || ret == MAC_CMP_FAILED_E) { return PSA_ERROR_INVALID_SIGNATURE; } @@ -617,13 +670,14 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesCcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesCcmDecrypt(&aes, plaintext, ctx->input, + ret = wc_AesCcmDecrypt(&aes, plaintext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret == AES_CCM_AUTH_E || ret == MAC_CMP_FAILED_E) { return PSA_ERROR_INVALID_SIGNATURE; } @@ -634,8 +688,13 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, else if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CHACHA20_POLY1305)) { size_t out_len = 0; uint8_t *ciphertext = ctx->input; - size_t ciphertext_len = ctx->input_length + tag_length; - uint8_t *tmp = (uint8_t *)XMALLOC(ciphertext_len, NULL, + size_t ciphertext_len; + uint8_t *tmp; + if (ctx->input_length > SIZE_MAX - tag_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + ciphertext_len = ctx->input_length + tag_length; + tmp = (uint8_t *)XMALLOC(ciphertext_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (tmp == NULL) { return PSA_ERROR_INSUFFICIENT_MEMORY; @@ -644,9 +703,10 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, XMEMCPY(tmp + ctx->input_length, tag, tag_length); ret = psa_chacha20_poly1305_decrypt(ctx->key, ctx->key_length, ctx->alg, ctx->nonce, ctx->nonce_length, - ctx->aad, ctx->aad_length, + aad, ctx->aad_length, tmp, ciphertext_len, plaintext, plaintext_size, &out_len); + wc_ForceZero(tmp, ciphertext_len); XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ret != 0) { return PSA_ERROR_INVALID_SIGNATURE; @@ -862,6 +922,7 @@ psa_status_t psa_aead_abort(psa_aead_operation_t *operation) wc_ForceZero(ctx->key, ctx->key_length); XFREE(ctx->key, NULL, DYNAMIC_TYPE_TMP_BUFFER); } + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; } diff --git a/src/psa_aead_internal.h b/src/psa_aead_internal.h index 1f71f54..149b001 100644 --- a/src/psa_aead_internal.h +++ b/src/psa_aead_internal.h @@ -1,3 +1,24 @@ +/* psa_aead_internal.h + * + * 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 + */ + #ifndef WOLFPSA_PSA_AEAD_INTERNAL_H #define WOLFPSA_PSA_AEAD_INTERNAL_H diff --git a/src/psa_api_stub.c b/src/psa_api_stub.c index bc70731..1d1798c 100644 --- a/src/psa_api_stub.c +++ b/src/psa_api_stub.c @@ -1,4 +1,24 @@ -/* psa_api_stub.c - auto-generated for test linkage */ +/* psa_api_stub.c + * + * 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 extern int wolfPSA_CryptoIsInitialized(void); diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index c61f69d..621f731 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -167,23 +167,28 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, } key_alg = psa_get_key_algorithm(attributes); - if (key_alg != PSA_ALG_NONE) { - if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { - if (PSA_ALG_KEY_AGREEMENT_GET_BASE(key_alg) != - PSA_ALG_KEY_AGREEMENT_GET_BASE(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 (key_alg != alg) { + 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; + } + + if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { + if (PSA_ALG_KEY_AGREEMENT_GET_BASE(key_alg) != + PSA_ALG_KEY_AGREEMENT_GET_BASE(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 (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; + } return PSA_SUCCESS; } diff --git a/src/psa_cipher.c b/src/psa_cipher.c index 69fc374..31342e1 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -75,6 +75,15 @@ static wolfpsa_cipher_ctx_t *wolfpsa_cipher_get_ctx( return (wolfpsa_cipher_ctx_t *)(uintptr_t)operation->opaque; } +psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation); + +static psa_status_t wolfpsa_cipher_fail(psa_cipher_operation_t *operation, + psa_status_t status) +{ + (void)psa_cipher_abort(operation); + return status; +} + static psa_status_t wolfpsa_cipher_check_alg(psa_algorithm_t alg) { if (!PSA_ALG_IS_CIPHER(alg)) { @@ -176,7 +185,7 @@ static psa_status_t wolfpsa_cipher_check_key( } key_alg = psa_get_key_algorithm(attributes); - if (key_alg != PSA_ALG_NONE && key_alg != alg) { + if (key_alg == PSA_ALG_NONE || key_alg != alg) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; @@ -369,6 +378,7 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, else if (!ctx->is_chacha) { wc_AesFree(&ctx->aes); } + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return wc_error_to_psa_status(ret); } @@ -510,6 +520,7 @@ psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation, else if (!ctx->is_chacha) { wc_AesFree(&ctx->aes); } + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return wc_error_to_psa_status(ret); } @@ -657,20 +668,20 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, size_t output_len = 0; if (output_length == NULL) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } *output_length = 0; if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (input == NULL && input_length > 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (output == NULL && output_size > 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (input_length == 0) { @@ -679,7 +690,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, if (ctx->alg == PSA_ALG_CBC_NO_PADDING) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } { size_t block_size = ctx->block_size; @@ -691,7 +702,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, output_len = full_len; if (output_size < output_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (blocks == 0) { @@ -720,7 +731,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -734,7 +745,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -761,7 +773,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -779,7 +792,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks; input_offset += full_blocks; @@ -797,7 +811,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } else if (ctx->alg == PSA_ALG_CBC_PKCS7) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } { size_t block_size = ctx->block_size; @@ -810,7 +824,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, size_t full_len = full_blocks * block_size; if (output_size < full_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_BUFFER_TOO_SMALL); } if (full_blocks == 0) { @@ -833,7 +848,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, ret = wc_Des3_CbcEncrypt(&ctx->des3, output, block, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -841,7 +857,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -860,7 +877,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, input + input_offset, (word32)full_blocks_len); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -870,7 +888,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks_len); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks_len; input_offset += full_blocks_len; @@ -906,7 +925,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } if (output_size < process_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->partial_len > 0) { @@ -921,7 +941,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, ret = wc_Des3_CbcDecrypt(&ctx->des3, output, block, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -929,7 +950,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -945,7 +967,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, input + input_offset, (word32)full_blocks_len); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -955,7 +978,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks_len); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks_len; input_offset += full_blocks_len; @@ -978,13 +1002,10 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } else if (ctx->alg == PSA_ALG_CCM_STAR_NO_TAG) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; - } - if (ctx->partial_len != 0) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (input_length == 0) { return PSA_SUCCESS; @@ -992,9 +1013,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, ret = wc_AesCtrEncrypt(&ctx->aes, output, input, (word32)input_length); if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } - ctx->partial_len = 1; *output_length = input_length; return PSA_SUCCESS; } @@ -1009,7 +1029,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, output_len = full_len; if (output_size < output_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (blocks == 0) { @@ -1038,7 +1058,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1052,7 +1072,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -1079,7 +1100,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1097,7 +1119,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks; input_offset += full_blocks; @@ -1115,23 +1138,23 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } else if (ctx->alg == PSA_ALG_CTR) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } ret = wc_AesCtrEncrypt(&ctx->aes, output, input, (word32)input_length); if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else if (ctx->alg == PSA_ALG_CFB) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->direction == AES_ENCRYPTION) { ret = wc_AesCfbEncrypt(&ctx->aes, output, input, @@ -1142,15 +1165,15 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)input_length); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else if (ctx->alg == PSA_ALG_OFB) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->direction == AES_ENCRYPTION) { ret = wc_AesOfbEncrypt(&ctx->aes, output, input, @@ -1161,24 +1184,24 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)input_length); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else if (ctx->alg == PSA_ALG_STREAM_CIPHER) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } ret = wc_Chacha_Process(&ctx->chacha, output, input, (word32)input_length); if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else { - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); } *output_length = input_length; @@ -1194,16 +1217,16 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, int ret = 0; if (operation == NULL || output_length == NULL) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->alg == PSA_ALG_CBC_PKCS7) { size_t block_size = ctx->block_size; if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->direction == AES_ENCRYPTION) { uint8_t block[AES_BLOCK_SIZE]; @@ -1213,7 +1236,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, pad_len = block_size; } if (output_size < block_size) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->partial_len > 0) { XMEMCPY(block, ctx->partial, ctx->partial_len); @@ -1225,7 +1248,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, ret = wc_Des3_CbcEncrypt(&ctx->des3, output, block, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1233,10 +1256,11 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } ctx->partial_len = 0; *output_length = block_size; + psa_cipher_abort(operation); return PSA_SUCCESS; } else { @@ -1247,7 +1271,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, psa_status_t status = PSA_SUCCESS; if (ctx->partial_len != block_size) { - return PSA_ERROR_INVALID_PADDING; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_PADDING); } if (ctx->is_des3) { @@ -1255,7 +1279,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, ret = wc_Des3_CbcDecrypt(&ctx->des3, block, ctx->partial, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1264,7 +1288,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, } if (ret != 0) { wc_ForceZero(block, sizeof(block)); - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } pad_len = block[block_size - 1]; @@ -1293,6 +1317,10 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, cbc_pkcs7_decrypt_done: wc_ForceZero(block, sizeof(block)); + if (status != PSA_SUCCESS) { + return wolfpsa_cipher_fail(operation, status); + } + psa_cipher_abort(operation); return status; } } @@ -1300,10 +1328,11 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, if ((ctx->alg == PSA_ALG_CBC_NO_PADDING || ctx->alg == PSA_ALG_ECB_NO_PADDING) && ctx->partial_len != 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } *output_length = 0; + psa_cipher_abort(operation); return PSA_SUCCESS; } diff --git a/src/psa_crypto.c b/src/psa_crypto.c index 8bce30c..f68fcf5 100644 --- a/src/psa_crypto.c +++ b/src/psa_crypto.c @@ -29,6 +29,8 @@ #include #include "psa_trace.h" +#include +#include static int g_psa_crypto_initialized = 0; @@ -39,7 +41,19 @@ int wolfPSA_CryptoIsInitialized(void) psa_status_t psa_crypto_init(void) { + int ret; + wolfpsa_trace("psa_crypto_init()"); + + if (g_psa_crypto_initialized) { + return PSA_SUCCESS; + } + + ret = wolfCrypt_Init(); + if (ret != 0) { + return wc_error_to_psa_status(ret); + } + g_psa_crypto_initialized = 1; return PSA_SUCCESS; } diff --git a/src/psa_engine.c b/src/psa_engine.c index 684f794..47c57ec 100644 --- a/src/psa_engine.c +++ b/src/psa_engine.c @@ -271,12 +271,17 @@ psa_status_t psa_check_key_size_valid(psa_key_type_t type, size_t bits) case PSA_KEY_TYPE_HMAC: case PSA_KEY_TYPE_RAW_DATA: - case PSA_KEY_TYPE_CHACHA20: if (bits > 0) { return PSA_SUCCESS; } return PSA_ERROR_INVALID_ARGUMENT; + case PSA_KEY_TYPE_CHACHA20: + if (bits == 256) { + return PSA_SUCCESS; + } + return PSA_ERROR_INVALID_ARGUMENT; + default: break; } diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index 5db9ecd..ccecb39 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -71,6 +71,8 @@ #include #endif +extern int wolfPSA_CryptoIsInitialized(void); + typedef struct psa_hash_operation_ctx { psa_algorithm_t alg; /* Hash algorithm */ int initialized; /* Whether operation is initialized */ @@ -115,6 +117,15 @@ static psa_hash_operation_ctx_t *psa_hash_get_ctx(psa_hash_operation_t *operatio return (psa_hash_operation_ctx_t *)(uintptr_t)operation->opaque; } +psa_status_t psa_hash_abort(psa_hash_operation_t *operation); + +static psa_status_t wolfpsa_hash_fail(psa_hash_operation_t *operation, + psa_status_t status) +{ + (void)psa_hash_abort(operation); + return status; +} + static const psa_hash_operation_ctx_t *psa_hash_get_ctx_const( const psa_hash_operation_t *operation) { @@ -314,6 +325,10 @@ psa_status_t psa_hash_setup(psa_hash_operation_t *operation, wolfpsa_trace("psa_hash_setup(alg=0x%08x)", (unsigned)alg); + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } + if (!PSA_ALG_IS_HASH(alg)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -424,18 +439,18 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation, psa_hash_operation_ctx_t *ctx = psa_hash_get_ctx(operation); if (operation == NULL || (input == NULL && input_length > 0)) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_hash_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (ctx == NULL || !ctx->initialized || !ctx->started) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->finalized) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_hash_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } /* Update the hash context based on algorithm */ @@ -507,11 +522,11 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation, break; #endif default: - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_hash_fail(operation, PSA_ERROR_NOT_SUPPORTED); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_hash_fail(operation, wc_error_to_psa_status(ret)); } return PSA_SUCCESS; @@ -528,26 +543,26 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, psa_hash_operation_ctx_t *ctx = psa_hash_get_ctx(operation); if (operation == NULL || hash == NULL || hash_length == NULL) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_hash_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (ctx == NULL || !ctx->initialized || !ctx->started) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->finalized) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } /* Get the expected hash size */ expected_hash_size = psa_hash_get_size(ctx->alg); if (expected_hash_size == 0) { - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_hash_fail(operation, PSA_ERROR_NOT_SUPPORTED); } /* Check if the output buffer is large enough */ if (hash_size < expected_hash_size) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_hash_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } /* Finalize the hash based on algorithm */ @@ -612,11 +627,11 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, break; #endif default: - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_hash_fail(operation, PSA_ERROR_NOT_SUPPORTED); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_hash_fail(operation, wc_error_to_psa_status(ret)); } *hash_length = expected_hash_size; @@ -679,6 +694,7 @@ psa_status_t psa_hash_abort(psa_hash_operation_t *operation) } psa_hash_cleanup_ctx(ctx); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; @@ -822,6 +838,10 @@ psa_status_t psa_hash_compute(psa_algorithm_t alg, { psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; psa_status_t status; + + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } /* Set up the operation */ status = psa_hash_setup(&operation, alg); @@ -857,6 +877,10 @@ psa_status_t psa_hash_compare(psa_algorithm_t alg, uint8_t computed_hash[PSA_HASH_MAX_SIZE]; size_t computed_hash_length; size_t expected_hash_size; + + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } /* Check if the reference hash length is valid */ if (!PSA_ALG_IS_HASH(alg)) { diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 0f8e57c..3dbbbcf 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -48,6 +48,7 @@ typedef struct wolfpsa_kdf_ctx { psa_algorithm_t alg; psa_algorithm_t ka_alg; size_t capacity; + size_t output_offset; uint32_t steps_set; uint8_t *secret; size_t secret_length; @@ -67,6 +68,8 @@ typedef struct wolfpsa_kdf_ctx { int is_key_agreement; int is_raw_kdf; int output_started; + uint8_t *output_cache; + size_t output_cache_length; } wolfpsa_kdf_ctx_t; #define WOLFPSA_KDF_STEP_SECRET (1u << 0) @@ -136,6 +139,17 @@ static psa_status_t wolfpsa_kdf_append(uint8_t **buf, size_t *len, return PSA_SUCCESS; } +static const uint8_t* wolfpsa_kdf_input_ptr(const uint8_t *buf, size_t len) +{ + static const uint8_t empty[1] = { 0 }; + + if (buf == NULL && len == 0) { + return empty; + } + + return buf; +} + static int wolfpsa_hash_type_from_alg(psa_algorithm_t alg) { psa_algorithm_t hash_alg = 0; @@ -415,6 +429,12 @@ psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t *operation) wolfpsa_kdf_free_buf(&ctx->label, &ctx->label_length); wolfpsa_kdf_free_buf(&ctx->seed, &ctx->seed_length); wolfpsa_kdf_free_buf(&ctx->password, &ctx->password_length); + if (ctx->output_cache != NULL) { + wc_ForceZero(ctx->output_cache, ctx->output_cache_length); + XFREE(ctx->output_cache, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ctx->output_cache = NULL; + ctx->output_cache_length = 0; + } XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; } @@ -553,6 +573,10 @@ psa_status_t psa_key_derivation_input_integer(psa_key_derivation_operation_t *op return PSA_ERROR_INVALID_ARGUMENT; } + if (value == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (value > 0xFFFFFFFFu) { return PSA_ERROR_NOT_SUPPORTED; } @@ -614,19 +638,22 @@ psa_status_t psa_key_derivation_input_key(psa_key_derivation_operation_t *operat } key_alg = psa_get_key_algorithm(&attributes); - if (key_alg != PSA_ALG_NONE) { - 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 (key_alg == PSA_ALG_NONE) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + return PSA_ERROR_NOT_PERMITTED; + } + + 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) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + return PSA_ERROR_NOT_PERMITTED; + } status = psa_key_derivation_input_bytes(operation, step, key_data, key_data_length); @@ -715,21 +742,32 @@ static psa_status_t wolfpsa_kdf_hkdf(wolfpsa_kdf_ctx_t *ctx, if (PSA_ALG_IS_HKDF_EXTRACT(ctx->alg)) { int hash_len = wc_HashGetDigestSize(hash_type); + uint8_t prk[WC_MAX_DIGEST_SIZE]; if (hash_len <= 0) { return PSA_ERROR_NOT_SUPPORTED; } + if ((size_t)hash_len > sizeof(prk)) { + return PSA_ERROR_NOT_SUPPORTED; + } if ((wolfpsa_check_word32_length(ctx->salt_length) != PSA_SUCCESS) || (wolfpsa_check_word32_length(ctx->secret_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } - if (output_length != (size_t)hash_len) { + if (output_length > (size_t)hash_len) { return PSA_ERROR_INVALID_ARGUMENT; } ret = wc_HKDF_Extract(hash_type, ctx->salt, (word32)ctx->salt_length, ctx->secret, (word32)ctx->secret_length, - output); - return ret == 0 ? PSA_SUCCESS : wc_error_to_psa_status(ret); + prk); + if (ret != 0) { + wc_ForceZero(prk, (size_t)hash_len); + return wc_error_to_psa_status(ret); + } + + XMEMCPY(output, prk, output_length); + wc_ForceZero(prk, (size_t)hash_len); + return PSA_SUCCESS; } if (PSA_ALG_IS_HKDF_EXPAND(ctx->alg)) { @@ -881,6 +919,10 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, uint8_t *output, size_t output_length) { + const uint8_t *password = wolfpsa_kdf_input_ptr(ctx->password, + ctx->password_length); + const uint8_t *salt = wolfpsa_kdf_input_ptr(ctx->salt, ctx->salt_length); + if (PSA_ALG_IS_PBKDF2_HMAC(ctx->alg)) { int hash_type = wolfpsa_hash_type_from_alg(ctx->alg); int ret; @@ -888,8 +930,8 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, if (hash_type == WC_HASH_TYPE_NONE) { return PSA_ERROR_NOT_SUPPORTED; } - ret = wc_PBKDF2(output, ctx->password, (int)ctx->password_length, - ctx->salt, (int)ctx->salt_length, + ret = wc_PBKDF2(output, password, (int)ctx->password_length, + salt, (int)ctx->salt_length, (int)ctx->cost, (int)output_length, hash_type); if (ret != 0) { return wc_error_to_psa_status(ret); @@ -925,7 +967,7 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, status = wc_error_to_psa_status(ret); goto cleanup; } - ret = wc_CmacUpdate(&cmac, ctx->password, (word32)ctx->password_length); + ret = wc_CmacUpdate(&cmac, password, (word32)ctx->password_length); if (ret != 0) { wc_CmacFree(&cmac); status = wc_error_to_psa_status(ret); @@ -953,7 +995,7 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, blocks = (output_length + WC_AES_BLOCK_SIZE - 1) / WC_AES_BLOCK_SIZE; for (i = 1; i <= blocks; i++) { - XMEMCPY(block_input, ctx->salt, ctx->salt_length); + XMEMCPY(block_input, salt, ctx->salt_length); block_input[ctx->salt_length + 0] = (uint8_t)((i >> 24) & 0xff); block_input[ctx->salt_length + 1] = (uint8_t)((i >> 16) & 0xff); block_input[ctx->salt_length + 2] = (uint8_t)((i >> 8) & 0xff); @@ -1037,11 +1079,37 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, return PSA_ERROR_NOT_SUPPORTED; } +static psa_status_t wolfpsa_kdf_compute_output(wolfpsa_kdf_ctx_t *ctx, + uint8_t *output, + size_t output_length) +{ + if (ctx->is_raw_kdf) { + XMEMCPY(output, ctx->secret, output_length); + return PSA_SUCCESS; + } + + if (PSA_ALG_IS_ANY_HKDF(ctx->alg)) { + return wolfpsa_kdf_hkdf(ctx, output, output_length); + } + if (PSA_ALG_IS_TLS12_PRF(ctx->alg)) { + return wolfpsa_kdf_tls12_prf(ctx, output, output_length); + } + if (PSA_ALG_IS_TLS12_PSK_TO_MS(ctx->alg)) { + return wolfpsa_kdf_tls12_psk_to_ms(ctx, output, output_length); + } + if (PSA_ALG_IS_PBKDF2(ctx->alg)) { + return wolfpsa_kdf_pbkdf2(ctx, output, output_length); + } + + return PSA_ERROR_NOT_SUPPORTED; +} + psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *operation, uint8_t *output, size_t output_length) { wolfpsa_kdf_ctx_t *ctx = wolfpsa_kdf_get_ctx(operation); + size_t total_output_length; psa_status_t status; if (ctx == NULL || output == NULL) { @@ -1050,7 +1118,8 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope if (ctx->is_raw_kdf) { if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || - output_length > ctx->secret_length) { + ctx->output_offset > ctx->secret_length || + output_length > ctx->secret_length - ctx->output_offset) { return PSA_ERROR_INSUFFICIENT_DATA; } } @@ -1101,26 +1170,66 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope } ctx->output_started = 1; + total_output_length = ctx->output_offset + output_length; + if (total_output_length < ctx->output_offset) { + return PSA_ERROR_INVALID_ARGUMENT; + } - if (ctx->is_raw_kdf) { - XMEMCPY(output, ctx->secret, output_length); - return PSA_SUCCESS; + if (ctx->output_cache == NULL + && !ctx->is_raw_kdf + && ctx->capacity != PSA_KEY_DERIVATION_UNLIMITED_CAPACITY + && ctx->output_offset == 0) { + /* First output_bytes() call with a bounded capacity: compute the + * full derivation once and cache it. Subsequent calls serve slices + * from the cache, avoiding O(n^2) recomputation. */ + size_t cache_length = output_length + ctx->capacity; + + ctx->output_cache = (uint8_t *)XMALLOC(cache_length, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (ctx->output_cache == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + status = wolfpsa_kdf_compute_output(ctx, ctx->output_cache, + cache_length); + if (status != PSA_SUCCESS) { + wc_ForceZero(ctx->output_cache, cache_length); + XFREE(ctx->output_cache, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ctx->output_cache = NULL; + return status; + } + ctx->output_cache_length = cache_length; } - if (PSA_ALG_IS_ANY_HKDF(ctx->alg)) { - return wolfpsa_kdf_hkdf(ctx, output, output_length); + if (ctx->output_cache != NULL) { + if (total_output_length > ctx->output_cache_length) { + return PSA_ERROR_INSUFFICIENT_DATA; + } + XMEMCPY(output, ctx->output_cache + ctx->output_offset, output_length); + status = PSA_SUCCESS; } - if (PSA_ALG_IS_TLS12_PRF(ctx->alg)) { - return wolfpsa_kdf_tls12_prf(ctx, output, output_length); + else if (ctx->output_offset == 0) { + status = wolfpsa_kdf_compute_output(ctx, output, output_length); } - if (PSA_ALG_IS_TLS12_PSK_TO_MS(ctx->alg)) { - return wolfpsa_kdf_tls12_psk_to_ms(ctx, output, output_length); + else { + uint8_t *full_output; + + full_output = (uint8_t *)XMALLOC(total_output_length, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (full_output == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + status = wolfpsa_kdf_compute_output(ctx, full_output, total_output_length); + if (status == PSA_SUCCESS) { + XMEMCPY(output, full_output + ctx->output_offset, output_length); + } + wc_ForceZero(full_output, total_output_length); + XFREE(full_output, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - if (PSA_ALG_IS_PBKDF2(ctx->alg)) { - return wolfpsa_kdf_pbkdf2(ctx, output, output_length); + if (status == PSA_SUCCESS) { + ctx->output_offset += output_length; } - - return PSA_ERROR_NOT_SUPPORTED; + return status; } psa_status_t psa_key_derivation_output_key(const psa_key_attributes_t *attributes, diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 358885a..d21850d 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -31,6 +31,7 @@ #include #include #include "psa_trace.h" +#include "psa_size.h" #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #ifdef WOLFPSA_DEBUG_IMPORT #include @@ -147,6 +149,15 @@ static psa_status_t psa_wc_error_to_psa_status(int ret) return status; } +static psa_status_t wolfpsa_validate_stored_key_data_length(size_t key_data_length) +{ + if (key_data_length == 0 || key_data_length > (size_t)INT_MAX) { + return PSA_ERROR_DATA_INVALID; + } + + return PSA_SUCCESS; +} + static psa_key_bits_t wolfpsa_ecc_bits_from_length(psa_ecc_family_t family, size_t length_bytes) { @@ -374,11 +385,18 @@ static psa_status_t wolfpsa_infer_key_bits(psa_key_attributes_t* attr, psa_key_bits_t inferred_bits; if (PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(attr->type)) { - if (data_length < 2u || ((data_length - 1u) & 1u) != 0u) { - return PSA_ERROR_INVALID_ARGUMENT; + if (family == PSA_ECC_FAMILY_MONTGOMERY || + family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { + inferred_bits = wolfpsa_ecc_bits_from_length(family, + data_length); + } + else { + if (data_length < 2u || ((data_length - 1u) & 1u) != 0u) { + return PSA_ERROR_INVALID_ARGUMENT; + } + inferred_bits = wolfpsa_ecc_bits_from_length(family, + (data_length - 1u) / 2u); } - inferred_bits = wolfpsa_ecc_bits_from_length(family, - (data_length - 1u) / 2u); } else { inferred_bits = wolfpsa_ecc_bits_from_length(family, data_length); @@ -561,6 +579,16 @@ void psa_key_storage_cleanup(void) g_key_storage_initialized = 0; } +psa_key_id_t wolfpsa_test_get_next_key_id(void) +{ + return g_next_key_id; +} + +void wolfpsa_test_set_next_key_id(psa_key_id_t key_id) +{ + g_next_key_id = key_id; +} + /* Check if the key storage is initialized */ static psa_status_t psa_key_storage_check_init(void) { @@ -743,9 +771,10 @@ psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, } XMEMCPY(key_data_length, header + attr_length, sizeof(size_t)); - if (*key_data_length == 0) { + status = wolfpsa_validate_stored_key_data_length(*key_data_length); + if (status != PSA_SUCCESS) { wolfPSA_Store_Close(store); - return PSA_ERROR_DATA_INVALID; + return status; } *key_data = (uint8_t*)XMALLOC(*key_data_length, NULL, @@ -759,6 +788,7 @@ psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, wolfPSA_Store_Close(store); store = NULL; if (ret != (int)*key_data_length) { + wc_ForceZero(*key_data, *key_data_length); XFREE(*key_data, NULL, DYNAMIC_TYPE_TMP_BUFFER); *key_data = NULL; *key_data_length = 0; @@ -806,6 +836,12 @@ psa_status_t psa_import_key( attr = *attributes; + if (attr.policy.alg2 != PSA_ALG_NONE) { + wolfpsa_debug_import_reason("unsupported secondary algorithm", &attr, + data_length); + return PSA_ERROR_NOT_SUPPORTED; + } + if (attr.type == PSA_KEY_TYPE_NONE) { wolfpsa_debug_import_reason("unsupported key type", &attr, data_length); return PSA_ERROR_NOT_SUPPORTED; @@ -817,6 +853,16 @@ psa_status_t psa_import_key( return status; } } + if (attr.type == PSA_KEY_TYPE_CHACHA20) { + if (attr.bits != 256) { + wolfpsa_debug_import_reason("invalid ChaCha20 key bits", &attr, data_length); + return PSA_ERROR_INVALID_ARGUMENT; + } + if (attr.bits != (psa_key_bits_t)(data_length * 8U)) { + wolfpsa_debug_import_reason("ChaCha20 bits/length mismatch", &attr, data_length); + return PSA_ERROR_INVALID_ARGUMENT; + } + } if (attr.type == PSA_KEY_TYPE_AES) { if (attr.bits != 128 && attr.bits != 192 && @@ -856,6 +902,9 @@ psa_status_t psa_import_key( *key_id = attr_id; } else { + if (g_next_key_id == PSA_KEY_ID_NULL) { + return PSA_ERROR_INSUFFICIENT_STORAGE; + } *key_id = g_next_key_id++; } } @@ -1216,6 +1265,11 @@ psa_status_t psa_export_key( /* Get key data length */ XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); + status = wolfpsa_validate_stored_key_data_length(key_data_length); + if (status != PSA_SUCCESS) { + wolfPSA_Store_Close(store); + return status; + } /* Check if the output buffer is large enough */ if (data_size < key_data_length) { @@ -1312,6 +1366,11 @@ psa_status_t psa_export_public_key( } XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); + status = wolfpsa_validate_stored_key_data_length(key_data_length); + if (status != PSA_SUCCESS) { + wolfPSA_Store_Close(store); + return status; + } key_data = (uint8_t*)XMALLOC(key_data_length, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (key_data == NULL) { @@ -1689,6 +1748,11 @@ psa_status_t psa_copy_key( psa_get_key_usage_flags(&dst_attr); XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); + status = wolfpsa_validate_stored_key_data_length(key_data_length); + if (status != PSA_SUCCESS) { + wolfPSA_Store_Close(store); + return status; + } buffer = (uint8_t*)XMALLOC(key_data_length, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (buffer == NULL) { wolfPSA_Store_Close(store); diff --git a/src/psa_mac.c b/src/psa_mac.c index f59f358..f1f5f29 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -67,6 +67,15 @@ static wolfpsa_mac_ctx_t* wolfpsa_mac_get_ctx(psa_mac_operation_t *operation) return (wolfpsa_mac_ctx_t *)(uintptr_t)operation->opaque; } +psa_status_t psa_mac_abort(psa_mac_operation_t *operation); + +static psa_status_t wolfpsa_mac_fail(psa_mac_operation_t *operation, + psa_status_t status) +{ + (void)psa_mac_abort(operation); + return status; +} + static int wolfpsa_hash_type_from_alg(psa_algorithm_t alg) { psa_algorithm_t hash_alg = 0; @@ -155,44 +164,49 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, key_alg = psa_get_key_algorithm(attributes); key_alg_full = PSA_ALG_FULL_LENGTH_MAC(key_alg); req_alg_full = PSA_ALG_FULL_LENGTH_MAC(alg); - if (key_alg != PSA_ALG_NONE) { - if (key_alg_full != req_alg_full) { + 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; + } + + if (key_alg_full != req_alg_full) { + 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; } - - 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) { + } + 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 ((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; - } + 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; } } @@ -272,6 +286,7 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, if (ctx->mac_length == 0 || ctx->full_length == 0 || ctx->mac_length > ctx->full_length) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_INVALID_ARGUMENT; } @@ -280,11 +295,13 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, size_t trunc_len = PSA_MAC_TRUNCATED_LENGTH(alg); if (trunc_len > ctx->full_length) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_INVALID_ARGUMENT; } if (trunc_len < 4u) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_NOT_SUPPORTED; } @@ -293,6 +310,7 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, wolfpsa_forcezero_free_key_data(key_data, key_data_length); if (ret != 0) { + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return wc_error_to_psa_status(ret); } @@ -323,13 +341,13 @@ psa_status_t psa_mac_update(psa_mac_operation_t *operation, int ret; if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } if (input == NULL && input_length > 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_mac_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_mac_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (input_length == 0) { @@ -343,11 +361,11 @@ psa_status_t psa_mac_update(psa_mac_operation_t *operation, ret = wc_CmacUpdate(&ctx->ctx.cmac, input, (word32)input_length); } else { - return PSA_ERROR_BAD_STATE; + return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_mac_fail(operation, wc_error_to_psa_status(ret)); } return PSA_SUCCESS; @@ -420,12 +438,12 @@ psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation, psa_status_t status; if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } status = wolfpsa_mac_final(ctx, mac, mac_size, mac_length); if (status != PSA_SUCCESS) { - return status; + return wolfpsa_mac_fail(operation, status); } psa_mac_abort(operation); diff --git a/src/psa_random.c b/src/psa_random.c index 1ccc747..e93db88 100644 --- a/src/psa_random.c +++ b/src/psa_random.c @@ -39,12 +39,18 @@ #include #include +extern int wolfPSA_CryptoIsInitialized(void); + /* Generate random bytes */ psa_status_t psa_generate_random(uint8_t *output, size_t output_size) { int ret; WC_RNG rng; - + + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } + if (output == NULL && output_size > 0) { return PSA_ERROR_INVALID_ARGUMENT; } diff --git a/src/psa_size.h b/src/psa_size.h index 059b851..621b8d2 100644 --- a/src/psa_size.h +++ b/src/psa_size.h @@ -1,3 +1,24 @@ +/* psa_size.h + * + * 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 + */ + #ifndef WOLFPSA_SIZE_H #define WOLFPSA_SIZE_H diff --git a/src/psa_trace.h b/src/psa_trace.h index 98e71f7..a449f5a 100644 --- a/src/psa_trace.h +++ b/src/psa_trace.h @@ -1,6 +1,22 @@ /* psa_trace.h * - * Lightweight tracing for PSA entry points. + * 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 */ #ifndef WOLFPSA_PSA_TRACE_H diff --git a/test/Makefile b/test/Makefile index a8105f2..78a0d73 100644 --- a/test/Makefile +++ b/test/Makefile @@ -57,13 +57,14 @@ WOLFSSL_LOCAL_SYMBOLS := $(if $(WOLFSSL_LOCAL_LIB),$(shell \ $(NM) $(WOLFSSL_LOCAL_LIB) 2>/dev/null)) WOLFSSL_HAS_PSA_TLS := $(findstring wolfSSL_CTX_psa_enable,$(WOLFSSL_LOCAL_SYMBOLS)) -BINARIES = psa_api_test psa_des3_stack_scrub_test psa_ecc_bit_inference_test psa_ecc_curve_id_test psa_random_size_test psa_rsa_pss_interop_test psa_tls_client wolfcrypt-psa-benchmark +BINARIES = psa_api_test psa_crypto_init_test psa_des3_stack_scrub_test psa_ecc_bit_inference_test psa_ecc_curve_id_test psa_random_size_test psa_rsa_pss_interop_test psa_tls_client wolfcrypt-psa-benchmark ifdef WOLFSSL_HAS_PSA_TLS BINARIES += psa_tls_server endif PSA_API_TEST_OBJS = psa_server/psa_api_test.o +PSA_CRYPTO_INIT_TEST_OBJS = psa_server/psa_crypto_init_test.o PSA_DES3_STACK_SCRUB_TEST_OBJS = psa_server/psa_des3_stack_scrub_test.o PSA_ECC_BIT_INFERENCE_TEST_OBJS = psa_server/psa_ecc_bit_inference_test.o PSA_ECC_CURVE_ID_TEST_OBJS = psa_server/psa_ecc_curve_id_test.o @@ -86,6 +87,9 @@ endif psa_api_test: require-wolfssl-lib $(PSA_API_TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(PSA_API_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) +psa_crypto_init_test: require-wolfssl-lib $(PSA_CRYPTO_INIT_TEST_OBJS) + $(CC) $(CFLAGS) -o $@ $(PSA_CRYPTO_INIT_TEST_OBJS) $(WOLFPSA_PATH)/libwolfpsa.a $(LDFLAGS) -lwolfssl -lpthread -lm $(RPATH_WOLFSSL) -Wl,--wrap=wolfCrypt_Init + psa_des3_stack_scrub_test: require-wolfssl-lib $(PSA_DES3_STACK_SCRUB_TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(PSA_DES3_STACK_SCRUB_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 47a5ffb..aa64352 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1,5 +1,22 @@ /* psa_api_test.c - * Standalone PSA API coverage test for wolfPSA. + * + * 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 "psa_api_test_user_settings.h" @@ -13,6 +30,7 @@ #include #include #include +#include #include #include "psa_aead_internal.h" @@ -38,6 +56,9 @@ typedef int (*test_fn_t)(void); static int tests_passed = 0; static int tests_skipped = 0; +extern psa_key_id_t wolfpsa_test_get_next_key_id(void); +extern void wolfpsa_test_set_next_key_id(psa_key_id_t key_id); + static int check_status(psa_status_t st, const char* what) { if (st != PSA_SUCCESS) { @@ -170,6 +191,282 @@ static int test_hmac(void) return TEST_OK; } +static int test_hash_error_aborts_operation(void) +{ + static const uint8_t msg[] = "hash error state"; + uint8_t out[WC_SHA256_DIGEST_SIZE]; + size_t out_len = 0; + psa_hash_operation_t op = psa_hash_operation_init(); + psa_status_t st; + + st = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (check_status(st, "psa_hash_setup(error state)") != TEST_OK) return TEST_FAIL; + + st = psa_hash_update(&op, msg, sizeof(msg) - 1); + if (check_status(st, "psa_hash_update(error state seed)") != TEST_OK) { + (void)psa_hash_abort(&op); + return TEST_FAIL; + } + + st = psa_hash_finish(&op, out, 1, &out_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_hash_finish(error state status)") != TEST_OK) { + (void)psa_hash_abort(&op); + return TEST_FAIL; + } + + st = psa_hash_update(&op, msg, 1); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_hash_update(error state aborted)") != TEST_OK) { + (void)psa_hash_abort(&op); + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_algorithm_none_rejects_key_usage(void) +{ + static const uint8_t aes_key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + static const uint8_t hmac_key[16] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81 + }; + static const uint8_t hash[WC_SHA256_DIGEST_SIZE] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + }; + uint8_t sig[80]; + size_t sig_len = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t cipher_op = psa_cipher_operation_init(); + psa_aead_operation_t aead_op = psa_aead_operation_init(); + psa_mac_operation_t mac_op = psa_mac_operation_init(); + psa_key_derivation_operation_t kdf_op = psa_key_derivation_operation_init(); + psa_key_id_t key_id = 0; + psa_status_t st; + int ret = TEST_FAIL; + + 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); + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_status(st, "psa_import_key(AES alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_CBC_NO_PADDING); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_cipher_encrypt_setup rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES cipher alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + 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); + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_status(st, "psa_import_key(AES AEAD alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&aead_op, key_id, PSA_ALG_GCM); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES AEAD alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(HMAC alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_mac_sign_setup(&mac_op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_mac_sign_setup rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(HMAC alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH); + st = psa_generate_key(&attrs, &key_id); + if (check_status(st, "psa_generate_key(ECDSA alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_sign_hash(key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), sig, sizeof(sig), &sig_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_sign_hash rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(ECDSA alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(DERIVE alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&kdf_op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF alg none)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, key_id); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_input_key rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + (void)psa_cipher_abort(&cipher_op); + (void)psa_aead_abort(&aead_op); + (void)psa_mac_abort(&mac_op); + (void)psa_key_derivation_abort(&kdf_op); + return ret; +} + +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 + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_status_t st; + + 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_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) { + 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) { + 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) { + 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) { + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_mac_error_aborts_operation(void) +{ + static const uint8_t key[] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81 + }; + static const uint8_t msg[] = "mac error state"; + uint8_t mac[WC_SHA256_DIGEST_SIZE]; + size_t mac_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_mac_operation_t op = psa_mac_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, (size_t)sizeof(key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(HMAC error state)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_mac_sign_setup(&op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_mac_sign_setup(error state)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_update(&op, msg, sizeof(msg) - 1); + if (check_status(st, "psa_mac_update(error state seed)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_sign_finish(&op, mac, 1, &mac_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_mac_sign_finish(error state status)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_update(&op, msg, 1); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_mac_update(error state aborted)") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_mac_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return ret; +} + static int test_cipher_cbc(void) { static const uint8_t key[16] = { @@ -244,142 +541,523 @@ static int test_cipher_cbc(void) return TEST_OK; } -static int test_cipher_cbc_pkcs7_multipart_decrypt(void) +static int test_cipher_rejects_algorithm_mismatch(void) { static const uint8_t key[16] = { 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c }; - static const uint8_t iv[16] = { - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f - }; - static const uint8_t plaintext[17] = { - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66, - 0x67 - }; - uint8_t ciphertext[sizeof(plaintext) + 16]; - uint8_t decrypted[sizeof(plaintext)]; - size_t ciphertext_len = 0; - size_t part_len = 0; - size_t finish_len = 0; - size_t dec_len = 0; - size_t dec_part_len = 0; - size_t dec_finish_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_cipher_operation_t op = psa_cipher_operation_init(); psa_status_t st; - int result = TEST_FAIL; + int ret = TEST_FAIL; 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_KEY_USAGE_DECRYPT); - psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(AES PKCS7)") != TEST_OK) goto cleanup; - - st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); - if (st == PSA_ERROR_NOT_SUPPORTED) { - result = TEST_SKIPPED; - goto cleanup; - } - if (check_status(st, "psa_cipher_encrypt_setup(PKCS7)") != TEST_OK) { - goto cleanup; - } - st = psa_cipher_set_iv(&op, iv, sizeof(iv)); - if (check_status(st, "psa_cipher_set_iv(PKCS7)") != TEST_OK) { + if (check_status(st, "psa_import_key(AES CBC policy)") != TEST_OK) { goto cleanup; } - st = psa_cipher_update(&op, plaintext, sizeof(plaintext), - ciphertext, sizeof(ciphertext), &part_len); - if (check_status(st, "psa_cipher_update(PKCS7 enc)") != TEST_OK) { + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CTR); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_cipher_encrypt_setup rejects mismatched algorithm policy") != TEST_OK) { goto cleanup; } - ciphertext_len += part_len; - st = psa_cipher_finish(&op, ciphertext + ciphertext_len, - sizeof(ciphertext) - ciphertext_len, &finish_len); - if (check_status(st, "psa_cipher_finish(PKCS7 enc)") != TEST_OK) { - goto cleanup; + + ret = TEST_OK; + +cleanup: + if (key_id != 0) { + (void)psa_destroy_key(key_id); } - ciphertext_len += finish_len; (void)psa_cipher_abort(&op); + return ret; +} - op = psa_cipher_operation_init(); - st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); - if (check_status(st, "psa_cipher_decrypt_setup(PKCS7)") != TEST_OK) { +static int test_mac_rejects_algorithm_mismatch(void) +{ + static const uint8_t hmac_key[16] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81 + }; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_mac_operation_t op = psa_mac_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(HMAC SHA-256 policy)") != TEST_OK) { goto cleanup; } - st = psa_cipher_set_iv(&op, iv, sizeof(iv)); - if (check_status(st, "psa_cipher_set_iv(PKCS7 dec)") != TEST_OK) { + + st = psa_mac_sign_setup(&op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_512)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_mac_sign_setup rejects mismatched HMAC algorithm policy") != TEST_OK) { goto cleanup; } - st = psa_cipher_update(&op, ciphertext, 1, - decrypted, sizeof(decrypted), &dec_part_len); - if (check_status(st, "psa_cipher_update(PKCS7 dec 1)") != TEST_OK) { - goto cleanup; + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(HMAC SHA-256 policy)") != TEST_OK) { + return TEST_FAIL; } - if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec 1) length") != TEST_OK) { + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, + PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(PSA_ALG_SHA_256), 16)); + + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(HMAC minimum-length policy)") != TEST_OK) { goto cleanup; } - st = psa_cipher_update(&op, ciphertext + 1, 16, - decrypted, sizeof(decrypted), &dec_part_len); - if (check_status(st, "psa_cipher_update(PKCS7 dec 16)") != TEST_OK) { + st = psa_mac_sign_setup(&op, key_id, + PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(PSA_ALG_SHA_256), 8)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_mac_sign_setup rejects MAC shorter than minimum policy") != TEST_OK) { goto cleanup; } - if (check_true(dec_part_len == 16, "psa_cipher_update(PKCS7 dec 16) length") != TEST_OK) { + + ret = TEST_OK; + +cleanup: + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + (void)psa_mac_abort(&op); + return ret; +} + +static int test_export_key_requires_usage_flag(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t exported[sizeof(key)]; + size_t exported_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + 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); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES no export)") != TEST_OK) return TEST_FAIL; + + st = psa_export_key(key_id, exported, sizeof(exported), &exported_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_export_key requires PSA_KEY_USAGE_EXPORT") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES no export)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + +static void setup_aes_key_attrs(psa_key_attributes_t* attrs, psa_key_usage_t usage, + psa_algorithm_t alg, psa_key_lifetime_t lifetime) +{ + *attrs = psa_key_attributes_init(); + psa_set_key_type(attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(attrs, 128); + psa_set_key_usage_flags(attrs, usage); + psa_set_key_algorithm(attrs, alg); + psa_set_key_lifetime(attrs, lifetime); +} + +static int get_persistent_key_store_path(psa_key_id_t key_id, char* path, + size_t path_size) +{ + const char* root = getenv("WOLFPSA_TOKEN_PATH"); + + if (root == NULL) { + root = "./.store"; + } + + return snprintf(path, path_size, "%s/psa_key_%016lx_%016lx", root, + (unsigned long)key_id, 0ul); +} + +static int overwrite_persistent_key_data_length(psa_key_id_t key_id, + size_t key_data_length) +{ + char path[256]; + FILE* file; + long offset; + size_t written; + int len; + + len = get_persistent_key_store_path(key_id, path, sizeof(path)); + if (len <= 0 || (size_t)len >= sizeof(path)) { + return TEST_FAIL; + } + + offset = (long)(sizeof(psa_key_type_t) + sizeof(psa_key_bits_t) + + sizeof(psa_key_usage_t) + sizeof(psa_algorithm_t) + + sizeof(psa_key_lifetime_t)); + + file = fopen(path, "r+b"); + if (file == NULL) { + return TEST_FAIL; + } + + if (fseek(file, offset, SEEK_SET) != 0) { + fclose(file); + return TEST_FAIL; + } + + written = fwrite(&key_data_length, sizeof(key_data_length), 1, file); + fclose(file); + return written == 1 ? TEST_OK : TEST_FAIL; +} + +static int test_copy_key_copies_material_and_attributes(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t exported[sizeof(key)]; + size_t exported_len = 0; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = 0; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_key_attributes_t got_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_COPY | PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES copy source)") != TEST_OK) { goto cleanup; } - if (check_buf_eq("psa_cipher_update(PKCS7 dec 16)", - decrypted, plaintext, dec_part_len) != TEST_OK) { + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_status(st, "psa_copy_key(success)") != TEST_OK) { + goto cleanup; + } + if (check_true(copy_key != src_key, "psa_copy_key returns a new key id") != TEST_OK) { goto cleanup; } - dec_len += dec_part_len; - st = psa_cipher_update(&op, ciphertext + 17, ciphertext_len - 17, - decrypted + dec_len, sizeof(decrypted) - dec_len, - &dec_part_len); - if (check_status(st, "psa_cipher_update(PKCS7 dec tail)") != TEST_OK) { + st = psa_export_key(copy_key, exported, sizeof(exported), &exported_len); + if (check_status(st, "psa_export_key(copied AES)") != TEST_OK) { goto cleanup; } - if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec tail) length") != TEST_OK) { + if (check_true(exported_len == sizeof(key), "psa_export_key(copied AES) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_export_key(copied AES)", exported, key, sizeof(key)) != TEST_OK) { goto cleanup; } - st = psa_cipher_finish(&op, decrypted + dec_len, sizeof(decrypted) - dec_len, - &dec_finish_len); - if (check_status(st, "psa_cipher_finish(PKCS7 dec)") != TEST_OK) { + st = psa_get_key_attributes(copy_key, &got_attrs); + if (check_status(st, "psa_get_key_attributes(copied AES)") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_type(&got_attrs) == PSA_KEY_TYPE_AES, + "psa_copy_key preserves type") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_bits(&got_attrs) == 128, + "psa_copy_key preserves bits") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_algorithm(&got_attrs) == PSA_ALG_CBC_NO_PADDING, + "psa_copy_key preserves algorithm") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_lifetime(&got_attrs) == PSA_KEY_LIFETIME_VOLATILE, + "psa_copy_key preserves lifetime") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_usage_flags(&got_attrs) == + (PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT), + "psa_copy_key intersects usage flags") != TEST_OK) { goto cleanup; } - dec_len += dec_finish_len; - (void)psa_cipher_abort(&op); - if (check_true(dec_len == sizeof(plaintext), "psa_cipher_decrypt(PKCS7) length") != TEST_OK) { + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&got_attrs); + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != 0) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_export_key_rejects_oversized_persistent_length(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t exported[sizeof(key)]; + size_t exported_len = 0; + psa_key_id_t key_id = 0; + psa_key_id_t persistent_id = PSA_KEY_ID_USER_MIN + 2406u; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + size_t oversized_length; + int ret = TEST_FAIL; + + if (sizeof(size_t) <= sizeof(int)) { + return TEST_SKIPPED; + } + + (void)psa_destroy_key(persistent_id); + + setup_aes_key_attrs(&attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attrs, persistent_id); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES persistent oversized length)") != TEST_OK) { goto cleanup; } - if (check_buf_eq("psa_cipher_decrypt(PKCS7)", decrypted, plaintext, sizeof(plaintext)) != TEST_OK) { + + oversized_length = (size_t)UINT32_MAX + 17u; + if (check_true(oversized_length > (size_t)INT_MAX, + "oversized persistent length exceeds INT_MAX") != TEST_OK) { + goto cleanup; + } + if (overwrite_persistent_key_data_length(key_id, oversized_length) != TEST_OK) { + printf("FAIL: overwrite_persistent_key_data_length\n"); goto cleanup; } - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(AES PKCS7)") != TEST_OK) return TEST_FAIL; + st = psa_export_key(key_id, exported, sizeof(exported), &exported_len); + if (check_true(st == PSA_ERROR_DATA_INVALID, + "psa_export_key rejects oversized persistent length") != TEST_OK) { + goto cleanup; + } - key_id = 0; - result = TEST_OK; + ret = TEST_OK; cleanup: - (void)psa_cipher_abort(&op); + psa_reset_key_attributes(&attrs); if (key_id != 0) { (void)psa_destroy_key(key_id); } - return result; + else { + (void)psa_destroy_key(persistent_id); + } + return ret; } -static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) +static int test_copy_key_requires_copy_usage_flag(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = PSA_KEY_ID_NULL; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES no copy)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_copy_key requires PSA_KEY_USAGE_COPY for volatile keys") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_copy_key_rejects_attribute_mismatch(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = PSA_KEY_ID_NULL; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_COPY | PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES copy mismatch source)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_type(&dst_attrs, PSA_KEY_TYPE_DES); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects type mismatch") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_bits(&dst_attrs, 192); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects bit-size mismatch") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_ECB_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects algorithm mismatch") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects lifetime mismatch") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_copy_key_requires_copy_usage_flag_persistent(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = PSA_KEY_ID_NULL; + psa_key_id_t persistent_id = PSA_KEY_ID_USER_MIN + 2405u; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + (void)psa_destroy_key(persistent_id); + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&src_attrs, persistent_id); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES persistent no copy)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_copy_key requires PSA_KEY_USAGE_COPY for persistent keys") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_cipher_cbc_pkcs7_multipart_decrypt(void) { static const uint8_t key[16] = { 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, @@ -395,15 +1073,18 @@ static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) 0x67 }; uint8_t ciphertext[sizeof(plaintext) + 16]; - uint8_t too_small[1]; + uint8_t decrypted[sizeof(plaintext)]; size_t ciphertext_len = 0; size_t part_len = 0; size_t finish_len = 0; - size_t out_len = 0; + size_t dec_len = 0; + size_t dec_part_len = 0; + size_t dec_finish_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_cipher_operation_t op = psa_cipher_operation_init(); psa_status_t st; + int result = TEST_FAIL; psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); psa_set_key_bits(&attrs, 128); @@ -411,45 +1092,177 @@ static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(AES PKCS7 small out)") != TEST_OK) { - return TEST_FAIL; - } + if (check_status(st, "psa_import_key(AES PKCS7)") != TEST_OK) goto cleanup; st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); - if (check_status(st, "psa_cipher_encrypt_setup(PKCS7 small out)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + if (st == PSA_ERROR_NOT_SUPPORTED) { + result = TEST_SKIPPED; + goto cleanup; + } + if (check_status(st, "psa_cipher_encrypt_setup(PKCS7)") != TEST_OK) { + goto cleanup; } st = psa_cipher_set_iv(&op, iv, sizeof(iv)); - if (check_status(st, "psa_cipher_set_iv(PKCS7 small out enc)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + if (check_status(st, "psa_cipher_set_iv(PKCS7)") != TEST_OK) { + goto cleanup; } st = psa_cipher_update(&op, plaintext, sizeof(plaintext), ciphertext, sizeof(ciphertext), &part_len); - if (check_status(st, "psa_cipher_update(PKCS7 small out enc)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + if (check_status(st, "psa_cipher_update(PKCS7 enc)") != TEST_OK) { + goto cleanup; } ciphertext_len += part_len; st = psa_cipher_finish(&op, ciphertext + ciphertext_len, sizeof(ciphertext) - ciphertext_len, &finish_len); - if (check_status(st, "psa_cipher_finish(PKCS7 small out enc)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + if (check_status(st, "psa_cipher_finish(PKCS7 enc)") != TEST_OK) { + goto cleanup; } ciphertext_len += finish_len; + (void)psa_cipher_abort(&op); op = psa_cipher_operation_init(); st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); - if (check_status(st, "psa_cipher_decrypt_setup(PKCS7 small out)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + if (check_status(st, "psa_cipher_decrypt_setup(PKCS7)") != TEST_OK) { + goto cleanup; } st = psa_cipher_set_iv(&op, iv, sizeof(iv)); - if (check_status(st, "psa_cipher_set_iv(PKCS7 small out dec)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + if (check_status(st, "psa_cipher_set_iv(PKCS7 dec)") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_update(&op, ciphertext, 1, + decrypted, sizeof(decrypted), &dec_part_len); + if (check_status(st, "psa_cipher_update(PKCS7 dec 1)") != TEST_OK) { + goto cleanup; + } + if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec 1) length") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_update(&op, ciphertext + 1, 16, + decrypted, sizeof(decrypted), &dec_part_len); + if (check_status(st, "psa_cipher_update(PKCS7 dec 16)") != TEST_OK) { + goto cleanup; + } + if (check_true(dec_part_len == 16, "psa_cipher_update(PKCS7 dec 16) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_cipher_update(PKCS7 dec 16)", + decrypted, plaintext, dec_part_len) != TEST_OK) { + goto cleanup; + } + dec_len += dec_part_len; + + st = psa_cipher_update(&op, ciphertext + 17, ciphertext_len - 17, + decrypted + dec_len, sizeof(decrypted) - dec_len, + &dec_part_len); + if (check_status(st, "psa_cipher_update(PKCS7 dec tail)") != TEST_OK) { + goto cleanup; + } + if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec tail) length") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_finish(&op, decrypted + dec_len, sizeof(decrypted) - dec_len, + &dec_finish_len); + if (check_status(st, "psa_cipher_finish(PKCS7 dec)") != TEST_OK) { + goto cleanup; + } + dec_len += dec_finish_len; + (void)psa_cipher_abort(&op); + + if (check_true(dec_len == sizeof(plaintext), "psa_cipher_decrypt(PKCS7) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_cipher_decrypt(PKCS7)", decrypted, plaintext, sizeof(plaintext)) != TEST_OK) { + goto cleanup; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES PKCS7)") != TEST_OK) return TEST_FAIL; + + key_id = 0; + result = TEST_OK; + +cleanup: + (void)psa_cipher_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return result; +} + +static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + static const uint8_t iv[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + }; + static const uint8_t plaintext[17] = { + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66, + 0x67 + }; + uint8_t ciphertext[sizeof(plaintext) + 16]; + uint8_t too_small[1]; + size_t ciphertext_len = 0; + size_t part_len = 0; + size_t finish_len = 0; + size_t out_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t op = psa_cipher_operation_init(); + psa_status_t st; + + 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_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES PKCS7 small out)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_encrypt_setup(PKCS7 small out)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(PKCS7 small out enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + ciphertext, sizeof(ciphertext), &part_len); + if (check_status(st, "psa_cipher_update(PKCS7 small out enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + ciphertext_len += part_len; + st = psa_cipher_finish(&op, ciphertext + ciphertext_len, + sizeof(ciphertext) - ciphertext_len, &finish_len); + if (check_status(st, "psa_cipher_finish(PKCS7 small out enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + ciphertext_len += finish_len; + + op = psa_cipher_operation_init(); + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_decrypt_setup(PKCS7 small out)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(PKCS7 small out dec)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; } st = psa_cipher_update(&op, ciphertext, 1, too_small, sizeof(too_small), &out_len); @@ -480,136 +1293,252 @@ static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) return TEST_OK; } -static int test_aead_gcm(void) +static int test_cipher_error_aborts_operation(void) { static const uint8_t key[16] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 - }; - static const uint8_t nonce[12] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00 + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c }; - static const uint8_t aad[1] = { 0 }; - static const size_t aad_len = 0; - static const uint8_t plaintext[16] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + static const uint8_t iv[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }; - static const uint8_t expected[sizeof(plaintext) + 16] = { - 0x03,0x88,0xda,0xce,0x60,0xb6,0xa3,0x92, - 0xf3,0x28,0xc2,0xb9,0x71,0xb2,0xfe,0x78, - 0xab,0x6e,0x47,0xd4,0x2c,0xec,0x13,0xbd, - 0xf5,0x3a,0x67,0xb2,0x12,0x57,0xbd,0xdf + static const uint8_t plaintext[17] = { + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66, + 0x67 }; - uint8_t out[sizeof(plaintext) + 16]; - uint8_t dec[sizeof(plaintext)]; + uint8_t ciphertext[sizeof(plaintext) + 16]; + uint8_t small[1]; + size_t ciphertext_len = 0; + size_t part_len = 0; + size_t finish_len = 0; size_t out_len = 0; - size_t dec_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t op = psa_cipher_operation_init(); psa_status_t st; + int ret = TEST_FAIL; 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_KEY_USAGE_DECRYPT); - psa_set_key_algorithm(&attrs, PSA_ALG_GCM); + psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(GCM)") != TEST_OK) return TEST_FAIL; + if (check_status(st, "psa_import_key(AES error state)") != TEST_OK) { + return TEST_FAIL; + } - st = psa_aead_encrypt(key_id, PSA_ALG_GCM, - nonce, sizeof(nonce), - aad, aad_len, - plaintext, sizeof(plaintext), - out, sizeof(out), &out_len); - if (check_status(st, "psa_aead_encrypt") != TEST_OK) return TEST_FAIL; - if (check_true(out_len == sizeof(expected), "psa_aead_encrypt length") != TEST_OK) return TEST_FAIL; - if (check_buf_eq("psa_aead_encrypt", out, expected, sizeof(expected)) != TEST_OK) return TEST_FAIL; + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_encrypt_setup(error state enc)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(error state enc)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + ciphertext, sizeof(ciphertext), &part_len); + if (check_status(st, "psa_cipher_update(error state enc)") != TEST_OK) { + goto cleanup; + } + ciphertext_len += part_len; + st = psa_cipher_finish(&op, ciphertext + ciphertext_len, + sizeof(ciphertext) - ciphertext_len, &finish_len); + if (check_status(st, "psa_cipher_finish(error state enc)") != TEST_OK) { + goto cleanup; + } + ciphertext_len += finish_len; + (void)psa_cipher_abort(&op); - st = psa_aead_decrypt(key_id, PSA_ALG_GCM, - nonce, sizeof(nonce), - aad, aad_len, - out, out_len, - dec, sizeof(dec), &dec_len); - if (check_status(st, "psa_aead_decrypt") != TEST_OK) return TEST_FAIL; - if (check_true(dec_len == sizeof(plaintext), "psa_aead_decrypt length") != TEST_OK) return TEST_FAIL; - if (check_buf_eq("psa_aead_decrypt", dec, plaintext, sizeof(plaintext)) != TEST_OK) return TEST_FAIL; + op = psa_cipher_operation_init(); + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_decrypt_setup(error state)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(error state dec)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, ciphertext, 1, small, sizeof(small), &out_len); + if (check_status(st, "psa_cipher_update(error state seed)") != TEST_OK) { + goto cleanup; + } - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(GCM)") != TEST_OK) return TEST_FAIL; + st = psa_cipher_update(&op, ciphertext + 1, 16, small, sizeof(small), &out_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_cipher_update(error state status)") != TEST_OK) { + goto cleanup; + } - return TEST_OK; + st = psa_cipher_finish(&op, ciphertext, sizeof(ciphertext), &out_len); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_cipher_finish(error state aborted)") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_cipher_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return ret; } -static int test_aead_multipart_length_overflow_rejected(void) +static int test_cipher_ccm_star_no_tag_multipart(void) { static const uint8_t key[16] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, + 0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf }; - static const uint8_t nonce[12] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00 + static const uint8_t nonce[13] = { + 0x00,0x00,0x00,0x03,0x02,0x01,0x00,0xa0, + 0xa1,0xa2,0xa3,0xa4,0xa5 }; - static const uint8_t chunk[2] = { 0xaa, 0x55 }; - uint8_t out[sizeof(chunk)]; - size_t out_len = 0; + static const uint8_t plaintext[32] = { + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27 + }; + uint8_t single[sizeof(plaintext)]; + uint8_t multi[sizeof(plaintext)]; + uint8_t decrypted[sizeof(plaintext)]; + size_t single_len = 0; + size_t multi_len = 0; + size_t part_len = 0; + size_t finish_len = 0; + size_t decrypted_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); - psa_aead_operation_t op = psa_aead_operation_init(); - wolfpsa_aead_ctx_t *ctx; - int ret = TEST_OK; + psa_cipher_operation_t op = psa_cipher_operation_init(); psa_status_t st; + int result = TEST_FAIL; 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_GCM); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CCM_STAR_NO_TAG); st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(GCM overflow)") != TEST_OK) return TEST_FAIL; + if (check_status(st, "psa_import_key(AES CCM* no tag)") != TEST_OK) { + goto cleanup; + } - st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); - if (check_status(st, "psa_aead_encrypt_setup") != TEST_OK) goto cleanup; - st = psa_aead_set_lengths(&op, SIZE_MAX, SIZE_MAX); - if (check_status(st, "psa_aead_set_lengths") != TEST_OK) goto cleanup; - st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); - if (check_status(st, "psa_aead_set_nonce") != TEST_OK) goto cleanup; + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CCM_STAR_NO_TAG); + if (st == PSA_ERROR_NOT_SUPPORTED) { + result = TEST_SKIPPED; + goto cleanup; + } + if (check_status(st, "psa_cipher_encrypt_setup(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_cipher_set_iv(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + single, sizeof(single), &single_len); + if (check_status(st, "psa_cipher_update(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_finish(&op, single + single_len, sizeof(single) - single_len, + &finish_len); + if (check_status(st, "psa_cipher_finish(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + single_len += finish_len; + (void)psa_cipher_abort(&op); - ctx = wolfpsa_aead_get_ctx_ptr(&op); - ctx->aad_length = SIZE_MAX - 1; - st = psa_aead_update_ad(&op, chunk, sizeof(chunk)); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_update_ad rejects wrapped accumulated length") != TEST_OK) { - ret = TEST_FAIL; + op = psa_cipher_operation_init(); + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CCM_STAR_NO_TAG); + if (check_status(st, "psa_cipher_encrypt_setup(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_cipher_set_iv(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, plaintext, 16, multi, sizeof(multi), &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag first chunk)") != TEST_OK) { + goto cleanup; + } + multi_len += part_len; + st = psa_cipher_update(&op, plaintext + 16, sizeof(plaintext) - 16, + multi + multi_len, sizeof(multi) - multi_len, &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag second chunk)") != TEST_OK) { + goto cleanup; + } + multi_len += part_len; + st = psa_cipher_finish(&op, multi + multi_len, sizeof(multi) - multi_len, + &finish_len); + if (check_status(st, "psa_cipher_finish(CCM* no tag multi)") != TEST_OK) { goto cleanup; } + multi_len += finish_len; + (void)psa_cipher_abort(&op); - st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); - if (check_status(st, "psa_aead_encrypt_setup(plaintext)") != TEST_OK) goto cleanup; - st = psa_aead_set_lengths(&op, 0, SIZE_MAX); - if (check_status(st, "psa_aead_set_lengths(plaintext)") != TEST_OK) goto cleanup; - st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); - if (check_status(st, "psa_aead_set_nonce(plaintext)") != TEST_OK) goto cleanup; + if (check_true(single_len == sizeof(plaintext), "psa_cipher_update(CCM* no tag single) length") != TEST_OK) { + goto cleanup; + } + if (check_true(multi_len == single_len, "psa_cipher_update(CCM* no tag multi) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_cipher_update(CCM* no tag split matches single)", + multi, single, single_len) != TEST_OK) { + goto cleanup; + } - ctx = wolfpsa_aead_get_ctx_ptr(&op); - ctx->input_length = SIZE_MAX - 1; - st = psa_aead_update(&op, chunk, sizeof(chunk), out, sizeof(out), &out_len); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_update rejects wrapped accumulated length") != TEST_OK) { - ret = TEST_FAIL; + op = psa_cipher_operation_init(); + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CCM_STAR_NO_TAG); + if (check_status(st, "psa_cipher_decrypt_setup(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_cipher_set_iv(CCM* no tag dec)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, multi, 16, decrypted, sizeof(decrypted), &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag dec first chunk)") != TEST_OK) { + goto cleanup; + } + decrypted_len += part_len; + st = psa_cipher_update(&op, multi + 16, sizeof(multi) - 16, + decrypted + decrypted_len, sizeof(decrypted) - decrypted_len, + &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag dec second chunk)") != TEST_OK) { + goto cleanup; + } + decrypted_len += part_len; + st = psa_cipher_finish(&op, decrypted + decrypted_len, + sizeof(decrypted) - decrypted_len, &finish_len); + if (check_status(st, "psa_cipher_finish(CCM* no tag dec)") != TEST_OK) { + goto cleanup; + } + decrypted_len += finish_len; + + if (check_true(decrypted_len == sizeof(plaintext), "psa_cipher_decrypt(CCM* no tag multi) length") != TEST_OK) { goto cleanup; } + if (check_buf_eq("psa_cipher_decrypt(CCM* no tag multi)", + decrypted, plaintext, sizeof(plaintext)) != TEST_OK) { + goto cleanup; + } + + result = TEST_OK; cleanup: - psa_aead_abort(&op); - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(GCM overflow)") != TEST_OK) return TEST_FAIL; - return ret; + (void)psa_cipher_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return result; } -static int test_aead_finish_verify_word32_overflow_rejected(void) +static int test_aead_gcm(void) { static const uint8_t key[16] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -619,19 +1548,24 @@ static int test_aead_finish_verify_word32_overflow_rejected(void) 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }; - static const uint8_t empty_gcm_tag[16] = { - 0x58,0xe2,0xfc,0xce,0xfa,0x7e,0x30,0x61, - 0x36,0x7f,0x1d,0x57,0xa4,0xe7,0x45,0x5a + static const uint8_t aad[1] = { 0 }; + static const size_t aad_len = 0; + static const uint8_t plaintext[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t out[1]; - uint8_t tag[sizeof(empty_gcm_tag)]; + static const uint8_t expected[sizeof(plaintext) + 16] = { + 0x03,0x88,0xda,0xce,0x60,0xb6,0xa3,0x92, + 0xf3,0x28,0xc2,0xb9,0x71,0xb2,0xfe,0x78, + 0xab,0x6e,0x47,0xd4,0x2c,0xec,0x13,0xbd, + 0xf5,0x3a,0x67,0xb2,0x12,0x57,0xbd,0xdf + }; + uint8_t out[sizeof(plaintext) + 16]; + uint8_t dec[sizeof(plaintext)]; size_t out_len = 0; - size_t tag_len = 0; + size_t dec_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); - psa_aead_operation_t op = psa_aead_operation_init(); - wolfpsa_aead_ctx_t *ctx; - int ret = TEST_OK; psa_status_t st; psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); @@ -640,138 +1574,735 @@ static int test_aead_finish_verify_word32_overflow_rejected(void) psa_set_key_algorithm(&attrs, PSA_ALG_GCM); st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(GCM word32 overflow)") != TEST_OK) { - return TEST_FAIL; - } - - st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); - if (check_status(st, "psa_aead_encrypt_setup(word32 input)") != TEST_OK) goto cleanup; - st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); - if (check_status(st, "psa_aead_set_nonce(word32 input)") != TEST_OK) goto cleanup; - ctx = wolfpsa_aead_get_ctx_ptr(&op); - ctx->input = NULL; - ctx->input_length = (size_t)UINT32_MAX + 1u; - st = psa_aead_finish(&op, out, SIZE_MAX, &out_len, tag, sizeof(tag), &tag_len); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_finish rejects input_length above word32") != TEST_OK) { - ret = TEST_FAIL; - goto cleanup; - } - - st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); - if (check_status(st, "psa_aead_encrypt_setup(word32 aad)") != TEST_OK) goto cleanup; - st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); - if (check_status(st, "psa_aead_set_nonce(word32 aad)") != TEST_OK) goto cleanup; - ctx = wolfpsa_aead_get_ctx_ptr(&op); - ctx->aad = NULL; - ctx->aad_length = (size_t)UINT32_MAX + 1u; - st = psa_aead_finish(&op, out, sizeof(out), &out_len, tag, sizeof(tag), &tag_len); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_finish rejects aad_length above word32") != TEST_OK) { - ret = TEST_FAIL; - goto cleanup; - } + if (check_status(st, "psa_import_key(GCM)") != TEST_OK) return TEST_FAIL; - st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); - if (check_status(st, "psa_aead_decrypt_setup(word32 input)") != TEST_OK) goto cleanup; - st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); - if (check_status(st, "psa_aead_set_nonce(word32 verify input)") != TEST_OK) goto cleanup; - ctx = wolfpsa_aead_get_ctx_ptr(&op); - ctx->input = NULL; - ctx->input_length = (size_t)UINT32_MAX + 1u; - st = psa_aead_verify(&op, out, SIZE_MAX, &out_len, empty_gcm_tag, sizeof(empty_gcm_tag)); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_verify rejects input_length above word32") != TEST_OK) { - ret = TEST_FAIL; - goto cleanup; - } + st = psa_aead_encrypt(key_id, PSA_ALG_GCM, + nonce, sizeof(nonce), + aad, aad_len, + plaintext, sizeof(plaintext), + out, sizeof(out), &out_len); + if (check_status(st, "psa_aead_encrypt") != TEST_OK) return TEST_FAIL; + if (check_true(out_len == sizeof(expected), "psa_aead_encrypt length") != TEST_OK) return TEST_FAIL; + if (check_buf_eq("psa_aead_encrypt", out, expected, sizeof(expected)) != TEST_OK) return TEST_FAIL; - st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); - if (check_status(st, "psa_aead_decrypt_setup(word32 aad)") != TEST_OK) goto cleanup; - st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); - if (check_status(st, "psa_aead_set_nonce(word32 verify aad)") != TEST_OK) goto cleanup; - ctx = wolfpsa_aead_get_ctx_ptr(&op); - ctx->aad = NULL; - ctx->aad_length = (size_t)UINT32_MAX + 1u; - st = psa_aead_verify(&op, out, sizeof(out), &out_len, empty_gcm_tag, sizeof(empty_gcm_tag)); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_verify rejects aad_length above word32") != TEST_OK) { - ret = TEST_FAIL; - goto cleanup; - } + st = psa_aead_decrypt(key_id, PSA_ALG_GCM, + nonce, sizeof(nonce), + aad, aad_len, + out, out_len, + dec, sizeof(dec), &dec_len); + if (check_status(st, "psa_aead_decrypt") != TEST_OK) return TEST_FAIL; + if (check_true(dec_len == sizeof(plaintext), "psa_aead_decrypt length") != TEST_OK) return TEST_FAIL; + if (check_buf_eq("psa_aead_decrypt", dec, plaintext, sizeof(plaintext)) != TEST_OK) return TEST_FAIL; -cleanup: - psa_aead_abort(&op); st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(GCM word32 overflow)") != TEST_OK) { - return TEST_FAIL; - } - return ret; + if (check_status(st, "psa_destroy_key(GCM)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; } -static int test_chacha20_poly1305_rejects_aes_key(void) +static int test_aead_gcm_multipart_zero_length_inputs(void) { - static const uint8_t key[32] = { - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + static const uint8_t key[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; static const uint8_t nonce[12] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }; + static const uint8_t aad[12] = { + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b + }; static const uint8_t plaintext[16] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f }; - uint8_t out[sizeof(plaintext) + 16]; - size_t out_len = 0; + uint8_t combined[sizeof(plaintext) + 16]; + uint8_t ciphertext[sizeof(plaintext)]; + uint8_t tag[16]; + uint8_t decrypt_out[sizeof(plaintext)]; + uint8_t update_out[sizeof(plaintext)]; + uint8_t dummy[1] = { 0 }; + size_t combined_len = 0; + size_t ciphertext_len = 0; + size_t plaintext_len = 0; + size_t tag_len = 0; + size_t update_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); psa_status_t st; + int ret = TEST_OK; psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attrs, 256); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); psa_set_key_algorithm(&attrs, PSA_ALG_GCM); - /* - * Import a valid AES AEAD key first, then prove that the ChaCha20-Poly1305 - * path rejects it when the operation algorithm does not match the key type. - */ st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(AES for GCM)") != TEST_OK) return TEST_FAIL; - - st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, - nonce, sizeof(nonce), - NULL, 0, - plaintext, sizeof(plaintext), - out, sizeof(out), &out_len); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_encrypt(AES key with ChaCha20) rejected") != TEST_OK) { - (void)psa_destroy_key(key_id); + if (check_status(st, "psa_import_key(GCM zero-length multipart)") != TEST_OK) { return TEST_FAIL; } - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(AES for GCM)") != TEST_OK) return TEST_FAIL; - - return TEST_OK; -} + st = psa_aead_encrypt(key_id, PSA_ALG_GCM, + nonce, sizeof(nonce), + aad, sizeof(aad), + dummy, 0, + combined, sizeof(combined), &combined_len); + if (check_status(st, "psa_aead_encrypt(GCM zero plaintext reference)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(combined_len == sizeof(tag), + "psa_aead_encrypt(GCM zero plaintext reference length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } -static int test_asym_ecc(void) -{ - static const uint8_t msg[] = "psa ecc sign"; - uint8_t hash[WC_SHA256_DIGEST_SIZE]; - uint8_t sig[128]; - size_t sig_len = 0; - uint8_t pub[128]; - size_t pub_len = 0; - psa_key_id_t sign_key = 0; - psa_key_id_t dh_key1 = 0; - psa_key_id_t dh_key2 = 0; - uint8_t dh_pub1[128]; + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, sizeof(aad)); + if (check_status(st, "psa_aead_update_ad(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, dummy, 0, update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(update_len == 0, "psa_aead_update(GCM zero plaintext) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_finish(&op, dummy, sizeof(dummy), &ciphertext_len, + tag, sizeof(tag), &tag_len); + if (check_status(st, "psa_aead_finish(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(ciphertext_len == 0, "psa_aead_finish(GCM zero plaintext) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(tag_len == sizeof(tag), "psa_aead_finish(GCM zero plaintext) tag length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(GCM zero plaintext) tag matches reference)", + tag, combined, sizeof(tag)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_decrypt_setup(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero plaintext verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, sizeof(aad)); + if (check_status(st, "psa_aead_update_ad(GCM zero plaintext verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, dummy, 0, update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero plaintext verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_verify(&op, dummy, sizeof(dummy), &plaintext_len, tag, tag_len); + if (check_status(st, "psa_aead_verify(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(plaintext_len == 0, "psa_aead_verify(GCM zero plaintext) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt(key_id, PSA_ALG_GCM, + nonce, sizeof(nonce), + aad, 0, + plaintext, sizeof(plaintext), + combined, sizeof(combined), &combined_len); + if (check_status(st, "psa_aead_encrypt(GCM zero aad reference)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(combined_len == sizeof(plaintext) + sizeof(tag), + "psa_aead_encrypt(GCM zero aad reference length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, 0); + if (check_status(st, "psa_aead_update_ad(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, plaintext, sizeof(plaintext), + update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(update_len == 0, "psa_aead_update(GCM zero aad) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_finish(&op, ciphertext, sizeof(ciphertext), &ciphertext_len, + tag, sizeof(tag), &tag_len); + if (check_status(st, "psa_aead_finish(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(ciphertext_len == sizeof(ciphertext), "psa_aead_finish(GCM zero aad) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(tag_len == sizeof(tag), "psa_aead_finish(GCM zero aad) tag length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(GCM zero aad) ciphertext matches reference)", + ciphertext, combined, sizeof(ciphertext)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(GCM zero aad) tag matches reference)", + tag, combined + sizeof(ciphertext), sizeof(tag)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_decrypt_setup(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero aad verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, 0); + if (check_status(st, "psa_aead_update_ad(GCM zero aad verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, ciphertext, ciphertext_len, + update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero aad verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_verify(&op, decrypt_out, sizeof(decrypt_out), &plaintext_len, tag, tag_len); + if (check_status(st, "psa_aead_verify(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(plaintext_len == sizeof(plaintext), "psa_aead_verify(GCM zero aad) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_verify(GCM zero aad)", decrypt_out, plaintext, sizeof(plaintext)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + if (key_id != 0) { + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM zero-length multipart)") != TEST_OK) { + return TEST_FAIL; + } + } + return ret; +} + +static int test_aead_multipart_length_overflow_rejected(void) +{ + static const uint8_t key[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + static const uint8_t nonce[12] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + static const uint8_t chunk[2] = { 0xaa, 0x55 }; + uint8_t out[sizeof(chunk)]; + size_t out_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + wolfpsa_aead_ctx_t *ctx; + int ret = TEST_OK; + psa_status_t st; + + 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_GCM); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM overflow)") != TEST_OK) return TEST_FAIL; + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup") != TEST_OK) goto cleanup; + st = psa_aead_set_lengths(&op, SIZE_MAX, SIZE_MAX); + if (check_status(st, "psa_aead_set_lengths") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce") != TEST_OK) goto cleanup; + + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->aad_length = SIZE_MAX - 1; + st = psa_aead_update_ad(&op, chunk, sizeof(chunk)); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_update_ad rejects wrapped accumulated length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(plaintext)") != TEST_OK) goto cleanup; + st = psa_aead_set_lengths(&op, 0, SIZE_MAX); + if (check_status(st, "psa_aead_set_lengths(plaintext)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(plaintext)") != TEST_OK) goto cleanup; + + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->input_length = SIZE_MAX - 1; + st = psa_aead_update(&op, chunk, sizeof(chunk), out, sizeof(out), &out_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_update rejects wrapped accumulated length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM overflow)") != TEST_OK) return TEST_FAIL; + return ret; +} + +static int test_aead_finish_verify_word32_overflow_rejected(void) +{ + static const uint8_t key[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + static const uint8_t nonce[12] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + static const uint8_t empty_gcm_tag[16] = { + 0x58,0xe2,0xfc,0xce,0xfa,0x7e,0x30,0x61, + 0x36,0x7f,0x1d,0x57,0xa4,0xe7,0x45,0x5a + }; + uint8_t out[1]; + uint8_t tag[sizeof(empty_gcm_tag)]; + size_t out_len = 0; + size_t tag_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + wolfpsa_aead_ctx_t *ctx; + int ret = TEST_OK; + psa_status_t st; + + 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_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_GCM); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM word32 overflow)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(word32 input)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 input)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->input = NULL; + ctx->input_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_finish(&op, out, SIZE_MAX, &out_len, tag, sizeof(tag), &tag_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_finish rejects input_length above word32") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(word32 aad)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 aad)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->aad = NULL; + ctx->aad_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_finish(&op, out, sizeof(out), &out_len, tag, sizeof(tag), &tag_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_finish rejects aad_length above word32") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_decrypt_setup(word32 input)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 verify input)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->input = NULL; + ctx->input_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_verify(&op, out, SIZE_MAX, &out_len, empty_gcm_tag, sizeof(empty_gcm_tag)); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_verify rejects input_length above word32") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_decrypt_setup(word32 aad)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 verify aad)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->aad = NULL; + ctx->aad_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_verify(&op, out, sizeof(out), &out_len, empty_gcm_tag, sizeof(empty_gcm_tag)); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_verify rejects aad_length above word32") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM word32 overflow)") != TEST_OK) { + return TEST_FAIL; + } + return ret; +} + +static int test_aead_policy_mismatch_rejected(void) +{ + static const uint8_t key[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + psa_status_t st; + int ret = TEST_OK; + + 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_GCM); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM policy)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_CCM); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects AEAD base mismatch") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 8)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects exact tag mismatch") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM policy)") != TEST_OK) { + return TEST_FAIL; + } + + psa_set_key_algorithm(&attrs, + PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_GCM, 8)); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM at least tag)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 12)); + if (check_status(st, "psa_aead_encrypt_setup(allows longer AEAD tag)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup_at_least; + } + psa_aead_abort(&op); + + st = psa_aead_encrypt_setup(&op, key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 4)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects shorter AEAD tag than policy") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup_at_least; + } + +cleanup_at_least: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM at least tag)") != TEST_OK) { + return TEST_FAIL; + } + + return ret; +} + +static int test_chacha20_poly1305_rejects_aes_key(void) +{ + static const uint8_t key[32] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + }; + static const uint8_t nonce[12] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + static const uint8_t plaintext[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + uint8_t out[sizeof(plaintext) + 16]; + size_t out_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_GCM); + + /* + * Import a valid AES AEAD key first, then prove that the ChaCha20-Poly1305 + * path rejects it when the operation algorithm does not match the key type. + */ + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES for GCM)") != TEST_OK) return TEST_FAIL; + + st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, + nonce, sizeof(nonce), + NULL, 0, + plaintext, sizeof(plaintext), + out, sizeof(out), &out_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_encrypt(AES key with ChaCha20) rejected") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES for GCM)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + +static int test_chacha20_import_rejects_invalid_key_size(void) +{ + static const uint8_t key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key_id = 0; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_CHACHA20); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_STREAM_CIPHER); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + psa_reset_key_attributes(&attrs); + + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_import_key rejects invalid ChaCha20 key size") != TEST_OK) { + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_chacha20_poly1305_multipart_finish_split_buffers(void) +{ + static const uint8_t key[32] = { + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f + }; + static const uint8_t nonce[12] = { + 0x07,0x00,0x00,0x00,0x40,0x41,0x42,0x43, + 0x44,0x45,0x46,0x47 + }; + static const uint8_t aad[12] = { + 0x50,0x51,0x52,0x53,0xc0,0xc1,0xc2,0xc3, + 0xc4,0xc5,0xc6,0xc7 + }; + static const uint8_t plaintext[16] = { + 0x4c,0x61,0x64,0x69,0x65,0x73,0x20,0x61, + 0x6e,0x64,0x20,0x47,0x65,0x6e,0x74,0x6c + }; + uint8_t ciphertext[sizeof(plaintext)]; + uint8_t tag[16]; + uint8_t combined[sizeof(plaintext) + sizeof(tag)]; + uint8_t update_out[sizeof(plaintext)]; + size_t ciphertext_len = 0; + size_t tag_len = 0; + size_t combined_len = 0; + size_t update_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + psa_status_t st; + int ret = TEST_OK; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_CHACHA20); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CHACHA20_POLY1305); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(ChaCha20 multipart)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_CHACHA20_POLY1305); + if (check_status(st, "psa_aead_encrypt_setup(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, sizeof(aad)); + if (check_status(st, "psa_aead_update_ad(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, plaintext, sizeof(plaintext), + update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(update_len == 0, + "psa_aead_update(ChaCha20 multipart) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_finish(&op, ciphertext, sizeof(ciphertext), &ciphertext_len, + tag, sizeof(tag), &tag_len); + if (check_status(st, "psa_aead_finish(ChaCha20 split buffers)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(ciphertext_len == sizeof(ciphertext), + "psa_aead_finish(ChaCha20 ciphertext length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(tag_len == sizeof(tag), + "psa_aead_finish(ChaCha20 tag length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, + nonce, sizeof(nonce), + aad, sizeof(aad), + plaintext, sizeof(plaintext), + combined, sizeof(combined), &combined_len); + if (check_status(st, "psa_aead_encrypt(ChaCha20 reference)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(combined_len == sizeof(combined), + "psa_aead_encrypt(ChaCha20 reference length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(ChaCha20 ciphertext matches reference)", + ciphertext, combined, sizeof(ciphertext)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(ChaCha20 tag matches reference)", + tag, combined + sizeof(ciphertext), sizeof(tag)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(ChaCha20 multipart)") != TEST_OK) { + return TEST_FAIL; + } + return ret; +} + +static int test_asym_ecc(void) +{ + static const uint8_t msg[] = "psa ecc sign"; + uint8_t hash[WC_SHA256_DIGEST_SIZE]; + uint8_t sig[128]; + size_t sig_len = 0; + uint8_t pub[128]; + size_t pub_len = 0; + psa_key_id_t sign_key = 0; + psa_key_id_t dh_key1 = 0; + psa_key_id_t dh_key2 = 0; + uint8_t dh_pub1[128]; uint8_t dh_pub2[128]; size_t dh_pub1_len = 0; size_t dh_pub2_len = 0; @@ -781,227 +2312,1066 @@ static int test_asym_ecc(void) size_t dh_out2_len = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_status_t st; - wc_Sha256 sha; - int ret; + wc_Sha256 sha; + int ret; + + ret = wc_InitSha256(&sha); + if (ret != 0) { + printf("FAIL: wc_InitSha256 (ecc) (%d)\n", ret); + return TEST_FAIL; + } + wc_Sha256Update(&sha, msg, (word32)sizeof(msg) - 1); + wc_Sha256Final(&sha, hash); + wc_Sha256Free(&sha); + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + + st = psa_generate_key(&attrs, &sign_key); + if (check_status(st, "psa_generate_key(ECDSA)") != TEST_OK) return TEST_FAIL; + + st = psa_sign_hash(sign_key, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), sig, sizeof(sig), &sig_len); + if (check_status(st, "psa_sign_hash") != TEST_OK) return TEST_FAIL; + + st = psa_verify_hash(sign_key, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), sig, sig_len); + if (check_status(st, "psa_verify_hash") != TEST_OK) return TEST_FAIL; + + st = psa_export_public_key(sign_key, pub, sizeof(pub), &pub_len); + if (check_status(st, "psa_export_public_key") != TEST_OK) return TEST_FAIL; + if (check_true(pub_len > 0, "psa_export_public_key length") != TEST_OK) return TEST_FAIL; + + st = psa_destroy_key(sign_key); + if (check_status(st, "psa_destroy_key(ECDSA)") != TEST_OK) return TEST_FAIL; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_generate_key(&attrs, &dh_key1); + if (check_status(st, "psa_generate_key(ECDH1)") != TEST_OK) return TEST_FAIL; + st = psa_generate_key(&attrs, &dh_key2); + if (check_status(st, "psa_generate_key(ECDH2)") != TEST_OK) return TEST_FAIL; + + st = psa_export_public_key(dh_key1, dh_pub1, sizeof(dh_pub1), &dh_pub1_len); + if (check_status(st, "psa_export_public_key(ECDH1)") != TEST_OK) return TEST_FAIL; + st = psa_export_public_key(dh_key2, dh_pub2, sizeof(dh_pub2), &dh_pub2_len); + if (check_status(st, "psa_export_public_key(ECDH2)") != TEST_OK) return TEST_FAIL; + + st = psa_raw_key_agreement(PSA_ALG_ECDH, dh_key1, dh_pub2, dh_pub2_len, + dh_out1, sizeof(dh_out1), &dh_out1_len); + if (check_status(st, "psa_raw_key_agreement(1)") != TEST_OK) return TEST_FAIL; + st = psa_raw_key_agreement(PSA_ALG_ECDH, dh_key2, dh_pub1, dh_pub1_len, + dh_out2, sizeof(dh_out2), &dh_out2_len); + if (check_status(st, "psa_raw_key_agreement(2)") != TEST_OK) return TEST_FAIL; + + if (check_true(dh_out1_len == dh_out2_len, "psa_raw_key_agreement length") != TEST_OK) return TEST_FAIL; + if (check_buf_eq("psa_raw_key_agreement", dh_out1, dh_out2, dh_out1_len) != TEST_OK) return TEST_FAIL; + + st = psa_destroy_key(dh_key1); + if (check_status(st, "psa_destroy_key(ECDH1)") != TEST_OK) return TEST_FAIL; + st = psa_destroy_key(dh_key2); + if (check_status(st, "psa_destroy_key(ECDH2)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + +static int test_asym_algorithm_mismatch_policy(void) +{ + static const uint8_t msg[] = "wolfpsa asym mismatch"; + uint8_t hash[WC_SHA256_DIGEST_SIZE]; + uint8_t peer_pub[100]; + uint8_t secret[100]; + uint8_t sig[80]; + size_t peer_pub_len = 0; + size_t secret_len = 0; + size_t sig_len = 0; + psa_key_id_t ecdsa_key = PSA_KEY_ID_NULL; + psa_key_id_t ecdh_key = PSA_KEY_ID_NULL; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + wc_Sha256 sha; + int ret; + + ret = wc_InitSha256(&sha); + if (ret != 0) { + printf("FAIL: wc_InitSha256 (asym mismatch) (%d)\n", ret); + return TEST_FAIL; + } + wc_Sha256Update(&sha, msg, (word32)sizeof(msg) - 1u); + wc_Sha256Final(&sha, hash); + wc_Sha256Free(&sha); + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + + st = psa_generate_key(&attrs, &ecdsa_key); + if (check_status(st, "psa_generate_key(ECDSA mismatch)") != TEST_OK) { + goto cleanup; + } + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_generate_key(&attrs, &ecdh_key); + if (check_status(st, "psa_generate_key(ECDH mismatch)") != TEST_OK) { + goto cleanup; + } + + st = psa_export_public_key(ecdh_key, peer_pub, sizeof(peer_pub), &peer_pub_len); + if (check_status(st, "psa_export_public_key(ECDH mismatch peer)") != TEST_OK) { + goto cleanup; + } + + st = psa_raw_key_agreement(PSA_ALG_ECDH, ecdsa_key, peer_pub, peer_pub_len, + secret, sizeof(secret), &secret_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_raw_key_agreement rejects ECDSA key policy") != TEST_OK) { + goto cleanup; + } + + st = psa_sign_hash(ecdh_key, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), sig, sizeof(sig), &sig_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_sign_hash rejects ECDH key policy") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + if (ecdh_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(ecdh_key); + } + if (ecdsa_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(ecdsa_key); + } + + return ret; +} + +static int test_generate_key_rejects_public_key_type(void) +{ + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + + st = psa_generate_key(&attrs, &key_id); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_generate_key rejects ECC public key type") != TEST_OK) { + return TEST_FAIL; + } + if (check_true(key_id == PSA_KEY_ID_NULL, + "psa_generate_key leaves key id null on public key type") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_import_key_rejects_wrapped_volatile_key_id(void) +{ + static const uint8_t aes_key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + const psa_key_id_t original_next_key_id = wolfpsa_test_get_next_key_id(); + const psa_key_id_t max_key_id = (psa_key_id_t)~(psa_key_id_t)0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t first_key_id = PSA_KEY_ID_NULL; + psa_key_id_t second_key_id = PSA_KEY_ID_NULL; + psa_status_t st; + int result = TEST_FAIL; + + 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_CTR); + psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_VOLATILE); + + wolfpsa_test_set_next_key_id(max_key_id); + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &first_key_id); + if (check_status(st, "psa_import_key(max volatile key id)") != TEST_OK) { + goto cleanup; + } + if (check_true(first_key_id == max_key_id, + "psa_import_key uses max volatile key id before wrap") != TEST_OK) { + goto cleanup; + } + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &second_key_id); + if (check_true(st == PSA_ERROR_INSUFFICIENT_STORAGE, + "psa_import_key rejects wrapped volatile key id") != TEST_OK) { + goto cleanup; + } + if (check_true(second_key_id == PSA_KEY_ID_NULL, + "psa_import_key leaves key id null on volatile key id wrap") != TEST_OK) { + goto cleanup; + } + + result = TEST_OK; + +cleanup: + if (first_key_id != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(first_key_id); + } + wolfpsa_test_set_next_key_id(original_next_key_id); + return result; +} + +static int test_asym_rsa_oaep_usage_policy(void) +{ + static const uint8_t plaintext[] = "psa rsa oaep"; + uint8_t exported_pub[256]; + uint8_t ciphertext[256]; + uint8_t decrypted[sizeof(plaintext)]; + size_t exported_pub_len = 0; + size_t ciphertext_len = 0; + size_t decrypted_len = 0; + psa_key_id_t decrypt_key = 0; + psa_key_id_t encrypt_pub_key = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + int result = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&attrs, 1024); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256)); + + st = psa_generate_key(&attrs, &decrypt_key); + if (st == PSA_ERROR_NOT_SUPPORTED) { + return TEST_SKIPPED; + } + if (check_status(st, "psa_generate_key(RSA OAEP decrypt)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_export_public_key(decrypt_key, exported_pub, sizeof(exported_pub), + &exported_pub_len); + if (check_status(st, "psa_export_public_key(RSA OAEP)") != TEST_OK) { + goto cleanup; + } + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + psa_set_key_bits(&attrs, 1024); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256)); + + st = psa_import_key(&attrs, exported_pub, exported_pub_len, &encrypt_pub_key); + if (check_status(st, "psa_import_key(RSA OAEP public)") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_encrypt(encrypt_pub_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + plaintext, sizeof(plaintext) - 1, + NULL, 0, ciphertext, sizeof(ciphertext), NULL); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_asymmetric_encrypt rejects NULL output_length") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_encrypt(decrypt_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + plaintext, sizeof(plaintext) - 1, + NULL, 0, ciphertext, sizeof(ciphertext), + &ciphertext_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_asymmetric_encrypt enforces ENCRYPT usage") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_encrypt(encrypt_pub_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + plaintext, sizeof(plaintext) - 1, + NULL, 0, ciphertext, sizeof(ciphertext), + &ciphertext_len); + if (check_status(st, "psa_asymmetric_encrypt(RSA OAEP)") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_decrypt(decrypt_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + ciphertext, ciphertext_len, + NULL, 0, decrypted, sizeof(decrypted), NULL); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_asymmetric_decrypt rejects NULL output_length") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_decrypt(decrypt_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + ciphertext, ciphertext_len, + NULL, 0, decrypted, sizeof(decrypted), + &decrypted_len); + if (check_status(st, "psa_asymmetric_decrypt(RSA OAEP)") != TEST_OK) { + goto cleanup; + } + if (check_true(decrypted_len == sizeof(plaintext) - 1, + "psa_asymmetric_decrypt(RSA OAEP) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_asymmetric_encrypt/decrypt(RSA OAEP)", + decrypted, plaintext, sizeof(plaintext) - 1) != TEST_OK) { + goto cleanup; + } + + result = TEST_OK; + +cleanup: + if (encrypt_pub_key != 0) { + psa_status_t destroy_st = psa_destroy_key(encrypt_pub_key); + if (result == TEST_OK && + check_status(destroy_st, "psa_destroy_key(RSA OAEP public)") != TEST_OK) { + result = TEST_FAIL; + } + } + if (decrypt_key != 0) { + psa_status_t destroy_st = psa_destroy_key(decrypt_key); + if (result == TEST_OK && + check_status(destroy_st, "psa_destroy_key(RSA OAEP decrypt)") != TEST_OK) { + result = TEST_FAIL; + } + } + + return result; +} + +static int test_ed25519_signature_length(void) +{ + static const uint8_t hash[64] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f + }; + static const uint8_t msg[] = "ed25519 message dispatch"; + uint8_t sig[128]; + /* + * The original bug cast `size_t*` to `word32*`. On 64-bit builds that can + * update only the low 32 bits, leaving the upper 32 bits stale. Seed + * `sig_len` with the low half clear and the upper half all ones so a + * truncated store becomes a large non-64 value instead of accidentally + * looking correct. + */ + size_t sig_len = ~((size_t)UINT32_MAX); + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; - ret = wc_InitSha256(&sha); - if (ret != 0) { - printf("FAIL: wc_InitSha256 (ecc) (%d)\n", ret); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); + psa_set_key_bits(&attrs, 255); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | + PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE); + psa_set_key_algorithm(&attrs, PSA_ALG_ED25519PH); + + st = psa_generate_key(&attrs, &key_id); + if (st == PSA_ERROR_NOT_SUPPORTED) { + return TEST_SKIPPED; + } + if (check_status(st, "psa_generate_key(ED25519)") != TEST_OK) return TEST_FAIL; + + st = psa_sign_hash(key_id, PSA_ALG_ED25519PH, + hash, sizeof(hash), + sig, sizeof(sig), &sig_len); + if (check_status(st, "psa_sign_hash(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(sig_len == 64u, "psa_sign_hash(ED25519PH) length") != TEST_OK) { + (void)psa_destroy_key(key_id); return TEST_FAIL; } - wc_Sha256Update(&sha, msg, (word32)sizeof(msg) - 1); - wc_Sha256Final(&sha, hash); - wc_Sha256Free(&sha); - psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); - psa_set_key_bits(&attrs, 256); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_EXPORT); - psa_set_key_algorithm(&attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + st = psa_verify_hash(key_id, PSA_ALG_ED25519PH, + hash, sizeof(hash), sig, sig_len); + if (check_status(st, "psa_verify_hash(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } - st = psa_generate_key(&attrs, &sign_key); - if (check_status(st, "psa_generate_key(ECDSA)") != TEST_OK) return TEST_FAIL; + sig_len = sizeof(sig); + st = psa_sign_message(key_id, PSA_ALG_ED25519PH, + msg, sizeof(msg) - 1, + sig, sizeof(sig), &sig_len); + if (check_status(st, "psa_sign_message(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(sig_len == 64u, "psa_sign_message(ED25519PH) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } - st = psa_sign_hash(sign_key, PSA_ALG_ECDSA(PSA_ALG_SHA_256), - hash, sizeof(hash), sig, sizeof(sig), &sig_len); - if (check_status(st, "psa_sign_hash") != TEST_OK) return TEST_FAIL; + st = psa_verify_message(key_id, PSA_ALG_ED25519PH, + msg, sizeof(msg) - 1, sig, sig_len); + if (check_status(st, "psa_verify_message(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } - st = psa_verify_hash(sign_key, PSA_ALG_ECDSA(PSA_ALG_SHA_256), - hash, sizeof(hash), sig, sig_len); - if (check_status(st, "psa_verify_hash") != TEST_OK) return TEST_FAIL; + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(ED25519)") != TEST_OK) return TEST_FAIL; - st = psa_export_public_key(sign_key, pub, sizeof(pub), &pub_len); - if (check_status(st, "psa_export_public_key") != TEST_OK) return TEST_FAIL; - if (check_true(pub_len > 0, "psa_export_public_key length") != TEST_OK) return TEST_FAIL; + return TEST_OK; +} + +static int test_twisted_edwards_export_public_key(size_t bits, + psa_algorithm_t alg, + size_t expected_pub_len, + const char* label) +{ + uint8_t pub[ED448_PUBLIC_KEY_BYTES]; + size_t pub_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + psa_set_key_type(&attrs, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, alg); + + st = psa_generate_key(&attrs, &key_id); + if (st == PSA_ERROR_NOT_SUPPORTED) { + return TEST_SKIPPED; + } + if (check_status(st, label) != TEST_OK) return TEST_FAIL; + + st = psa_export_public_key(key_id, pub, sizeof(pub), &pub_len); + if (check_status(st, "psa_export_public_key(Twisted Edwards)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(pub_len == expected_pub_len, + "psa_export_public_key(Twisted Edwards) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(Twisted Edwards)") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_ed25519_export_public_key(void) +{ + return test_twisted_edwards_export_public_key(255, PSA_ALG_ED25519PH, + ED25519_PUBLIC_KEY_BYTES, + "psa_generate_key(ED25519 export)"); +} + +static int test_ed448_export_public_key(void) +{ + return test_twisted_edwards_export_public_key(448, PSA_ALG_ED448PH, + ED448_PUBLIC_KEY_BYTES, + "psa_generate_key(ED448 export)"); +} + +static int test_kdf_null_capacity(void) +{ + size_t capacity = 0; + psa_status_t st; + + st = psa_key_derivation_get_capacity(NULL, &capacity); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_get_capacity(NULL)") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_kdf_pbkdf2_zero_cost_rejected(void) +{ + static const uint8_t password[] = "pbkdf2-password"; + static const uint8_t salt[] = "pbkdf2-salt"; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 zero cost)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + password, sizeof(password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + salt, sizeof(salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PBKDF2 salt)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 0); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_input_integer rejects zero PBKDF2 cost") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + return ret; +} + +static int test_kdf_input_key_policy(void) +{ + static const uint8_t secret[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + static const uint8_t info[] = "kdf-input-key"; + uint8_t output[16]; + size_t output_len = 0; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t derive_key = 0; + psa_key_id_t no_usage_key = 0; + psa_key_id_t raw_key = 0; + psa_key_id_t password_key = 0; + psa_key_id_t wrong_pbkdf2_key = 0; + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &derive_key); + if (check_status(st, "psa_import_key(KDF derive key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF input key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, derive_key); + if (check_status(st, "psa_key_derivation_input_key(HKDF derive key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + info, sizeof(info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO input key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, output, sizeof(output)); + if (check_status(st, "psa_key_derivation_output_bytes(input key)") != TEST_OK) { + goto cleanup; + } + output_len = sizeof(output); + if (check_true(output_len == sizeof(output), + "psa_key_derivation_output_bytes(input key) length") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF input key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); - st = psa_destroy_key(sign_key); - if (check_status(st, "psa_destroy_key(ECDSA)") != TEST_OK) return TEST_FAIL; + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &no_usage_key); + if (check_status(st, "psa_import_key(KDF no usage key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF no usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, no_usage_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_input_key rejects missing DERIVE usage") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF no usage)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); psa_reset_key_attributes(&attrs); - psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); - psa_set_key_bits(&attrs, 256); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); - psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &raw_key); + if (check_status(st, "psa_import_key(KDF raw key)") != TEST_OK) { + goto cleanup; + } - st = psa_generate_key(&attrs, &dh_key1); - if (check_status(st, "psa_generate_key(ECDH1)") != TEST_OK) return TEST_FAIL; - st = psa_generate_key(&attrs, &dh_key2); - if (check_status(st, "psa_generate_key(ECDH2)") != TEST_OK) return TEST_FAIL; + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF raw key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, raw_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_input_key rejects non-DERIVE secret key type") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF raw key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); - st = psa_export_public_key(dh_key1, dh_pub1, sizeof(dh_pub1), &dh_pub1_len); - if (check_status(st, "psa_export_public_key(ECDH1)") != TEST_OK) return TEST_FAIL; - st = psa_export_public_key(dh_key2, dh_pub2, sizeof(dh_pub2), &dh_pub2_len); - if (check_status(st, "psa_export_public_key(ECDH2)") != TEST_OK) return TEST_FAIL; + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_PASSWORD); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &password_key); + if (check_status(st, "psa_import_key(PBKDF2 password key)") != TEST_OK) { + goto cleanup; + } - st = psa_raw_key_agreement(PSA_ALG_ECDH, dh_key1, dh_pub2, dh_pub2_len, - dh_out1, sizeof(dh_out1), &dh_out1_len); - if (check_status(st, "psa_raw_key_agreement(1)") != TEST_OK) return TEST_FAIL; - st = psa_raw_key_agreement(PSA_ALG_ECDH, dh_key2, dh_pub1, dh_pub1_len, - dh_out2, sizeof(dh_out2), &dh_out2_len); - if (check_status(st, "psa_raw_key_agreement(2)") != TEST_OK) return TEST_FAIL; + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, password_key); + if (check_status(st, "psa_key_derivation_input_key(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); - if (check_true(dh_out1_len == dh_out2_len, "psa_raw_key_agreement length") != TEST_OK) return TEST_FAIL; - if (check_buf_eq("psa_raw_key_agreement", dh_out1, dh_out2, dh_out1_len) != TEST_OK) return TEST_FAIL; + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &wrong_pbkdf2_key); + if (check_status(st, "psa_import_key(PBKDF2 wrong type key)") != TEST_OK) { + goto cleanup; + } - st = psa_destroy_key(dh_key1); - if (check_status(st, "psa_destroy_key(ECDH1)") != TEST_OK) return TEST_FAIL; - st = psa_destroy_key(dh_key2); - if (check_status(st, "psa_destroy_key(ECDH2)") != TEST_OK) return TEST_FAIL; + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 wrong type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + wrong_pbkdf2_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_input_key rejects non-PASSWORD PBKDF2 key type") != TEST_OK) { + goto cleanup; + } - return TEST_OK; + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + if (wrong_pbkdf2_key != 0) { + (void)psa_destroy_key(wrong_pbkdf2_key); + } + if (password_key != 0) { + (void)psa_destroy_key(password_key); + } + if (raw_key != 0) { + (void)psa_destroy_key(raw_key); + } + if (no_usage_key != 0) { + (void)psa_destroy_key(no_usage_key); + } + if (derive_key != 0) { + (void)psa_destroy_key(derive_key); + } + psa_reset_key_attributes(&attrs); + return ret; } -static int test_ed25519_signature_length(void) +static int test_kdf_verify_key_policy(void) { - static const uint8_t hash[64] = { - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f + static const uint8_t hkdf_secret[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; - static const uint8_t msg[] = "ed25519 message dispatch"; - uint8_t sig[128]; - /* - * The original bug cast `size_t*` to `word32*`. On 64-bit builds that can - * update only the low 32 bits, leaving the upper 32 bits stale. Seed - * `sig_len` with the low half clear and the upper half all ones so a - * truncated store becomes a large non-64 value instead of accidentally - * looking correct. - */ - size_t sig_len = ~((size_t)UINT32_MAX); - psa_key_id_t key_id = 0; + static const uint8_t hkdf_info[] = "kdf-verify-key"; + static const uint8_t pbkdf2_password[] = "password"; + static const uint8_t pbkdf2_salt[] = "salt"; + uint8_t hkdf_expected[16]; + uint8_t pbkdf2_expected[16]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t hkdf_verify_key = 0; + psa_key_id_t hkdf_no_usage_key = 0; + psa_key_id_t pbkdf2_verify_key = 0; + psa_key_id_t pbkdf2_wrong_type_key = 0; psa_status_t st; + int ret = TEST_FAIL; - psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); - psa_set_key_bits(&attrs, 255); - psa_set_key_usage_flags(&attrs, - PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | - PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE); - psa_set_key_algorithm(&attrs, PSA_ALG_ED25519PH); + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + hkdf_secret, sizeof(hkdf_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_info, sizeof(hkdf_info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, hkdf_expected, sizeof(hkdf_expected)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF verify expected)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); - st = psa_generate_key(&attrs, &key_id); - if (st == PSA_ERROR_NOT_SUPPORTED) { - return TEST_SKIPPED; + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_DERIVATION); + st = psa_import_key(&attrs, hkdf_expected, sizeof(hkdf_expected), &hkdf_verify_key); + if (check_status(st, "psa_import_key(HKDF verify key)") != TEST_OK) { + goto cleanup; } - if (check_status(st, "psa_generate_key(ED25519)") != TEST_OK) return TEST_FAIL; - st = psa_sign_hash(key_id, PSA_ALG_ED25519PH, - hash, sizeof(hash), - sig, sizeof(sig), &sig_len); - if (check_status(st, "psa_sign_hash(ED25519PH)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF verify key)") != TEST_OK) { + goto cleanup; } - if (check_true(sig_len == 64u, "psa_sign_hash(ED25519PH) length") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + hkdf_secret, sizeof(hkdf_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_info, sizeof(hkdf_info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, hkdf_verify_key); + if (check_status(st, "psa_key_derivation_verify_key(HKDF verify usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF verify key)") != TEST_OK) { + goto cleanup; } + op = psa_key_derivation_operation_init(); - st = psa_verify_hash(key_id, PSA_ALG_ED25519PH, - hash, sizeof(hash), sig, sig_len); - if (check_status(st, "psa_verify_hash(ED25519PH)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + st = psa_import_key(&attrs, hkdf_expected, sizeof(hkdf_expected), &hkdf_no_usage_key); + if (check_status(st, "psa_import_key(HKDF no verify usage key)") != TEST_OK) { + goto cleanup; } - sig_len = sizeof(sig); - st = psa_sign_message(key_id, PSA_ALG_ED25519PH, - msg, sizeof(msg) - 1, - sig, sizeof(sig), &sig_len); - if (check_status(st, "psa_sign_message(ED25519PH)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF no verify usage)") != TEST_OK) { + goto cleanup; } - if (check_true(sig_len == 64u, "psa_sign_message(ED25519PH) length") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + hkdf_secret, sizeof(hkdf_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET no verify usage)") != TEST_OK) { + goto cleanup; } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_info, sizeof(hkdf_info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO no verify usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, hkdf_no_usage_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_verify_key rejects missing VERIFY_DERIVATION usage") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF no verify usage)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); - st = psa_verify_message(key_id, PSA_ALG_ED25519PH, - msg, sizeof(msg) - 1, sig, sig_len); - if (check_status(st, "psa_verify_message(ED25519PH)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + pbkdf2_password, sizeof(pbkdf2_password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PASSWORD verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + pbkdf2_salt, sizeof(pbkdf2_salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SALT verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 2); + if (check_status(st, "psa_key_derivation_input_integer(COST verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, pbkdf2_expected, sizeof(pbkdf2_expected)); + if (check_status(st, "psa_key_derivation_output_bytes(PBKDF2 verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(PBKDF2 verify expected)") != TEST_OK) { + goto cleanup; } + op = psa_key_derivation_operation_init(); - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(ED25519)") != TEST_OK) return TEST_FAIL; + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_PASSWORD_HASH); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_DERIVATION); + st = psa_import_key(&attrs, pbkdf2_expected, sizeof(pbkdf2_expected), &pbkdf2_verify_key); + if (check_status(st, "psa_import_key(PBKDF2 verify key)") != TEST_OK) { + goto cleanup; + } - return TEST_OK; + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + pbkdf2_password, sizeof(pbkdf2_password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PASSWORD verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + pbkdf2_salt, sizeof(pbkdf2_salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SALT verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 2); + if (check_status(st, "psa_key_derivation_input_integer(COST verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, pbkdf2_verify_key); + if (check_status(st, "psa_key_derivation_verify_key(PBKDF2 password hash)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(PBKDF2 verify key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_DERIVATION); + st = psa_import_key(&attrs, pbkdf2_expected, sizeof(pbkdf2_expected), + &pbkdf2_wrong_type_key); + if (check_status(st, "psa_import_key(PBKDF2 wrong expected key type)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + pbkdf2_password, sizeof(pbkdf2_password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PASSWORD wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + pbkdf2_salt, sizeof(pbkdf2_salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SALT wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 2); + if (check_status(st, "psa_key_derivation_input_integer(COST wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, pbkdf2_wrong_type_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_verify_key rejects non-PASSWORD_HASH PBKDF2 expected key") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + if (pbkdf2_wrong_type_key != 0) { + (void)psa_destroy_key(pbkdf2_wrong_type_key); + } + if (pbkdf2_verify_key != 0) { + (void)psa_destroy_key(pbkdf2_verify_key); + } + if (hkdf_no_usage_key != 0) { + (void)psa_destroy_key(hkdf_no_usage_key); + } + if (hkdf_verify_key != 0) { + (void)psa_destroy_key(hkdf_verify_key); + } + psa_reset_key_attributes(&attrs); + return ret; } -static int test_twisted_edwards_export_public_key(size_t bits, - psa_algorithm_t alg, - size_t expected_pub_len, - const char* label) +static int test_kdf_key_agreement_policy(void) { - uint8_t pub[ED448_PUBLIC_KEY_BYTES]; - size_t pub_len = 0; - psa_key_id_t key_id = 0; + uint8_t peer_pub[128]; + size_t peer_pub_len = 0; + uint8_t good_pub[128]; + size_t good_pub_len = 0; + uint8_t expected_secret[128]; + size_t expected_secret_len = 0; + uint8_t derived_secret[128]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t good_key = 0; + psa_key_id_t peer_key = 0; + psa_key_id_t no_usage_key = 0; + psa_key_id_t public_key = 0; psa_status_t st; + int ret = TEST_FAIL; - psa_set_key_type(&attrs, - PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); - psa_set_key_bits(&attrs, bits); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); - psa_set_key_algorithm(&attrs, alg); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); - st = psa_generate_key(&attrs, &key_id); - if (st == PSA_ERROR_NOT_SUPPORTED) { - return TEST_SKIPPED; + st = psa_generate_key(&attrs, &good_key); + if (check_status(st, "psa_generate_key(KDF ECDH derive key)") != TEST_OK) { + goto cleanup; + } + st = psa_generate_key(&attrs, &peer_key); + if (check_status(st, "psa_generate_key(KDF ECDH peer key)") != TEST_OK) { + goto cleanup; } - if (check_status(st, label) != TEST_OK) return TEST_FAIL; - st = psa_export_public_key(key_id, pub, sizeof(pub), &pub_len); - if (check_status(st, "psa_export_public_key(Twisted Edwards)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_export_public_key(peer_key, peer_pub, sizeof(peer_pub), &peer_pub_len); + if (check_status(st, "psa_export_public_key(KDF ECDH peer key)") != TEST_OK) { + goto cleanup; } - if (check_true(pub_len == expected_pub_len, - "psa_export_public_key(Twisted Edwards) length") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + st = psa_export_public_key(good_key, good_pub, sizeof(good_pub), &good_pub_len); + if (check_status(st, "psa_export_public_key(KDF ECDH derive key)") != TEST_OK) { + goto cleanup; } - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(Twisted Edwards)") != TEST_OK) { - return TEST_FAIL; + st = psa_raw_key_agreement(PSA_ALG_ECDH, good_key, peer_pub, peer_pub_len, + expected_secret, sizeof(expected_secret), + &expected_secret_len); + if (check_status(st, "psa_raw_key_agreement(KDF ECDH expected secret)") != TEST_OK) { + goto cleanup; } - return TEST_OK; -} + st = psa_key_derivation_setup(&op, PSA_ALG_ECDH); + if (check_status(st, "psa_key_derivation_setup(raw ECDH)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_key_agreement(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + good_key, peer_pub, peer_pub_len); + if (check_status(st, "psa_key_derivation_key_agreement(derive key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, derived_secret, expected_secret_len); + if (check_status(st, "psa_key_derivation_output_bytes(raw ECDH)") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_key_derivation_key_agreement matches raw ECDH", + derived_secret, expected_secret, expected_secret_len) != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(raw ECDH success)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); -static int test_ed25519_export_public_key(void) -{ - return test_twisted_edwards_export_public_key(255, PSA_ALG_ED25519PH, - ED25519_PUBLIC_KEY_BYTES, - "psa_generate_key(ED25519 export)"); -} + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); -static int test_ed448_export_public_key(void) -{ - return test_twisted_edwards_export_public_key(448, PSA_ALG_ED448PH, - ED448_PUBLIC_KEY_BYTES, - "psa_generate_key(ED448 export)"); -} + st = psa_generate_key(&attrs, &no_usage_key); + if (check_status(st, "psa_generate_key(KDF ECDH no usage key)") != TEST_OK) { + goto cleanup; + } -static int test_kdf_null_capacity(void) -{ - size_t capacity = 0; - psa_status_t st; + st = psa_key_derivation_setup(&op, PSA_ALG_ECDH); + if (check_status(st, "psa_key_derivation_setup(raw ECDH no usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_key_agreement(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + no_usage_key, peer_pub, peer_pub_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_key_agreement rejects missing DERIVE usage") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(raw ECDH no usage)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); - st = psa_key_derivation_get_capacity(NULL, &capacity); + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_import_key(&attrs, good_pub, good_pub_len, &public_key); + if (check_status(st, "psa_import_key(KDF ECDH public key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_ECDH); + if (check_status(st, "psa_key_derivation_setup(raw ECDH public key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_key_agreement(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + public_key, peer_pub, peer_pub_len); if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_key_derivation_get_capacity(NULL)") != TEST_OK) { - return TEST_FAIL; + "psa_key_derivation_key_agreement rejects public key input") != TEST_OK) { + goto cleanup; } - return TEST_OK; + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + if (public_key != 0) { + (void)psa_destroy_key(public_key); + } + if (no_usage_key != 0) { + (void)psa_destroy_key(no_usage_key); + } + if (peer_key != 0) { + (void)psa_destroy_key(peer_key); + } + if (good_key != 0) { + (void)psa_destroy_key(good_key); + } + psa_reset_key_attributes(&attrs); + return ret; } static int test_kdf_tls12_psk_to_ms_rfc4279_order(void) @@ -1189,6 +3559,62 @@ static int test_kdf_hkdf_extract_optional_salt(void) return TEST_OK; } +static int test_kdf_hkdf_extract_prefix_output(void) +{ + static const uint8_t secret[] = "hkdf extract secret"; + uint8_t expected[WC_SHA256_DIGEST_SIZE]; + uint8_t output[16]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + ret = wc_HKDF_Extract(WC_HASH_TYPE_SHA256, + NULL, 0, + secret, (word32)(sizeof(secret) - 1u), + expected); + if (ret != 0) { + printf("FAIL: wc_HKDF_Extract(HKDF_EXTRACT prefix output reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF_EXTRACT prefix output)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, NULL, 0); + if (check_status(st, "psa_key_derivation_input_bytes(SALT HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + secret, sizeof(secret) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_set_capacity(&op, sizeof(expected)); + if (check_status(st, "psa_key_derivation_set_capacity(HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_output_bytes(&op, output, sizeof(output)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF_EXTRACT prefix output)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF_EXTRACT prefix output)", + output, expected, sizeof(output)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int test_kdf_hkdf_expand_optional_info(void) { static const uint8_t prk[WC_SHA256_DIGEST_SIZE] = { @@ -1297,6 +3723,76 @@ static int test_kdf_hkdf_optional_info(void) return TEST_OK; } +static int test_kdf_output_bytes_are_sequential(void) +{ + static const uint8_t secret[] = "hkdf sequential output secret"; + static const uint8_t info[] = "hkdf sequential output info"; + uint8_t expected[32]; + uint8_t output_part1[16]; + uint8_t output_part2[16]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + ret = wc_HKDF(WC_HASH_TYPE_SHA256, + secret, (word32)(sizeof(secret) - 1u), + NULL, 0, + info, (word32)(sizeof(info) - 1u), + expected, (word32)sizeof(expected)); + if (ret != 0) { + printf("FAIL: wc_HKDF(sequential output reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF sequential output)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + secret, sizeof(secret) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET HKDF sequential output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + info, sizeof(info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO HKDF sequential output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_set_capacity(&op, sizeof(expected)); + if (check_status(st, "psa_key_derivation_set_capacity(HKDF sequential output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_output_bytes(&op, output_part1, sizeof(output_part1)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF sequential output first)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_output_bytes(&op, output_part2, sizeof(output_part2)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF sequential output second)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF sequential output)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF sequential output first)", + output_part1, expected, sizeof(output_part1)) != TEST_OK) { + return TEST_FAIL; + } + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF sequential output second)", + output_part2, expected + sizeof(output_part1), + sizeof(output_part2)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { int ret; @@ -1327,12 +3823,84 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "hash") == 0) { if (run_named_test("hash", test_hash) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "hash_error_state") == 0) { + if (run_named_test("hash_error_state", + test_hash_error_aborts_operation) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "hmac") == 0) { if (run_named_test("hmac", test_hmac) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "mac_error_state") == 0) { + if (run_named_test("mac_error_state", + test_mac_error_aborts_operation) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "alg_none_policy") == 0) { + if (run_named_test("alg_none_policy", + test_algorithm_none_rejects_key_usage) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "secondary_algorithm_not_supported") == 0) { + if (run_named_test("secondary_algorithm_not_supported", + test_secondary_algorithm_is_not_supported) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "cipher_cbc") == 0) { if (run_named_test("cipher_cbc", test_cipher_cbc) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "cipher_algorithm_mismatch") == 0) { + if (run_named_test("cipher_algorithm_mismatch", + test_cipher_rejects_algorithm_mismatch) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "mac_algorithm_mismatch") == 0) { + if (run_named_test("mac_algorithm_mismatch", + test_mac_rejects_algorithm_mismatch) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "export_requires_usage") == 0) { + if (run_named_test("export_requires_usage", + test_export_key_requires_usage_flag) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "export_oversized_persistent_length") == 0) { + if (run_named_test("export_oversized_persistent_length", + test_export_key_rejects_oversized_persistent_length) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_success") == 0) { + if (run_named_test("copy_key_success", + test_copy_key_copies_material_and_attributes) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_requires_usage") == 0) { + if (run_named_test("copy_key_requires_usage", + test_copy_key_requires_copy_usage_flag) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_attribute_mismatch") == 0) { + if (run_named_test("copy_key_attribute_mismatch", + test_copy_key_rejects_attribute_mismatch) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_persistent_requires_usage") == 0) { + if (run_named_test("copy_key_persistent_requires_usage", + test_copy_key_requires_copy_usage_flag_persistent) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "cipher_cbc_pkcs7_multipart") == 0) { if (run_named_test("cipher_cbc_pkcs7_multipart", test_cipher_cbc_pkcs7_multipart_decrypt) == TEST_FAIL) { @@ -1345,9 +3913,27 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "cipher_error_state") == 0) { + if (run_named_test("cipher_error_state", + test_cipher_error_aborts_operation) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "cipher_ccm_star_no_tag_multipart") == 0) { + if (run_named_test("cipher_ccm_star_no_tag_multipart", + test_cipher_ccm_star_no_tag_multipart) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "aead_gcm") == 0) { if (run_named_test("aead_gcm", test_aead_gcm) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "aead_gcm_multipart_zero_length") == 0) { + if (run_named_test("aead_gcm_multipart_zero_length", + test_aead_gcm_multipart_zero_length_inputs) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "aead_multipart_length_overflow") == 0) { if (run_named_test("aead_multipart_length_overflow", test_aead_multipart_length_overflow_rejected) == TEST_FAIL) { @@ -1360,15 +3946,57 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "aead_policy_mismatch") == 0) { + if (run_named_test("aead_policy_mismatch", + test_aead_policy_mismatch_rejected) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "chacha20_aes_reject") == 0) { if (run_named_test("chacha20_aes_reject", test_chacha20_poly1305_rejects_aes_key) == TEST_FAIL) { return TEST_FAIL; } } + if (only == NULL || strcmp(only, "chacha20_import_key_size") == 0) { + if (run_named_test("chacha20_import_key_size", + test_chacha20_import_rejects_invalid_key_size) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "chacha20_multipart_split_buffers") == 0) { + if (run_named_test("chacha20_multipart_split_buffers", + test_chacha20_poly1305_multipart_finish_split_buffers) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "asym_algorithm_mismatch_policy") == 0) { + if (run_named_test("asym_algorithm_mismatch_policy", + test_asym_algorithm_mismatch_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "generate_key_rejects_public_key_type") == 0) { + if (run_named_test("generate_key_rejects_public_key_type", + test_generate_key_rejects_public_key_type) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "import_key_volatile_key_id_wrap") == 0) { + if (run_named_test("import_key_volatile_key_id_wrap", + test_import_key_rejects_wrapped_volatile_key_id) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "asym_rsa_oaep_policy") == 0) { + if (run_named_test("asym_rsa_oaep_policy", + test_asym_rsa_oaep_usage_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "ed25519_sig_len") == 0) { if (run_named_test("ed25519_sig_len", test_ed25519_signature_length) == TEST_FAIL) { return TEST_FAIL; @@ -1387,6 +4015,28 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "kdf_null_capacity") == 0) { if (run_named_test("kdf_null_capacity", test_kdf_null_capacity) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "kdf_pbkdf2_zero_cost") == 0) { + if (run_named_test("kdf_pbkdf2_zero_cost", + test_kdf_pbkdf2_zero_cost_rejected) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "kdf_input_key_policy") == 0) { + if (run_named_test("kdf_input_key_policy", test_kdf_input_key_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "kdf_verify_key_policy") == 0) { + if (run_named_test("kdf_verify_key_policy", test_kdf_verify_key_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "kdf_key_agreement_policy") == 0) { + if (run_named_test("kdf_key_agreement_policy", + test_kdf_key_agreement_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_tls12_psk_to_ms") == 0) { if (run_named_test("kdf_tls12_psk_to_ms", test_kdf_tls12_psk_to_ms_rfc4279_order) == TEST_FAIL) { @@ -1403,6 +4053,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_hkdf_extract_prefix_output") == 0) { + if (run_named_test("kdf_hkdf_extract_prefix_output", + test_kdf_hkdf_extract_prefix_output) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_hkdf_expand_optional_info") == 0) { if (run_named_test("kdf_hkdf_expand_optional_info", test_kdf_hkdf_expand_optional_info) == TEST_FAIL) { @@ -1415,6 +4071,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_output_bytes_are_sequential") == 0) { + if (run_named_test("kdf_output_bytes_are_sequential", + test_kdf_output_bytes_are_sequential) == TEST_FAIL) { + return TEST_FAIL; + } + } printf("PSA API test: OK (passed=%d skipped=%d)\n", tests_passed, tests_skipped); diff --git a/test/psa_server/psa_api_test_user_settings.h b/test/psa_server/psa_api_test_user_settings.h index 4cda5a6..5ac64de 100644 --- a/test/psa_server/psa_api_test_user_settings.h +++ b/test/psa_server/psa_api_test_user_settings.h @@ -1,3 +1,24 @@ +/* psa_api_test_user_settings.h + * + * 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 + */ + #ifndef PSA_API_TEST_USER_SETTINGS_H #define PSA_API_TEST_USER_SETTINGS_H diff --git a/test/psa_server/psa_crypto_init_test.c b/test/psa_server/psa_crypto_init_test.c new file mode 100644 index 0000000..c540aa2 --- /dev/null +++ b/test/psa_server/psa_crypto_init_test.c @@ -0,0 +1,142 @@ +/* psa_crypto_init_test.c + * + * 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 "psa_api_test_user_settings.h" + +#ifndef WOLFSSL_USER_SETTINGS +#define WOLFSSL_USER_SETTINGS +#endif + +#include +#include + +#include + +#include + +static int g_wolfcrypt_init_calls; +static int g_wolfcrypt_init_result; + +int __wrap_wolfCrypt_Init(void) +{ + g_wolfcrypt_init_calls++; + return g_wolfcrypt_init_result; +} + +static int expect_status(const char* label, psa_status_t status, psa_status_t expected) +{ + if (status != expected) { + printf("FAIL %s status=%d expected=%d\n", label, (int)status, (int)expected); + return 1; + } + + return 0; +} + +static int test_hash_requires_psa_crypto_init(void) +{ + static const uint8_t msg[] = "abc"; + static const uint8_t sha256_abc[] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + }; + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + uint8_t hash[PSA_HASH_LENGTH(PSA_ALG_SHA_256)]; + size_t hash_length = 0; + psa_status_t st; + + st = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (expect_status("psa_hash_setup before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + st = psa_hash_compute(PSA_ALG_SHA_256, msg, sizeof(msg) - 1, hash, + sizeof(hash), &hash_length); + if (expect_status("psa_hash_compute before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + st = psa_hash_compare(PSA_ALG_SHA_256, msg, sizeof(msg) - 1, sha256_abc, + sizeof(sha256_abc)); + if (expect_status("psa_hash_compare before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + return 0; +} + +static int test_random_requires_psa_crypto_init(void) +{ + uint8_t buf[16]; + psa_status_t st; + + st = psa_generate_random(buf, sizeof(buf)); + if (expect_status("psa_generate_random before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + return 0; +} + +int main(void) +{ + psa_status_t st; + + if (test_hash_requires_psa_crypto_init() != 0) { + return 1; + } + + if (test_random_requires_psa_crypto_init() != 0) { + return 1; + } + + g_wolfcrypt_init_calls = 0; + g_wolfcrypt_init_result = RNG_FAILURE_E; + + st = psa_crypto_init(); + if (expect_status("psa_crypto_init rng failure", st, + PSA_ERROR_INSUFFICIENT_ENTROPY) != 0) { + return 1; + } + if (g_wolfcrypt_init_calls != 1) { + printf("FAIL wolfCrypt_Init calls=%d expected=1\n", g_wolfcrypt_init_calls); + return 1; + } + + g_wolfcrypt_init_result = 0; + st = psa_crypto_init(); + if (expect_status("psa_crypto_init success", st, PSA_SUCCESS) != 0) { + return 1; + } + if (g_wolfcrypt_init_calls != 2) { + printf("FAIL wolfCrypt_Init calls=%d expected=2\n", g_wolfcrypt_init_calls); + return 1; + } + + printf("PSA crypto init test: OK\n"); + return 0; +} diff --git a/test/psa_server/psa_des3_stack_scrub_test.c b/test/psa_server/psa_des3_stack_scrub_test.c index 90d37d3..ce3f38c 100644 --- a/test/psa_server/psa_des3_stack_scrub_test.c +++ b/test/psa_server/psa_des3_stack_scrub_test.c @@ -1,3 +1,24 @@ +/* psa_des3_stack_scrub_test.c + * + * 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 "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_ecc_bit_inference_test.c b/test/psa_server/psa_ecc_bit_inference_test.c index 488f438..51d463f 100644 --- a/test/psa_server/psa_ecc_bit_inference_test.c +++ b/test/psa_server/psa_ecc_bit_inference_test.c @@ -1,3 +1,24 @@ +/* psa_ecc_bit_inference_test.c + * + * 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 "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS @@ -11,36 +32,34 @@ #include -static int test_public_key_bits(void) +static int check_inferred_bits(psa_key_type_t key_type, const uint8_t* key_data, + size_t key_data_len, psa_key_bits_t expected_bits, + const char* label) { - uint8_t pub[133]; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_key_attributes_t got = psa_key_attributes_init(); psa_key_id_t key_id = PSA_KEY_ID_NULL; psa_status_t st; - memset(pub, 0, sizeof(pub)); - pub[0] = 0x04; - - psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_type(&attrs, key_type); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); - st = psa_import_key(&attrs, pub, sizeof(pub), &key_id); + st = psa_import_key(&attrs, key_data, key_data_len, &key_id); if (st != PSA_SUCCESS) { - printf("FAIL public import status=%d\n", (int)st); + printf("FAIL %s import status=%d\n", label, (int)st); return 1; } st = psa_get_key_attributes(key_id, &got); if (st != PSA_SUCCESS) { - printf("FAIL public attrs status=%d\n", (int)st); + printf("FAIL %s attrs status=%d\n", label, (int)st); (void)psa_destroy_key(key_id); return 1; } - if (psa_get_key_bits(&got) != 521) { - printf("FAIL public bits=%u expected=521\n", - (unsigned)psa_get_key_bits(&got)); + if (psa_get_key_bits(&got) != expected_bits) { + printf("FAIL %s bits=%u expected=%u\n", label, + (unsigned)psa_get_key_bits(&got), (unsigned)expected_bits); (void)psa_destroy_key(key_id); return 1; } @@ -49,6 +68,18 @@ static int test_public_key_bits(void) return 0; } +static int test_public_key_bits(void) +{ + uint8_t pub[133]; + + memset(pub, 0, sizeof(pub)); + pub[0] = 0x04; + + return check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1), + pub, sizeof(pub), 521, "secp521r1 public"); +} + static int test_private_key_bits(void) { uint8_t priv[66]; @@ -87,6 +118,33 @@ static int test_private_key_bits(void) return 0; } +static int test_raw_public_key_bits(void) +{ + uint8_t x25519_pub[32]; + uint8_t ed25519_pub[32]; + uint8_t ed448_pub[57]; + + memset(x25519_pub, 0xA5, sizeof(x25519_pub)); + memset(ed25519_pub, 0x5A, sizeof(ed25519_pub)); + memset(ed448_pub, 0x3C, sizeof(ed448_pub)); + + if (check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_MONTGOMERY), + x25519_pub, sizeof(x25519_pub), 255, "x25519 public") != 0) { + return 1; + } + + if (check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS), + ed25519_pub, sizeof(ed25519_pub), 255, "ed25519 public") != 0) { + return 1; + } + + return check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS), + ed448_pub, sizeof(ed448_pub), 448, "ed448 public"); +} + int main(void) { psa_status_t st = psa_crypto_init(); @@ -101,6 +159,9 @@ int main(void) if (test_private_key_bits() != 0) { return 1; } + if (test_raw_public_key_bits() != 0) { + return 1; + } printf("PSA ECC bit inference test: OK\n"); return 0; diff --git a/test/psa_server/psa_ecc_curve_id_test.c b/test/psa_server/psa_ecc_curve_id_test.c index 8e6cf60..b8fe21b 100644 --- a/test/psa_server/psa_ecc_curve_id_test.c +++ b/test/psa_server/psa_ecc_curve_id_test.c @@ -1,3 +1,24 @@ +/* psa_ecc_curve_id_test.c + * + * 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 "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_random_size_test.c b/test/psa_server/psa_random_size_test.c index f4145fb..aedbac6 100644 --- a/test/psa_server/psa_random_size_test.c +++ b/test/psa_server/psa_random_size_test.c @@ -1,3 +1,24 @@ +/* psa_random_size_test.c + * + * 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 "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_rsa_pss_interop_test.c b/test/psa_server/psa_rsa_pss_interop_test.c index 692d859..c3e717b 100644 --- a/test/psa_server/psa_rsa_pss_interop_test.c +++ b/test/psa_server/psa_rsa_pss_interop_test.c @@ -1,3 +1,24 @@ +/* psa_rsa_pss_interop_test.c + * + * 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 diff --git a/test/psa_server/psa_tls_server.c b/test/psa_server/psa_tls_server.c index b377e6a..a9f8d1e 100644 --- a/test/psa_server/psa_tls_server.c +++ b/test/psa_server/psa_tls_server.c @@ -1,5 +1,22 @@ /* psa_tls_server.c - * TLS server that uses wolfSSL PSA PK callbacks for ECC operations. + * + * 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 */ #ifndef NO_FILESYSTEM diff --git a/test/psa_server/tls_client/user_settings.h b/test/psa_server/tls_client/user_settings.h index ffbac83..caee103 100644 --- a/test/psa_server/tls_client/user_settings.h +++ b/test/psa_server/tls_client/user_settings.h @@ -1,3 +1,24 @@ +/* user_settings.h + * + * 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 + */ + #ifndef WOLFSSL_USER_SETTINGS_H #define WOLFSSL_USER_SETTINGS_H diff --git a/test/wolfcrypt-benchmark/main.c b/test/wolfcrypt-benchmark/main.c index cda526b..a03dfc0 100644 --- a/test/wolfcrypt-benchmark/main.c +++ b/test/wolfcrypt-benchmark/main.c @@ -1,4 +1,23 @@ -/* wolfcrypt-benchmark main for wolfPSA + wolfSSL PSA path */ +/* main.c + * + * 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 diff --git a/wolfpsa.map b/wolfpsa.map index 130078d..081d22d 100644 --- a/wolfpsa.map +++ b/wolfpsa.map @@ -89,6 +89,8 @@ WOLFPSA_1.0 { psa_verify_message; wolfpsa_free_key_data; wolfpsa_get_key_data; + wolfpsa_test_get_next_key_id; + wolfpsa_test_set_next_key_id; local: *; };