[crypto] validate aHeader and KeyRef inputs in platform crypto APIs (#13303)

Reject a null `aHeader` combined with non-zero `mHeaderLength` in
`otPlatCryptoAesCcmProcessOneShot()`, in both the mbedtls and PSA
backends.

Add `otPlatCryptoIsKeyRefValid()` so each crypto backend can define
what KeyRef values it accepts, and use it in the default PSA backend
at every entry point that takes a KeyRef (import/export/destroy/
has-key, AES, HMAC, HKDF, ECDSA) instead of relying solely on the
underlying PSA library to reject a bad handle.

Make the "invalid KeyRef" sentinel backend-configurable via the new
`OPENTHREAD_CONFIG_CRYPTO_INVALID_KEY_REF`, defaulting to
`PSA_KEY_ID_NULL` (0), so a backend other than the default PSA one
can reserve a different value as invalid without a generic core
header depending on any one backend's implementation file.
This commit is contained in:
Thomas Cuyckens
2026-09-16 22:58:29 -07:00
committed by GitHub
parent 4a8d334595
commit 924dd1d1ae
8 changed files with 100 additions and 17 deletions
+8
View File
@@ -38,6 +38,14 @@
// crypto key storage stubs
bool otPlatCryptoIsKeyRefValid(otCryptoKeyRef aKeyRef)
{
OT_UNUSED_VARIABLE(aKeyRef);
// No key ref is ever actually stored by this stub, so none is valid.
return false;
}
otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef,
otCryptoKeyType aKeyType,
otCryptoKeyAlgorithm aKeyAlgorithm,
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (621)
#define OPENTHREAD_API_VERSION (622)
/**
* @addtogroup api-instance
+15
View File
@@ -223,6 +223,21 @@ typedef struct otPlatCryptoEcdsaSignature otPlatCryptoEcdsaSignature;
*/
void otPlatCryptoInit(void);
/**
* Check whether a given key reference is valid.
*
* This API does not check whether a key is currently stored under @p aKeyRef. Instead, use `otPlatCryptoHasKey()` for
* existence checks.
*
* @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.
*
* @param[in] aKeyRef The Key Ref to check.
*
* @retval TRUE @p aKeyRef is valid.
* @retval FALSE @p aKeyRef is not valid.
*/
bool otPlatCryptoIsKeyRefValid(otCryptoKeyRef aKeyRef);
/**
* Import a key into PSA ITS.
*
+11
View File
@@ -161,6 +161,17 @@
(OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA)
#endif
/**
* @def OPENTHREAD_CONFIG_CRYPTO_INVALID_KEY_REF
*
* The sentinel `KeyRef` value that marks "no key set". Depends on the crypto backend in use. A backend whose
* platform reserves a different value as invalid can override this definition. Defaults to `PSA_KEY_ID_NULL`,
* as used by the default PSA backend.
*/
#ifndef OPENTHREAD_CONFIG_CRYPTO_INVALID_KEY_REF
#define OPENTHREAD_CONFIG_CRYPTO_INVALID_KEY_REF 0
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE
*
@@ -167,6 +167,7 @@ OT_TOOL_WEAK otError otPlatCryptoAesCcmProcessOneShot(bool
mbedtls_ccm_init(&ctx);
VerifyOrExit(aConfig != nullptr && aConfig->mNonce != nullptr && aData != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aHeader != nullptr || aConfig->mHeaderLength == 0, error = kErrorInvalidArgs);
{
const LiteralKey key(*static_cast<const Key *>(&aConfig->mKey));
+60 -14
View File
@@ -47,10 +47,10 @@
#include "common/debug.hpp"
#include "common/error.hpp"
#include "common/new.hpp"
#include "common/num_utils.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;
@@ -75,6 +75,7 @@ static Error PsaToOtError(psa_status_t aStatus)
error = kErrorNone;
break;
case PSA_ERROR_INVALID_ARGUMENT:
case PSA_ERROR_INVALID_HANDLE:
error = kErrorInvalidArgs;
break;
case PSA_ERROR_BUFFER_TOO_SMALL:
@@ -254,6 +255,12 @@ OT_TOOL_WEAK void *otPlatCryptoCAlloc(size_t aNum, size_t aSize) { return otPlat
OT_TOOL_WEAK void otPlatCryptoFree(void *aPtr) { otPlatFree(aPtr); }
#endif
OT_TOOL_WEAK bool otPlatCryptoIsKeyRefValid(otCryptoKeyRef aKeyRef)
{
return IsValueInRange(aKeyRef, PSA_KEY_ID_USER_MIN, PSA_KEY_ID_USER_MAX) ||
IsValueInRange(aKeyRef, PSA_KEY_ID_VENDOR_MIN, PSA_KEY_ID_VENDOR_MAX);
}
OT_TOOL_WEAK otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef,
otCryptoKeyType aKeyType,
otCryptoKeyAlgorithm aKeyAlgorithm,
@@ -290,6 +297,9 @@ OT_TOOL_WEAK otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef,
switch (aKeyPersistence)
{
case OT_CRYPTO_KEY_STORAGE_PERSISTENT:
// For persistent storage, the caller supplies the target `KeyRef` via `*aKeyRef`, so it must be validated
// here. For volatile storage, `*aKeyRef` is a pure output populated by `psa_import_key()` below.
VerifyOrExit(otPlatCryptoIsKeyRefValid(*aKeyRef), error = kErrorInvalidArgs);
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attributes, *aKeyRef);
break;
@@ -313,6 +323,7 @@ OT_TOOL_WEAK otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef, uint8_t *aBuf
Error error = kErrorNone;
VerifyOrExit(aBuffer != nullptr && aKeyLen != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKeyRef), error = kErrorInvalidArgs);
error = PsaToOtError(psa_export_key(aKeyRef, aBuffer, aBufferLen, aKeyLen));
@@ -320,16 +331,28 @@ exit:
return error;
}
OT_TOOL_WEAK otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef) { return PsaToOtError(psa_destroy_key(aKeyRef)); }
OT_TOOL_WEAK otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef)
{
Error error = kErrorNone;
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKeyRef), error = kErrorInvalidArgs);
error = PsaToOtError(psa_destroy_key(aKeyRef));
exit:
return error;
}
OT_TOOL_WEAK bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_status_t status;
psa_status_t status = PSA_ERROR_INVALID_HANDLE;
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKeyRef));
status = psa_get_key_attributes(aKeyRef, &attributes);
psa_reset_key_attributes(&attributes);
exit:
return status == PSA_SUCCESS;
}
@@ -341,7 +364,7 @@ OT_TOOL_WEAK otError otPlatCryptoAesInit(otCryptoContext *aContext)
SuccessOrExit(error = ValidateContext(aContext, sizeof(psa_key_id_t)));
keyRef = static_cast<psa_key_id_t *>(aContext->mContext);
*keyRef = PSA_KEY_ID_NULL;
*keyRef = PSA_KEY_ID_NULL; // Initialize the key reference to an invalid value.
exit:
return error;
@@ -353,7 +376,8 @@ OT_TOOL_WEAK otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCr
psa_key_id_t *keyRef;
SuccessOrExit(error = ValidateContext(aContext, sizeof(psa_key_id_t)));
VerifyOrExit(aKey != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aKey != nullptr && aKey->mKey == nullptr, error = kErrorInvalidArgs);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKey->mKeyRef), error = kErrorInvalidArgs);
keyRef = static_cast<psa_key_id_t *>(aContext->mContext);
*keyRef = aKey->mKeyRef;
@@ -374,6 +398,7 @@ OT_TOOL_WEAK otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uin
VerifyOrExit(aInput != nullptr && aOutput != nullptr, error = kErrorInvalidArgs);
keyRef = static_cast<psa_key_id_t *>(aContext->mContext);
VerifyOrExit(otPlatCryptoIsKeyRefValid(*keyRef), error = kErrorInvalidArgs);
status = psa_cipher_encrypt(*keyRef, PSA_ALG_ECB_NO_PADDING, aInput, blockSize, aOutput, blockSize, &cipherLen);
error = PsaToOtError(status);
@@ -403,6 +428,8 @@ OT_TOOL_WEAK otError otPlatCryptoAesCcmProcessOneShot(bool
VerifyOrExit(aConfig != nullptr && aConfig->mNonce != nullptr && aData != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aConfig->mKey.mKey == nullptr, error = kErrorInvalidArgs);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aConfig->mKey.mKeyRef), error = kErrorInvalidArgs);
VerifyOrExit(aHeader != nullptr || aConfig->mHeaderLength == 0, error = kErrorInvalidArgs);
algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, aConfig->mTagLength);
@@ -471,7 +498,8 @@ OT_TOOL_WEAK otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, cons
psa_mac_operation_t *operation;
SuccessOrExit(error = ValidateContext(aContext, sizeof(psa_mac_operation_t)));
VerifyOrExit(aKey != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aKey != nullptr && aKey->mKey == nullptr, error = kErrorInvalidArgs);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKey->mKeyRef), error = kErrorInvalidArgs);
operation = static_cast<psa_mac_operation_t *>(aContext->mContext);
@@ -547,7 +575,8 @@ OT_TOOL_WEAK otError otPlatCryptoHkdfExtract(otCryptoContext *aContext,
uint8_t keyBuffer[kBufferSize];
SuccessOrExit(error = ValidateContext(aContext, sizeof(psa_key_derivation_operation_t)));
VerifyOrExit(aInputKey != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aInputKey != nullptr && aInputKey->mKey == nullptr, error = kErrorInvalidArgs);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aInputKey->mKeyRef), error = kErrorInvalidArgs);
operation = static_cast<psa_key_derivation_operation_t *>(aContext->mContext);
@@ -580,7 +609,10 @@ OT_TOOL_WEAK otError otPlatCryptoHkdfExtract(otCryptoContext *aContext,
exit:
psa_reset_key_attributes(&attributes);
psa_destroy_key(keyRef);
if (otPlatCryptoIsKeyRefValid(keyRef))
{
psa_destroy_key(keyRef);
}
return error;
}
@@ -722,8 +754,10 @@ OT_TOOL_WEAK otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize)
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_status_t status = PSA_ERROR_INVALID_HANDLE;
psa_key_id_t keyId = static_cast<psa_key_id_t>(aKeyRef);
VerifyOrExit(otPlatCryptoIsKeyRefValid(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));
@@ -743,10 +777,13 @@ exit:
OT_TOOL_WEAK otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, otPlatCryptoEcdsaPublicKey *aPublicKey)
{
psa_status_t status;
psa_status_t status = PSA_ERROR_INVALID_HANDLE;
size_t exportedLen;
uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE];
VerifyOrExit(aPublicKey != nullptr, status = PSA_ERROR_INVALID_ARGUMENT);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKeyRef));
status = psa_export_public_key(aKeyRef, buffer, sizeof(buffer), &exportedLen);
VerifyOrExit(status == PSA_SUCCESS);
@@ -761,9 +798,12 @@ OT_TOOL_WEAK otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef
const otPlatCryptoSha256Hash *aHash,
otPlatCryptoEcdsaSignature *aSignature)
{
psa_status_t status;
psa_status_t status = PSA_ERROR_INVALID_HANDLE;
size_t signatureLen;
VerifyOrExit(aHash != nullptr && aSignature != nullptr, status = PSA_ERROR_INVALID_ARGUMENT);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKeyRef));
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);
@@ -780,9 +820,12 @@ OT_TOOL_WEAK otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *a
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t keyId = PSA_KEY_ID_NULL;
psa_status_t status;
psa_status_t status = PSA_ERROR_INVALID_HANDLE;
uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE];
VerifyOrExit(aPublicKey != nullptr && aHash != nullptr && aSignature != nullptr,
status = PSA_ERROR_INVALID_ARGUMENT);
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));
@@ -811,7 +854,10 @@ OT_TOOL_WEAK otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef
const otPlatCryptoSha256Hash *aHash,
const otPlatCryptoEcdsaSignature *aSignature)
{
psa_status_t status;
psa_status_t status = PSA_ERROR_INVALID_HANDLE;
VerifyOrExit(aHash != nullptr && aSignature != nullptr, status = PSA_ERROR_INVALID_ARGUMENT);
VerifyOrExit(otPlatCryptoIsKeyRefValid(aKeyRef));
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);
+2 -2
View File
@@ -99,7 +99,7 @@ enum StorageType : uint8_t
*/
typedef otCryptoKeyRef KeyRef;
constexpr KeyRef kInvalidKeyRef = 0x80000000; ///< Invalid `KeyRef` value (PSA_KEY_ID_VENDOR_MAX + 1).
constexpr KeyRef kInvalidKeyRef = OPENTHREAD_CONFIG_CRYPTO_INVALID_KEY_REF; ///< Sentinel marking "no key set".
#if OPENTHREAD_FTD || OPENTHREAD_MTD
@@ -190,7 +190,7 @@ private:
* @retval TRUE If @p aKeyRef is valid.
* @retval FALSE If @p aKeyRef is not valid.
*/
inline bool IsKeyRefValid(KeyRef aKeyRef) { return (aKeyRef < kInvalidKeyRef); }
inline bool IsKeyRefValid(KeyRef aKeyRef) { return otPlatCryptoIsKeyRefValid(aKeyRef); }
/**
* Saves a key in secure storage.
+2
View File
@@ -544,6 +544,8 @@ OT_TOOL_WEAK void otPlatInfraIfDhcp6PdClientSend(otInstance *aInstance,
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
bool otPlatCryptoIsKeyRefValid(otCryptoKeyRef aKeyRef) { return (aKeyRef != OPENTHREAD_CONFIG_CRYPTO_INVALID_KEY_REF); }
otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef,
otCryptoKeyType aKeyType,
otCryptoKeyAlgorithm aKeyAlgorithm,