Skip to content

Commit

Permalink
Implement Message Counter Synchronization Protocol (MCSP) part
Browse files Browse the repository at this point in the history
  • Loading branch information
yufengwangca committed Feb 8, 2021
1 parent f4980c1 commit 16fd9bb
Show file tree
Hide file tree
Showing 12 changed files with 692 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/messaging/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ static_library("messaging") {
"ReliableMessageMgr.cpp",
"ReliableMessageMgr.h",
"ReliableMessageProtocolConfig.h",
"SecureChannelMgr.cpp",
"SecureChannelMgr.h",
]

cflags = [ "-Wconversion" ]
Expand Down
13 changes: 13 additions & 0 deletions src/messaging/ExchangeContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
#include <transport/SecureSessionMgr.h>

namespace chip {

constexpr uint16_t kMsgCounterChallengeSize = 8; // The size of the message counter synchronization request message.

namespace Messaging {

class ExchangeManager;
Expand Down Expand Up @@ -149,6 +152,12 @@ class DLL_EXPORT ExchangeContext : public ReferenceCounted<ExchangeContext, Exch

uint16_t GetExchangeId() const { return mExchangeId; }

void SetChallenge(const uint8_t * value) { memcpy(&mChallenage[0], value, kMsgCounterChallengeSize); }

const uint8_t * GetChallenge() const { return mChallenage; }

SecureSessionHandle GetSecureSessionHandle() const { return mSecureSession; }

/*
* In order to use reference counting (see refCount below) we use a hold/free paradigm where users of the exchange
* can hold onto it while it's out of their direct control to make sure it isn't closed before everyone's ready.
Expand Down Expand Up @@ -179,6 +188,10 @@ class DLL_EXPORT ExchangeContext : public ReferenceCounted<ExchangeContext, Exch
SecureSessionHandle mSecureSession; // The connection state
uint16_t mExchangeId; // Assigned exchange ID.

// [TODO: #4711]: this field need to be moved to appState object which implement 'exchange-specific' contextual
// actions with a delegate pattern.
uint8_t mChallenage[kMsgCounterChallengeSize]; // Challenge number to identify the sychronization request cryptographically.

BitFlags<uint16_t, ExFlagValues> mFlags; // Internal state flags

/**
Expand Down
13 changes: 10 additions & 3 deletions src/messaging/ExchangeMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/messaging/ExchangeMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <messaging/ExchangeContext.h>
#include <messaging/ReliableMessageMgr.h>
#include <messaging/SecureChannelMgr.h>
#include <support/DLLUtil.h>
#include <transport/SecureSessionMgr.h>

Expand Down Expand Up @@ -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:
Expand All @@ -197,6 +200,7 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate
State mState;
SecureSessionMgr * mSessionMgr;
ReliableMessageMgr mReliableMessageMgr;
SecureChannel::SecureChannelMgr mSecureChannelMgr;

std::array<ExchangeContext, CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS> mContextPool;
size_t mContextsInUse;
Expand Down
3 changes: 2 additions & 1 deletion src/messaging/ReliableMessageMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include <messaging/ReliableMessageMgr.h>

#include <core/CHIPKeyIds.h>
#include <messaging/ErrorCategory.h>
#include <messaging/Flags.h>
#include <messaging/ReliableMessageContext.h>
Expand Down Expand Up @@ -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.
*
Expand Down
270 changes: 270 additions & 0 deletions src/messaging/SecureChannelMgr.cpp
Original file line number Diff line number Diff line change
@@ -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 <core/CHIPCore.h>
#include <core/CHIPEncoding.h>
#include <core/CHIPKeyIds.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <messaging/SecureChannelMgr.h>
#include <protocols/Protocols.h>
#include <support/BufferWriter.h>
#include <support/CodeUtils.h>
#include <support/logging/CHIPLogging.h>

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
{
Encoding::LittleEndian::BufferWriter 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
Loading

0 comments on commit 16fd9bb

Please sign in to comment.