mirror of
https://github.com/espressif/openthread.git
synced 2026-08-17 07:59:51 +00:00
[dataset] simplify saving/reading of TLVs in/from secure storage (#9626)
This commit simplifies the handling of Dataset TLVs that need to be securely stored when the `PLATFORM_KEY_REFERENCES_ENABLE` feature is enabled. Two new methods are added to the `Dataset` class: - `SaveTlvInSecureStorageAndClearValue()` which saves the value of a given TLV type in secure storage and clears the TLV value by setting all value bytes to zero. - `ReadTlvFromSecureStorage()` which reads the TLV value back from secure storage and updates it in the `Dataset`. These methods are used by `DatasetLocal` to manage the set of TLVs that need to be securely stored defined by a constant array of `SecurelyStoredTlv` entries. Each entry provides the TLV type along with the associated `Crypto::Storage::KeyRef` to use for active or pending Dataset.
This commit is contained in:
@@ -605,5 +605,45 @@ void Dataset::ConvertToActive(void)
|
||||
|
||||
const char *Dataset::TypeToString(Type aType) { return (aType == kActive) ? "Active" : "Pending"; }
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
|
||||
void Dataset::SaveTlvInSecureStorageAndClearValue(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef)
|
||||
{
|
||||
using namespace ot::Crypto::Storage;
|
||||
|
||||
Tlv *tlv = GetTlv(aTlvType);
|
||||
|
||||
VerifyOrExit(tlv != nullptr);
|
||||
VerifyOrExit(tlv->GetLength() > 0);
|
||||
|
||||
SuccessOrAssert(ImportKey(aKeyRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent, tlv->GetValue(),
|
||||
tlv->GetLength()));
|
||||
|
||||
memset(tlv->GetValue(), 0, tlv->GetLength());
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Error Dataset::ReadTlvFromSecureStorage(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef)
|
||||
{
|
||||
using namespace ot::Crypto::Storage;
|
||||
|
||||
Error error = kErrorNone;
|
||||
Tlv *tlv = GetTlv(aTlvType);
|
||||
size_t readLength;
|
||||
|
||||
VerifyOrExit(tlv != nullptr);
|
||||
VerifyOrExit(tlv->GetLength() > 0);
|
||||
|
||||
SuccessOrExit(error = ExportKey(aKeyRef, tlv->GetValue(), tlv->GetLength(), readLength));
|
||||
VerifyOrExit(readLength == tlv->GetLength(), error = OT_ERROR_FAILED);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
|
||||
} // namespace MeshCoP
|
||||
} // namespace ot
|
||||
|
||||
@@ -918,6 +918,35 @@ public:
|
||||
*/
|
||||
static const char *TypeToString(Type aType);
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
|
||||
/**
|
||||
* Saves a given TLV value in secure storage and clears the TLV value by setting all value bytes to zero.
|
||||
*
|
||||
* If the Dataset does not contain the @p aTlvType, no action is performed.
|
||||
*
|
||||
* @param[in] aTlvType The TLV type.
|
||||
* @param[in] aKeyRef The `KeyRef` to use with secure storage.
|
||||
*
|
||||
*/
|
||||
void SaveTlvInSecureStorageAndClearValue(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef);
|
||||
|
||||
/**
|
||||
* Reads and updates a given TLV value in Dataset from secure storage.
|
||||
*
|
||||
* If the Dataset does not contain the @p aTlvType, no action is performed and `kErrorNone` is returned.
|
||||
*
|
||||
* @param[in] aTlvType The TLV type.
|
||||
* @param[in] aKeyRef The `KeyRef` to use with secure storage.
|
||||
*
|
||||
* @retval kErrorNone Successfully read the TLV value from secure storage and updated the Dataset.
|
||||
* @retval KErrorFailed Could not read the @aKeyRef from secure storage.
|
||||
*
|
||||
*/
|
||||
Error ReadTlvFromSecureStorage(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef);
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
|
||||
private:
|
||||
void RemoveTlv(Tlv *aTlv);
|
||||
|
||||
|
||||
@@ -218,112 +218,64 @@ exit:
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
|
||||
const DatasetLocal::SecurelyStoredTlv DatasetLocal::kSecurelyStoredTlvs[] = {
|
||||
{
|
||||
Tlv::kNetworkKey,
|
||||
Crypto::Storage::kActiveDatasetNetworkKeyRef,
|
||||
Crypto::Storage::kPendingDatasetNetworkKeyRef,
|
||||
},
|
||||
{
|
||||
Tlv::kPskc,
|
||||
Crypto::Storage::kActiveDatasetPskcRef,
|
||||
Crypto::Storage::kPendingDatasetPskcRef,
|
||||
},
|
||||
};
|
||||
|
||||
void DatasetLocal::DestroySecurelyStoredKeys(void) const
|
||||
{
|
||||
using namespace Crypto::Storage;
|
||||
|
||||
KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef;
|
||||
KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef;
|
||||
|
||||
// Destroy securely stored keys associated with the given operational dataset type.
|
||||
DestroyKey(networkKeyRef);
|
||||
DestroyKey(pskcRef);
|
||||
for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs)
|
||||
{
|
||||
Crypto::Storage::DestroyKey(entry.GetKeyRef(mType));
|
||||
}
|
||||
}
|
||||
|
||||
void DatasetLocal::MoveKeysToSecureStorage(Dataset &aDataset) const
|
||||
{
|
||||
using namespace Crypto::Storage;
|
||||
|
||||
KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef;
|
||||
KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef;
|
||||
NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv<NetworkKeyTlv>();
|
||||
PskcTlv *pskcTlv = aDataset.GetTlv<PskcTlv>();
|
||||
|
||||
if (networkKeyTlv != nullptr)
|
||||
for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs)
|
||||
{
|
||||
// If the dataset contains a network key, put it in the secure storage
|
||||
// and zero the corresponding TLV element.
|
||||
NetworkKey networkKey;
|
||||
SuccessOrAssert(ImportKey(networkKeyRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent,
|
||||
networkKeyTlv->GetNetworkKey().m8, NetworkKey::kSize));
|
||||
networkKey.Clear();
|
||||
networkKeyTlv->SetNetworkKey(networkKey);
|
||||
}
|
||||
|
||||
if (pskcTlv != nullptr)
|
||||
{
|
||||
// If the dataset contains a PSKC, put it in the secure storage and zero
|
||||
// the corresponding TLV element.
|
||||
Pskc pskc;
|
||||
SuccessOrAssert(ImportKey(pskcRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent,
|
||||
pskcTlv->GetPskc().m8, Pskc::kSize));
|
||||
pskc.Clear();
|
||||
pskcTlv->SetPskc(pskc);
|
||||
aDataset.SaveTlvInSecureStorageAndClearValue(entry.mTlvType, entry.GetKeyRef(mType));
|
||||
}
|
||||
}
|
||||
|
||||
void DatasetLocal::EmplaceSecurelyStoredKeys(Dataset &aDataset) const
|
||||
{
|
||||
using namespace Crypto::Storage;
|
||||
bool moveKeys = false;
|
||||
|
||||
KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef;
|
||||
KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef;
|
||||
NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv<NetworkKeyTlv>();
|
||||
PskcTlv *pskcTlv = aDataset.GetTlv<PskcTlv>();
|
||||
bool moveKeys = false;
|
||||
size_t keyLen;
|
||||
Error error;
|
||||
// If reading any of the TLVs fails, it indicates they are not yet
|
||||
// stored in secure storage and are still contained in the `Dataset`
|
||||
// read from `Settings`. In this case, we move the keys to secure
|
||||
// storage and then clear them from 'Settings'.
|
||||
|
||||
if (networkKeyTlv != nullptr)
|
||||
for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs)
|
||||
{
|
||||
// If the dataset contains a network key, its real value must have been moved to
|
||||
// the secure storage upon saving the dataset, so restore it back now.
|
||||
NetworkKey networkKey;
|
||||
error = ExportKey(networkKeyRef, networkKey.m8, NetworkKey::kSize, keyLen);
|
||||
|
||||
if (error != kErrorNone)
|
||||
if (aDataset.ReadTlvFromSecureStorage(entry.mTlvType, entry.GetKeyRef(mType)) != kErrorNone)
|
||||
{
|
||||
// If ExportKey fails, key is not in secure storage and is stored in settings
|
||||
moveKeys = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
OT_ASSERT(keyLen == NetworkKey::kSize);
|
||||
networkKeyTlv->SetNetworkKey(networkKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (pskcTlv != nullptr)
|
||||
{
|
||||
// If the dataset contains a PSKC, its real value must have been moved to
|
||||
// the secure storage upon saving the dataset, so restore it back now.
|
||||
Pskc pskc;
|
||||
error = ExportKey(pskcRef, pskc.m8, Pskc::kSize, keyLen);
|
||||
|
||||
if (error != kErrorNone)
|
||||
{
|
||||
// If ExportKey fails, key is not in secure storage and is stored in settings
|
||||
moveKeys = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
OT_ASSERT(keyLen == Pskc::kSize);
|
||||
pskcTlv->SetPskc(pskc);
|
||||
}
|
||||
}
|
||||
|
||||
if (moveKeys)
|
||||
{
|
||||
// Clear the networkkey and Pskc stored in the settings and move them to secure storage.
|
||||
// Store the network key and PSKC in the secure storage instead of settings.
|
||||
Dataset dataset;
|
||||
|
||||
dataset.Set(GetType(), aDataset);
|
||||
MoveKeysToSecureStorage(dataset);
|
||||
SuccessOrAssert(error = Get<Settings>().SaveOperationalDataset(mType, dataset));
|
||||
SuccessOrAssert(Get<Settings>().SaveOperationalDataset(mType, dataset));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
|
||||
} // namespace MeshCoP
|
||||
} // namespace ot
|
||||
|
||||
@@ -184,14 +184,29 @@ public:
|
||||
Error Save(const Dataset &aDataset);
|
||||
|
||||
private:
|
||||
bool IsActive(void) const { return (mType == Dataset::kActive); }
|
||||
void SetTimestamp(const Dataset &aDataset);
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
||||
struct SecurelyStoredTlv
|
||||
{
|
||||
Crypto::Storage::KeyRef GetKeyRef(Dataset::Type aType) const
|
||||
{
|
||||
return (aType == Dataset::kActive) ? mActiveKeyRef : mPendingKeyRef;
|
||||
}
|
||||
|
||||
Tlv::Type mTlvType;
|
||||
Crypto::Storage::KeyRef mActiveKeyRef;
|
||||
Crypto::Storage::KeyRef mPendingKeyRef;
|
||||
};
|
||||
|
||||
static const SecurelyStoredTlv kSecurelyStoredTlvs[];
|
||||
|
||||
void MoveKeysToSecureStorage(Dataset &aDataset) const;
|
||||
void DestroySecurelyStoredKeys(void) const;
|
||||
void EmplaceSecurelyStoredKeys(Dataset &aDataset) const;
|
||||
#endif
|
||||
|
||||
bool IsActive(void) const { return (mType == Dataset::kActive); }
|
||||
void SetTimestamp(const Dataset &aDataset);
|
||||
|
||||
Timestamp mTimestamp; ///< Active or Pending Timestamp
|
||||
TimeMilli mUpdateTime; ///< Local time last updated
|
||||
Dataset::Type mType; ///< Active or Pending
|
||||
|
||||
Reference in New Issue
Block a user