mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-20 03:53:49 +01:00
Currently PSA mac backends can only implement psa_mac_compute() from the PSA crypto API, but not psa_mac_verify() and the associated multi-part functions. Extend the location and algorithm dispatchers to connect the above PSA API functions to suitable backends. Also extend the MAC backend API to allow backends to implement those additional functions. Due to a design issue with the SE backend API (context size is dynamic, thus requiring a memory allocation) only psa_mac_verify() can be accelerated by SE backends. Signed-off-by: Armin Wolf <W_Armin@gmx.de>
2307 lines
71 KiB
C
2307 lines
71 KiB
C
/*
|
|
* Copyright (C) 2021 HAW Hamburg
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup sys_psa_crypto
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief PSA Crypto API implementation
|
|
*
|
|
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "psa/crypto.h"
|
|
|
|
#if IS_USED(MODULE_PSA_KEY_MANAGEMENT)
|
|
#include "psa_crypto_slot_management.h"
|
|
#endif
|
|
|
|
#include "psa_crypto_se_driver.h"
|
|
#include "psa_crypto_se_management.h"
|
|
#include "psa_crypto_location_dispatch.h"
|
|
#include "psa_crypto_algorithm_dispatch.h"
|
|
|
|
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
|
|
#include "psa_crypto_persistent_storage.h"
|
|
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
|
|
|
|
#include "random.h"
|
|
#include "kernel_defines.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* @brief Set by psa_crypto_init, which is required to be called once before
|
|
* PSA Crypto. Must be checked by other operations first.
|
|
*/
|
|
static uint8_t lib_initialized = 0;
|
|
|
|
#if IS_USED(MODULE_PSA_HASH)
|
|
/**
|
|
* @brief Compares the content of two same-sized buffers while maintaining
|
|
* constant processing time
|
|
*
|
|
* @param a Buffer A to compare with B
|
|
* @param b Buffer B to compare with A
|
|
* @param n Size of the input buffers
|
|
*
|
|
* @return int
|
|
* 0 if buffer contents are the same
|
|
* 1 if buffer contents differ
|
|
*/
|
|
static inline int constant_time_memcmp(const uint8_t *a, const uint8_t *b, size_t n)
|
|
{
|
|
uint8_t diff = 0;
|
|
|
|
for (size_t i = 0; i < n; i++) {
|
|
diff |= a[i] ^ b[i];
|
|
}
|
|
|
|
return diff;
|
|
}
|
|
#endif /* MODULE_PSA_HASH */
|
|
|
|
#if IS_USED(MODULE_PSA_KEY_MANAGEMENT)
|
|
static psa_status_t psa_get_and_lock_key_slot_with_policy(psa_key_id_t id,
|
|
psa_key_slot_t **p_slot,
|
|
psa_key_usage_t usage,
|
|
psa_algorithm_t alg);
|
|
#endif /* MODULE_PSA_KEY_MANAGEMENT */
|
|
|
|
const char *psa_status_to_humanly_readable(psa_status_t status)
|
|
{
|
|
switch (status) {
|
|
case PSA_ERROR_GENERIC_ERROR:
|
|
return "PSA_ERROR_GENERIC_ERROR";
|
|
case PSA_ERROR_NOT_SUPPORTED:
|
|
return "PSA_ERROR_NOT_SUPPORTED";
|
|
case PSA_ERROR_NOT_PERMITTED:
|
|
return "PSA_ERROR_NOT_PERMITTED";
|
|
case PSA_ERROR_BUFFER_TOO_SMALL:
|
|
return "PSA_ERROR_BUFFER_TOO_SMALL";
|
|
case PSA_ERROR_ALREADY_EXISTS:
|
|
return "PSA_ERROR_ALREADY_EXISTS";
|
|
case PSA_ERROR_DOES_NOT_EXIST:
|
|
return "PSA_ERROR_DOES_NOT_EXIST";
|
|
case PSA_ERROR_BAD_STATE:
|
|
return "PSA_ERROR_BAD_STATE";
|
|
case PSA_ERROR_INVALID_ARGUMENT:
|
|
return "PSA_ERROR_INVALID_ARGUMENT";
|
|
case PSA_ERROR_INSUFFICIENT_MEMORY:
|
|
return "PSA_ERROR_INSUFFICIENT_MEMORY";
|
|
case PSA_ERROR_INSUFFICIENT_STORAGE:
|
|
return "PSA_ERROR_INSUFFICIENT_STORAGE";
|
|
case PSA_ERROR_COMMUNICATION_FAILURE:
|
|
return "PSA_ERROR_COMMUNICATION_FAILURE";
|
|
case PSA_ERROR_STORAGE_FAILURE:
|
|
return "PSA_ERROR_STORAGE_FAILURE";
|
|
case PSA_ERROR_DATA_CORRUPT:
|
|
return "PSA_ERROR_DATA_CORRUPT";
|
|
case PSA_ERROR_DATA_INVALID:
|
|
return "PSA_ERROR_DATA_INVALID";
|
|
case PSA_ERROR_HARDWARE_FAILURE:
|
|
return "PSA_ERROR_HARDWARE_FAILURE";
|
|
case PSA_ERROR_CORRUPTION_DETECTED:
|
|
return "PSA_ERROR_CORRUPTION_DETECTED";
|
|
case PSA_ERROR_INSUFFICIENT_ENTROPY:
|
|
return "PSA_ERROR_INSUFFICIENT_ENTROPY";
|
|
case PSA_ERROR_INVALID_SIGNATURE:
|
|
return "PSA_ERROR_INVALID_SIGNATURE";
|
|
case PSA_ERROR_INVALID_PADDING:
|
|
return "PSA_ERROR_INVALID_PADDING";
|
|
case PSA_ERROR_INSUFFICIENT_DATA:
|
|
return "PSA_ERROR_INSUFFICIENT_DATA";
|
|
case PSA_ERROR_INVALID_HANDLE:
|
|
return "PSA_ERROR_INVALID_HANDLE";
|
|
case PSA_SUCCESS:
|
|
return "PSA_SUCCESS";
|
|
default:
|
|
return "Error value not recognized";
|
|
}
|
|
}
|
|
|
|
psa_status_t psa_crypto_init(void)
|
|
{
|
|
if (lib_initialized) {
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
lib_initialized = 1;
|
|
|
|
#if (IS_USED(MODULE_PSA_KEY_SLOT_MGMT))
|
|
psa_init_key_slots();
|
|
#endif
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
#if IS_USED(MODULE_PSA_AEAD)
|
|
psa_status_t psa_aead_abort(psa_aead_operation_t *operation)
|
|
{
|
|
(void)operation;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
/**
|
|
* @brief aead encrypt and decrypt function
|
|
*
|
|
* See @ref psa_aead_encrypt(...)
|
|
* See @ref psa_aead_decrypt(...)
|
|
*
|
|
* @param direction Whether to encrypt or decrypt, see @ref psa_encrypt_or_decrypt_t
|
|
* @return @ref psa_status_t
|
|
*/
|
|
static psa_status_t psa_aead_encrypt_decrypt( psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t * nonce,
|
|
size_t nonce_length,
|
|
const uint8_t * additional_data,
|
|
size_t additional_data_length,
|
|
const uint8_t * input,
|
|
size_t input_length,
|
|
uint8_t * output,
|
|
size_t output_size,
|
|
size_t * output_length,
|
|
psa_encrypt_or_decrypt_t direction)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if ((additional_data_length != 0 && !additional_data) || !nonce) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg) != PSA_ALG_CCM) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_AEAD(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (nonce_length > PSA_AEAD_NONCE_MAX_SIZE) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (direction == PSA_CRYPTO_DRIVER_DECRYPT && (!input || input_length == 0)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (direction == PSA_CRYPTO_DRIVER_ENCRYPT && (!output || output_size == 0)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
psa_key_usage_t usage = (direction == PSA_CRYPTO_DRIVER_ENCRYPT ?
|
|
PSA_KEY_USAGE_ENCRYPT :
|
|
PSA_KEY_USAGE_DECRYPT);
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, usage, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
if (direction == PSA_CRYPTO_DRIVER_ENCRYPT) {
|
|
if (output_size < PSA_AEAD_ENCRYPT_OUTPUT_SIZE(slot->attr.type, alg, input_length)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
status = psa_location_dispatch_aead_encrypt(&slot->attr, alg, slot, nonce, nonce_length,
|
|
additional_data, additional_data_length,
|
|
input, input_length, output,
|
|
output_size, output_length);
|
|
}
|
|
else {
|
|
if (output_size < PSA_AEAD_DECRYPT_OUTPUT_SIZE(slot->attr.type, alg, input_length)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
status = psa_location_dispatch_aead_decrypt(&slot->attr, alg, slot, nonce, nonce_length,
|
|
additional_data, additional_data_length,
|
|
input, input_length, output,
|
|
output_size, output_length);
|
|
}
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
psa_status_t psa_aead_decrypt( psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *nonce,
|
|
size_t nonce_length,
|
|
const uint8_t *additional_data,
|
|
size_t additional_data_length,
|
|
const uint8_t *ciphertext,
|
|
size_t ciphertext_length,
|
|
uint8_t *plaintext,
|
|
size_t plaintext_size,
|
|
size_t *plaintext_length)
|
|
{
|
|
return psa_aead_encrypt_decrypt(key, alg, nonce, nonce_length, additional_data,
|
|
additional_data_length, ciphertext, ciphertext_length,
|
|
plaintext, plaintext_size, plaintext_length,
|
|
PSA_CRYPTO_DRIVER_DECRYPT);
|
|
}
|
|
|
|
psa_status_t psa_aead_decrypt_setup(psa_aead_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg)
|
|
{
|
|
(void)operation;
|
|
(void)key;
|
|
(void)alg;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_encrypt( psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *nonce,
|
|
size_t nonce_length,
|
|
const uint8_t *additional_data,
|
|
size_t additional_data_length,
|
|
const uint8_t *plaintext,
|
|
size_t plaintext_length,
|
|
uint8_t *ciphertext,
|
|
size_t ciphertext_size,
|
|
size_t *ciphertext_length)
|
|
{
|
|
return psa_aead_encrypt_decrypt(key, alg, nonce, nonce_length, additional_data,
|
|
additional_data_length, plaintext, plaintext_length,
|
|
ciphertext, ciphertext_size, ciphertext_length,
|
|
PSA_CRYPTO_DRIVER_ENCRYPT);
|
|
}
|
|
|
|
psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg)
|
|
{
|
|
(void)operation;
|
|
(void)key;
|
|
(void)alg;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_finish( psa_aead_operation_t *operation,
|
|
uint8_t *ciphertext,
|
|
size_t ciphertext_size,
|
|
size_t *ciphertext_length,
|
|
uint8_t *tag,
|
|
size_t tag_size,
|
|
size_t *tag_length)
|
|
{
|
|
(void)operation;
|
|
(void)ciphertext;
|
|
(void)ciphertext_size;
|
|
(void)ciphertext_length;
|
|
(void)tag;
|
|
(void)tag_size;
|
|
(void)tag_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_generate_nonce( psa_aead_operation_t *operation,
|
|
uint8_t *nonce,
|
|
size_t nonce_size,
|
|
size_t *nonce_length)
|
|
{
|
|
(void)operation;
|
|
(void)nonce;
|
|
(void)nonce_size;
|
|
(void)nonce_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_set_lengths( psa_aead_operation_t *operation,
|
|
size_t ad_length,
|
|
size_t plaintext_length)
|
|
{
|
|
(void)operation;
|
|
(void)ad_length;
|
|
(void)plaintext_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation,
|
|
const uint8_t *nonce,
|
|
size_t nonce_length)
|
|
{
|
|
(void)operation;
|
|
(void)nonce;
|
|
(void)nonce_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_update( psa_aead_operation_t *operation,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
(void)operation;
|
|
(void)input;
|
|
(void)input_length;
|
|
(void)output;
|
|
(void)output_size;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation,
|
|
const uint8_t *input,
|
|
size_t input_length)
|
|
{
|
|
(void)operation;
|
|
(void)input;
|
|
(void)input_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_aead_verify( psa_aead_operation_t *operation,
|
|
uint8_t *plaintext,
|
|
size_t plaintext_size,
|
|
size_t *plaintext_length,
|
|
const uint8_t *tag,
|
|
size_t tag_length)
|
|
{
|
|
(void)operation;
|
|
(void)plaintext;
|
|
(void)plaintext_size;
|
|
(void)plaintext_length;
|
|
(void)tag;
|
|
(void)tag_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
#endif /* MODULE_PSA_AEAD */
|
|
|
|
#if IS_USED(MODULE_PSA_ASYMMETRIC)
|
|
psa_status_t psa_asymmetric_decrypt(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
const uint8_t *salt,
|
|
size_t salt_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
(void)key;
|
|
(void)alg;
|
|
(void)input;
|
|
(void)input_length;
|
|
(void)salt;
|
|
(void)salt_length;
|
|
(void)output;
|
|
(void)output_size;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_asymmetric_encrypt(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
const uint8_t *salt,
|
|
size_t salt_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
(void)key;
|
|
(void)alg;
|
|
(void)input;
|
|
(void)input_length;
|
|
(void)salt;
|
|
(void)salt_length;
|
|
(void)output;
|
|
(void)output_size;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
#endif /* MODULE_PSA_ASYMMETRIC */
|
|
|
|
#if IS_USED(MODULE_PSA_KEY_MANAGEMENT)
|
|
/**
|
|
* @brief Checks whether a key's policy permits the usage of a given algorithm
|
|
*
|
|
* @param policy Policy of the given key
|
|
* @param type Type of the given key
|
|
* @param requested_alg Algorithm to be used
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_NOT_PERMITTED
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT If @c requested_alg is not a valid algorithm
|
|
*/
|
|
static psa_status_t psa_key_policy_permits( const psa_key_policy_t *policy,
|
|
psa_key_type_t type,
|
|
psa_algorithm_t requested_alg)
|
|
{
|
|
if (requested_alg == 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (policy->alg == requested_alg) {
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
(void)type;
|
|
return PSA_ERROR_NOT_PERMITTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether the policy of the key associated with the given ID permits the requested
|
|
* usage and return the key slot.
|
|
*
|
|
* @param id ID of the key to be used
|
|
* @param p_slot Pointer to a @c psa_key_slot_t type to return the desired key slot.
|
|
* @c NULL if something went wrong.
|
|
* @param usage The requested usage of the key
|
|
* @param alg The requested algorithm that uses the key
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_NOT_PERMITTED
|
|
* @ref PSA_ERROR_DOES_NOT_EXIST
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
* @ref PSA_ERROR_NOT_SUPPORTED
|
|
* @ref PSA_ERROR_CORRUPTION_DETECTED
|
|
*/
|
|
static psa_status_t psa_get_and_lock_key_slot_with_policy(psa_key_id_t id,
|
|
psa_key_slot_t **p_slot,
|
|
psa_key_usage_t usage,
|
|
psa_algorithm_t alg)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
status = psa_get_and_lock_key_slot(id, p_slot);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
slot = *p_slot;
|
|
|
|
if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) {
|
|
/* Export is always permitted for asymmetric public keys */
|
|
usage &= ~PSA_KEY_USAGE_EXPORT;
|
|
}
|
|
|
|
if ((slot->attr.policy.usage & usage) != usage) {
|
|
*p_slot = NULL;
|
|
psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_NOT_PERMITTED;
|
|
}
|
|
|
|
if (alg != 0) {
|
|
status = psa_key_policy_permits( &slot->attr.policy, slot->attr.type, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
*p_slot = NULL;
|
|
psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
}
|
|
return PSA_SUCCESS;
|
|
}
|
|
#endif /* MODULE_PSA_KEY_MANAGEMENT */
|
|
|
|
#if IS_USED(MODULE_PSA_CIPHER)
|
|
psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
*operation = psa_cipher_operation_init();
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Setup a cipher encrypt or decrypt operation.
|
|
*
|
|
* See @ref psa_cipher_encrypt_setup(...)
|
|
* See @ref psa_cipher_decrypt_setup(...)
|
|
*/
|
|
static psa_status_t psa_cipher_setup( psa_cipher_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
psa_encrypt_or_decrypt_t direction)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
psa_key_usage_t usage = (direction == PSA_CRYPTO_DRIVER_ENCRYPT ?
|
|
PSA_KEY_USAGE_ENCRYPT :
|
|
PSA_KEY_USAGE_DECRYPT);
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_CIPHER(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, usage, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
psa_cipher_abort(operation);
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
|
|
operation->iv_set = 0;
|
|
if (alg == PSA_ALG_ECB_NO_PADDING) {
|
|
operation->iv_required = 0;
|
|
}
|
|
else {
|
|
operation->iv_required = 1;
|
|
}
|
|
operation->default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
|
|
|
|
psa_key_attributes_t attr = slot->attr;
|
|
|
|
if (direction == PSA_CRYPTO_DRIVER_ENCRYPT) {
|
|
status = psa_location_dispatch_cipher_encrypt_setup(operation, &attr, slot, alg);
|
|
}
|
|
else if (direction == PSA_CRYPTO_DRIVER_DECRYPT) {
|
|
status = psa_location_dispatch_cipher_decrypt_setup(operation, &attr, slot, alg);
|
|
}
|
|
|
|
if (status != PSA_SUCCESS) {
|
|
psa_cipher_abort(operation);
|
|
}
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
/**
|
|
* @brief Cipher encrypt and decrypt function
|
|
*
|
|
* See @ref psa_cipher_encrypt(...)
|
|
* See @ref psa_cipher_decrypt(...)
|
|
*/
|
|
static psa_status_t psa_cipher_encrypt_decrypt( psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length,
|
|
psa_encrypt_or_decrypt_t direction)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !output || !output_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_CIPHER(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
psa_key_usage_t usage = (direction == PSA_CRYPTO_DRIVER_ENCRYPT ?
|
|
PSA_KEY_USAGE_ENCRYPT :
|
|
PSA_KEY_USAGE_DECRYPT);
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, usage, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
if (((alg == PSA_ALG_CBC_NO_PADDING) || (alg == PSA_ALG_ECB_NO_PADDING)) &&
|
|
(input_length % PSA_BLOCK_CIPHER_BLOCK_LENGTH(slot->attr.type))) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (direction == PSA_CRYPTO_DRIVER_ENCRYPT) {
|
|
if (output_size < PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(slot->attr.type, alg, input_length)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
status = psa_location_dispatch_cipher_encrypt(&slot->attr, alg, slot, input, input_length,
|
|
output, output_size, output_length);
|
|
}
|
|
else {
|
|
size_t iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
|
|
if (output_size < (input_length - iv_length)) {
|
|
/* Input buffer contains iv + cipher, so output must be at least input_length - IV */
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
status = psa_location_dispatch_cipher_decrypt(&slot->attr, alg, slot, input, input_length,
|
|
output, output_size, output_length);
|
|
}
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
psa_status_t psa_cipher_decrypt(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
return psa_cipher_encrypt_decrypt(key, alg, input, input_length, output, output_size,
|
|
output_length, PSA_CRYPTO_DRIVER_DECRYPT);
|
|
}
|
|
|
|
psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg)
|
|
{
|
|
return psa_cipher_setup(operation, key, alg, PSA_CRYPTO_DRIVER_DECRYPT);
|
|
}
|
|
|
|
psa_status_t psa_cipher_encrypt(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
return psa_cipher_encrypt_decrypt(key, alg, input, input_length, output, output_size,
|
|
output_length, PSA_CRYPTO_DRIVER_ENCRYPT);
|
|
}
|
|
|
|
psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg)
|
|
{
|
|
return psa_cipher_setup(operation, key, alg, PSA_CRYPTO_DRIVER_ENCRYPT);
|
|
}
|
|
|
|
psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
(void)operation;
|
|
(void)output;
|
|
(void)output_size;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation,
|
|
uint8_t *iv,
|
|
size_t iv_size,
|
|
size_t *iv_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !iv || !iv_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
*iv_length = 0;
|
|
|
|
if (!operation->iv_required || operation->iv_set) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (iv_size < operation->default_iv_length) {
|
|
psa_cipher_abort(operation);
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
status = psa_generate_random(iv, iv_size);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
operation->iv_set = 1;
|
|
*iv_length = iv_size;
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation,
|
|
const uint8_t *iv,
|
|
size_t iv_length)
|
|
{
|
|
(void)operation;
|
|
(void)iv;
|
|
(void)iv_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_cipher_update(psa_cipher_operation_t *operation,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
(void)operation;
|
|
(void)input;
|
|
(void)input_length;
|
|
(void)output;
|
|
(void)output_size;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
#endif /* MODULE_PSA_CIPHER */
|
|
|
|
#if IS_USED(MODULE_PSA_HASH)
|
|
psa_status_t psa_hash_setup(psa_hash_operation_t *operation,
|
|
psa_algorithm_t alg)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (operation->alg != 0) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_HASH(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
psa_status_t status = psa_algorithm_dispatch_hash_setup(operation, alg);
|
|
if (status == PSA_SUCCESS) {
|
|
operation->alg = alg;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_hash_update(psa_hash_operation_t *operation,
|
|
const uint8_t *input,
|
|
size_t input_length)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !input) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (operation->alg == 0) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
psa_status_t status = psa_algorithm_dispatch_hash_update(operation, input, input_length);
|
|
|
|
if (status != PSA_SUCCESS) {
|
|
psa_hash_abort(operation);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_hash_finish(psa_hash_operation_t *operation,
|
|
uint8_t *hash,
|
|
size_t hash_size,
|
|
size_t *hash_length)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !hash || !hash_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (operation->alg == 0) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
uint8_t actual_hash_length = PSA_HASH_LENGTH(operation->alg);
|
|
|
|
if (hash_size < actual_hash_length) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
psa_status_t status =
|
|
psa_algorithm_dispatch_hash_finish(operation, hash, hash_size, hash_length);
|
|
if (status == PSA_SUCCESS) {
|
|
*hash_length = actual_hash_length;
|
|
}
|
|
|
|
/* Make sure operation becomes inactive after successful execution */
|
|
psa_hash_abort(operation);
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_hash_verify(psa_hash_operation_t *operation,
|
|
const uint8_t *hash,
|
|
size_t hash_length)
|
|
{
|
|
int status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
uint8_t digest[PSA_HASH_MAX_SIZE];
|
|
size_t actual_hash_length = 0;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !hash) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_hash_finish(operation, digest, PSA_HASH_MAX_SIZE, &actual_hash_length);
|
|
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
if (actual_hash_length != hash_length) {
|
|
return PSA_ERROR_INVALID_SIGNATURE;
|
|
}
|
|
|
|
if (constant_time_memcmp(hash, digest, hash_length) != 0) {
|
|
return PSA_ERROR_INVALID_SIGNATURE;
|
|
}
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_hash_suspend(psa_hash_operation_t *operation,
|
|
uint8_t *hash_state,
|
|
size_t hash_state_size,
|
|
size_t *hash_state_length)
|
|
{
|
|
(void)operation;
|
|
(void)hash_state;
|
|
(void)hash_state_size;
|
|
(void)hash_state_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_hash_resume(psa_hash_operation_t *operation,
|
|
const uint8_t *hash_state,
|
|
size_t hash_state_length)
|
|
{
|
|
(void)operation;
|
|
(void)hash_state;
|
|
(void)hash_state_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_hash_abort(psa_hash_operation_t *operation)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
*operation = psa_hash_operation_init();
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation,
|
|
psa_hash_operation_t *target_operation)
|
|
{
|
|
(void)source_operation;
|
|
(void)target_operation;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_hash_compare(psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
const uint8_t *hash,
|
|
size_t hash_length)
|
|
{
|
|
psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !hash) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_hash_setup(&operation, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
status = psa_hash_update(&operation, input, input_length);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
status = psa_hash_verify(&operation, hash, hash_length);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_hash_compute(psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *hash,
|
|
size_t hash_size,
|
|
size_t *hash_length)
|
|
{
|
|
psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !hash || !hash_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (hash_size < PSA_HASH_LENGTH(alg)) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
*hash_length = hash_size;
|
|
status = psa_hash_setup(&operation, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
status = psa_hash_update(&operation, input, input_length);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
status = psa_hash_finish(&operation, hash, hash_size, hash_length);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
#endif /* MODULE_PSA_HASH */
|
|
|
|
psa_status_t psa_builtin_generate_random(uint8_t *output,
|
|
size_t output_size)
|
|
{
|
|
if (!output) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
/* TODO: Should point to a CSPRNG API in the future */
|
|
random_bytes(output, output_size);
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_generate_random(uint8_t *output,
|
|
size_t output_size)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!output) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
return psa_location_dispatch_generate_random(output, output_size);
|
|
}
|
|
|
|
/* Key Management */
|
|
#if IS_USED(MODULE_PSA_KEY_MANAGEMENT)
|
|
/**
|
|
* @brief Check whether the key policy is valid
|
|
*
|
|
* @param policy Policy of type @ref psa_key_policy_t of the key to be used
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
*/
|
|
static psa_status_t psa_validate_key_policy(const psa_key_policy_t *policy)
|
|
{
|
|
if ((policy->usage & ~(PSA_KEY_USAGE_EXPORT |
|
|
PSA_KEY_USAGE_COPY |
|
|
PSA_KEY_USAGE_ENCRYPT |
|
|
PSA_KEY_USAGE_DECRYPT |
|
|
PSA_KEY_USAGE_SIGN_MESSAGE |
|
|
PSA_KEY_USAGE_VERIFY_MESSAGE |
|
|
PSA_KEY_USAGE_SIGN_HASH |
|
|
PSA_KEY_USAGE_VERIFY_HASH |
|
|
PSA_KEY_USAGE_DERIVE)) != 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether the size of a symmetric key is supported
|
|
*
|
|
* @param type Type of the used key as @ref psa_key_type_t
|
|
* @param bits Size of the key as @c size_t
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
* @ref PSA_ERROR_NOT_SUPPORTED
|
|
*/
|
|
static psa_status_t psa_validate_unstructured_key_size(psa_key_type_t type, size_t bits)
|
|
{
|
|
switch (type) {
|
|
case PSA_KEY_TYPE_AES:
|
|
if (bits != 128 && bits != 192 && bits != 256) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
break;
|
|
case PSA_KEY_TYPE_HMAC:
|
|
if (bits % 8 != 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
break;
|
|
case PSA_KEY_TYPE_CHACHA20:
|
|
if (bits != 256) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
break;
|
|
default:
|
|
(void)bits;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether the key size is valid for key generation
|
|
*
|
|
* @param type Type of the used key as @ref psa_key_type_t
|
|
* @param bits Size of the key as @c size_t
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
* @ref PSA_ERROR_NOT_SUPPORTED
|
|
*/
|
|
static psa_status_t psa_validate_key_for_key_generation(psa_key_type_t type, size_t bits)
|
|
{
|
|
if (PSA_KEY_TYPE_IS_UNSTRUCTURED(type)) {
|
|
return psa_validate_unstructured_key_size(type, bits);
|
|
}
|
|
#if IS_USED(MODULE_PSA_ASYMMETRIC)
|
|
else if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) {
|
|
return PSA_ECC_KEY_SIZE_IS_VALID(type, bits) ? PSA_SUCCESS : PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
#endif
|
|
/* TODO: add validation for other key types */
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Check validity of key attributes and get secure element driver in case the key is
|
|
* stored on a secure element
|
|
*
|
|
* @param attributes Key attributes that are to be checked
|
|
* @param p_drv Pointer which will contain the SE driver, if one exists
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
* @ref PSA_ERROR_NOT_SUPPORTED
|
|
*/
|
|
static psa_status_t psa_validate_key_attributes(const psa_key_attributes_t *attributes,
|
|
psa_se_drv_data_t **p_drv)
|
|
{
|
|
psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
|
|
psa_key_lifetime_t lifetime = psa_get_key_lifetime(attributes);
|
|
psa_key_id_t key = psa_get_key_id(attributes);
|
|
|
|
status = psa_validate_key_location(lifetime, p_drv);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_validate_key_persistence(lifetime);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
|
|
if (key != 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
}
|
|
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
|
|
else {
|
|
if (!psa_is_valid_key_id(key, 1)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
}
|
|
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
|
|
|
|
status = psa_validate_key_policy(&attributes->policy);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Set up a key creation process.
|
|
*
|
|
* Validate key attributes for key creation and find free slots and drivers if they exists.
|
|
*
|
|
* @param method Key creation method (see @ref psa_key_creation_method_t)
|
|
* @param attributes Key attributes of the key that should be created
|
|
* @param p_slot Pointer which will contain a key slot to store the key in
|
|
* @param p_drv Pointer to a SE driver if one exists for the given key location
|
|
*
|
|
* @return @ref psa_status_t
|
|
*/
|
|
static psa_status_t psa_start_key_creation(psa_key_creation_method_t method,
|
|
const psa_key_attributes_t *attributes,
|
|
psa_key_slot_t **p_slot, psa_se_drv_data_t **p_drv)
|
|
{
|
|
psa_status_t status;
|
|
psa_key_id_t key_id;
|
|
psa_key_slot_t *slot;
|
|
|
|
*p_drv = NULL;
|
|
|
|
status = psa_validate_key_attributes(attributes, p_drv);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_allocate_empty_key_slot(&key_id, attributes, p_slot);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
slot = *p_slot;
|
|
slot->attr = *attributes;
|
|
|
|
/* See 9.5.2. Key usage flags */
|
|
if (slot->attr.policy.usage & PSA_KEY_USAGE_SIGN_HASH) {
|
|
slot->attr.policy.usage |= PSA_KEY_USAGE_SIGN_MESSAGE;
|
|
}
|
|
|
|
if (slot->attr.policy.usage & PSA_KEY_USAGE_VERIFY_HASH) {
|
|
slot->attr.policy.usage |= PSA_KEY_USAGE_VERIFY_MESSAGE;
|
|
}
|
|
|
|
if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
|
|
slot->attr.id = key_id;
|
|
}
|
|
|
|
#if IS_USED(MODULE_PSA_SECURE_ELEMENT)
|
|
/* Find a free slot on a secure element and store SE slot number in key_data */
|
|
if (*p_drv != NULL) {
|
|
psa_key_slot_number_t *slot_number = psa_key_slot_get_slot_number(slot);
|
|
status = psa_find_free_se_slot(attributes, method, *p_drv, slot_number);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
/* TODO: Start transaction for persistent key storage */
|
|
}
|
|
#endif /* CONFIG_PSA_SECURE_ELEMENT */
|
|
|
|
(void)method;
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Finish up key creation process
|
|
*
|
|
* @param slot Pointer to slot that the key is stored in
|
|
* @param driver SE driver, in case the key creation took place on a secure element
|
|
* @param key_id Pointer which will contain the key ID assigned to the key
|
|
*
|
|
* @return @ref psa_status_t
|
|
*/
|
|
static psa_status_t psa_finish_key_creation(psa_key_slot_t *slot, psa_se_drv_data_t *driver,
|
|
psa_key_id_t *key_id)
|
|
{
|
|
psa_status_t status = PSA_SUCCESS;
|
|
|
|
if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
|
|
*key_id = slot->attr.id;
|
|
}
|
|
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
|
|
else {
|
|
status = psa_persist_key_slot_in_storage(slot);
|
|
}
|
|
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
|
|
|
|
(void)driver;
|
|
psa_status_t unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
/**
|
|
* @brief Abort key creation and clean up in case of failure
|
|
*
|
|
* @param slot Slot that the key has been written to
|
|
* @param driver SE driver, in case the key creation took place on a secure element
|
|
*/
|
|
static void psa_fail_key_creation(psa_key_slot_t *slot, psa_se_drv_data_t *driver)
|
|
{
|
|
(void)driver;
|
|
if (slot == NULL) {
|
|
return;
|
|
}
|
|
/* TODO: Destroy key in secure element (see mbedtls code) */
|
|
/* TODO: Secure Element stop transaction */
|
|
psa_wipe_key_slot(slot);
|
|
}
|
|
|
|
psa_status_t psa_copy_key(psa_key_id_t source_key,
|
|
const psa_key_attributes_t *attributes,
|
|
psa_key_id_t *target_key)
|
|
{
|
|
(void)source_key;
|
|
(void)attributes;
|
|
(void)target_key;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_destroy_key(psa_key_id_t key)
|
|
{
|
|
psa_status_t status;
|
|
psa_key_slot_t *slot;
|
|
|
|
status = psa_get_and_lock_key_slot(key, &slot);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
if (slot->lock_count > 1) {
|
|
return PSA_ERROR_CORRUPTION_DETECTED;
|
|
}
|
|
|
|
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
|
|
if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
|
|
status = psa_destroy_persistent_key(key);
|
|
if (status != PSA_SUCCESS) {
|
|
DEBUG("PSA destroy key: Persistent key destruction failed: %s\n",
|
|
psa_status_to_humanly_readable(status));
|
|
return PSA_ERROR_STORAGE_FAILURE;
|
|
}
|
|
}
|
|
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
|
|
|
|
return psa_wipe_key_slot(slot);
|
|
}
|
|
|
|
/**
|
|
* @brief Export key that is stored in local memory
|
|
*
|
|
* See @ref psa_export_key
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
*/
|
|
static psa_status_t psa_builtin_export_key(const uint8_t *key_buffer,
|
|
size_t key_buffer_size,
|
|
uint8_t *data,
|
|
size_t data_size,
|
|
size_t *data_length)
|
|
{
|
|
if (!key_buffer || !data || !data_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (key_buffer_size == 0 || data_size == 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (data_size < key_buffer_size) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
memcpy(data, key_buffer, key_buffer_size);
|
|
*data_length = key_buffer_size;
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_export_key(psa_key_id_t key,
|
|
uint8_t *data,
|
|
size_t data_size,
|
|
size_t *data_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
uint8_t *privkey_data = NULL;
|
|
size_t *privkey_data_len = NULL;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!data || !data_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
*data_length = 0;
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_EXPORT, 0);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
psa_key_lifetime_t lifetime = psa_get_key_lifetime(&slot->attr);
|
|
if (psa_key_lifetime_is_external(lifetime)) {
|
|
/* key export from an external device is currently not supported */
|
|
status = PSA_ERROR_NOT_SUPPORTED;
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
if (!PSA_KEY_TYPE_IS_ECC(slot->attr.type) ||
|
|
(PSA_KEY_TYPE_ECC_GET_FAMILY(slot->attr.type) != PSA_ECC_FAMILY_TWISTED_EDWARDS &&
|
|
PSA_KEY_TYPE_ECC_GET_FAMILY(slot->attr.type) != PSA_ECC_FAMILY_SECP_R1)) {
|
|
/* key export is currently only supported for ed25519 and secp_r1 keys */
|
|
status = PSA_ERROR_NOT_SUPPORTED;
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
psa_get_key_data_from_key_slot(slot, &privkey_data, &privkey_data_len);
|
|
|
|
status =
|
|
psa_builtin_export_key(privkey_data, *privkey_data_len, data, data_size, data_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
/**
|
|
* @brief Export asymmetric public key that is stored in local memory
|
|
*
|
|
* See @ref psa_export_public_key
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
*/
|
|
static psa_status_t psa_builtin_export_public_key( const uint8_t *key_buffer,
|
|
size_t key_buffer_size,
|
|
uint8_t *data,
|
|
size_t data_size,
|
|
size_t *data_length)
|
|
{
|
|
if (key_buffer_size == 0 || data_size == 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (data_size < key_buffer_size) {
|
|
DEBUG("PSA Crypto Builtin Export Key: Output buffer too small\n");
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
memcpy(data, key_buffer, key_buffer_size);
|
|
*data_length = key_buffer_size;
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_export_public_key(psa_key_id_t key,
|
|
uint8_t *data,
|
|
size_t data_size,
|
|
size_t *data_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
uint8_t *pubkey_data = NULL;
|
|
size_t *pubkey_data_len = NULL;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!data || !data_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
*data_length = 0;
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
if ((data_size == 0) ||
|
|
(data_size < PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(slot->attr.type, slot->attr.bits))) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (!PSA_KEY_TYPE_IS_ECC(slot->attr.type)) {
|
|
status = PSA_ERROR_INVALID_ARGUMENT;
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
psa_get_public_key_data_from_key_slot(slot, &pubkey_data, &pubkey_data_len);
|
|
|
|
status =
|
|
psa_builtin_export_public_key(pubkey_data, *pubkey_data_len, data, data_size, data_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
psa_status_t psa_builtin_generate_key(const psa_key_attributes_t *attributes, uint8_t *key_buffer,
|
|
size_t key_buffer_size, size_t *key_buffer_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
|
|
if (!attributes || !key_buffer || !key_buffer_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
psa_key_type_t type = attributes->type;
|
|
|
|
if (PSA_KEY_TYPE_IS_UNSTRUCTURED(type)) {
|
|
status = psa_generate_random(key_buffer, key_buffer_size);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
*key_buffer_length = key_buffer_size;
|
|
return PSA_SUCCESS;
|
|
}
|
|
(void)key_buffer;
|
|
(void)key_buffer_size;
|
|
(void)key_buffer_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
|
|
psa_key_id_t *key)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot = NULL;
|
|
psa_se_drv_data_t *driver = NULL;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!attributes || !key) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (psa_get_key_bits(attributes) == 0) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
|
|
*key = PSA_KEY_ID_NULL;
|
|
}
|
|
|
|
/* Find empty slot */
|
|
status = psa_start_key_creation(PSA_KEY_CREATION_GENERATE, attributes, &slot, &driver);
|
|
if (status != PSA_SUCCESS) {
|
|
psa_fail_key_creation(slot, driver);
|
|
return status;
|
|
}
|
|
|
|
if (PSA_KEY_LIFETIME_GET_LOCATION(attributes->lifetime) == PSA_KEY_LOCATION_LOCAL_STORAGE) {
|
|
status = psa_validate_key_for_key_generation(attributes->type, attributes->bits);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
status = psa_location_dispatch_generate_key(attributes, slot);
|
|
|
|
if (status != PSA_SUCCESS) {
|
|
psa_fail_key_creation(slot, driver);
|
|
return status;
|
|
}
|
|
|
|
status = psa_finish_key_creation(slot, driver, key);
|
|
if (status != PSA_SUCCESS) {
|
|
psa_fail_key_creation(slot, driver);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_get_key_attributes(psa_key_id_t key,
|
|
psa_key_attributes_t *attributes)
|
|
{
|
|
psa_status_t status;
|
|
psa_key_slot_t *slot = NULL;
|
|
|
|
if (!attributes) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
psa_reset_key_attributes(attributes);
|
|
|
|
status = psa_get_and_lock_key_slot(key, &slot);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
*attributes = slot->attr;
|
|
|
|
status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_builtin_import_key(const psa_key_attributes_t *attributes,
|
|
const uint8_t *data, size_t data_length,
|
|
uint8_t *key_buffer, size_t key_buffer_size,
|
|
size_t *key_buffer_length, size_t *bits)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
|
|
if (!attributes || !data || !key_buffer || !key_buffer_length || !bits) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (data_length == 0) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (data_length > key_buffer_size) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
psa_key_type_t type = attributes->type;
|
|
|
|
if (PSA_KEY_TYPE_IS_UNSTRUCTURED(type)) {
|
|
*bits = PSA_BYTES_TO_BITS(data_length);
|
|
status = psa_validate_unstructured_key_size(type, *bits);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
memcpy(key_buffer, data, data_length);
|
|
*key_buffer_length = data_length;
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
else if (PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(type)) {
|
|
/* key material does not match expected size */
|
|
if (data_length != PSA_EXPORT_KEY_OUTPUT_SIZE(type, attributes->bits)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
/* key material too large to be represented */
|
|
if (data_length > PSA_EXPORT_PUBLIC_KEY_MAX_SIZE) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
memcpy(key_buffer, data, data_length);
|
|
*key_buffer_length = data_length;
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_import_key(const psa_key_attributes_t *attributes,
|
|
const uint8_t *data,
|
|
size_t data_length,
|
|
psa_key_id_t *key)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot = NULL;
|
|
psa_se_drv_data_t *driver = NULL;
|
|
size_t bits;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!attributes || !data) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (attributes->bits != 0 && attributes->bits > PSA_BYTES_TO_BITS(data_length)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
|
|
*key = PSA_KEY_ID_NULL;
|
|
}
|
|
|
|
/* Find empty slot */
|
|
status = psa_start_key_creation(PSA_KEY_CREATION_IMPORT, attributes, &slot, &driver);
|
|
if (status != PSA_SUCCESS) {
|
|
psa_fail_key_creation(slot, driver);
|
|
return status;
|
|
}
|
|
|
|
bits = slot->attr.bits;
|
|
|
|
status = psa_location_dispatch_import_key(attributes, data, data_length, slot, &bits);
|
|
|
|
if (status != PSA_SUCCESS) {
|
|
psa_fail_key_creation(slot, driver);
|
|
return status;
|
|
}
|
|
|
|
if (slot->attr.bits == 0) {
|
|
slot->attr.bits = (psa_key_bits_t)bits;
|
|
}
|
|
else if (bits != slot->attr.bits) {
|
|
psa_fail_key_creation(slot, driver);
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_finish_key_creation(slot, driver, key);
|
|
if (status != PSA_SUCCESS) {
|
|
psa_fail_key_creation(slot, driver);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
#endif /* MODULE_PSA_KEY_MANAGEMENT */
|
|
|
|
#if IS_USED(MODULE_PSA_KEY_DERIVATION)
|
|
psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t *operation)
|
|
{
|
|
(void)operation;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t *operation,
|
|
size_t *capacity)
|
|
{
|
|
(void)operation;
|
|
(void)capacity;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_input_bytes(psa_key_derivation_operation_t *operation,
|
|
psa_key_derivation_step_t step,
|
|
const uint8_t *data,
|
|
size_t data_length)
|
|
{
|
|
(void)operation;
|
|
(void)step;
|
|
(void)data;
|
|
(void)data_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_input_key(psa_key_derivation_operation_t *operation,
|
|
psa_key_derivation_step_t step,
|
|
psa_key_id_t key)
|
|
{
|
|
(void)operation;
|
|
(void)step;
|
|
(void)key;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_key_agreement(psa_key_derivation_operation_t *operation,
|
|
psa_key_derivation_step_t step,
|
|
psa_key_id_t private_key,
|
|
const uint8_t *peer_key,
|
|
size_t peer_key_length)
|
|
{
|
|
(void)operation;
|
|
(void)step;
|
|
(void)private_key;
|
|
(void)peer_key;
|
|
(void)peer_key_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)
|
|
{
|
|
(void)operation;
|
|
(void)output;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_output_key(const psa_key_attributes_t *attributes,
|
|
psa_key_derivation_operation_t *operation,
|
|
psa_key_id_t *key)
|
|
{
|
|
(void)attributes;
|
|
(void)operation;
|
|
(void)key;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_set_capacity(psa_key_derivation_operation_t *operation,
|
|
size_t capacity)
|
|
{
|
|
(void)operation;
|
|
(void)capacity;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_status_t psa_key_derivation_setup(psa_key_derivation_operation_t *operation,
|
|
psa_algorithm_t alg)
|
|
{
|
|
(void)operation;
|
|
(void)alg;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
#endif /* MODULE_PSA_KEY_DERIVATION */
|
|
|
|
#if IS_USED(MODULE_PSA_MAC)
|
|
/**
|
|
* @brief Validate algorithm and key for a MAC operation
|
|
*
|
|
* @param attr Attributes of the key that is supposed to be used
|
|
* @param alg Algorithm for performing the MAC operation
|
|
* @param mac_size Size of the MAC that is to be generated
|
|
*
|
|
* @return @ref PSA_SUCCESS
|
|
* @ref PSA_ERROR_NOT_SUPPORTED
|
|
* @ref PSA_ERROR_INVALID_ARGUMENT
|
|
*/
|
|
static psa_status_t psa_mac_validate_alg_and_key_and_size(psa_key_attributes_t *attr,
|
|
psa_algorithm_t alg,
|
|
size_t mac_size)
|
|
{
|
|
psa_key_type_t type = psa_get_key_type(attr);
|
|
psa_key_bits_t bits = psa_get_key_bits(attr);
|
|
|
|
if (!PSA_ALG_IS_HMAC(alg)) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
size_t operation_mac_size = PSA_MAC_LENGTH(type, bits, alg);
|
|
|
|
if (operation_mac_size < 4) {
|
|
/**
|
|
* A very short MAC is too short for security since it can be
|
|
* brute-forced. Ancient protocols with 32-bit MACs do exist,
|
|
* so we make this our minimum, even though 32 bits is still
|
|
* too small for security.
|
|
*/
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (operation_mac_size > PSA_MAC_MAX_SIZE) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (mac_size < operation_mac_size) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
return PSA_SUCCESS;
|
|
}
|
|
|
|
psa_status_t psa_mac_compute(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *mac,
|
|
size_t mac_size,
|
|
size_t *mac_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_attributes_t attr = psa_key_attributes_init();
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !mac || !mac_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_key_attributes(key, &attr);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_mac_validate_alg_and_key_and_size(&attr, alg, mac_size);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_SIGN_MESSAGE, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
if (unlock_status != PSA_SUCCESS) {
|
|
status = unlock_status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status = psa_location_dispatch_mac_compute(&slot->attr, alg, slot, input, input_length, mac,
|
|
mac_size, mac_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
|
|
}
|
|
|
|
psa_status_t psa_mac_verify(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
const uint8_t *mac,
|
|
size_t mac_length)
|
|
{
|
|
psa_key_attributes_t attr = psa_key_attributes_init();
|
|
psa_key_slot_t *slot;
|
|
psa_status_t status;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !mac || !mac_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_key_attributes(key, &attr);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_mac_validate_alg_and_key_and_size(&attr, alg, mac_length);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_VERIFY_MESSAGE, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_location_dispatch_mac_verify(&slot->attr, alg, slot, input, input_length,
|
|
mac, mac_length);
|
|
psa_unlock_key_slot(slot);
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg)
|
|
{
|
|
psa_key_attributes_t attr = psa_key_attributes_init();
|
|
psa_key_slot_t *slot;
|
|
psa_status_t status;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_HMAC(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_key_attributes(key, &attr);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_SIGN_MESSAGE, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_location_dispatch_mac_sign_setup(operation, &attr, slot, alg);
|
|
psa_unlock_key_slot(slot);
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_mac_verify_setup(psa_mac_operation_t *operation,
|
|
psa_key_id_t key,
|
|
psa_algorithm_t alg)
|
|
{
|
|
psa_key_attributes_t attr = psa_key_attributes_init();
|
|
psa_key_slot_t *slot;
|
|
psa_status_t status;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_HMAC(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_key_attributes(key, &attr);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_VERIFY_MESSAGE, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
status = psa_location_dispatch_mac_verify_setup(operation, &attr, slot, alg);
|
|
psa_unlock_key_slot(slot);
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_mac_update(psa_mac_operation_t *operation,
|
|
const uint8_t *input,
|
|
size_t input_length)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !input) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
return psa_location_dispatch_mac_update(operation, input, input_length);
|
|
}
|
|
|
|
psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation,
|
|
uint8_t *mac,
|
|
size_t mac_size,
|
|
size_t *mac_length)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !mac || !mac_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
return psa_location_dispatch_mac_sign_finish(operation, mac, mac_size, mac_length);
|
|
}
|
|
|
|
psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation,
|
|
const uint8_t *mac,
|
|
size_t mac_length)
|
|
{
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation || !mac) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
return psa_location_dispatch_mac_verify_finish(operation, mac, mac_length);
|
|
}
|
|
|
|
psa_status_t psa_mac_abort(psa_mac_operation_t *operation)
|
|
{
|
|
psa_status_t status;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!operation) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_location_dispatch_mac_abort(operation);
|
|
*operation = psa_mac_operation_init();
|
|
|
|
return status;
|
|
}
|
|
|
|
psa_status_t psa_purge_key(psa_key_id_t key)
|
|
{
|
|
(void)key;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
#endif /* MODULE_PSA_MAC */
|
|
|
|
#if IS_USED(MODULE_PSA_KEY_AGREEMENT)
|
|
psa_status_t psa_raw_key_agreement(psa_algorithm_t alg,
|
|
psa_key_id_t private_key,
|
|
const uint8_t *peer_key,
|
|
size_t peer_key_length,
|
|
uint8_t *output,
|
|
size_t output_size,
|
|
size_t *output_length)
|
|
{
|
|
(void)alg;
|
|
(void)private_key;
|
|
(void)peer_key;
|
|
(void)peer_key_length;
|
|
(void)output;
|
|
(void)output_size;
|
|
(void)output_length;
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
#endif /* MODULE_PSA_KEY_AGREEMENT */
|
|
|
|
#if IS_USED(MODULE_PSA_ASYMMETRIC)
|
|
psa_status_t psa_sign_hash(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *hash,
|
|
size_t hash_length,
|
|
uint8_t *signature,
|
|
size_t signature_size,
|
|
size_t *signature_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!hash || !signature || !signature_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_ECDSA(alg)) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_SIGN_HASH(alg) || (hash_length != PSA_HASH_LENGTH(alg) && PSA_ALG_IS_HASH(alg))) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_SIGN_HASH, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
|
|
if (signature_size < PSA_ECDSA_SIGNATURE_SIZE(slot->attr.bits)) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
psa_key_attributes_t attributes = slot->attr;
|
|
|
|
status = psa_location_dispatch_sign_hash(&attributes, alg, slot, hash, hash_length,
|
|
signature, signature_size, signature_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
psa_status_t psa_sign_message(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
uint8_t *signature,
|
|
size_t signature_size,
|
|
size_t *signature_length)
|
|
{
|
|
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !signature || !signature_length) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_ECDSA(alg)) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_SIGN_MESSAGE(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_SIGN_MESSAGE, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
|
|
if (signature_size < PSA_ECDSA_SIGNATURE_SIZE(slot->attr.bits)) {
|
|
return PSA_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
psa_key_attributes_t attributes = slot->attr;
|
|
|
|
status = psa_location_dispatch_sign_message(&attributes, alg, slot, input, input_length,
|
|
signature, signature_size, signature_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
psa_status_t psa_verify_hash(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *hash,
|
|
size_t hash_length,
|
|
const uint8_t *signature,
|
|
size_t signature_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!hash || !signature) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_ECDSA(alg)) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_SIGN_HASH(alg) || (hash_length != PSA_HASH_LENGTH(alg) && PSA_ALG_IS_HASH(alg))) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_VERIFY_HASH, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
|
|
if (signature_length != PSA_ECDSA_SIGNATURE_SIZE(slot->attr.bits)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
/**
|
|
* When key location is a secure element, this implementation only supports
|
|
* the use of public keys stored on the secure element, not key pairs in
|
|
* which the public key is stored locally.
|
|
*/
|
|
if ((PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) != PSA_KEY_LOCATION_LOCAL_STORAGE) &&
|
|
PSA_KEY_TYPE_IS_ECC_KEY_PAIR(slot->attr.type)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_key_attributes_t attributes = slot->attr;
|
|
|
|
status = psa_location_dispatch_verify_hash(&attributes, alg, slot, hash, hash_length,
|
|
signature, signature_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
|
|
psa_status_t psa_verify_message(psa_key_id_t key,
|
|
psa_algorithm_t alg,
|
|
const uint8_t *input,
|
|
size_t input_length,
|
|
const uint8_t *signature,
|
|
size_t signature_length)
|
|
{
|
|
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
|
|
psa_key_slot_t *slot;
|
|
|
|
if (!lib_initialized) {
|
|
return PSA_ERROR_BAD_STATE;
|
|
}
|
|
|
|
if (!input || !signature) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_ECDSA(alg)) {
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!PSA_ALG_IS_SIGN_MESSAGE(alg)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_VERIFY_MESSAGE, alg);
|
|
if (status != PSA_SUCCESS) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return status;
|
|
}
|
|
|
|
if (signature_length != PSA_ECDSA_SIGNATURE_SIZE(slot->attr.bits)) {
|
|
return PSA_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
/**
|
|
* When key location is a secure element, this implementation only supports
|
|
* the use of public keys stored on the secure element, not key pairs in
|
|
* which the public key is stored locally.
|
|
*/
|
|
if ((PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) != PSA_KEY_LOCATION_LOCAL_STORAGE) &&
|
|
PSA_KEY_TYPE_IS_ECC_KEY_PAIR(slot->attr.type)) {
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return PSA_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
psa_key_attributes_t attributes = slot->attr;
|
|
|
|
status = psa_location_dispatch_verify_message(&attributes, alg, slot, input, input_length,
|
|
signature, signature_length);
|
|
|
|
unlock_status = psa_unlock_key_slot(slot);
|
|
return ((status == PSA_SUCCESS) ? unlock_status : status);
|
|
}
|
|
#endif /* MODULE_PSA_ASYMMETRIC */
|