From 924dd1d1aef2f416db56a42d2ef8934376d61073 Mon Sep 17 00:00:00 2001 From: Thomas Cuyckens Date: Thu, 17 Sep 2026 07:58:29 +0200 Subject: [PATCH] [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. --- examples/platforms/simulation/crypto.c | 8 +++ include/openthread/instance.h | 2 +- include/openthread/platform/crypto.h | 15 +++++ src/core/config/platform.h | 11 +++ src/core/crypto/crypto_platform_mbedtls.cpp | 1 + src/core/crypto/crypto_platform_psa.cpp | 74 +++++++++++++++++---- src/core/crypto/storage.hpp | 4 +- tests/unit/test_platform.cpp | 2 + 8 files changed, 100 insertions(+), 17 deletions(-) diff --git a/examples/platforms/simulation/crypto.c b/examples/platforms/simulation/crypto.c index 20c151b46..f4aa8bc4c 100644 --- a/examples/platforms/simulation/crypto.c +++ b/examples/platforms/simulation/crypto.c @@ -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, diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 53265ae18..edff8f034 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -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 diff --git a/include/openthread/platform/crypto.h b/include/openthread/platform/crypto.h index 2de0b07d8..cd6318522 100644 --- a/include/openthread/platform/crypto.h +++ b/include/openthread/platform/crypto.h @@ -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. * diff --git a/src/core/config/platform.h b/src/core/config/platform.h index f191a89c5..6d3863c55 100644 --- a/src/core/config/platform.h +++ b/src/core/config/platform.h @@ -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 * diff --git a/src/core/crypto/crypto_platform_mbedtls.cpp b/src/core/crypto/crypto_platform_mbedtls.cpp index 1f1e0fbdf..e04d20665 100644 --- a/src/core/crypto/crypto_platform_mbedtls.cpp +++ b/src/core/crypto/crypto_platform_mbedtls.cpp @@ -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(&aConfig->mKey)); diff --git a/src/core/crypto/crypto_platform_psa.cpp b/src/core/crypto/crypto_platform_psa.cpp index b204b2308..7551db2c9 100644 --- a/src/core/crypto/crypto_platform_psa.cpp +++ b/src/core/crypto/crypto_platform_psa.cpp @@ -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(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(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(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(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(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(aKeyRef); + psa_status_t status = PSA_ERROR_INVALID_HANDLE; + psa_key_id_t keyId = static_cast(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); diff --git a/src/core/crypto/storage.hpp b/src/core/crypto/storage.hpp index 10ac5f14d..59d15c39f 100644 --- a/src/core/crypto/storage.hpp +++ b/src/core/crypto/storage.hpp @@ -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. diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index f9c171a44..54376ae92 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -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,