From 14129729314cac6aced7148d293e6f83c5d9bfb5 Mon Sep 17 00:00:00 2001 From: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:27:55 -0500 Subject: [PATCH] [ICD] Refactor ICD Monitoring Table to support HMAC handle and AES key handle (#30881) * Rename key to keyHandle * Refactor ICD Monitoring Table to not assume key type Add Hmac Key Handle to ICD Monitoring Table * remove utility functions * replace helpers with memcpy * Add test validation for Hmac key handle * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Clarify comments --------- Co-authored-by: Boris Zbarsky --- src/app/icd/ICDCheckInSender.cpp | 6 +- src/app/icd/ICDCheckInSender.h | 2 +- src/app/icd/ICDMonitoringTable.cpp | 84 ++++++++++++++++++------ src/app/icd/ICDMonitoringTable.h | 5 +- src/app/tests/TestICDMonitoringTable.cpp | 52 ++++++++++++++- src/crypto/SessionKeystore.h | 28 +++++--- 6 files changed, 143 insertions(+), 34 deletions(-) diff --git a/src/app/icd/ICDCheckInSender.cpp b/src/app/icd/ICDCheckInSender.cpp index 173a1df50ac037..ea827c2c7ac6c1 100644 --- a/src/app/icd/ICDCheckInSender.cpp +++ b/src/app/icd/ICDCheckInSender.cpp @@ -61,7 +61,7 @@ CHIP_ERROR ICDCheckInSender::SendCheckInMsg(const Transport::PeerAddress & addr) VerifyOrReturnError(!buffer.IsNull(), CHIP_ERROR_NO_MEMORY); MutableByteSpan output{ buffer->Start(), buffer->MaxDataLength() }; - ReturnErrorOnFailure(CheckinMessage::GenerateCheckinMessagePayload(mKey, mICDCounter, ByteSpan(), output)); + ReturnErrorOnFailure(CheckinMessage::GenerateCheckinMessagePayload(mAesKeyHandle, mICDCounter, ByteSpan(), output)); buffer->SetDataLength(static_cast(output.size())); VerifyOrReturnError(mExchangeManager->GetSessionManager() != nullptr, CHIP_ERROR_INTERNAL); @@ -89,8 +89,8 @@ CHIP_ERROR ICDCheckInSender::RequestResolve(ICDMonitoringEntry & entry, FabricTa AddressResolve::NodeLookupRequest request(peerId); - memcpy(mKey.AsMutable(), entry.key.As(), - sizeof(Crypto::Symmetric128BitsKeyByteArray)); + memcpy(mAesKeyHandle.AsMutable(), + entry.aesKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); CHIP_ERROR err = AddressResolve::Resolver::Instance().LookupNode(request, mAddressLookupHandle); diff --git a/src/app/icd/ICDCheckInSender.h b/src/app/icd/ICDCheckInSender.h index 0055d66804d0fb..b6e2dfa2212f92 100644 --- a/src/app/icd/ICDCheckInSender.h +++ b/src/app/icd/ICDCheckInSender.h @@ -50,7 +50,7 @@ class ICDCheckInSender : public AddressResolve::NodeListener Messaging::ExchangeManager * mExchangeManager = nullptr; - Crypto::Aes128KeyHandle mKey = Crypto::Aes128KeyHandle(); + Crypto::Aes128KeyHandle mAesKeyHandle = Crypto::Aes128KeyHandle(); uint32_t mICDCounter = 0; }; diff --git a/src/app/icd/ICDMonitoringTable.cpp b/src/app/icd/ICDMonitoringTable.cpp index 83a06c4f35ad80..57e6fa265821f1 100644 --- a/src/app/icd/ICDMonitoringTable.cpp +++ b/src/app/icd/ICDMonitoringTable.cpp @@ -25,7 +25,8 @@ enum class Fields : uint8_t { kCheckInNodeID = 1, kMonitoredSubject = 2, - kKey = 3, + kAesKeyHandle = 3, + kHmacKeyHandle = 4, }; CHIP_ERROR ICDMonitoringEntry::UpdateKey(StorageKeyName & skey) @@ -42,8 +43,12 @@ CHIP_ERROR ICDMonitoringEntry::Serialize(TLV::TLVWriter & writer) const ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kCheckInNodeID), checkInNodeID)); ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kMonitoredSubject), monitoredSubject)); - ByteSpan buf(key.As()); - ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kKey), buf)); + ByteSpan aesKeybuf(aesKeyHandle.As()); + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kAesKeyHandle), aesKeybuf)); + + ByteSpan hmacKeybuf(hmacKeyHandle.As()); + ReturnErrorOnFailure(writer.Put(TLV::ContextTag(Fields::kHmacKeyHandle), hmacKeybuf)); + ReturnErrorOnFailure(writer.EndContainer(outer)); return CHIP_NO_ERROR; } @@ -69,18 +74,38 @@ CHIP_ERROR ICDMonitoringEntry::Deserialize(TLV::TLVReader & reader) case to_underlying(Fields::kMonitoredSubject): ReturnErrorOnFailure(reader.Get(monitoredSubject)); break; - case to_underlying(Fields::kKey): { - ByteSpan buf(key.AsMutable()); + case to_underlying(Fields::kAesKeyHandle): { + ByteSpan buf; ReturnErrorOnFailure(reader.Get(buf)); // Since we are storing either the raw key or a key ID, we must // simply copy the data as is in the keyHandle. - // Calling SetKey here would create another key in storage and will cause - // key leakage in some implementation. - memcpy(key.AsMutable(), buf.data(), + // Calling SetKey here would create another keyHandle in storage and will cause + // key leaks in some implementations. + memcpy(aesKeyHandle.AsMutable(), buf.data(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); keyHandleValid = true; } break; + case to_underlying(Fields::kHmacKeyHandle): { + ByteSpan buf; + CHIP_ERROR error = reader.Get(buf); + + if (error != CHIP_NO_ERROR) + { + // If retreiving the hmac key handle failed, we need to set an invalid key handle + // even if the AesKeyHandle is valid. + keyHandleValid = false; + return error; + } + + // Since we are storing either the raw key or a key ID, we must + // simply copy the data as is in the keyHandle. + // Calling SetKey here would create another keyHandle in storage and will cause + // key leaks in some implementations. + memcpy(hmacKeyHandle.AsMutable(), buf.data(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)); + } + break; default: break; } @@ -108,16 +133,29 @@ CHIP_ERROR ICDMonitoringEntry::SetKey(ByteSpan keyData) Crypto::Symmetric128BitsKeyByteArray keyMaterial; memcpy(keyMaterial, keyData.data(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); - ReturnErrorOnFailure(symmetricKeystore->CreateKey(keyMaterial, key)); - keyHandleValid = true; + // TODO - Add function to set PSA key lifetime + ReturnErrorOnFailure(symmetricKeystore->CreateKey(keyMaterial, aesKeyHandle)); + CHIP_ERROR error = symmetricKeystore->CreateKey(keyMaterial, hmacKeyHandle); - return CHIP_NO_ERROR; + if (error == CHIP_NO_ERROR) + { + // If the creation of the HmacKeyHandle succeeds, both key handles are valid + keyHandleValid = true; + } + else + { + // Creation of the HmacKeyHandle failed, we need to delete the AesKeyHandle to avoid a key leak + symmetricKeystore->DestroyKey(this->aesKeyHandle); + } + + return error; } CHIP_ERROR ICDMonitoringEntry::DeleteKey() { VerifyOrReturnError(symmetricKeystore != nullptr, CHIP_ERROR_INTERNAL); - symmetricKeystore->DestroyKey(this->key); + symmetricKeystore->DestroyKey(this->aesKeyHandle); + symmetricKeystore->DestroyKey(this->hmacKeyHandle); keyHandleValid = false; return CHIP_NO_ERROR; } @@ -141,7 +179,7 @@ bool ICDMonitoringEntry::IsKeyEquivalent(ByteSpan keyData) uint64_t data = Crypto::GetRandU64(), validation, encrypted; validation = data; - err = Crypto::AES_CCM_encrypt(reinterpret_cast(&data), sizeof(data), nullptr, 0, tempEntry.key, aead, + err = Crypto::AES_CCM_encrypt(reinterpret_cast(&data), sizeof(data), nullptr, 0, tempEntry.aesKeyHandle, aead, Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, reinterpret_cast(&encrypted), mic, Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES); @@ -149,7 +187,7 @@ bool ICDMonitoringEntry::IsKeyEquivalent(ByteSpan keyData) if (err == CHIP_NO_ERROR) { err = Crypto::AES_CCM_decrypt(reinterpret_cast(&encrypted), sizeof(encrypted), nullptr, 0, mic, - Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, key, aead, + Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, aesKeyHandle, aead, Crypto::CHIP_CRYPTO_AEAD_NONCE_LENGTH_BYTES, reinterpret_cast(&data)); } tempEntry.DeleteKey(); @@ -175,7 +213,11 @@ ICDMonitoringEntry & ICDMonitoringEntry::operator=(const ICDMonitoringEntry & ic index = icdMonitoringEntry.index; keyHandleValid = icdMonitoringEntry.keyHandleValid; symmetricKeystore = icdMonitoringEntry.symmetricKeystore; - memcpy(key.AsMutable(), icdMonitoringEntry.key.As(), + memcpy(aesKeyHandle.AsMutable(), + icdMonitoringEntry.aesKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)); + memcpy(hmacKeyHandle.AsMutable(), + icdMonitoringEntry.hmacKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); return *this; @@ -211,12 +253,16 @@ CHIP_ERROR ICDMonitoringTable::Set(uint16_t index, const ICDMonitoringEntry & en VerifyOrReturnError(kUndefinedNodeId != entry.checkInNodeID, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(kUndefinedNodeId != entry.monitoredSubject, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(entry.keyHandleValid, CHIP_ERROR_INVALID_ARGUMENT); + ICDMonitoringEntry e(this->mFabric, index); e.checkInNodeID = entry.checkInNodeID; e.monitoredSubject = entry.monitoredSubject; e.index = index; - memcpy(e.key.AsMutable(), entry.key.As(), - sizeof(Crypto::Symmetric128BitsKeyByteArray)); + + memcpy(e.aesKeyHandle.AsMutable(), + entry.aesKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); + memcpy(e.hmacKeyHandle.AsMutable(), + entry.hmacKeyHandle.As(), sizeof(Crypto::Symmetric128BitsKeyByteArray)); return e.Save(this->mStorage); } @@ -225,8 +271,8 @@ CHIP_ERROR ICDMonitoringTable::Remove(uint16_t index) { ICDMonitoringEntry entry(mSymmetricKeystore, this->mFabric); - // Retrieve entry and delete the key first as to not - // cause any key leakage. + // Retrieve entry and delete the keyHandle first as to not + // cause any key leaks. this->Get(index, entry); ReturnErrorOnFailure(entry.DeleteKey()); diff --git a/src/app/icd/ICDMonitoringTable.h b/src/app/icd/ICDMonitoringTable.h index c97703b226aa75..e996d0c757cc23 100644 --- a/src/app/icd/ICDMonitoringTable.h +++ b/src/app/icd/ICDMonitoringTable.h @@ -33,7 +33,7 @@ using SymmetricKeystore = SessionKeystore; namespace chip { -inline constexpr size_t kICDMonitoringBufferSize = 40; +inline constexpr size_t kICDMonitoringBufferSize = 60; struct ICDMonitoringEntry : public PersistentData { @@ -104,7 +104,8 @@ struct ICDMonitoringEntry : public PersistentData chip::FabricIndex fabricIndex = kUndefinedFabricIndex; chip::NodeId checkInNodeID = kUndefinedNodeId; uint64_t monitoredSubject = static_cast(0); - Crypto::Aes128KeyHandle key = Crypto::Aes128KeyHandle(); + Crypto::Aes128KeyHandle aesKeyHandle = Crypto::Aes128KeyHandle(); + Crypto::Hmac128KeyHandle hmacKeyHandle = Crypto::Hmac128KeyHandle(); bool keyHandleValid = false; uint16_t index = 0; Crypto::SymmetricKeystore * symmetricKeystore = nullptr; diff --git a/src/app/tests/TestICDMonitoringTable.cpp b/src/app/tests/TestICDMonitoringTable.cpp index 170ece20e1778d..1a6460e31640e3 100644 --- a/src/app/tests/TestICDMonitoringTable.cpp +++ b/src/app/tests/TestICDMonitoringTable.cpp @@ -155,11 +155,16 @@ void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) // Retrieve first entry err = loading.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry1.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Retrieve second entry err = loading.Get(1, entry); @@ -168,6 +173,10 @@ void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry2.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // No more entries err = loading.Get(2, entry); @@ -176,6 +185,7 @@ void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) // Remove first entry saving.Remove(0); + ICDMonitoringEntry entry4(&keystore); entry4.checkInNodeID = kClientNodeId13; entry4.monitoredSubject = kClientNodeId11; @@ -190,6 +200,10 @@ void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry2.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Retrieve second entry err = loading.Get(1, entry); @@ -198,6 +212,10 @@ void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, kClientNodeId13 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1b))); + NL_TEST_ASSERT(aSuite, + memcmp(entry4.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); } void TestSaveAllInvalidRegistrationValues(nlTestSuite * aSuite, void * aContext) @@ -296,14 +314,22 @@ void TestSaveLoadRegistrationValueForMultipleFabrics(nlTestSuite * aSuite, void NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry1.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); - // Retrieve fabric2, second entry + // Retrieve fabric1, second entry err = table1.Get(1, entry); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1b))); + NL_TEST_ASSERT(aSuite, + memcmp(entry2.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Retrieve fabric2, first entry err = table2.Get(0, entry); @@ -312,6 +338,10 @@ void TestSaveLoadRegistrationValueForMultipleFabrics(nlTestSuite * aSuite, void NL_TEST_ASSERT(aSuite, kClientNodeId21 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId22 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry3.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); } void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) @@ -358,6 +388,10 @@ void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry1.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Retrieve second entry (not modified) err = table1.Get(1, entry); @@ -366,6 +400,10 @@ void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry2.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Remove (existing) err = table1.Remove(0); @@ -381,6 +419,10 @@ void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer2a))); + NL_TEST_ASSERT(aSuite, + memcmp(entry2.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Retrieve fabric2, first entry err = table2.Get(0, entry); @@ -389,6 +431,10 @@ void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) NL_TEST_ASSERT(aSuite, kClientNodeId21 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId22 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1b))); + NL_TEST_ASSERT(aSuite, + memcmp(entry3.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Remove all (fabric 1) err = table1.RemoveAll(); @@ -404,6 +450,10 @@ void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) NL_TEST_ASSERT(aSuite, kClientNodeId21 == entry.checkInNodeID); NL_TEST_ASSERT(aSuite, kClientNodeId22 == entry.monitoredSubject); NL_TEST_ASSERT(aSuite, entry.IsKeyEquivalent(ByteSpan(kKeyBuffer1b))); + NL_TEST_ASSERT(aSuite, + memcmp(entry3.hmacKeyHandle.As(), + entry.hmacKeyHandle.As(), + sizeof(Crypto::Symmetric128BitsKeyByteArray)) == 0); // Remove all (fabric 2) err = table2.RemoveAll(); diff --git a/src/crypto/SessionKeystore.h b/src/crypto/SessionKeystore.h index dbf0ea3996bfe2..b5d1a26e30e6a9 100644 --- a/src/crypto/SessionKeystore.h +++ b/src/crypto/SessionKeystore.h @@ -29,12 +29,20 @@ namespace Crypto { * The session keystore interface provides an abstraction that allows the application to store * session keys in a secure environment. It uses the concept of key handles that isolate the * application from the actual key material. + * + * @note Refactor has begun to refactor this API into two disctinct APIs : SymmetrycKeyStore & SessionKeyDerivation + * Work has not been completed so the SessionKeystore has APIs that shouldn't go together for the time being + * The SessionKeystore APIs are split into two sections, one for each futur API. */ class SessionKeystore { public: virtual ~SessionKeystore() {} + /**************************** + * SymmetricKeyStore APIs + *****************************/ + /** * @brief Import raw key material and return a key handle for a key that be used to do AES 128 encryption. * @@ -59,6 +67,18 @@ class SessionKeystore */ virtual CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key) = 0; + /** + * @brief Destroy key. + * + * The method can take an uninitialized handle in which case it is a no-op. + * As a result of calling this method, the handle is put in the uninitialized state. + */ + virtual void DestroyKey(Symmetric128BitsKeyHandle & key) = 0; + + /**************************** + * SessionKeyDerivation APIs + *****************************/ + /** * @brief Derive key from a shared secret. * @@ -83,14 +103,6 @@ class SessionKeystore virtual CHIP_ERROR DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey, AttestationChallenge & attestationChallenge) = 0; - - /** - * @brief Destroy key. - * - * The method can take an uninitialized handle in which case it is a no-op. - * As a result of calling this method, the handle is put in the uninitialized state. - */ - virtual void DestroyKey(Symmetric128BitsKeyHandle & key) = 0; }; /**