| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements unit tests for the CASESession implementation. |
| */ |
| |
| #include <errno.h> |
| #include <nlunit-test.h> |
| |
| #include <credentials/CHIPCert.h> |
| #include <lib/core/CHIPCore.h> |
| #include <lib/core/CHIPSafeCasts.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/ScopedBuffer.h> |
| #include <lib/support/UnitTestRegistration.h> |
| #include <messaging/tests/MessagingContext.h> |
| #include <protocols/secure_channel/CASEServer.h> |
| #include <protocols/secure_channel/CASESession.h> |
| #include <stdarg.h> |
| #include <transport/raw/tests/NetworkTestHelpers.h> |
| |
| #include "credentials/tests/CHIPCert_test_vectors.h" |
| |
| using namespace chip; |
| using namespace Credentials; |
| using namespace TestCerts; |
| |
| using namespace chip; |
| using namespace chip::Inet; |
| using namespace chip::Transport; |
| using namespace chip::Messaging; |
| using namespace chip::Protocols; |
| |
| using TestContext = chip::Test::MessagingContext; |
| |
| namespace { |
| TransportMgrBase gTransportMgr; |
| Test::LoopbackTransport gLoopback; |
| chip::Test::IOContext gIOContext; |
| |
| FabricTable gCommissionerFabrics; |
| FabricIndex gCommissionerFabricIndex; |
| FabricTable gDeviceFabrics; |
| FabricIndex gDeviceFabricIndex; |
| |
| NodeId Node01_01 = 0xDEDEDEDE00010001; |
| } // namespace |
| |
| enum |
| { |
| kStandardCertsCount = 3, |
| }; |
| |
| class TestCASESecurePairingDelegate : public SessionEstablishmentDelegate |
| { |
| public: |
| void OnSessionEstablishmentError(CHIP_ERROR error) override { mNumPairingErrors++; } |
| |
| void OnSessionEstablished() override { mNumPairingComplete++; } |
| |
| uint32_t mNumPairingErrors = 0; |
| uint32_t mNumPairingComplete = 0; |
| }; |
| |
| class TestCASESessionIPK : public CASESession |
| { |
| protected: |
| ByteSpan * GetIPKList() const override |
| { |
| // TODO: Remove this list. Replace it with an actual method to retrieve an IPK list (e.g. from a Crypto Store API) |
| static uint8_t sIPKList[][kIPKSize] = { |
| { 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, |
| 0x1D }, /* Corresponds to the FabricID for the Node01_01 Test Vector */ |
| }; |
| static ByteSpan ipkListSpan[] = { ByteSpan(sIPKList[0]) }; |
| return ipkListSpan; |
| } |
| size_t GetIPKListEntries() const override { return 1; } |
| }; |
| |
| class TestCASEServerIPK : public CASEServer |
| { |
| public: |
| TestCASESessionIPK & GetSession() override { return mPairingSession; } |
| |
| private: |
| TestCASESessionIPK mPairingSession; |
| }; |
| |
| static CHIP_ERROR InitCredentialSets() |
| { |
| FabricInfo commissionerFabric; |
| |
| P256SerializedKeypair opKeysSerialized; |
| memcpy((uint8_t *) (opKeysSerialized), sTestCert_Node01_01_PublicKey, sTestCert_Node01_01_PublicKey_Len); |
| memcpy((uint8_t *) (opKeysSerialized) + sTestCert_Node01_01_PublicKey_Len, sTestCert_Node01_01_PrivateKey, |
| sTestCert_Node01_01_PrivateKey_Len); |
| |
| ReturnErrorOnFailure(opKeysSerialized.SetLength(sTestCert_Node01_01_PublicKey_Len + sTestCert_Node01_01_PrivateKey_Len)); |
| |
| P256Keypair opKey; |
| ReturnErrorOnFailure(opKey.Deserialize(opKeysSerialized)); |
| ReturnErrorOnFailure(commissionerFabric.SetEphemeralKey(&opKey)); |
| |
| ReturnErrorOnFailure(commissionerFabric.SetRootCert(ByteSpan(sTestCert_Root01_Chip, sTestCert_Root01_Chip_Len))); |
| ReturnErrorOnFailure(commissionerFabric.SetICACert(ByteSpan(sTestCert_ICA01_Chip, sTestCert_ICA01_Chip_Len))); |
| ReturnErrorOnFailure(commissionerFabric.SetNOCCert(ByteSpan(sTestCert_Node01_01_Chip, sTestCert_Node01_01_Chip_Len))); |
| |
| ReturnErrorOnFailure(gCommissionerFabrics.AddNewFabric(commissionerFabric, &gCommissionerFabricIndex)); |
| |
| FabricInfo deviceFabric; |
| |
| memcpy((uint8_t *) (opKeysSerialized), sTestCert_Node01_01_PublicKey, sTestCert_Node01_01_PublicKey_Len); |
| memcpy((uint8_t *) (opKeysSerialized) + sTestCert_Node01_01_PublicKey_Len, sTestCert_Node01_01_PrivateKey, |
| sTestCert_Node01_01_PrivateKey_Len); |
| |
| ReturnErrorOnFailure(opKeysSerialized.SetLength(sTestCert_Node01_01_PublicKey_Len + sTestCert_Node01_01_PrivateKey_Len)); |
| |
| ReturnErrorOnFailure(opKey.Deserialize(opKeysSerialized)); |
| ReturnErrorOnFailure(deviceFabric.SetEphemeralKey(&opKey)); |
| |
| ReturnErrorOnFailure(deviceFabric.SetRootCert(ByteSpan(sTestCert_Root01_Chip, sTestCert_Root01_Chip_Len))); |
| ReturnErrorOnFailure(deviceFabric.SetICACert(ByteSpan(sTestCert_ICA01_Chip, sTestCert_ICA01_Chip_Len))); |
| ReturnErrorOnFailure(deviceFabric.SetNOCCert(ByteSpan(sTestCert_Node01_01_Chip, sTestCert_Node01_01_Chip_Len))); |
| |
| ReturnErrorOnFailure(gDeviceFabrics.AddNewFabric(deviceFabric, &gDeviceFabricIndex)); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void CASE_SecurePairingWaitTest(nlTestSuite * inSuite, void * inContext) |
| { |
| // Test all combinations of invalid parameters |
| TestCASESecurePairingDelegate delegate; |
| TestCASESessionIPK pairing; |
| FabricTable fabrics; |
| |
| NL_TEST_ASSERT(inSuite, pairing.ListenForSessionEstablishment(0, nullptr, nullptr) == CHIP_ERROR_INVALID_ARGUMENT); |
| NL_TEST_ASSERT(inSuite, pairing.ListenForSessionEstablishment(0, nullptr, &delegate) == CHIP_ERROR_INVALID_ARGUMENT); |
| NL_TEST_ASSERT(inSuite, pairing.ListenForSessionEstablishment(0, &fabrics, &delegate) == CHIP_NO_ERROR); |
| } |
| |
| void CASE_SecurePairingStartTest(nlTestSuite * inSuite, void * inContext) |
| { |
| TestContext & ctx = *reinterpret_cast<TestContext *>(inContext); |
| |
| // Test all combinations of invalid parameters |
| TestCASESecurePairingDelegate delegate; |
| CASESession pairing; |
| FabricInfo * fabric = gCommissionerFabrics.FindFabricWithIndex(gCommissionerFabricIndex); |
| NL_TEST_ASSERT(inSuite, fabric != nullptr); |
| |
| NL_TEST_ASSERT(inSuite, pairing.MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| ExchangeContext * context = ctx.NewUnauthenticatedExchangeToBob(&pairing); |
| |
| NL_TEST_ASSERT(inSuite, |
| pairing.EstablishSession(Transport::PeerAddress(Transport::Type::kBle), nullptr, Node01_01, 0, nullptr, |
| nullptr) != CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, |
| pairing.EstablishSession(Transport::PeerAddress(Transport::Type::kBle), fabric, Node01_01, 0, nullptr, |
| nullptr) != CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, |
| pairing.EstablishSession(Transport::PeerAddress(Transport::Type::kBle), fabric, Node01_01, 0, context, |
| &delegate) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, gLoopback.mSentMessageCount == 1); |
| |
| // Clear pending packet in CRMP |
| ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); |
| ReliableMessageContext * rc = context->GetReliableMessageContext(); |
| rm->ClearRetransTable(rc); |
| |
| gLoopback.mMessageSendError = CHIP_ERROR_BAD_REQUEST; |
| |
| CASESession pairing1; |
| NL_TEST_ASSERT(inSuite, pairing1.MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| |
| gLoopback.mSentMessageCount = 0; |
| gLoopback.mMessageSendError = CHIP_ERROR_BAD_REQUEST; |
| ExchangeContext * context1 = ctx.NewUnauthenticatedExchangeToBob(&pairing1); |
| |
| NL_TEST_ASSERT(inSuite, |
| pairing1.EstablishSession(Transport::PeerAddress(Transport::Type::kBle), fabric, Node01_01, 0, context1, |
| &delegate) == CHIP_ERROR_BAD_REQUEST); |
| gLoopback.mMessageSendError = CHIP_NO_ERROR; |
| } |
| |
| void CASE_SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, CASESession & pairingCommissioner, |
| TestCASESecurePairingDelegate & delegateCommissioner) |
| { |
| TestContext & ctx = *reinterpret_cast<TestContext *>(inContext); |
| |
| // Test all combinations of invalid parameters |
| TestCASESecurePairingDelegate delegateAccessory; |
| TestCASESessionIPK pairingAccessory; |
| CASESessionSerializable serializableCommissioner; |
| CASESessionSerializable serializableAccessory; |
| |
| gLoopback.mSentMessageCount = 0; |
| NL_TEST_ASSERT(inSuite, pairingCommissioner.MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, pairingAccessory.MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, |
| ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_Sigma1, |
| &pairingAccessory) == CHIP_NO_ERROR); |
| |
| ExchangeContext * contextCommissioner = ctx.NewUnauthenticatedExchangeToBob(&pairingCommissioner); |
| |
| FabricInfo * fabric = gCommissionerFabrics.FindFabricWithIndex(gCommissionerFabricIndex); |
| NL_TEST_ASSERT(inSuite, fabric != nullptr); |
| |
| NL_TEST_ASSERT(inSuite, |
| pairingAccessory.ListenForSessionEstablishment(0, &gDeviceFabrics, &delegateAccessory) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, |
| pairingCommissioner.EstablishSession(Transport::PeerAddress(Transport::Type::kBle), fabric, Node01_01, 0, |
| contextCommissioner, &delegateCommissioner) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, gLoopback.mSentMessageCount == 5); |
| NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingComplete == 1); |
| NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == 1); |
| |
| NL_TEST_ASSERT(inSuite, pairingCommissioner.ToSerializable(serializableCommissioner) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, pairingAccessory.ToSerializable(serializableAccessory) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, |
| memcmp(serializableCommissioner.mSharedSecret, serializableAccessory.mSharedSecret, |
| serializableCommissioner.mSharedSecretLen) == 0); |
| } |
| |
| void CASE_SecurePairingHandshakeTest(nlTestSuite * inSuite, void * inContext) |
| { |
| TestCASESecurePairingDelegate delegateCommissioner; |
| TestCASESessionIPK pairingCommissioner; |
| CASE_SecurePairingHandshakeTestCommon(inSuite, inContext, pairingCommissioner, delegateCommissioner); |
| } |
| |
| class TestPersistentStorageDelegate : public PersistentStorageDelegate, public FabricStorage |
| { |
| public: |
| TestPersistentStorageDelegate() |
| { |
| memset(keys, 0, sizeof(keys)); |
| memset(keysize, 0, sizeof(keysize)); |
| memset(values, 0, sizeof(values)); |
| memset(valuesize, 0, sizeof(valuesize)); |
| } |
| |
| ~TestPersistentStorageDelegate() { Cleanup(); } |
| |
| void Cleanup() |
| { |
| for (int i = 0; i < 16; i++) |
| { |
| if (keys[i] != nullptr) |
| { |
| chip::Platform::MemoryFree(keys[i]); |
| keys[i] = nullptr; |
| } |
| if (values[i] != nullptr) |
| { |
| chip::Platform::MemoryFree(values[i]); |
| values[i] = nullptr; |
| } |
| } |
| } |
| |
| CHIP_ERROR SyncGetKeyValue(const char * key, void * buffer, uint16_t & size) override |
| { |
| for (int i = 0; i < 16; i++) |
| { |
| if (keys[i] != nullptr && keysize[i] != 0 && size >= valuesize[i]) |
| { |
| if (memcmp(key, keys[i], keysize[i]) == 0) |
| { |
| memcpy(buffer, values[i], valuesize[i]); |
| size = valuesize[i]; |
| return CHIP_NO_ERROR; |
| } |
| } |
| } |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR SyncSetKeyValue(const char * key, const void * value, uint16_t size) override |
| { |
| for (int i = 0; i < 16; i++) |
| { |
| if (keys[i] == nullptr && keysize[i] == 0 && valuesize[i] == 0) |
| { |
| keysize[i] = static_cast<uint16_t>(strlen(key)); |
| keysize[i]++; |
| keys[i] = reinterpret_cast<char *>(chip::Platform::MemoryAlloc(keysize[i])); |
| strcpy(keys[i], key); |
| values[i] = reinterpret_cast<char *>(chip::Platform::MemoryAlloc(size)); |
| memcpy(values[i], value, size); |
| valuesize[i] = size; |
| return CHIP_NO_ERROR; |
| } |
| } |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR SyncDeleteKeyValue(const char * key) override { return CHIP_NO_ERROR; } |
| |
| CHIP_ERROR SyncStore(FabricIndex fabricIndex, const char * key, const void * buffer, uint16_t size) override |
| { |
| return SyncSetKeyValue(key, buffer, size); |
| }; |
| |
| CHIP_ERROR SyncLoad(FabricIndex fabricIndex, const char * key, void * buffer, uint16_t & size) override |
| { |
| return SyncGetKeyValue(key, buffer, size); |
| }; |
| |
| CHIP_ERROR SyncDelete(FabricIndex fabricIndex, const char * key) override { return SyncDeleteKeyValue(key); }; |
| |
| private: |
| char * keys[16]; |
| void * values[16]; |
| uint16_t keysize[16]; |
| uint16_t valuesize[16]; |
| }; |
| |
| TestPersistentStorageDelegate gCommissionerStorageDelegate; |
| TestPersistentStorageDelegate gDeviceStorageDelegate; |
| |
| TestCASEServerIPK gPairingServer; |
| |
| void CASE_SecurePairingHandshakeServerTest(nlTestSuite * inSuite, void * inContext) |
| { |
| TestCASESecurePairingDelegate delegateCommissioner; |
| |
| auto * pairingCommissioner = chip::Platform::New<TestCASESessionIPK>(); |
| |
| TestContext & ctx = *reinterpret_cast<TestContext *>(inContext); |
| |
| gLoopback.mSentMessageCount = 0; |
| NL_TEST_ASSERT(inSuite, pairingCommissioner->MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, gPairingServer.GetSession().MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| |
| SessionIDAllocator idAllocator; |
| |
| NL_TEST_ASSERT(inSuite, |
| gPairingServer.ListenForSessionEstablishment(&ctx.GetExchangeManager(), &gTransportMgr, nullptr, |
| &ctx.GetSecureSessionManager(), &gDeviceFabrics, |
| &idAllocator) == CHIP_NO_ERROR); |
| |
| ExchangeContext * contextCommissioner = ctx.NewUnauthenticatedExchangeToBob(pairingCommissioner); |
| |
| FabricInfo * fabric = gCommissionerFabrics.FindFabricWithIndex(gCommissionerFabricIndex); |
| NL_TEST_ASSERT(inSuite, fabric != nullptr); |
| |
| NL_TEST_ASSERT(inSuite, |
| pairingCommissioner->EstablishSession(Transport::PeerAddress(Transport::Type::kBle), fabric, Node01_01, 0, |
| contextCommissioner, &delegateCommissioner) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, gLoopback.mSentMessageCount == 5); |
| NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == 1); |
| |
| auto * pairingCommissioner1 = chip::Platform::New<TestCASESessionIPK>(); |
| NL_TEST_ASSERT(inSuite, pairingCommissioner1->MessageDispatch().Init(&ctx.GetSecureSessionManager()) == CHIP_NO_ERROR); |
| ExchangeContext * contextCommissioner1 = ctx.NewUnauthenticatedExchangeToBob(pairingCommissioner1); |
| |
| NL_TEST_ASSERT(inSuite, |
| pairingCommissioner1->EstablishSession(Transport::PeerAddress(Transport::Type::kBle), fabric, Node01_01, 0, |
| contextCommissioner1, &delegateCommissioner) == CHIP_NO_ERROR); |
| |
| chip::Platform::Delete(pairingCommissioner); |
| chip::Platform::Delete(pairingCommissioner1); |
| } |
| |
| void CASE_SecurePairingDeserialize(nlTestSuite * inSuite, void * inContext, CASESession & pairingCommissioner, |
| CASESession & deserialized) |
| { |
| CASESessionSerialized serialized; |
| NL_TEST_ASSERT(inSuite, pairingCommissioner.Serialize(serialized) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, deserialized.Deserialize(serialized) == CHIP_NO_ERROR); |
| |
| // Serialize from the deserialized session, and check we get the same string back |
| CASESessionSerialized serialized2; |
| NL_TEST_ASSERT(inSuite, deserialized.Serialize(serialized2) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, strncmp(Uint8::to_char(serialized.inner), Uint8::to_char(serialized2.inner), sizeof(serialized)) == 0); |
| } |
| |
| void CASE_SecurePairingSerializeTest(nlTestSuite * inSuite, void * inContext) |
| { |
| TestCASESecurePairingDelegate delegateCommissioner; |
| |
| // Allocate on the heap to avoid stack overflow in some restricted test scenarios (e.g. QEMU) |
| auto * testPairingSession1 = chip::Platform::New<TestCASESessionIPK>(); |
| auto * testPairingSession2 = chip::Platform::New<TestCASESessionIPK>(); |
| |
| CASE_SecurePairingHandshakeTestCommon(inSuite, inContext, *testPairingSession1, delegateCommissioner); |
| CASE_SecurePairingDeserialize(inSuite, inContext, *testPairingSession1, *testPairingSession2); |
| |
| const uint8_t plain_text[] = { 0x86, 0x74, 0x64, 0xe5, 0x0b, 0xd4, 0x0d, 0x90, 0xe1, 0x17, 0xa3, 0x2d, 0x4b, 0xd4, 0xe1, 0xe6 }; |
| uint8_t encrypted[64]; |
| PacketHeader header; |
| MessageAuthenticationCode mac; |
| |
| header.SetSessionId(1); |
| NL_TEST_ASSERT(inSuite, header.IsEncrypted() == true); |
| NL_TEST_ASSERT(inSuite, header.MICTagLength() == 16); |
| |
| // Let's try encrypting using original session, and decrypting using deserialized |
| { |
| CryptoContext session1; |
| |
| NL_TEST_ASSERT(inSuite, |
| testPairingSession1->DeriveSecureSession(session1, CryptoContext::SessionRole::kInitiator) == CHIP_NO_ERROR); |
| |
| NL_TEST_ASSERT(inSuite, session1.Encrypt(plain_text, sizeof(plain_text), encrypted, header, mac) == CHIP_NO_ERROR); |
| } |
| |
| { |
| CryptoContext session2; |
| NL_TEST_ASSERT(inSuite, |
| testPairingSession2->DeriveSecureSession(session2, CryptoContext::SessionRole::kResponder) == CHIP_NO_ERROR); |
| |
| uint8_t decrypted[64]; |
| NL_TEST_ASSERT(inSuite, session2.Decrypt(encrypted, sizeof(plain_text), decrypted, header, mac) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, memcmp(plain_text, decrypted, sizeof(plain_text)) == 0); |
| } |
| |
| chip::Platform::Delete(testPairingSession1); |
| chip::Platform::Delete(testPairingSession2); |
| } |
| |
| struct Sigma1Params |
| { |
| // Purposefully not using constants like kSigmaParamRandomNumberSize that |
| // the code uses, so we have a cross-check. |
| static constexpr size_t initiatorRandomLen = 32; |
| static constexpr uint16_t initiatorSessionId = 0; |
| static constexpr size_t destinationIdLen = 32; |
| static constexpr size_t initiatorEphPubKeyLen = 65; |
| static constexpr size_t resumptionIdLen = 0; // Nonzero means include it. |
| static constexpr size_t initiatorResumeMICLen = 0; // Nonzero means include it. |
| |
| static constexpr uint8_t initiatorRandomTag = 1; |
| static constexpr uint8_t initiatorSessionIdTag = 2; |
| static constexpr uint8_t destinationIdTag = 3; |
| static constexpr uint8_t initiatorEphPubKeyTag = 4; |
| static constexpr uint8_t resumptionIdTag = 6; |
| static constexpr uint8_t initiatorResumeMICTag = 7; |
| static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ContextTag(num); } |
| |
| static constexpr bool includeStructEnd = true; |
| |
| static constexpr bool expectSuccess = true; |
| }; |
| |
| template <typename Params> |
| static CHIP_ERROR EncodeSigma1(MutableByteSpan & buf) |
| { |
| using namespace TLV; |
| |
| TLVWriter writer; |
| writer.Init(buf); |
| |
| TLVType containerType; |
| ReturnErrorOnFailure(writer.StartContainer(AnonymousTag, kTLVType_Structure, containerType)); |
| uint8_t initiatorRandom[Params::initiatorRandomLen] = { 1 }; |
| ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorRandomTag), ByteSpan(initiatorRandom))); |
| |
| ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorSessionIdTag), Params::initiatorSessionId)); |
| |
| uint8_t destinationId[Params::destinationIdLen] = { 2 }; |
| ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::destinationIdTag), ByteSpan(destinationId))); |
| |
| uint8_t initiatorEphPubKey[Params::initiatorEphPubKeyLen] = { 3 }; |
| ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorEphPubKeyTag), ByteSpan(initiatorEphPubKey))); |
| |
| // I wish we had "if constexpr" support here, so the compiler would know |
| // resumptionIdLen is nonzero inside the block.... |
| if (Params::resumptionIdLen != 0) |
| { |
| uint8_t resumptionId[Params::resumptionIdLen]; |
| |
| // to fix _FORTIFY_SOURCE issue, _FORTIFY_SOURCE=2 by default on Android |
| (&memset)(resumptionId, 4, Params::resumptionIdLen); |
| ReturnErrorOnFailure( |
| writer.Put(Params::NumToTag(Params::resumptionIdTag), ByteSpan(resumptionId, Params::resumptionIdLen))); |
| } |
| |
| if (Params::initiatorResumeMICLen != 0) |
| { |
| uint8_t initiatorResumeMIC[Params::initiatorResumeMICLen]; |
| // to fix _FORTIFY_SOURCE issue, _FORTIFY_SOURCE=2 by default on Android |
| (&memset)(initiatorResumeMIC, 5, Params::initiatorResumeMICLen); |
| ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorResumeMICTag), |
| ByteSpan(initiatorResumeMIC, Params::initiatorResumeMICLen))); |
| } |
| |
| if (Params::includeStructEnd) |
| { |
| ReturnErrorOnFailure(writer.EndContainer(containerType)); |
| } |
| |
| buf.reduce_size(writer.GetLengthWritten()); |
| return CHIP_NO_ERROR; |
| } |
| |
| // A macro, so we can tell which test failed based on line number. |
| #define TestSigma1Parsing(inSuite, mem, bufferSize, params) \ |
| do \ |
| { \ |
| MutableByteSpan buf(mem.Get(), bufferSize); \ |
| CHIP_ERROR err = EncodeSigma1<params>(buf); \ |
| NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); \ |
| \ |
| TLV::ContiguousBufferTLVReader reader; \ |
| reader.Init(buf); \ |
| \ |
| ByteSpan initiatorRandom; \ |
| uint16_t initiatorSessionId; \ |
| ByteSpan destinationId; \ |
| ByteSpan initiatorEphPubKey; \ |
| bool resumptionRequested; \ |
| ByteSpan resumptionId; \ |
| ByteSpan initiatorResumeMIC; \ |
| err = CASESession::ParseSigma1(reader, initiatorRandom, initiatorSessionId, destinationId, initiatorEphPubKey, \ |
| resumptionRequested, resumptionId, initiatorResumeMIC); \ |
| NL_TEST_ASSERT(inSuite, (err == CHIP_NO_ERROR) == params::expectSuccess); \ |
| if (params::expectSuccess) \ |
| { \ |
| NL_TEST_ASSERT(inSuite, resumptionRequested == (params::resumptionIdLen != 0 && params::initiatorResumeMICLen != 0)); \ |
| /* Add other verification tests here as desired */ \ |
| } \ |
| } while (0) |
| |
| struct BadSigma1ParamsBase : public Sigma1Params |
| { |
| static constexpr bool expectSuccess = false; |
| }; |
| |
| struct Sigma1NoStructEnd : public BadSigma1ParamsBase |
| { |
| static constexpr bool includeStructEnd = false; |
| }; |
| |
| struct Sigma1WrongTags : public BadSigma1ParamsBase |
| { |
| static constexpr TLV::Tag NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } |
| }; |
| |
| struct Sigma1TooLongRandom : public BadSigma1ParamsBase |
| { |
| static constexpr size_t initiatorRandomLen = 33; |
| }; |
| |
| struct Sigma1TooShortRandom : public BadSigma1ParamsBase |
| { |
| static constexpr size_t initiatorRandomLen = 31; |
| }; |
| |
| struct Sigma1TooLongDest : public BadSigma1ParamsBase |
| { |
| static constexpr size_t destinationIdLen = 33; |
| }; |
| |
| struct Sigma1TooShortDest : public BadSigma1ParamsBase |
| { |
| static constexpr size_t destinationIdLen = 31; |
| }; |
| |
| struct Sigma1TooLongPubkey : public BadSigma1ParamsBase |
| { |
| static constexpr size_t initiatorEphPubKeyLen = 66; |
| }; |
| |
| struct Sigma1TooShortPubkey : public BadSigma1ParamsBase |
| { |
| static constexpr size_t initiatorEphPubKeyLen = 64; |
| }; |
| |
| struct Sigma1WithResumption : public Sigma1Params |
| { |
| static constexpr size_t resumptionIdLen = 16; |
| static constexpr size_t initiatorResumeMICLen = 16; |
| }; |
| |
| struct Sigma1TooLongResumptionId : public Sigma1WithResumption |
| { |
| static constexpr size_t resumptionIdLen = 17; |
| static constexpr bool expectSuccess = false; |
| }; |
| |
| struct Sigma1TooShortResumptionId : public BadSigma1ParamsBase |
| { |
| static constexpr size_t resumptionIdLen = 15; |
| static constexpr bool expectSuccess = false; |
| }; |
| |
| struct Sigma1TooLongResumeMIC : public Sigma1WithResumption |
| { |
| static constexpr size_t resumptionIdLen = 17; |
| static constexpr bool expectSuccess = false; |
| }; |
| |
| struct Sigma1TooShortResumeMIC : public Sigma1WithResumption |
| { |
| static constexpr size_t initiatorResumeMICLen = 15; |
| static constexpr bool expectSuccess = false; |
| }; |
| |
| struct Sigma1SessionIdMax : public Sigma1Params |
| { |
| static constexpr uint32_t initiatorSessionId = UINT16_MAX; |
| }; |
| |
| struct Sigma1SessionIdTooBig : public BadSigma1ParamsBase |
| { |
| static constexpr uint32_t initiatorSessionId = UINT16_MAX + 1; |
| }; |
| |
| static void CASE_Sigma1ParsingTest(nlTestSuite * inSuite, void * inContext) |
| { |
| // 1280 bytes must be enough by definition. |
| constexpr size_t bufferSize = 1280; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> mem; |
| NL_TEST_ASSERT(inSuite, mem.Calloc(bufferSize)); |
| |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1Params); |
| |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1NoStructEnd); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1WrongTags); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongRandom); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortRandom); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongDest); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortDest); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongPubkey); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortPubkey); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1WithResumption); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongResumptionId); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortResumptionId); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongResumeMIC); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortResumeMIC); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1SessionIdMax); |
| TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1SessionIdTooBig); |
| } |
| |
| // Test Suite |
| |
| /** |
| * Test Suite that lists all the test functions. |
| */ |
| // clang-format off |
| static const nlTest sTests[] = |
| { |
| NL_TEST_DEF("WaitInit", CASE_SecurePairingWaitTest), |
| NL_TEST_DEF("Start", CASE_SecurePairingStartTest), |
| NL_TEST_DEF("Handshake", CASE_SecurePairingHandshakeTest), |
| NL_TEST_DEF("ServerHandshake", CASE_SecurePairingHandshakeServerTest), |
| NL_TEST_DEF("Serialize", CASE_SecurePairingSerializeTest), |
| NL_TEST_DEF("Sigma1Parsing", CASE_Sigma1ParsingTest), |
| |
| NL_TEST_SENTINEL() |
| }; |
| // clang-format on |
| |
| int CASE_TestSecurePairing_Setup(void * inContext); |
| int CASE_TestSecurePairing_Teardown(void * inContext); |
| |
| // clang-format off |
| static nlTestSuite sSuite = |
| { |
| "Test-CHIP-SecurePairing-CASE", |
| &sTests[0], |
| CASE_TestSecurePairing_Setup, |
| CASE_TestSecurePairing_Teardown, |
| }; |
| // clang-format on |
| |
| static TestContext sContext; |
| |
| namespace { |
| /* |
| * Set up the test suite. |
| */ |
| CHIP_ERROR CASETestSecurePairingSetup(void * inContext) |
| { |
| TestContext & ctx = *reinterpret_cast<TestContext *>(inContext); |
| |
| ReturnErrorOnFailure(chip::Platform::MemoryInit()); |
| |
| gTransportMgr.Init(&gLoopback); |
| ReturnErrorOnFailure(gIOContext.Init()); |
| |
| ReturnErrorOnFailure(ctx.Init(&gTransportMgr, &gIOContext)); |
| |
| ctx.SetBobNodeId(kPlaceholderNodeId); |
| ctx.SetAliceNodeId(kPlaceholderNodeId); |
| ctx.SetBobKeyId(0); |
| ctx.SetAliceKeyId(0); |
| ctx.SetFabricIndex(kUndefinedFabricIndex); |
| |
| gTransportMgr.SetSessionManager(&ctx.GetSecureSessionManager()); |
| |
| gCommissionerFabrics.Init(&gCommissionerStorageDelegate); |
| gDeviceFabrics.Init(&gDeviceStorageDelegate); |
| |
| return InitCredentialSets(); |
| } |
| } // anonymous namespace |
| |
| /** |
| * Set up the test suite. |
| */ |
| int CASE_TestSecurePairing_Setup(void * inContext) |
| { |
| return CASETestSecurePairingSetup(inContext) == CHIP_NO_ERROR ? SUCCESS : FAILURE; |
| } |
| |
| /** |
| * Tear down the test suite. |
| */ |
| int CASE_TestSecurePairing_Teardown(void * inContext) |
| { |
| reinterpret_cast<TestContext *>(inContext)->Shutdown(); |
| gIOContext.Shutdown(); |
| gCommissionerStorageDelegate.Cleanup(); |
| gDeviceStorageDelegate.Cleanup(); |
| gCommissionerFabrics.Reset(); |
| gDeviceFabrics.Reset(); |
| chip::Platform::MemoryShutdown(); |
| return SUCCESS; |
| } |
| |
| /** |
| * Main |
| */ |
| int TestCASESession() |
| { |
| // Run test suit against one context |
| nlTestRunner(&sSuite, &sContext); |
| |
| return (nlTestRunnerStats(&sSuite)); |
| } |
| |
| CHIP_REGISTER_TEST_SUITE(TestCASESession) |