zephyr/modules/openthread/platform/crypto_psa.c
Maciej Baczmanski 85bc24eb07 net: openthread: Add PSA implementation for PBDKF2 genkey
Add implementation of openthread pbkdf2 generate key using PSA
functions.

Co-authored-by: Joakim Andersson <joakim.andersson@nordicsemi.no>

Signed-off-by: Maciej Baczmanski <maciej.baczmanski@nordicsemi.no>
2024-01-26 11:03:42 +01:00

669 lines
18 KiB
C

/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <openthread/platform/crypto.h>
#include <psa/crypto.h>
#include <zephyr/sys/__assert.h>
#if !defined(CONFIG_BUILD_WITH_TFM) && defined(CONFIG_OPENTHREAD_CRYPTO_PSA)
#include <zephyr/settings/settings.h>
#endif
#if defined(CONFIG_OPENTHREAD_ECDSA)
#include <string.h>
#include <mbedtls/asn1.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;
case OT_CRYPTO_KEY_TYPE_ECDSA:
return PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
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);
case OT_CRYPTO_KEY_ALG_ECDSA:
return PSA_ALG_DETERMINISTIC_ECDSA(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;
}
if (aUsage & OT_CRYPTO_KEY_USAGE_VERIFY_HASH) {
usage |= PSA_KEY_USAGE_VERIFY_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 |
OT_CRYPTO_KEY_USAGE_VERIFY_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;
}
void otPlatCryptoInit(void)
{
psa_crypto_init();
#if !defined(CONFIG_BUILD_WITH_TFM) && defined(CONFIG_OPENTHREAD_CRYPTO_PSA)
/*
* In OpenThread, Settings are initialized after KeyManager by default. If device uses
* PSA with emulated TFM, Settings have to be initialized at the end of otPlatCryptoInit(),
* to be available before storing Network Key.
*/
__ASSERT_EVAL((void)settings_subsys_init(), int err = settings_subsys_init(), !err,
"Failed to initialize settings");
#endif
}
otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType,
otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage,
otCryptoKeyStorage aKeyPersistence, const uint8_t *aKey,
size_t aKeyLen)
{
#if defined(CONFIG_OPENTHREAD_ECDSA)
int version;
size_t len;
unsigned char *p = (unsigned char *)aKey;
unsigned char *end;
#endif
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status = 0;
if (aKeyRef == NULL || aKey == NULL || !checkKeyUsage(aKeyUsage)) {
return OT_ERROR_INVALID_ARGS;
}
#if defined(CONFIG_OPENTHREAD_ECDSA)
/* Check if key is ECDSA pair and extract private key from it since PSA expects it. */
if (aKeyType == OT_CRYPTO_KEY_TYPE_ECDSA) {
end = p + aKeyLen;
status = mbedtls_asn1_get_tag(&p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (status != 0) {
return OT_ERROR_FAILED;
}
end = p + len;
status = mbedtls_asn1_get_int(&p, end, &version);
if (status != 0) {
return OT_ERROR_FAILED;
}
status = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
if (status != 0 || len != 32) {
return OT_ERROR_FAILED;
}
aKey = p;
aKeyLen = len;
}
#endif
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;
}
return psaToOtError(psa_export_key(aKeyRef, aBuffer, aBufferLen, aKeyLen));
}
otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef)
{
return psaToOtError(psa_destroy_key(aKeyRef));
}
bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
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;
}
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_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;
}
key_ref = aContext->mContext;
status = psa_cipher_encrypt(*key_ref, PSA_ALG_ECB_NO_PADDING, aInput, block_size, aOutput,
block_size, &cipher_length);
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)
{
psa_crypto_init();
}
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 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);
}
otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
uint16_t aPasswordLen,
const uint8_t *aSalt,
uint16_t aSaltLen,
uint32_t aIterationCounter,
uint16_t aKeyLen,
uint8_t *aKey)
{
psa_status_t status = PSA_SUCCESS;
psa_key_id_t key_id = PSA_KEY_ID_NULL;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_algorithm_t algorithm = PSA_ALG_PBKDF2_AES_CMAC_PRF_128;
psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT;
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
psa_set_key_algorithm(&attributes, algorithm);
psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD);
psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(aPasswordLen));
status = psa_import_key(&attributes, aPassword, aPasswordLen, &key_id);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_key_derivation_setup(&operation, algorithm);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_key_derivation_input_integer(&operation, PSA_KEY_DERIVATION_INPUT_COST,
aIterationCounter);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SALT,
aSalt, aSaltLen);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_key_derivation_input_key(&operation, PSA_KEY_DERIVATION_INPUT_PASSWORD,
key_id);
if (status != PSA_SUCCESS) {
goto out;
}
status = psa_key_derivation_output_bytes(&operation, aKey, aKeyLen);
if (status != PSA_SUCCESS) {
goto out;
}
out:
psa_reset_key_attributes(&attributes);
psa_key_derivation_abort(&operation);
psa_destroy_key(key_id);
__ASSERT_NO_MSG(status == PSA_SUCCESS);
return psaToOtError(status);
}
#endif /* #if CONFIG_OPENTHREAD_ECDSA */