blob: 641c568c9e701b679090ffcf7d1082eee883c070 [file] [log] [blame]
/*
*
* Copyright (c) 2020-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 SessionManager implementation.
*/
#define CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API // Up here in case some other header
// includes SessionManager.h indirectly
#include <credentials/GroupDataProviderImpl.h>
#include <credentials/PersistentStorageOpCertStore.h>
#include <crypto/PersistentStorageOperationalKeystore.h>
#include <lib/core/CHIPCore.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/UnitTestContext.h>
#include <lib/support/UnitTestRegistration.h>
#include <protocols/secure_channel/MessageCounterManager.h>
#include <transport/SessionManager.h>
#include <transport/TransportMgr.h>
#include <transport/tests/LoopbackTransportManager.h>
#include <nlbyteorder.h>
#include <nlunit-test.h>
#include <errno.h>
#undef CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API
namespace {
using namespace chip;
using namespace chip::Inet;
using namespace chip::Transport;
using namespace chip::Test;
using namespace chip::Credentials;
using GroupInfo = GroupDataProvider::GroupInfo;
using GroupKey = GroupDataProvider::GroupKey;
using KeySet = GroupDataProvider::KeySet;
using SecurityPolicy = GroupDataProvider::SecurityPolicy;
using TestContext = chip::Test::LoopbackTransportManager;
struct MessageTestEntry
{
const char * name;
const char * peerAddr;
const char * payload;
const char * plain;
const char * encrypted;
const char * privacy;
size_t payloadLength;
size_t plainLength;
size_t encryptedLength;
size_t privacyLength;
const char * encryptKey;
const char * privacyKey;
const char * epochKey;
const char * nonce;
const char * privacyNonce;
const char * compressedFabricId;
const char * mic;
uint16_t sessionId;
NodeId peerNodeId;
GroupId groupId;
NodeId sourceNodeId;
};
struct MessageTestEntry theMessageTestVector[] = {
{
.name = "secure pase message (no payload)",
.peerAddr = "::1",
.payload = "",
.plain = "\x00\xb8\x0b\x00\x39\x30\x00\x00\x05\x64\xee\x0e\x20\x7d",
.encrypted = "\x00\xb8\x0b\x00\x39\x30\x00\x00\x5a\x98\x9a\xe4\x2e\x8d"
"\x84\x7f\x53\x5c\x30\x07\xe6\x15\x0c\xd6\x58\x67\xf2\xb8\x17\xdb", // Includes MIC
.privacy = "\x00\xb8\x0b\x00\x39\x30\x00\x00\x5a\x98\x9a\xe4\x2e\x8d"
"\x84\x7f\x53\x5c\x30\x07\xe6\x15\x0c\xd6\x58\x67\xf2\xb8\x17\xdb", // Includes MIC
.payloadLength = 0,
.plainLength = 14,
.encryptedLength = 30,
.privacyLength = 30,
// TODO(#22830): unicast message tests must use test key currently
.encryptKey = "\x5e\xde\xd2\x44\xe5\x53\x2b\x3c\xdc\x23\x40\x9d\xba\xd0\x52\xd2",
.nonce = "\x00\x39\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
.sessionId = 0x0bb8, // 3000
.peerNodeId = 0x0000000000000000ULL,
},
{
.name = "secure pase message (short payload)",
.peerAddr = "::1",
.payload = "\x11\x22\x33\x44\x55",
.plain = "\x00\xb8\x0b\x00\x39\x30\x00\x00\x05\x64\xee\x0e\x20\x7d\x11\x22\x33\x44\x55",
.encrypted = "\x00\xb8\x0b\x00\x39\x30\x00\x00\x5a\x98\x9a\xe4\x2e\x8d\x0f\x7f\x88\x5d\xfb"
"\x2f\xaa\x89\x49\xcf\x73\x0a\x57\x28\xe0\x35\x46\x10\xa0\xc4\xa7", // Includes MIC
.privacy = "\x00\xb8\x0b\x00\x39\x30\x00\x00\x5a\x98\x9a\xe4\x2e\x8d\x0f\x7f\x88\x5d\xfb"
"\x2f\xaa\x89\x49\xcf\x73\x0a\x57\x28\xe0\x35\x46\x10\xa0\xc4\xa7", // Includes MIC
.payloadLength = 5,
.plainLength = 19,
.encryptedLength = 35,
.privacyLength = 35,
// TODO(#22830): unicast message tests must use test key currently
.encryptKey = "\x5e\xde\xd2\x44\xe5\x53\x2b\x3c\xdc\x23\x40\x9d\xba\xd0\x52\xd2",
.nonce = "\x00\x39\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
.sessionId = 0x0bb8, // 3000
.peerNodeId = 0x0000000000000000ULL,
},
#if !CHIP_CONFIG_SECURITY_TEST_MODE
{
.name = "secure group message (no privacy)",
.peerAddr = "::1",
.payload = "",
.plain = "\06\x7d\xdb\x01\x78\x56\x34\x12\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x64\xee\x0e\x20\x7d",
.encrypted = "\x06\x7d\xdb\x01\x78\x56\x34\x12\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x65\xc7\x67\xbc\x6c\xda"
"\x01\x06\xc9\x80\x13\x23\x90\x0e\x9b\x3c\xe6\xd4\xbb\x03\x27\xd6", // Includes MIC
.privacy = "\x06\x7d\xdb\x01\x78\x56\x34\x12\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x65\xc7\x67\xbc\x6c\xda"
"\x01\x06\xc9\x80\x13\x23\x90\x0e\x9b\x3c\xe6\xd4\xbb\x03\x27\xd6", // Includes MIC
.payloadLength = 0,
.plainLength = 24,
.encryptedLength = 40,
.privacyLength = 40,
.encryptKey = "\xca\x92\xd7\xa0\x94\x2d\x1a\x51\x1a\x0e\x26\xad\x07\x4f\x4c\x2f",
.privacyKey = "\xbf\xe9\xda\x01\x6a\x76\x53\x65\xf2\xdd\x97\xa9\xf9\x39\xe4\x25",
.epochKey = "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
.nonce = "\x01\x78\x56\x34\x12\x01\x00\x00\x00\x00\x00\x00\x00",
.privacyNonce = "\xdb\x7d\x23\x90\x0e\x9b\x3c\xe6\xd4\xbb\x03\x27\xd6",
.sessionId = 0xdb7d, // 56189
.peerNodeId = 0x0000000000000000ULL,
.groupId = 2,
.sourceNodeId = 0x0000000000000002ULL,
},
#endif // !CHIP_CONFIG_SECURITY_TEST_MODE
};
const uint16_t theMessageTestVectorLength = sizeof(theMessageTestVector) / sizeof(theMessageTestVector[0]);
// Just enough init to replace a ton of boilerplate
constexpr FabricIndex kFabricIndex = kMinValidFabricIndex;
constexpr size_t kGroupIndex = 0;
constexpr uint16_t kMaxGroupsPerFabric = 5;
constexpr uint16_t kMaxGroupKeysPerFabric = 8;
static chip::TestPersistentStorageDelegate sStorageDelegate;
static GroupDataProviderImpl sProvider(kMaxGroupsPerFabric, kMaxGroupKeysPerFabric);
class FabricTableHolder
{
public:
FabricTableHolder() {}
~FabricTableHolder()
{
mFabricTable.Shutdown();
mOpKeyStore.Finish();
mOpCertStore.Finish();
}
CHIP_ERROR Init()
{
ReturnErrorOnFailure(mOpKeyStore.Init(&mStorage));
ReturnErrorOnFailure(mOpCertStore.Init(&mStorage));
// Initialize Group Data Provider
sProvider.SetStorageDelegate(&sStorageDelegate);
// sProvider.SetListener(&chip::app::TestGroups::sListener);
ReturnErrorOnFailure(sProvider.Init());
Credentials::SetGroupDataProvider(&sProvider);
// Initialize Fabric Table
chip::FabricTable::InitParams initParams;
initParams.storage = &mStorage;
initParams.operationalKeystore = &mOpKeyStore;
initParams.opCertStore = &mOpCertStore;
return mFabricTable.Init(initParams);
}
FabricTable & GetFabricTable() { return mFabricTable; }
private:
chip::FabricTable mFabricTable;
chip::TestPersistentStorageDelegate mStorage;
chip::PersistentStorageOperationalKeystore mOpKeyStore;
chip::Credentials::PersistentStorageOpCertStore mOpCertStore;
};
class TestSessionManagerCallback : public SessionMessageDelegate
{
public:
void OnMessageReceived(const PacketHeader & header, const PayloadHeader & payloadHeader, const SessionHandle & session,
DuplicateMessage isDuplicate, System::PacketBufferHandle && msgBuf) override
{
mReceivedCount++;
MessageTestEntry & testEntry = theMessageTestVector[mTestVectorIndex];
ChipLogProgress(Test, "OnMessageReceived: sessionId=0x%04x", testEntry.sessionId);
NL_TEST_ASSERT(mSuite, header.GetSessionId() == testEntry.sessionId);
size_t dataLength = msgBuf->DataLength();
size_t expectLength = testEntry.payloadLength;
NL_TEST_ASSERT(mSuite, dataLength == expectLength);
NL_TEST_ASSERT(mSuite, memcmp(msgBuf->Start(), testEntry.payload, dataLength) == 0);
ChipLogProgress(Test, "::: TestSessionManagerDispatch[%d] PASS", mTestVectorIndex);
}
void ResetTest(unsigned testVectorIndex)
{
mTestVectorIndex = testVectorIndex;
mReceivedCount = 0;
}
unsigned NumMessagesReceived() { return mReceivedCount; }
nlTestSuite * mSuite = nullptr;
unsigned mTestVectorIndex = 0;
unsigned mReceivedCount = 0;
};
PeerAddress AddressFromString(const char * str)
{
Inet::IPAddress addr;
VerifyOrDie(Inet::IPAddress::FromString(str, addr));
return PeerAddress::UDP(addr);
}
void TestSessionManagerInit(nlTestSuite * inSuite, TestContext & ctx, SessionManager & sessionManager)
{
static FabricTableHolder fabricTableHolder;
static secure_channel::MessageCounterManager gMessageCounterManager;
static chip::TestPersistentStorageDelegate deviceStorage;
NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == fabricTableHolder.Init());
NL_TEST_ASSERT(inSuite,
CHIP_NO_ERROR ==
sessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &gMessageCounterManager, &deviceStorage,
&fabricTableHolder.GetFabricTable()));
}
// constexpr chip::FabricId kFabricId1 = 0x2906C908D115D362;
static const uint8_t kCompressedFabricIdBuffer1[] = { 0x87, 0xe1, 0xb0, 0x04, 0xe2, 0x35, 0xa1, 0x30 };
constexpr ByteSpan kCompressedFabricId1(kCompressedFabricIdBuffer1);
CHIP_ERROR InjectGroupSessionWithTestKey(SessionHolder & sessionHolder, MessageTestEntry & testEntry)
{
constexpr uint16_t kKeySetIndex = 0x0;
GroupId groupId = testEntry.groupId;
GroupDataProvider * provider = GetGroupDataProvider();
static KeySet sKeySet(kKeySetIndex, SecurityPolicy::kTrustFirst, 1);
static GroupKey sGroupKeySet(groupId, kKeySetIndex);
static GroupInfo sGroupInfo(groupId, "Name Matter Not");
static Transport::IncomingGroupSession sSessionBobToFriends(groupId, kFabricIndex, testEntry.sourceNodeId);
if (testEntry.epochKey)
{
memcpy(sKeySet.epoch_keys[0].key, testEntry.epochKey, 16);
sKeySet.epoch_keys[0].start_time = 0;
sGroupInfo.group_id = groupId;
sGroupKeySet.group_id = groupId;
ReturnErrorOnFailure(provider->SetKeySet(kFabricIndex, kCompressedFabricId1, sKeySet));
ReturnErrorOnFailure(provider->SetGroupKeyAt(kFabricIndex, kGroupIndex, sGroupKeySet));
ReturnErrorOnFailure(provider->SetGroupInfoAt(kFabricIndex, kGroupIndex, sGroupInfo));
}
sessionHolder = SessionHandle(sSessionBobToFriends);
return CHIP_NO_ERROR;
}
void TestSessionManagerDispatch(nlTestSuite * inSuite, void * inContext)
{
CHIP_ERROR err = CHIP_NO_ERROR;
TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
SessionManager sessionManager;
TestSessionManagerCallback callback;
TestSessionManagerInit(inSuite, ctx, sessionManager);
sessionManager.SetMessageDelegate(&callback);
IPAddress addr;
IPAddress::FromString("::1", addr);
Transport::PeerAddress peer(Transport::PeerAddress::UDP(addr, CHIP_PORT));
SessionHolder aliceToBobSession;
SessionHolder testGroupSession;
callback.mSuite = inSuite;
for (unsigned i = 0; i < theMessageTestVectorLength; i++)
{
MessageTestEntry & testEntry = theMessageTestVector[i];
callback.ResetTest(i);
ChipLogProgress(Test, "===> TestSessionManagerDispatch[%d] '%s': sessionId=0x%04x", i, testEntry.name, testEntry.sessionId);
// TODO(#22830): inject raw keys rather than always defaulting to test key
// TODO: switch on session type
// Inject Sessions
err = sessionManager.InjectPaseSessionWithTestKey(aliceToBobSession, testEntry.sessionId, testEntry.peerNodeId,
testEntry.sessionId, kFabricIndex, peer,
CryptoContext::SessionRole::kResponder);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
err = InjectGroupSessionWithTestKey(testGroupSession, testEntry);
NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == err);
const char * plain = testEntry.plain;
const ByteSpan expectedPlain(reinterpret_cast<const uint8_t *>(plain), testEntry.plainLength);
const char * privacy = testEntry.privacy;
chip::System::PacketBufferHandle msg =
chip::MessagePacketBuffer::NewWithData(reinterpret_cast<const uint8_t *>(privacy), testEntry.privacyLength);
const PeerAddress peerAddress = AddressFromString(testEntry.peerAddr);
sessionManager.OnMessageReceived(peerAddress, std::move(msg));
NL_TEST_ASSERT(inSuite, callback.NumMessagesReceived() > 0);
}
sessionManager.Shutdown();
}
// ============================================================================
// Test Suite Instrumenation
// ============================================================================
/**
* Initialize the test suite.
*/
int Initialize(void * aContext)
{
CHIP_ERROR err = reinterpret_cast<TestContext *>(aContext)->Init();
return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE;
}
/**
* Finalize the test suite.
*/
int Finalize(void * aContext)
{
reinterpret_cast<TestContext *>(aContext)->Shutdown();
return SUCCESS;
}
/**
* Test Suite that lists all the test functions.
*/
// clang-format off
const nlTest sTests[] =
{
NL_TEST_DEF("Test Session Manager Dispatch", TestSessionManagerDispatch),
NL_TEST_SENTINEL()
};
nlTestSuite sSuite =
{
"TestSessionManagerDispatch",
&sTests[0],
Initialize,
Finalize
};
// clang-format on
} // namespace
/**
* Main
*/
int TestSessionManagerDispatchSuite()
{
return chip::ExecuteTestsWithContext<TestContext>(&sSuite);
}
CHIP_REGISTER_TEST_SUITE(TestSessionManagerDispatchSuite);