blob: d9deb114f3697e26301599d56ed7a31b363a02b3 [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <openthread/platform/crypto.h>
#include <psa/crypto.h>
#if defined(CONFIG_OPENTHREAD_ECDSA)
#include <string.h>
#include <zephyr/sys/__assert.h>
#endif
static otError psaToOtError(psa_status_t aStatus)
{
switch (aStatus) {
case PSA_SUCCESS:
return OT_ERROR_NONE;
case PSA_ERROR_INVALID_ARGUMENT:
return OT_ERROR_INVALID_ARGS;
case PSA_ERROR_BUFFER_TOO_SMALL:
return OT_ERROR_NO_BUFS;
default:
return OT_ERROR_FAILED;
}
}
static psa_key_type_t toPsaKeyType(otCryptoKeyType aType)
{
switch (aType) {
case OT_CRYPTO_KEY_TYPE_RAW:
return PSA_KEY_TYPE_RAW_DATA;
case OT_CRYPTO_KEY_TYPE_AES:
return PSA_KEY_TYPE_AES;
case OT_CRYPTO_KEY_TYPE_HMAC:
return PSA_KEY_TYPE_HMAC;
default:
return PSA_KEY_TYPE_NONE;
}
}
static psa_algorithm_t toPsaAlgorithm(otCryptoKeyAlgorithm aAlgorithm)
{
switch (aAlgorithm) {
case OT_CRYPTO_KEY_ALG_AES_ECB:
return PSA_ALG_ECB_NO_PADDING;
case OT_CRYPTO_KEY_ALG_HMAC_SHA_256:
return PSA_ALG_HMAC(PSA_ALG_SHA_256);
default:
/*
* There is currently no constant like PSA_ALG_NONE, but 0 is used
* to indicate an unknown algorithm.
*/
return (psa_algorithm_t) 0;
}
}
static psa_key_usage_t toPsaKeyUsage(int aUsage)
{
psa_key_usage_t usage = 0;
if (aUsage & OT_CRYPTO_KEY_USAGE_EXPORT) {
usage |= PSA_KEY_USAGE_EXPORT;
}
if (aUsage & OT_CRYPTO_KEY_USAGE_ENCRYPT) {
usage |= PSA_KEY_USAGE_ENCRYPT;
}
if (aUsage & OT_CRYPTO_KEY_USAGE_DECRYPT) {
usage |= PSA_KEY_USAGE_DECRYPT;
}
if (aUsage & OT_CRYPTO_KEY_USAGE_SIGN_HASH) {
usage |= PSA_KEY_USAGE_SIGN_HASH;
}
return usage;
}
static bool checkKeyUsage(int aUsage)
{
/* Check if only supported flags have been passed */
int supported_flags = OT_CRYPTO_KEY_USAGE_EXPORT |
OT_CRYPTO_KEY_USAGE_ENCRYPT |
OT_CRYPTO_KEY_USAGE_DECRYPT |
OT_CRYPTO_KEY_USAGE_SIGN_HASH;
return (aUsage & ~supported_flags) == 0;
}
static bool checkContext(otCryptoContext *aContext, size_t aMinSize)
{
/* Verify that the passed context is initialized and points to a big enough buffer */
return aContext != NULL && aContext->mContext != NULL && aContext->mContextSize >= aMinSize;
}
static void ensureKeyIsLoaded(otCryptoKeyRef aKeyRef)
{
/*
* The workaround below will no longer be need after updating TF-M version used in Zephyr
* to 1.5.0 (see upstream commit 42e77b561fcfe19819ff1e63cb7c0b672ee8ba41).
* In the recent versions of TF-M the concept of key handles and psa_open_key()/
* psa_close_key() APIs have been being deprecated, but the version currently used in Zephyr
* is in the middle of that transition. Consequently, psa_destroy_key() and lots of other
* functions will fail when a key ID that they take as a parameter is not loaded from the
* persistent storage. That may occur when a given persistent key is created via
* psa_generate_key() or psa_import_key(), and then the device reboots.
*
* Use psa_open_key() when the key has not been loaded yet to work around the issue.
*/
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status = psa_get_key_attributes(aKeyRef, &attributes);
psa_key_id_t key_handle;
if (status == PSA_ERROR_INVALID_HANDLE) {
psa_open_key(aKeyRef, &key_handle);
}
psa_reset_key_attributes(&attributes);
}
void otPlatCryptoInit(void)
{
psa_crypto_init();
}
otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef,
otCryptoKeyType aKeyType,
otCryptoKeyAlgorithm aKeyAlgorithm,
int aKeyUsage,
otCryptoKeyStorage aKeyPersistence,
const uint8_t *aKey,
size_t aKeyLen)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
if (aKeyRef == NULL || aKey == NULL || !checkKeyUsage(aKeyUsage)) {
return OT_ERROR_INVALID_ARGS;
}
psa_set_key_type(&attributes, toPsaKeyType(aKeyType));
psa_set_key_algorithm(&attributes, toPsaAlgorithm(aKeyAlgorithm));
psa_set_key_usage_flags(&attributes, toPsaKeyUsage(aKeyUsage));
switch (aKeyPersistence) {
case OT_CRYPTO_KEY_STORAGE_PERSISTENT:
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attributes, *aKeyRef);
break;
case OT_CRYPTO_KEY_STORAGE_VOLATILE:
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
break;
}
status = psa_import_key(&attributes, aKey, aKeyLen, aKeyRef);
psa_reset_key_attributes(&attributes);
return psaToOtError(status);
}
otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef,
uint8_t *aBuffer,
size_t aBufferLen,
size_t *aKeyLen)
{
if (aBuffer == NULL) {
return OT_ERROR_INVALID_ARGS;
}
ensureKeyIsLoaded(aKeyRef);
return psaToOtError(psa_export_key(aKeyRef, aBuffer, aBufferLen, aKeyLen));
}
otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef)
{
ensureKeyIsLoaded(aKeyRef);
return psaToOtError(psa_destroy_key(aKeyRef));
}
bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
ensureKeyIsLoaded(aKeyRef);
status = psa_get_key_attributes(aKeyRef, &attributes);
psa_reset_key_attributes(&attributes);
return status == PSA_SUCCESS;
}
otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext)
{
psa_mac_operation_t *operation;
if (!checkContext(aContext, sizeof(psa_mac_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
*operation = psa_mac_operation_init();
return OT_ERROR_NONE;
}
otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext)
{
psa_mac_operation_t *operation;
if (!checkContext(aContext, sizeof(psa_mac_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_mac_abort(operation));
}
otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, const otCryptoKey *aKey)
{
psa_mac_operation_t *operation;
psa_status_t status;
if (aKey == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
ensureKeyIsLoaded(aKey->mKeyRef);
operation = aContext->mContext;
status = psa_mac_sign_setup(operation, aKey->mKeyRef, PSA_ALG_HMAC(PSA_ALG_SHA_256));
return psaToOtError(status);
}
otError otPlatCryptoHmacSha256Update(otCryptoContext *aContext,
const void *aBuf,
uint16_t aBufLength)
{
psa_mac_operation_t *operation;
if (aBuf == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_mac_update(operation, (const uint8_t *) aBuf, aBufLength));
}
otError otPlatCryptoHmacSha256Finish(otCryptoContext *aContext, uint8_t *aBuf, size_t aBufLength)
{
psa_mac_operation_t *operation;
size_t mac_length;
if (aBuf == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_mac_sign_finish(operation, aBuf, aBufLength, &mac_length));
}
otError otPlatCryptoAesInit(otCryptoContext *aContext)
{
psa_key_id_t *key_ref;
if (!checkContext(aContext, sizeof(psa_key_id_t))) {
return OT_ERROR_INVALID_ARGS;
}
key_ref = aContext->mContext;
*key_ref = (psa_key_id_t) 0; /* In TF-M 1.5.0 this can be replaced with PSA_KEY_ID_NULL */
return OT_ERROR_NONE;
}
otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCryptoKey *aKey)
{
psa_key_id_t *key_ref;
if (aKey == NULL || !checkContext(aContext, sizeof(psa_key_id_t))) {
return OT_ERROR_INVALID_ARGS;
}
key_ref = aContext->mContext;
*key_ref = aKey->mKeyRef;
return OT_ERROR_NONE;
}
otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uint8_t *aInput, uint8_t *aOutput)
{
const size_t block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES);
psa_status_t status = PSA_SUCCESS;
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
psa_key_id_t *key_ref;
size_t cipher_length;
if (aInput == NULL || aOutput == NULL || !checkContext(aContext, sizeof(psa_key_id_t))) {
return OT_ERROR_INVALID_ARGS;
}
/*
* The code below can be simplified after updating TF-M version used in Zephyr to 1.5.0
* (see upstream commit: 045ec4abfc73152a0116684ba9127d0a97cc8d34), using
* psa_cipher_encrypt() function which will replace the setup-update-finish sequence below.
*/
key_ref = aContext->mContext;
ensureKeyIsLoaded(*key_ref);
status = psa_cipher_encrypt_setup(&operation, *key_ref, PSA_ALG_ECB_NO_PADDING);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_cipher_update(&operation,
aInput,
block_size,
aOutput,
block_size,
&cipher_length);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_cipher_finish(&operation,
aOutput + cipher_length,
block_size - cipher_length,
&cipher_length);
out:
psa_cipher_abort(&operation);
return psaToOtError(status);
}
otError otPlatCryptoAesFree(otCryptoContext *aContext)
{
return OT_ERROR_NONE;
}
otError otPlatCryptoSha256Init(otCryptoContext *aContext)
{
psa_hash_operation_t *operation;
if (!checkContext(aContext, sizeof(psa_hash_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
*operation = psa_hash_operation_init();
return OT_ERROR_NONE;
}
otError otPlatCryptoSha256Deinit(otCryptoContext *aContext)
{
psa_hash_operation_t *operation;
if (!checkContext(aContext, sizeof(psa_hash_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_hash_abort(operation));
}
otError otPlatCryptoSha256Start(otCryptoContext *aContext)
{
psa_hash_operation_t *operation;
if (!checkContext(aContext, sizeof(psa_hash_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_hash_setup(operation, PSA_ALG_SHA_256));
}
otError otPlatCryptoSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength)
{
psa_hash_operation_t *operation;
if (aBuf == NULL || !checkContext(aContext, sizeof(psa_hash_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_hash_update(operation, (const uint8_t *) aBuf, aBufLength));
}
otError otPlatCryptoSha256Finish(otCryptoContext *aContext, uint8_t *aHash, uint16_t aHashSize)
{
psa_hash_operation_t *operation;
size_t hash_size;
if (aHash == NULL || !checkContext(aContext, sizeof(psa_hash_operation_t))) {
return OT_ERROR_INVALID_ARGS;
}
operation = aContext->mContext;
return psaToOtError(psa_hash_finish(operation, aHash, aHashSize, &hash_size));
}
void otPlatCryptoRandomInit(void)
{
}
void otPlatCryptoRandomDeinit(void)
{
}
otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize)
{
return psaToOtError(psa_generate_random(aBuffer, aSize));
}
#if defined(CONFIG_OPENTHREAD_ECDSA)
otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id = 0;
psa_status_t status;
size_t exported_length;
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256));
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, 256);
status = psa_generate_key(&attributes, &key_id);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_export_key(key_id, aKeyPair->mDerBytes, OT_CRYPTO_ECDSA_MAX_DER_SIZE,
&exported_length);
if (status != PSA_SUCCESS) {
goto out;
}
aKeyPair->mDerLength = exported_length;
out:
psa_reset_key_attributes(&attributes);
psa_destroy_key(key_id);
return psaToOtError(status);
}
otError otPlatCryptoEcdsaGetPublicKey(const otPlatCryptoEcdsaKeyPair *aKeyPair,
otPlatCryptoEcdsaPublicKey *aPublicKey)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id = 0;
psa_status_t status;
size_t exported_length;
uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE];
psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256));
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, 256);
status = psa_import_key(&attributes, aKeyPair->mDerBytes, aKeyPair->mDerLength, &key_id);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_export_public_key(key_id, buffer, sizeof(buffer), &exported_length);
if (status != PSA_SUCCESS) {
goto out;
}
__ASSERT_NO_MSG(exported_length == sizeof(buffer));
memcpy(aPublicKey->m8, buffer + 1, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE);
out:
psa_reset_key_attributes(&attributes);
psa_destroy_key(key_id);
return psaToOtError(status);
}
otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair,
const otPlatCryptoSha256Hash *aHash,
otPlatCryptoEcdsaSignature *aSignature)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id;
psa_status_t status;
size_t signature_length;
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256));
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, 256);
status = psa_import_key(&attributes, aKeyPair->mDerBytes, aKeyPair->mDerLength, &key_id);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_sign_hash(key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8,
OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8,
OT_CRYPTO_ECDSA_SIGNATURE_SIZE, &signature_length);
if (status != PSA_SUCCESS) {
goto out;
}
out:
psa_reset_key_attributes(&attributes);
psa_destroy_key(key_id);
return psaToOtError(status);
}
otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey,
const otPlatCryptoSha256Hash *aHash,
const otPlatCryptoEcdsaSignature *aSignature)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id;
psa_status_t status;
uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE];
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256));
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&attributes, 256);
/*
* `psa_import_key` expects a key format as specified by SEC1 &sect;2.3.3 for the
* uncompressed representation of the ECPoint.
*/
buffer[0] = 0x04;
memcpy(buffer + 1, aPublicKey->m8, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE);
status = psa_import_key(&attributes, buffer, sizeof(buffer), &key_id);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_verify_hash(key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8,
OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8,
OT_CRYPTO_ECDSA_SIGNATURE_SIZE);
if (status != PSA_SUCCESS) {
goto out;
}
out:
psa_reset_key_attributes(&attributes);
psa_destroy_key(key_id);
return psaToOtError(status);
}
otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef,
const otPlatCryptoSha256Hash *aHash,
otPlatCryptoEcdsaSignature *aSignature)
{
psa_status_t status;
size_t signature_length;
status = psa_sign_hash(aKeyRef, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8,
OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8,
OT_CRYPTO_ECDSA_SIGNATURE_SIZE, &signature_length);
if (status != PSA_SUCCESS) {
goto out;
}
__ASSERT_NO_MSG(signature_length == OT_CRYPTO_ECDSA_SIGNATURE_SIZE);
out:
return psaToOtError(status);
}
otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKeyRef,
const otPlatCryptoSha256Hash *aHash,
const otPlatCryptoEcdsaSignature *aSignature)
{
psa_status_t status;
status = psa_verify_hash(aKeyRef, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8,
OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8,
OT_CRYPTO_ECDSA_SIGNATURE_SIZE);
if (status != PSA_SUCCESS) {
goto out;
}
out:
return psaToOtError(status);
}
otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef,
otPlatCryptoEcdsaPublicKey *aPublicKey)
{
psa_status_t status;
size_t exported_length;
uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE];
status = psa_export_public_key(aKeyRef, buffer, sizeof(buffer), &exported_length);
if (status != PSA_SUCCESS) {
goto out;
}
__ASSERT_NO_MSG(exported_length == sizeof(buffer));
memcpy(aPublicKey->m8, buffer + 1, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE);
out:
return psaToOtError(status);
}
otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
psa_key_id_t key_id = (psa_key_id_t)aKeyRef;
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256));
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attributes, key_id);
psa_set_key_bits(&attributes, 256);
status = psa_generate_key(&attributes, &key_id);
if (status != PSA_SUCCESS) {
goto out;
}
out:
psa_reset_key_attributes(&attributes);
return psaToOtError(status);
}
#endif /* #if CONFIG_OPENTHREAD_ECDSA */