[crypto] PSA API: introduce default OpenThread crypto platform port

This commit adds a default implementation of the OpenThread crypto
platform API for the PSA Crypto API.

Signed-off-by: Łukasz Duda <lukasz.duda@nordicsemi.no>
This commit is contained in:
Łukasz Duda
2025-04-22 16:37:04 +02:00
parent 3a6573fee8
commit baed8372c3
5 changed files with 774 additions and 75 deletions
+5 -1
View File
@@ -300,6 +300,10 @@ if(ot_index EQUAL -1)
message(FATAL_ERROR "Invalid value for OT_PLATFORM - valid values are:" "${OT_PLATFORM_VALUES}")
endif()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
set(OT_CRYPTO_LIB_VALUES "MBEDTLS" "PSA" "PLATFORM")
ot_multi_option(OT_CRYPTO_LIB OT_CRYPTO_LIB_VALUES OPENTHREAD_CONFIG_CRYPTO_LIB OPENTHREAD_CONFIG_CRYPTO_LIB_ "set Crypto backend library")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1" "1.4")
set(OT_THREAD_VERSION "1.4" CACHE STRING "set Thread version")
@@ -357,7 +361,7 @@ ot_int_option(OT_RCP_TX_WAIT_TIME_SECS OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(NOT OT_EXTERNAL_MBEDTLS)
set(OT_MBEDTLS mbedtls)
set(OT_MBEDTLS mbedtls mbedcrypto)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS=1")
else()
set(OT_MBEDTLS ${OT_EXTERNAL_MBEDTLS})
+4 -2
View File
@@ -481,7 +481,8 @@ openthread_core_files = [
"crypto/aes_ecb.cpp",
"crypto/aes_ecb.hpp",
"crypto/context_size.hpp",
"crypto/crypto_platform.cpp",
"crypto/crypto_platform_mbedtls.cpp",
"crypto/crypto_platform_psa.cpp",
"crypto/ecdsa.hpp",
"crypto/hkdf_sha256.cpp",
"crypto/hkdf_sha256.hpp",
@@ -803,7 +804,8 @@ openthread_radio_sources = [
"common/uptime.cpp",
"crypto/aes_ccm.cpp",
"crypto/aes_ecb.cpp",
"crypto/crypto_platform.cpp",
"crypto/crypto_platform_mbedtls.cpp",
"crypto/crypto_platform_psa.cpp",
"crypto/storage.cpp",
"diags/factory_diags.cpp",
"instance/instance.cpp",
+4 -2
View File
@@ -135,7 +135,8 @@ set(COMMON_SOURCES
common/uptime.cpp
crypto/aes_ccm.cpp
crypto/aes_ecb.cpp
crypto/crypto_platform.cpp
crypto/crypto_platform_mbedtls.cpp
crypto/crypto_platform_psa.cpp
crypto/hkdf_sha256.cpp
crypto/hmac_sha256.cpp
crypto/mbedtls.cpp
@@ -307,7 +308,8 @@ set(RADIO_COMMON_SOURCES
common/uptime.cpp
crypto/aes_ccm.cpp
crypto/aes_ecb.cpp
crypto/crypto_platform.cpp
crypto/crypto_platform_mbedtls.cpp
crypto/crypto_platform_psa.cpp
crypto/storage.cpp
diags/factory_diags.cpp
instance/instance.cpp
@@ -749,74 +749,4 @@ exit:
#endif // #if OPENTHREAD_FTD
#elif OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA
#if OPENTHREAD_FTD || OPENTHREAD_MTD
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
OT_TOOL_WEAK otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair)
{
OT_UNUSED_VARIABLE(aKeyPair);
return OT_ERROR_NOT_CAPABLE;
}
OT_TOOL_WEAK otError otPlatCryptoEcdsaGetPublicKey(const otPlatCryptoEcdsaKeyPair *aKeyPair,
otPlatCryptoEcdsaPublicKey *aPublicKey)
{
OT_UNUSED_VARIABLE(aKeyPair);
OT_UNUSED_VARIABLE(aPublicKey);
return OT_ERROR_NOT_CAPABLE;
}
OT_TOOL_WEAK otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair,
const otPlatCryptoSha256Hash *aHash,
otPlatCryptoEcdsaSignature *aSignature)
{
OT_UNUSED_VARIABLE(aKeyPair);
OT_UNUSED_VARIABLE(aHash);
OT_UNUSED_VARIABLE(aSignature);
return OT_ERROR_NOT_CAPABLE;
}
OT_TOOL_WEAK otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey,
const otPlatCryptoSha256Hash *aHash,
const otPlatCryptoEcdsaSignature *aSignature)
{
OT_UNUSED_VARIABLE(aPublicKey);
OT_UNUSED_VARIABLE(aHash);
OT_UNUSED_VARIABLE(aSignature);
return OT_ERROR_NOT_CAPABLE;
}
#endif // #if OPENTHREAD_CONFIG_ECDSA_ENABLE
#endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
#if OPENTHREAD_FTD
OT_TOOL_WEAK 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)
{
OT_UNUSED_VARIABLE(aPassword);
OT_UNUSED_VARIABLE(aPasswordLen);
OT_UNUSED_VARIABLE(aSalt);
OT_UNUSED_VARIABLE(aSaltLen);
OT_UNUSED_VARIABLE(aIterationCounter);
OT_UNUSED_VARIABLE(aKeyLen);
OT_UNUSED_VARIABLE(aKey);
return OT_ERROR_NOT_CAPABLE;
}
#endif // #if OPENTHREAD_FTD
#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS
+761
View File
@@ -0,0 +1,761 @@
/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the default/weak Crypto platform APIs using ARM PSA API.
*/
#include "openthread-core-config.h"
#include <string.h>
#include <mbedtls/asn1.h>
#include <psa/crypto.h>
#include <openthread/instance.h>
#include <openthread/platform/crypto.h>
#include <openthread/platform/entropy.h>
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/new.hpp"
#include "config/crypto.h"
#include "crypto/ecdsa.hpp"
#include "crypto/hmac_sha256.hpp"
#include "crypto/storage.hpp"
#include "instance/instance.hpp"
using namespace ot;
using namespace Crypto;
#if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA
//---------------------------------------------------------------------------------------------------------------------
// Default/weak implementation of crypto platform APIs
static otError psaToOtError(psa_status_t aStatus)
{
switch (aStatus)
{
case PSA_SUCCESS:
return kErrorNone;
case PSA_ERROR_INVALID_ARGUMENT:
return kErrorInvalidArgs;
case PSA_ERROR_BUFFER_TOO_SMALL:
return kErrorNoBufs;
default:
return kErrorFailed;
}
}
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);
case OT_CRYPTO_KEY_TYPE_DERIVE:
return PSA_KEY_TYPE_DERIVE;
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);
case OT_CRYPTO_KEY_ALG_HKDF_SHA256:
return PSA_ALG_HKDF(PSA_ALG_SHA_256);
default:
return PSA_ALG_NONE;
}
}
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;
}
if (aUsage & OT_CRYPTO_KEY_USAGE_DERIVE)
{
usage |= PSA_KEY_USAGE_DERIVE;
}
return usage;
}
static bool checkKeyUsage(int aUsage)
{
/* Check if only supported flags have been passed */
int supportedFlags = 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 | OT_CRYPTO_KEY_USAGE_DERIVE;
return (aUsage & ~supportedFlags) == 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 != nullptr && aContext->mContext != nullptr && aContext->mContextSize >= aMinSize;
}
static otError extractPrivateKeyInfo(const uint8_t *aAsn1KeyPair,
size_t aAsn1KeyPairLen,
size_t *aKeyOffset,
size_t *aKeyLen)
{
Error error = kErrorNone;
unsigned char *p = const_cast<unsigned char *>(aAsn1KeyPair);
const unsigned char *end = p + aAsn1KeyPairLen;
size_t len;
// Parse the ASN.1 SEQUENCE headers
int ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
VerifyOrExit(ret == 0, error = kErrorInvalidArgs);
// Parse the version (INTEGER)
ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
VerifyOrExit(ret == 0, error = kErrorInvalidArgs);
// Skip the version.
p += len;
// Parse the private key (OCTET STRING)
ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
VerifyOrExit(ret == 0, error = kErrorInvalidArgs);
// Check if the private key includes a padding byte (0x00)
if (*p == 0x00)
{
p++;
len--; // Skip the padding byte and reduce length by 1
}
*aKeyOffset = (p - aAsn1KeyPair);
*aKeyLen = len;
exit:
return error;
}
OT_TOOL_WEAK void otPlatCryptoInit(void) { psa_crypto_init(); }
OT_TOOL_WEAK otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef,
otCryptoKeyType aKeyType,
otCryptoKeyAlgorithm aKeyAlgorithm,
int aKeyUsage,
otCryptoKeyStorage aKeyPersistence,
const uint8_t *aKey,
size_t aKeyLen)
{
Error error = kErrorNone;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status = PSA_SUCCESS;
VerifyOrExit(checkKeyUsage(aKeyUsage), error = kErrorInvalidArgs);
VerifyOrExit(aKeyRef != nullptr && aKey != nullptr, error = kErrorInvalidArgs);
// PSA Crypto API expects the private key to be provided, not the full ASN1 buffer.
if (aKeyType == OT_CRYPTO_KEY_TYPE_ECDSA)
{
size_t pkOffset;
size_t pkLength;
SuccessOrExit(error = extractPrivateKeyInfo(aKey, aKeyLen, &pkOffset, &pkLength));
// Overwrite the content of the key.
aKey += pkOffset;
aKeyLen = pkLength;
psa_set_key_bits(&attributes, 256);
}
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;
default:
OT_ASSERT(false);
}
status = psa_import_key(&attributes, aKey, aKeyLen, aKeyRef);
exit:
psa_reset_key_attributes(&attributes);
if (error != kErrorNone)
{
return error;
}
return psaToOtError(status);
}
OT_TOOL_WEAK otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef, uint8_t *aBuffer, size_t aBufferLen, size_t *aKeyLen)
{
Error error = kErrorNone;
VerifyOrExit(aBuffer != nullptr && aKeyLen != nullptr, error = kErrorInvalidArgs);
error = psaToOtError(psa_export_key(aKeyRef, aBuffer, aBufferLen, aKeyLen));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef) { return psaToOtError(psa_destroy_key(aKeyRef)); }
OT_TOOL_WEAK 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;
}
OT_TOOL_WEAK otError otPlatCryptoAesInit(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_key_id_t *keyRef;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_id_t)), error = kErrorInvalidArgs);
keyRef = static_cast<psa_key_id_t *>(aContext->mContext);
*keyRef = PSA_KEY_ID_NULL;
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCryptoKey *aKey)
{
Error error = kErrorNone;
psa_key_id_t *keyRef;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_id_t)), error = kErrorInvalidArgs);
VerifyOrExit(aKey != nullptr, error = kErrorInvalidArgs);
keyRef = static_cast<psa_key_id_t *>(aContext->mContext);
*keyRef = aKey->mKeyRef;
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uint8_t *aInput, uint8_t *aOutput)
{
Error error = kErrorNone;
const size_t blockSize = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES);
psa_status_t status = PSA_SUCCESS;
psa_key_id_t *keyRef;
size_t cipherLen;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_id_t)), error = kErrorInvalidArgs);
VerifyOrExit(aInput != nullptr && aOutput != nullptr, error = kErrorInvalidArgs);
keyRef = static_cast<psa_key_id_t *>(aContext->mContext);
status = psa_cipher_encrypt(*keyRef, PSA_ALG_ECB_NO_PADDING, aInput, blockSize, aOutput, blockSize, &cipherLen);
error = psaToOtError(status);
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoAesFree(otCryptoContext *aContext)
{
OT_UNUSED_VARIABLE(aContext);
return kErrorNone;
}
#if !OPENTHREAD_RADIO
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_mac_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_mac_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_mac_operation_t *>(aContext->mContext);
// Initialize the structure using memset as documented alternative for psa_mac_operation_init().
memset(operation, 0, sizeof(*operation));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_mac_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_mac_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_mac_operation_t *>(aContext->mContext);
error = psaToOtError(psa_mac_abort(operation));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, const otCryptoKey *aKey)
{
Error error = kErrorNone;
psa_mac_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_mac_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aKey != nullptr, error = kErrorInvalidArgs);
operation = static_cast<psa_mac_operation_t *>(aContext->mContext);
error = psaToOtError(psa_mac_sign_setup(operation, aKey->mKeyRef, PSA_ALG_HMAC(PSA_ALG_SHA_256)));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength)
{
Error error = kErrorNone;
psa_mac_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_mac_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aBuf != nullptr, error = kErrorInvalidArgs);
operation = static_cast<psa_mac_operation_t *>(aContext->mContext);
error = psaToOtError(psa_mac_update(operation, static_cast<const uint8_t *>(aBuf), aBufLength));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Finish(otCryptoContext *aContext, uint8_t *aBuf, size_t aBufLength)
{
Error error = kErrorNone;
psa_mac_operation_t *operation;
size_t macLength;
VerifyOrExit(checkContext(aContext, sizeof(psa_mac_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aBuf != nullptr, error = kErrorInvalidArgs);
operation = static_cast<psa_mac_operation_t *>(aContext->mContext);
error = psaToOtError(psa_mac_sign_finish(operation, aBuf, aBufLength, &macLength));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHkdfInit(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_key_derivation_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_derivation_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_key_derivation_operation_t *>(aContext->mContext);
*operation = PSA_KEY_DERIVATION_OPERATION_INIT;
error = psaToOtError(psa_key_derivation_setup(operation, PSA_ALG_HKDF(PSA_ALG_SHA_256)));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHkdfExtract(otCryptoContext *aContext,
const uint8_t *aSalt,
uint16_t aSaltLength,
const otCryptoKey *aInputKey)
{
Error error = kErrorNone;
psa_status_t status = PSA_SUCCESS;
psa_key_derivation_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_derivation_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aInputKey != nullptr, error = kErrorInvalidArgs);
operation = static_cast<psa_key_derivation_operation_t *>(aContext->mContext);
status = psa_key_derivation_input_bytes(operation, PSA_KEY_DERIVATION_INPUT_SALT, aSalt, aSaltLength);
SuccessOrExit(error = psaToOtError(status));
status = psa_key_derivation_input_key(operation, PSA_KEY_DERIVATION_INPUT_SECRET, aInputKey->mKeyRef);
SuccessOrExit(error = psaToOtError(status));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHkdfExpand(otCryptoContext *aContext,
const uint8_t *aInfo,
uint16_t aInfoLength,
uint8_t *aOutputKey,
uint16_t aOutputKeyLength)
{
Error error = kErrorNone;
psa_status_t status = PSA_SUCCESS;
psa_key_derivation_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_derivation_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aOutputKey != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aOutputKeyLength != 0, error = kErrorInvalidArgs);
operation = static_cast<psa_key_derivation_operation_t *>(aContext->mContext);
status = psa_key_derivation_input_bytes(operation, PSA_KEY_DERIVATION_INPUT_INFO, aInfo, aInfoLength);
SuccessOrExit(error = psaToOtError(status));
status = psa_key_derivation_output_bytes(operation, aOutputKey, aOutputKeyLength);
SuccessOrExit(error = psaToOtError(status));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoHkdfDeinit(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_key_derivation_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_key_derivation_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_key_derivation_operation_t *>(aContext->mContext);
error = psaToOtError(psa_key_derivation_abort(operation));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoSha256Init(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_hash_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_hash_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_hash_operation_t *>(aContext->mContext);
// Initialize the structure using memset as documented alternative for psa_hash_operation_init().
memset(operation, 0, sizeof(*operation));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoSha256Deinit(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_hash_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_hash_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_hash_operation_t *>(aContext->mContext);
error = psaToOtError(psa_hash_abort(operation));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoSha256Start(otCryptoContext *aContext)
{
Error error = kErrorNone;
psa_hash_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_hash_operation_t)), error = kErrorInvalidArgs);
operation = static_cast<psa_hash_operation_t *>(aContext->mContext);
error = psaToOtError(psa_hash_setup(operation, PSA_ALG_SHA_256));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength)
{
Error error = kErrorNone;
psa_hash_operation_t *operation;
VerifyOrExit(checkContext(aContext, sizeof(psa_hash_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aBuf != nullptr, error = kErrorInvalidArgs);
operation = static_cast<psa_hash_operation_t *>(aContext->mContext);
error = psaToOtError(psa_hash_update(operation, static_cast<const uint8_t *>(aBuf), aBufLength));
exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoSha256Finish(otCryptoContext *aContext, uint8_t *aHash, uint16_t aHashSize)
{
Error error = kErrorNone;
psa_hash_operation_t *operation;
size_t hashSize;
VerifyOrExit(checkContext(aContext, sizeof(psa_hash_operation_t)), error = kErrorInvalidArgs);
VerifyOrExit(aHash != nullptr, error = kErrorInvalidArgs);
operation = static_cast<psa_hash_operation_t *>(aContext->mContext);
error = psaToOtError(psa_hash_finish(operation, aHash, aHashSize, &hashSize));
exit:
return error;
}
OT_TOOL_WEAK void otPlatCryptoRandomInit(void) { psa_crypto_init(); }
OT_TOOL_WEAK void otPlatCryptoRandomDeinit(void)
{
// Intentionally empty
}
OT_TOOL_WEAK otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize)
{
return psaToOtError(psa_generate_random(aBuffer, aSize));
}
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
OT_TOOL_WEAK otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
psa_key_id_t keyId = static_cast<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, keyId);
psa_set_key_bits(&attributes, 256);
status = psa_generate_key(&attributes, &keyId);
VerifyOrExit(status == PSA_SUCCESS);
exit:
psa_reset_key_attributes(&attributes);
return psaToOtError(status);
}
OT_TOOL_WEAK otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, otPlatCryptoEcdsaPublicKey *aPublicKey)
{
psa_status_t status;
size_t exportedLen;
uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE];
status = psa_export_public_key(aKeyRef, buffer, sizeof(buffer), &exportedLen);
VerifyOrExit(status == PSA_SUCCESS);
OT_ASSERT(exportedLen == sizeof(buffer));
memcpy(aPublicKey->m8, buffer + 1, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE);
exit:
return psaToOtError(status);
}
OT_TOOL_WEAK otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef,
const otPlatCryptoSha256Hash *aHash,
otPlatCryptoEcdsaSignature *aSignature)
{
psa_status_t status;
size_t signatureLen;
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, &signatureLen);
VerifyOrExit(status == PSA_SUCCESS);
OT_ASSERT(signatureLen == OT_CRYPTO_ECDSA_SIGNATURE_SIZE);
exit:
return psaToOtError(status);
}
OT_TOOL_WEAK otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey,
const otPlatCryptoSha256Hash *aHash,
const otPlatCryptoEcdsaSignature *aSignature)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t keyId = PSA_KEY_ID_NULL;
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), &keyId);
VerifyOrExit(status == PSA_SUCCESS);
status = psa_verify_hash(keyId, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, OT_CRYPTO_SHA256_HASH_SIZE,
aSignature->m8, OT_CRYPTO_ECDSA_SIGNATURE_SIZE);
VerifyOrExit(status == PSA_SUCCESS);
exit:
psa_reset_key_attributes(&attributes);
psa_destroy_key(keyId);
return psaToOtError(status);
}
OT_TOOL_WEAK 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);
VerifyOrExit(status == PSA_SUCCESS);
exit:
return psaToOtError(status);
}
#endif // #if OPENTHREAD_CONFIG_ECDSA_ENABLE
#endif // #if !OPENTHREAD_RADIO
#if OPENTHREAD_FTD
OT_TOOL_WEAK 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);
VerifyOrExit(status == PSA_SUCCESS);
status = psa_key_derivation_setup(&operation, algorithm);
VerifyOrExit(status == PSA_SUCCESS);
status = psa_key_derivation_input_integer(&operation, PSA_KEY_DERIVATION_INPUT_COST, aIterationCounter);
VerifyOrExit(status == PSA_SUCCESS);
status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SALT, aSalt, aSaltLen);
VerifyOrExit(status == PSA_SUCCESS);
status = psa_key_derivation_input_key(&operation, PSA_KEY_DERIVATION_INPUT_PASSWORD, key_id);
VerifyOrExit(status == PSA_SUCCESS);
status = psa_key_derivation_output_bytes(&operation, aKey, aKeyLen);
VerifyOrExit(status == PSA_SUCCESS);
exit:
psa_reset_key_attributes(&attributes);
psa_key_derivation_abort(&operation);
psa_destroy_key(key_id);
return psaToOtError(status);
}
#endif // #if OPENTHREAD_FTD
#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA