From a9497739cf6ca41acc98900c0c0ca5c9c4cf7555 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <44623591+yufengwangca@users.noreply.github.com> Date: Wed, 17 Feb 2021 10:10:50 -0800 Subject: [PATCH] Implement Message Counter Synchronization Protocol part (#4712) --- src/messaging/BUILD.gn | 2 + src/messaging/ExchangeContext.h | 13 + src/messaging/ExchangeMgr.cpp | 12 +- src/messaging/ExchangeMgr.h | 4 + src/messaging/MessageCounterSync.cpp | 266 +++++++++++++++++ src/messaging/MessageCounterSync.h | 76 +++++ src/messaging/tests/BUILD.gn | 2 + .../tests/TestMessageCounterSyncMgr.cpp | 272 ++++++++++++++++++ .../tests/TestMessageCounterSyncMgrDriver.cpp | 35 +++ src/messaging/tests/TestMessagingLayer.h | 1 + src/transport/SecureSessionMgr.h | 3 + 11 files changed, 683 insertions(+), 3 deletions(-) create mode 100644 src/messaging/MessageCounterSync.cpp create mode 100644 src/messaging/MessageCounterSync.h create mode 100644 src/messaging/tests/TestMessageCounterSyncMgr.cpp create mode 100644 src/messaging/tests/TestMessageCounterSyncMgrDriver.cpp diff --git a/src/messaging/BUILD.gn b/src/messaging/BUILD.gn index e3ef612a761296..766354d0a4ba70 100644 --- a/src/messaging/BUILD.gn +++ b/src/messaging/BUILD.gn @@ -27,6 +27,8 @@ static_library("messaging") { "ExchangeMgr.cpp", "ExchangeMgr.h", "Flags.h", + "MessageCounterSync.cpp", + "MessageCounterSync.h", "ReliableMessageContext.cpp", "ReliableMessageContext.h", "ReliableMessageMgr.cpp", diff --git a/src/messaging/ExchangeContext.h b/src/messaging/ExchangeContext.h index 9f14609c5caa20..340a902916d952 100644 --- a/src/messaging/ExchangeContext.h +++ b/src/messaging/ExchangeContext.h @@ -34,6 +34,9 @@ #include namespace chip { + +constexpr uint16_t kMsgCounterChallengeSize = 8; // The size of the message counter synchronization request message. + namespace Messaging { class ExchangeManager; @@ -164,6 +167,12 @@ class DLL_EXPORT ExchangeContext : public ReferenceCounted mFlags; // Internal state flags /** diff --git a/src/messaging/ExchangeMgr.cpp b/src/messaging/ExchangeMgr.cpp index 76bd6dca62d234..54f27b66e8ea14 100644 --- a/src/messaging/ExchangeMgr.cpp +++ b/src/messaging/ExchangeMgr.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include using namespace chip::Encoding; @@ -66,8 +67,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; + + VerifyOrReturnError(mState == State::kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE); mSessionMgr = sessionMgr; @@ -82,13 +84,17 @@ CHIP_ERROR ExchangeManager::Init(SecureSessionMgr * sessionMgr) mReliableMessageMgr.Init(sessionMgr->SystemLayer(), sessionMgr); + err = mMessageCounterSyncMgr.Init(this); + ReturnErrorOnFailure(err); + mState = State::kState_Initialized; - return CHIP_NO_ERROR; + return err; } CHIP_ERROR ExchangeManager::Shutdown() { + mMessageCounterSyncMgr.Shutdown(); mReliableMessageMgr.Shutdown(); if (mSessionMgr != nullptr) diff --git a/src/messaging/ExchangeMgr.h b/src/messaging/ExchangeMgr.h index 4b17d7b81f5088..7ab119c1235720 100644 --- a/src/messaging/ExchangeMgr.h +++ b/src/messaging/ExchangeMgr.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -177,6 +178,8 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate ReliableMessageMgr * GetReliableMessageMgr() { return &mReliableMessageMgr; }; + MessageCounterSyncMgr * GetMessageCounterSyncMgr() { return &mMessageCounterSyncMgr; }; + size_t GetContextsInUse() const { return mContextsInUse; } private: @@ -197,6 +200,7 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate State mState; SecureSessionMgr * mSessionMgr; ReliableMessageMgr mReliableMessageMgr; + MessageCounterSyncMgr mMessageCounterSyncMgr; std::array mContextPool; size_t mContextsInUse; diff --git a/src/messaging/MessageCounterSync.cpp b/src/messaging/MessageCounterSync.cpp new file mode 100644 index 00000000000000..829c49b85a9e82 --- /dev/null +++ b/src/messaging/MessageCounterSync.cpp @@ -0,0 +1,266 @@ +/* + * + * 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 +#include + +namespace chip { +namespace Messaging { + +CHIP_ERROR MessageCounterSyncMgr::Init(Messaging::ExchangeManager * exchangeMgr) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrReturnError(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); + + ReturnErrorOnFailure(err); + + return err; +} + +void MessageCounterSyncMgr::Shutdown() +{ + if (mExchangeMgr != nullptr) + { + mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::kProtocol_SecureChannel); + mExchangeMgr = nullptr; + } +} + +void MessageCounterSyncMgr::OnMessageReceived(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader, + const PayloadHeader & payloadHeader, System::PacketBufferHandle msgBuf) +{ + if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq)) + { + HandleMsgCounterSyncReq(exchangeContext, packetHeader, std::move(msgBuf)); + } + else if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp)) + { + HandleMsgCounterSyncResp(exchangeContext, packetHeader, std::move(msgBuf)); + } +} + +void MessageCounterSyncMgr::OnResponseTimeout(Messaging::ExchangeContext * exchangeContext) +{ + // Close the exchange if MsgCounterSyncRsp is not received before kMsgCounterSyncTimeout. + if (exchangeContext != nullptr) + exchangeContext->Close(); +} + +// Create and initialize new exchange for the message counter synchronization request/response messages. +CHIP_ERROR MessageCounterSyncMgr::NewMsgCounterSyncExchange(SecureSessionHandle session, + Messaging::ExchangeContext *& exchangeContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Message counter synchronization protocol is only applicable for application group keys. + VerifyOrReturnError(ChipKeyId::IsAppGroupKey(session.GetPeerKeyId()), err = CHIP_ERROR_INVALID_ARGUMENT); + + // Create new exchange context. + exchangeContext = mExchangeMgr->NewContext(session, this); + VerifyOrReturnError(exchangeContext != nullptr, err = CHIP_ERROR_NO_MEMORY); + + return err; +} + +CHIP_ERROR MessageCounterSyncMgr::SendMsgCounterSyncReq(SecureSessionHandle session) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Messaging::ExchangeContext * exchangeContext = nullptr; + System::PacketBufferHandle msgBuf; + Messaging::SendFlags sendFlags; + uint8_t challenge[kMsgCounterChallengeSize]; + + // Create and initialize new exchange. + err = NewMsgCounterSyncExchange(session, exchangeContext); + SuccessOrExit(err); + + // Allocate a buffer for the null message. + msgBuf = MessagePacketBuffer::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. + exchangeContext->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. + exchangeContext->SetResponseTimeout(kMsgCounterSyncTimeout); + + // Send the message counter synchronization request in a Secure Channel Protocol::MsgCounterSyncReq message. + err = exchangeContext->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 MessageCounterSyncMgr::SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, 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 + { + Encoding::LittleEndian::BufferWriter bbuf(msg, kMsgCounterSyncRespMsgSize); + + // Write the message id (counter) field. + bbuf.Put32(state->GetSendMessageIndex()); + + // Fill in the random value + bbuf.Put(exchangeContext->GetChallenge(), kMsgCounterChallengeSize); + + VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY); + } + + // Set message length. + msgBuf->SetDataLength(kMsgCounterSyncRespMsgSize); + + // Send message counter synchronization response message. + err = exchangeContext->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 MessageCounterSyncMgr::HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext, 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. + exchangeContext->SetChallenge(req); + + // Respond with MsgCounterSyncResp + err = SendMsgCounterSyncResp(exchangeContext, { packetHeader.GetSourceNodeId().Value(), packetHeader.GetEncryptionKeyID(), 0 }); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncReq message with error:%s", ErrorStr(err)); + } + + if (exchangeContext != nullptr) + exchangeContext->Close(); + + return; +} + +void MessageCounterSyncMgr::HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, + 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(exchangeContext->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 (exchangeContext != nullptr) + exchangeContext->Close(); + + return; +} + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/MessageCounterSync.h b/src/messaging/MessageCounterSync.h new file mode 100644 index 00000000000000..efcae568110019 --- /dev/null +++ b/src/messaging/MessageCounterSync.h @@ -0,0 +1,76 @@ +/* + * + * 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 + +namespace chip { +namespace Messaging { + +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 MessageCounterSyncMgr : public Messaging::ExchangeDelegate +{ +public: + MessageCounterSyncMgr() : mExchangeMgr(nullptr) {} + + 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 *& exchangeContext); + + CHIP_ERROR SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, SecureSessionHandle session); + + void HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader, + System::PacketBufferHandle msgBuf); + + void HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader, + System::PacketBufferHandle msgBuf); + + void OnMessageReceived(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader, + const PayloadHeader & payloadHeader, System::PacketBufferHandle payload) override; + + void OnResponseTimeout(Messaging::ExchangeContext * exchangeContext) override; +}; + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/tests/BUILD.gn b/src/messaging/tests/BUILD.gn index 5e72204ad35d7f..b8975653459c95 100644 --- a/src/messaging/tests/BUILD.gn +++ b/src/messaging/tests/BUILD.gn @@ -26,6 +26,7 @@ chip_test_suite("tests") { "MessagingContext.cpp", "MessagingContext.h", "TestExchangeMgr.cpp", + "TestMessageCounterSyncMgr.cpp", "TestMessagingLayer.h", "TestReliableMessageProtocol.cpp", ] @@ -46,6 +47,7 @@ chip_test_suite("tests") { tests = [ "TestExchangeMgr", + "TestMessageCounterSyncMgr", "TestReliableMessageProtocol", ] } diff --git a/src/messaging/tests/TestMessageCounterSyncMgr.cpp b/src/messaging/tests/TestMessageCounterSyncMgr.cpp new file mode 100644 index 00000000000000..3301dbdbf5cd3a --- /dev/null +++ b/src/messaging/tests/TestMessageCounterSyncMgr.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 MessageCounterSyncMgr 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); + + MessageCounterSyncMgr * sm = ctx.GetExchangeManager().GetMessageCounterSyncMgr(); + 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, 0); + + // 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; + + MessageCounterSyncMgr * sm = ctx.GetExchangeManager().GetMessageCounterSyncMgr(); + 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, 0); + + 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 MessageCounterSyncMgr::ReceiveMsgCounterSyncReq", CheckReceiveMsgCounterSyncReq), + NL_TEST_DEF("Test MessageCounterSyncMgr::SendMsgCounterSyncReq", CheckSendMsgCounterSyncReq), + NL_TEST_SENTINEL() +}; +// clang-format on + +int Initialize(void * aContext); +int Finalize(void * aContext); + +// clang-format off +nlTestSuite sSuite = +{ + "Test-MessageCounterSyncMgr", + &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 TestMessageCounterSyncMgr() +{ + // Run test suit against one context + nlTestRunner(&sSuite, &sContext); + + return (nlTestRunnerStats(&sSuite)); +} diff --git a/src/messaging/tests/TestMessageCounterSyncMgrDriver.cpp b/src/messaging/tests/TestMessageCounterSyncMgrDriver.cpp new file mode 100644 index 00000000000000..0bf0a9aa49c677 --- /dev/null +++ b/src/messaging/tests/TestMessageCounterSyncMgrDriver.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 (TestMessageCounterSyncMgr()); +} diff --git a/src/messaging/tests/TestMessagingLayer.h b/src/messaging/tests/TestMessagingLayer.h index 7bdcef314c4c0b..fbab413701831b 100644 --- a/src/messaging/tests/TestMessagingLayer.h +++ b/src/messaging/tests/TestMessagingLayer.h @@ -29,6 +29,7 @@ extern "C" { #endif int TestExchangeMgr(void); +int TestMessageCounterSyncMgr(void); int TestReliableMessageProtocol(void); #ifdef __cplusplus diff --git a/src/transport/SecureSessionMgr.h b/src/transport/SecureSessionMgr.h index 098bdd4a074546..443262006894f9 100644 --- a/src/transport/SecureSessionMgr.h +++ b/src/transport/SecureSessionMgr.h @@ -62,6 +62,9 @@ class SecureSessionHandle return mPeerNodeId == that.mPeerNodeId && mPeerKeyId == that.mPeerKeyId && mAdmin == that.mAdmin; } + NodeId GetPeerNodeId() const { return mPeerNodeId; } + uint16_t GetPeerKeyId() const { return mPeerKeyId; } + private: friend class SecureSessionMgr; NodeId mPeerNodeId;