diff --git a/src/messaging/BUILD.gn b/src/messaging/BUILD.gn index e94e4873061f34..b3c126a962abd4 100644 --- a/src/messaging/BUILD.gn +++ b/src/messaging/BUILD.gn @@ -31,6 +31,8 @@ static_library("messaging") { "ReliableMessageMgr.cpp", "ReliableMessageMgr.h", "ReliableMessageProtocolConfig.h", + "SecureChannelMgr.cpp", + "SecureChannelMgr.h", ] cflags = [ "-Wconversion" ] diff --git a/src/messaging/ExchangeContext.h b/src/messaging/ExchangeContext.h index 55ef88aa8ec35d..f6fb57e243af48 100644 --- a/src/messaging/ExchangeContext.h +++ b/src/messaging/ExchangeContext.h @@ -33,6 +33,9 @@ #include namespace chip { + +constexpr uint16_t kMsgCounterChallengeSize = 8; // The size of the message counter synchronization request message. + namespace Messaging { class ExchangeManager; @@ -149,6 +152,12 @@ class DLL_EXPORT ExchangeContext : public ReferenceCounted mFlags; // Internal state flags /** diff --git a/src/messaging/ExchangeMgr.cpp b/src/messaging/ExchangeMgr.cpp index 91c0202f4d25fc..3d8dc5af9c2185 100644 --- a/src/messaging/ExchangeMgr.cpp +++ b/src/messaging/ExchangeMgr.cpp @@ -66,8 +66,9 @@ ExchangeManager::ExchangeManager() : mReliableMessageMgr(mContextPool) CHIP_ERROR ExchangeManager::Init(SecureSessionMgr * sessionMgr) { - if (mState != State::kState_NotInitialized) - return CHIP_ERROR_INCORRECT_STATE; + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(mState == State::kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE); mSessionMgr = sessionMgr; @@ -82,13 +83,19 @@ CHIP_ERROR ExchangeManager::Init(SecureSessionMgr * sessionMgr) mReliableMessageMgr.Init(sessionMgr->SystemLayer(), sessionMgr); + err = mSecureChannelMgr.Init(this); + SuccessOrExit(err); + mState = State::kState_Initialized; - return CHIP_NO_ERROR; +exit: + return err; } CHIP_ERROR ExchangeManager::Shutdown() { + mSecureChannelMgr.Shutdown(); + if (mSessionMgr != nullptr) { mSessionMgr->SetDelegate(nullptr); diff --git a/src/messaging/ExchangeMgr.h b/src/messaging/ExchangeMgr.h index 4b17d7b81f5088..e375a8a34532ad 100644 --- a/src/messaging/ExchangeMgr.h +++ b/src/messaging/ExchangeMgr.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -177,6 +178,8 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate ReliableMessageMgr * GetReliableMessageMgr() { return &mReliableMessageMgr; }; + SecureChannel::SecureChannelMgr * GetSecureChannelMgr() { return &mSecureChannelMgr; }; + size_t GetContextsInUse() const { return mContextsInUse; } private: @@ -197,6 +200,7 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate State mState; SecureSessionMgr * mSessionMgr; ReliableMessageMgr mReliableMessageMgr; + SecureChannel::SecureChannelMgr mSecureChannelMgr; std::array mContextPool; size_t mContextsInUse; diff --git a/src/messaging/ReliableMessageMgr.cpp b/src/messaging/ReliableMessageMgr.cpp index 005974e0e4a17b..253c1533e26ec5 100644 --- a/src/messaging/ReliableMessageMgr.cpp +++ b/src/messaging/ReliableMessageMgr.cpp @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -386,7 +387,7 @@ bool ReliableMessageMgr::CheckAndRemRetransTable(ReliableMessageContext * rc, ui /** * Send the specified entry from the retransmission table. * - * @param[in] entry A pointer to a retransmission table entry object that needs to be sent. + * @param[in] entry A pointer to a retransmission table entry object that needs to be sent. * * @return #CHIP_NO_ERROR On success, else corresponding CHIP_ERROR returned from SendMessage. * diff --git a/src/messaging/SecureChannelMgr.cpp b/src/messaging/SecureChannelMgr.cpp new file mode 100644 index 00000000000000..92172e422204ae --- /dev/null +++ b/src/messaging/SecureChannelMgr.cpp @@ -0,0 +1,270 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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 the CHIP Secure Channel protocol. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace SecureChannel { + +SecureChannelMgr::SecureChannelMgr() +{ + mExchangeMgr = nullptr; +} + +CHIP_ERROR SecureChannelMgr::Init(Messaging::ExchangeManager * exchangeMgr) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(exchangeMgr != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + mExchangeMgr = exchangeMgr; + + // Register to receive unsolicited Secure Channel Request messages from the exchange manager. + err = mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::kProtocol_SecureChannel, this); + + SuccessOrExit(err); + +exit: + return err; +} + +void SecureChannelMgr::Shutdown() +{ + if (mExchangeMgr != nullptr) + { + mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::kProtocol_SecureChannel); + mExchangeMgr = nullptr; + } +} + +void SecureChannelMgr::OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, + const PayloadHeader & payloadHeader, System::PacketBufferHandle msgBuf) +{ + if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq)) + { + HandleMsgCounterSyncReq(ec, packetHeader, std::move(msgBuf)); + } + else if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp)) + { + HandleMsgCounterSyncResp(ec, packetHeader, std::move(msgBuf)); + } +} + +void SecureChannelMgr::OnResponseTimeout(Messaging::ExchangeContext * ec) +{ + // Close the exchange if MsgCounterSyncRsp is not received before kMsgCounterSyncTimeout. + if (ec != nullptr) + ec->Close(); +} + +// Create and initialize new exchange for the message counter synchronization request/response messages. +CHIP_ERROR SecureChannelMgr::NewMsgCounterSyncExchange(SecureSessionHandle session, Messaging::ExchangeContext *& ec) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Message counter synchronization protocol is only applicable for application group keys. + VerifyOrExit(ChipKeyId::IsAppGroupKey(session.GetPeerKeyId()), err = CHIP_ERROR_INVALID_ARGUMENT); + + // Create new exchange context. + ec = mExchangeMgr->NewContext(session, this); + VerifyOrExit(ec != nullptr, err = CHIP_ERROR_NO_MEMORY); + +exit: + return err; +} + +CHIP_ERROR SecureChannelMgr::SendMsgCounterSyncReq(SecureSessionHandle session) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Messaging::ExchangeContext * ec = nullptr; + System::PacketBufferHandle msgBuf; + Messaging::SendFlags sendFlags; + uint8_t challenge[kMsgCounterChallengeSize]; + + // Create and initialize new exchange. + err = NewMsgCounterSyncExchange(session, ec); + SuccessOrExit(err); + + // Allocate a buffer for the null message. + msgBuf = System::PacketBufferHandle::New(kMsgCounterChallengeSize); + VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY); + + // Generate a 64-bit random number to uniquely identify the request. + err = DRBG_get_bytes(challenge, kMsgCounterChallengeSize); + SuccessOrExit(err); + + // Store generated Challenge value to ExchangeContext to resolve synchronization response. + ec->SetChallenge(challenge); + + memcpy(msgBuf->Start(), challenge, kMsgCounterChallengeSize); + msgBuf->SetDataLength(kMsgCounterChallengeSize); + + // TODO:(#4641) We shall also set the C flag in the packet header, this will be used for message protection later. + sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck, true).Set(Messaging::SendMessageFlags::kExpectResponse, true); + + // Arm a timer to enforce that a MsgCounterSyncRsp is received before kMsgCounterSyncTimeout. + ec->SetResponseTimeout(kMsgCounterSyncTimeout); + + // Send the message counter synchronization request in a Secure Channel Protocol::MsgCounterSyncReq message. + err = ec->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, std::move(msgBuf), sendFlags); + SuccessOrExit(err); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(ExchangeManager, "Failed to send message counter synchronization request with error:%s", ErrorStr(err)); + } + + return err; +} + +CHIP_ERROR SecureChannelMgr::SendMsgCounterSyncResp(Messaging::ExchangeContext * ec, SecureSessionHandle session) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Transport::PeerConnectionState * state = nullptr; + System::PacketBufferHandle msgBuf; + uint8_t * msg = nullptr; + + state = mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(session); + VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED); + + // Allocate new buffer. + msgBuf = System::PacketBufferHandle::New(kMsgCounterSyncRespMsgSize); + VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY); + + msg = msgBuf->Start(); + + // Let's construct the message using BufBound + { + BufBound bbuf(msg, kMsgCounterSyncRespMsgSize); + + // Write the message id (counter) field. + bbuf.Put32(state->GetSendMessageIndex()); + + // Fill in the random value + bbuf.Put(ec->GetChallenge(), kMsgCounterChallengeSize); + + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY); + } + + // Set message length. + msgBuf->SetDataLength(kMsgCounterSyncRespMsgSize); + + // Send message counter synchronization response message. + err = ec->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp, std::move(msgBuf), + Messaging::SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck)); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(ExchangeManager, "Failed to send message counter synchronization response with error:%s", ErrorStr(err)); + } + + return err; +} + +void SecureChannelMgr::HandleMsgCounterSyncReq(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, + System::PacketBufferHandle msgBuf) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + const uint8_t * req = msgBuf->Start(); + size_t reqlen = msgBuf->DataLength(); + + ChipLogDetail(ExchangeManager, "Received MsgCounterSyncReq request"); + + VerifyOrExit(packetHeader.GetSourceNodeId().HasValue(), err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()), err = CHIP_ERROR_WRONG_KEY_TYPE); + VerifyOrExit(req != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); + VerifyOrExit(reqlen == kMsgCounterChallengeSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + // Store the 64-bit value sent in the Challenge filed of the MsgCounterSyncReq. + ec->SetChallenge(req); + + // Respond with MsgCounterSyncResp + err = SendMsgCounterSyncResp(ec, { packetHeader.GetSourceNodeId().Value(), packetHeader.GetEncryptionKeyID() }); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncReq message with error:%s", ErrorStr(err)); + } + + if (ec != nullptr) + ec->Close(); + + return; +} + +void SecureChannelMgr::HandleMsgCounterSyncResp(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, + System::PacketBufferHandle msgBuf) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + uint32_t syncCounter = 0; + uint8_t challenge[kMsgCounterChallengeSize]; + + const uint8_t * resp = msgBuf->Start(); + size_t resplen = msgBuf->DataLength(); + + ChipLogDetail(ExchangeManager, "Received MsgCounterSyncResp response"); + + VerifyOrExit(msgBuf->DataLength() == kMsgCounterSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + VerifyOrExit(ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()), err = CHIP_ERROR_WRONG_KEY_TYPE); + + // Store the 64-bit value sent in the Challenge filed of the MsgCounterSyncReq. + VerifyOrExit(resp != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); + VerifyOrExit(resplen == kMsgCounterSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + syncCounter = chip::Encoding::LittleEndian::Read32(resp); + memcpy(challenge, resp, kMsgCounterChallengeSize); + + // Verify that the response field matches the expected Challenge field for the exchange. + VerifyOrExit(memcmp(ec->GetChallenge(), challenge, kMsgCounterChallengeSize) == 0, err = CHIP_ERROR_INVALID_SIGNATURE); + + // ToDo:(#4628)Initialize/synchronize peer's message counter to FabricState. + VerifyOrExit(syncCounter != 0, err = CHIP_ERROR_READ_FAILED); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncResp message with error:%s", ErrorStr(err)); + } + + if (ec != nullptr) + ec->Close(); + + return; +} + +} // namespace SecureChannel +} // namespace chip diff --git a/src/messaging/SecureChannelMgr.h b/src/messaging/SecureChannelMgr.h new file mode 100644 index 00000000000000..7d7b6640c2724e --- /dev/null +++ b/src/messaging/SecureChannelMgr.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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 defines types and objects for CHIP Secure Channel protocol. + * + */ + +#pragma once + +#include + +namespace chip { +namespace SecureChannel { + +constexpr uint16_t kMsgCounterSyncRespMsgSize = 12; // The size of the message counter synchronization response message. +constexpr uint32_t kMsgCounterSyncTimeout = 500; // The amount of time(in milliseconds) which a peer is given to respond + // to a message counter synchronization request. + +class ExchangeManager; + +class SecureChannelMgr : public Messaging::ExchangeDelegate +{ +public: + SecureChannelMgr(); + + CHIP_ERROR Init(Messaging::ExchangeManager * exchangeMgr); + void Shutdown(); + + /** + * Send peer message counter synchronization request. + * This function is called while processing a message encrypted with an application key from a peer whose message counter is not + * synchronized. This message is sent on a newly created exchange, which is closed immediately after. + * + * @param[in] session The secure session handle of the received message. + * + * @retval #CHIP_ERROR_NO_MEMORY If memory could not be allocated for the new + * exchange context or new message buffer. + * @retval #CHIP_NO_ERROR On success. + * + */ + CHIP_ERROR SendMsgCounterSyncReq(SecureSessionHandle session); + +private: + Messaging::ExchangeManager * mExchangeMgr; // [READ ONLY] Associated Exchange Manager object. + + CHIP_ERROR NewMsgCounterSyncExchange(SecureSessionHandle session, Messaging::ExchangeContext *& ec); + + CHIP_ERROR SendMsgCounterSyncResp(Messaging::ExchangeContext * ec, SecureSessionHandle session); + + void HandleMsgCounterSyncReq(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, + System::PacketBufferHandle msgBuf); + + void HandleMsgCounterSyncResp(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, + System::PacketBufferHandle msgBuf); + + void OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, + System::PacketBufferHandle payload) override; + + void OnResponseTimeout(Messaging::ExchangeContext * ec) override; +}; + +} // namespace SecureChannel +} // namespace chip diff --git a/src/messaging/tests/BUILD.gn b/src/messaging/tests/BUILD.gn index 5e72204ad35d7f..bc6d76bc4bb663 100644 --- a/src/messaging/tests/BUILD.gn +++ b/src/messaging/tests/BUILD.gn @@ -28,6 +28,7 @@ chip_test_suite("tests") { "TestExchangeMgr.cpp", "TestMessagingLayer.h", "TestReliableMessageProtocol.cpp", + "TestSecureChannelMgr.cpp", ] cflags = [ "-Wconversion" ] @@ -46,6 +47,7 @@ chip_test_suite("tests") { tests = [ "TestExchangeMgr", + "TestSecureChannelMgr", "TestReliableMessageProtocol", ] } diff --git a/src/messaging/tests/TestMessagingLayer.h b/src/messaging/tests/TestMessagingLayer.h index 7bdcef314c4c0b..bf50d519b5ced8 100644 --- a/src/messaging/tests/TestMessagingLayer.h +++ b/src/messaging/tests/TestMessagingLayer.h @@ -29,6 +29,7 @@ extern "C" { #endif int TestExchangeMgr(void); +int TestSecureChannelMgr(void); int TestReliableMessageProtocol(void); #ifdef __cplusplus diff --git a/src/messaging/tests/TestSecureChannelMgr.cpp b/src/messaging/tests/TestSecureChannelMgr.cpp new file mode 100644 index 00000000000000..641cbc491853dc --- /dev/null +++ b/src/messaging/tests/TestSecureChannelMgr.cpp @@ -0,0 +1,272 @@ +/* + * + * 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 SecureChannelMgr implementation. + */ + +#include "TestMessagingLayer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace { + +using namespace chip; +using namespace chip::Inet; +using namespace chip::Transport; +using namespace chip::Messaging; +using namespace chip::Protocols; + +using TestContext = chip::Test::MessagingContext; + +TestContext sContext; + +constexpr NodeId kSourceNodeId = 123654; +constexpr NodeId kDestinationNodeId = 111222333; +constexpr chip::NodeId kTestPeerGroupKeyId = 0x4000; +constexpr chip::NodeId kTestLocalGroupKeyId = 0x5000; + +class LoopbackTransport : public Transport::Base +{ +public: + /// Transports are required to have a constructor that takes exactly one argument + CHIP_ERROR Init(const char * unused) { return CHIP_NO_ERROR; } + + CHIP_ERROR SendMessage(const PacketHeader & header, const PeerAddress & address, System::PacketBufferHandle msgBuf) override + { + // We need to change the peerKeyId to local Key Id to be received by itself. + PacketHeader tmpHeader = header; + + tmpHeader.SetEncryptionKeyID(kTestLocalGroupKeyId); + HandleMessageReceived(tmpHeader, address, std::move(msgBuf)); + + return CHIP_NO_ERROR; + } + + bool CanSendToPeer(const PeerAddress & address) override { return true; } +}; + +class TestExchangeMgr : public SecureSessionMgrDelegate +{ +public: + void OnMessageReceived(const PacketHeader & header, const PayloadHeader & payloadHeader, SecureSessionHandle session, + System::PacketBufferHandle msgBuf, SecureSessionMgr * mgr) override + { + NL_TEST_ASSERT(mSuite, header.GetSourceNodeId() == Optional::Value(kSourceNodeId)); + NL_TEST_ASSERT(mSuite, header.GetDestinationNodeId() == Optional::Value(kDestinationNodeId)); + NL_TEST_ASSERT(mSuite, msgBuf->DataLength() == kMsgCounterChallengeSize); + + ReceiveHandlerCallCount++; + } + + void OnNewConnection(SecureSessionHandle session, SecureSessionMgr * mgr) override {} + + void OnConnectionExpired(SecureSessionHandle session, SecureSessionMgr * mgr) override {} + + nlTestSuite * mSuite = nullptr; + int ReceiveHandlerCallCount = 0; +}; + +class MockAppDelegate : public ExchangeDelegate +{ +public: + void OnMessageReceived(ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, + System::PacketBufferHandle msgBuf) override + { + IsOnMessageReceivedCalled = true; + + NL_TEST_ASSERT(mSuite, payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq)); + NL_TEST_ASSERT(mSuite, packetHeader.GetSourceNodeId() == Optional::Value(kSourceNodeId)); + NL_TEST_ASSERT(mSuite, packetHeader.GetDestinationNodeId() == Optional::Value(kDestinationNodeId)); + NL_TEST_ASSERT(mSuite, msgBuf->DataLength() == kMsgCounterChallengeSize); + } + + void OnResponseTimeout(ExchangeContext * ec) override {} + + nlTestSuite * mSuite = nullptr; + bool IsOnMessageReceivedCalled = false; +}; + +TransportMgr gTransportMgr; + +void CheckSendMsgCounterSyncReq(nlTestSuite * inSuite, void * inContext) +{ + TestContext & ctx = *reinterpret_cast(inContext); + + ctx.GetInetLayer().SystemLayer()->Init(nullptr); + + IPAddress addr; + IPAddress::FromString("127.0.0.1", addr); + + CHIP_ERROR err = CHIP_NO_ERROR; + TestExchangeMgr testExchangeMgr; + + testExchangeMgr.mSuite = inSuite; + ctx.GetSecureSessionManager().SetDelegate(&testExchangeMgr); + + chip::SecureChannel::SecureChannelMgr * sm = ctx.GetExchangeManager().GetSecureChannelMgr(); + NL_TEST_ASSERT(inSuite, sm != nullptr); + + Optional peer(Transport::PeerAddress::UDP(addr, CHIP_PORT)); + + SecurePairingUsingTestSecret pairingLocalToPeer(Optional::Value(kDestinationNodeId), kTestPeerGroupKeyId, + kTestLocalGroupKeyId); + + err = ctx.GetSecureSessionManager().NewPairing(peer, kDestinationNodeId, &pairingLocalToPeer, 0); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + SecurePairingUsingTestSecret pairingPeerToLocal(Optional::Value(kSourceNodeId), kTestLocalGroupKeyId, + kTestPeerGroupKeyId); + + err = ctx.GetSecureSessionManager().NewPairing(peer, kSourceNodeId, &pairingPeerToLocal, 1); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + SecureSessionHandle session(kDestinationNodeId, 0x4000); + + // Should be able to send a message to itself by just calling send. + testExchangeMgr.ReceiveHandlerCallCount = 0; + + err = sm->SendMsgCounterSyncReq(session); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, testExchangeMgr.ReceiveHandlerCallCount == 1); +} + +void CheckReceiveMsgCounterSyncReq(nlTestSuite * inSuite, void * inContext) +{ + TestContext & ctx = *reinterpret_cast(inContext); + + ctx.GetInetLayer().SystemLayer()->Init(nullptr); + + IPAddress addr; + IPAddress::FromString("127.0.0.1", addr); + + CHIP_ERROR err = CHIP_NO_ERROR; + MockAppDelegate mockAppDelegate; + + mockAppDelegate.mSuite = inSuite; + + chip::SecureChannel::SecureChannelMgr * sm = ctx.GetExchangeManager().GetSecureChannelMgr(); + NL_TEST_ASSERT(inSuite, sm != nullptr); + + // Register to receive unsolicited Secure Channel Request messages from the exchange manager. + err = + ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForProtocol(Protocols::kProtocol_SecureChannel, &mockAppDelegate); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + Optional peer(Transport::PeerAddress::UDP(addr, CHIP_PORT)); + + SecurePairingUsingTestSecret pairingLocalToPeer(Optional::Value(kDestinationNodeId), kTestPeerGroupKeyId, + kTestLocalGroupKeyId); + + err = ctx.GetSecureSessionManager().NewPairing(peer, kDestinationNodeId, &pairingLocalToPeer, 0); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + SecurePairingUsingTestSecret pairingPeerToLocal(Optional::Value(kSourceNodeId), kTestLocalGroupKeyId, + kTestPeerGroupKeyId); + + err = ctx.GetSecureSessionManager().NewPairing(peer, kSourceNodeId, &pairingPeerToLocal, 1); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + SecureSessionHandle session(kDestinationNodeId, 0x4000); + + err = sm->SendMsgCounterSyncReq(session); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mockAppDelegate.IsOnMessageReceivedCalled == true); +} + +// Test Suite + +/** + * Test Suite that lists all the test functions. + */ +// clang-format off +const nlTest sTests[] = +{ + NL_TEST_DEF("Test SecureChannelMgr::ReceiveMsgCounterSyncReq", CheckReceiveMsgCounterSyncReq), + NL_TEST_DEF("Test SecureChannelMgr::SendMsgCounterSyncReq", CheckSendMsgCounterSyncReq), + NL_TEST_SENTINEL() +}; +// clang-format on + +int Initialize(void * aContext); +int Finalize(void * aContext); + +// clang-format off +nlTestSuite sSuite = +{ + "Test-SecureChannelMgr", + &sTests[0], + Initialize, + Finalize +}; +// clang-format on + +/** + * Initialize the test suite. + */ +int Initialize(void * aContext) +{ + CHIP_ERROR err = gTransportMgr.Init("LOOPBACK"); + if (err != CHIP_NO_ERROR) + return FAILURE; + + err = reinterpret_cast(aContext)->Init(&sSuite, &gTransportMgr); + return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE; +} + +/** + * Finalize the test suite. + */ +int Finalize(void * aContext) +{ + CHIP_ERROR err = reinterpret_cast(aContext)->Shutdown(); + return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE; +} + +} // namespace + +/** + * Main + */ +int TestSecureChannelMgr() +{ + // Run test suit against one context + nlTestRunner(&sSuite, &sContext); + + return (nlTestRunnerStats(&sSuite)); +} diff --git a/src/messaging/tests/TestSecureChannelMgrDriver.cpp b/src/messaging/tests/TestSecureChannelMgrDriver.cpp new file mode 100644 index 00000000000000..e637457efc025f --- /dev/null +++ b/src/messaging/tests/TestSecureChannelMgrDriver.cpp @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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 a standalone/native program executable + * test driver for the SecureChannelMgr tests. + * + */ + +#include "TestMessagingLayer.h" + +#include + +int main() +{ + // Generate machine-readable, comma-separated value (CSV) output. + nlTestSetOutputStyle(OUTPUT_CSV); + + return (TestSecureChannelMgr()); +} diff --git a/src/transport/SecureSessionMgr.h b/src/transport/SecureSessionMgr.h index 6858d09ea08a8e..5be2c16d9c5222 100644 --- a/src/transport/SecureSessionMgr.h +++ b/src/transport/SecureSessionMgr.h @@ -56,6 +56,9 @@ class SecureSessionHandle return mPeerNodeId == that.mPeerNodeId && mPeerKeyId == that.mPeerKeyId; } + NodeId GetPeerNodeId() const { return mPeerNodeId; } + uint16_t GetPeerKeyId() const { return mPeerKeyId; } + private: friend class SecureSessionMgr; NodeId mPeerNodeId;