3 * Copyright (c) 2021 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * This file implements the CHIP Secure Channel protocol.
24 #include <core/CHIPCore.h>
25 #include <core/CHIPEncoding.h>
26 #include <core/CHIPKeyIds.h>
27 #include <messaging/ExchangeContext.h>
28 #include <messaging/ExchangeMgr.h>
29 #include <messaging/Flags.h>
30 #include <messaging/MessageCounterSync.h>
31 #include <protocols/Protocols.h>
32 #include <support/BufferWriter.h>
33 #include <support/CodeUtils.h>
34 #include <support/logging/CHIPLogging.h>
39 CHIP_ERROR MessageCounterSyncMgr::Init(Messaging::ExchangeManager * exchangeMgr)
41 CHIP_ERROR err = CHIP_NO_ERROR;
43 VerifyOrReturnError(exchangeMgr != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
44 mExchangeMgr = exchangeMgr;
46 // Register to receive unsolicited Secure Channel Request messages from the exchange manager.
47 err = mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::SecureChannel::Id, this);
49 ReturnErrorOnFailure(err);
54 void MessageCounterSyncMgr::Shutdown()
56 if (mExchangeMgr != nullptr)
58 mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::SecureChannel::Id);
59 mExchangeMgr = nullptr;
63 void MessageCounterSyncMgr::OnMessageReceived(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader,
64 const PayloadHeader & payloadHeader, System::PacketBufferHandle msgBuf)
66 if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq))
68 HandleMsgCounterSyncReq(exchangeContext, packetHeader, std::move(msgBuf));
70 else if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp))
72 HandleMsgCounterSyncResp(exchangeContext, packetHeader, std::move(msgBuf));
76 void MessageCounterSyncMgr::OnResponseTimeout(Messaging::ExchangeContext * exchangeContext)
78 Transport::PeerConnectionState * state =
79 mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(exchangeContext->GetSecureSessionHandle());
83 state->SetMsgCounterSyncInProgress(false);
87 ChipLogError(ExchangeManager, "Timed out! Failed to clear message counter synchronization status.");
90 // Close the exchange if MsgCounterSyncRsp is not received before kMsgCounterSyncTimeout.
91 if (exchangeContext != nullptr)
92 exchangeContext->Close();
95 CHIP_ERROR MessageCounterSyncMgr::AddToRetransmissionTable(Protocols::Id protocolId, uint8_t msgType, const SendFlags & sendFlags,
96 System::PacketBufferHandle msgBuf,
97 Messaging::ExchangeContext * exchangeContext)
100 CHIP_ERROR err = CHIP_NO_ERROR;
102 VerifyOrReturnError(exchangeContext != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
104 for (RetransTableEntry & entry : mRetransTable)
106 // Entries are in use if they have an exchangeContext.
107 if (entry.exchangeContext == nullptr)
109 entry.protocolId = protocolId;
110 entry.msgType = msgType;
111 entry.msgBuf = std::move(msgBuf);
112 entry.exchangeContext = exchangeContext;
113 entry.exchangeContext->Retain();
122 ChipLogError(ExchangeManager, "MCSP RetransTable Already Full");
123 err = CHIP_ERROR_NO_MEMORY;
130 * Retransmit all pending messages that were encrypted with application
131 * group key and were addressed to the specified node.
133 * @param[in] peerNodeId Node ID of the destination node.
136 void MessageCounterSyncMgr::RetransPendingGroupMsgs(NodeId peerNodeId)
138 // Find all retransmit entries matching peerNodeId. Note that everything in
139 // this table was using an application group key; that's why it was added.
140 for (RetransTableEntry & entry : mRetransTable)
142 if (entry.exchangeContext != nullptr && entry.exchangeContext->GetSecureSession().GetPeerNodeId() == peerNodeId)
144 // Retramsmit message.
146 entry.exchangeContext->SendMessage(entry.protocolId, entry.msgType, std::move(entry.msgBuf), entry.sendFlags);
148 if (err != CHIP_NO_ERROR)
150 ChipLogError(ExchangeManager, "Failed to resend cached group message to node: %d with error:%s", peerNodeId,
154 entry.exchangeContext->Release();
155 entry.exchangeContext = nullptr;
160 CHIP_ERROR MessageCounterSyncMgr::AddToReceiveTable(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
161 const SecureSessionHandle & session, System::PacketBufferHandle msgBuf)
164 CHIP_ERROR err = CHIP_NO_ERROR;
166 for (ReceiveTableEntry & entry : mReceiveTable)
168 // Entries are in use if they have a message buffer.
169 if (entry.msgBuf.IsNull())
171 entry.packetHeader = packetHeader;
172 entry.payloadHeader = payloadHeader;
173 entry.session = session;
174 entry.msgBuf = std::move(msgBuf);
183 ChipLogError(ExchangeManager, "MCSP ReceiveTable Already Full");
184 err = CHIP_ERROR_NO_MEMORY;
191 * Reprocess all pending messages that were encrypted with application
192 * group key and were addressed to the specified node id.
194 * @param[in] peerNodeId Node ID of the destination node.
197 void MessageCounterSyncMgr::ProcessPendingGroupMsgs(NodeId peerNodeId)
199 // Find all receive entries matching peerNodeId. Note that everything in
200 // this table was using an application group key; that's why it was added.
201 for (ReceiveTableEntry & entry : mReceiveTable)
203 if (!entry.msgBuf.IsNull() && entry.session.GetPeerNodeId() == peerNodeId)
205 // Reprocess message.
206 mExchangeMgr->HandleGroupMessageReceived(entry.packetHeader, entry.payloadHeader, entry.session,
207 std::move(entry.msgBuf));
209 // Explicitly free any buffer owned by this handle. The
210 // HandleGroupMessageReceived() call should really handle this, but
211 // just in case it messes up we don't want to get confused about
212 // wheter the entry is in use.
213 entry.msgBuf = nullptr;
218 // Create and initialize new exchange for the message counter synchronization request/response messages.
219 CHIP_ERROR MessageCounterSyncMgr::NewMsgCounterSyncExchange(SecureSessionHandle session,
220 Messaging::ExchangeContext *& exchangeContext)
222 CHIP_ERROR err = CHIP_NO_ERROR;
224 // Message counter synchronization protocol is only applicable for application group keys.
225 VerifyOrReturnError(ChipKeyId::IsAppGroupKey(session.GetPeerKeyId()), err = CHIP_ERROR_INVALID_ARGUMENT);
227 // Create new exchange context.
228 exchangeContext = mExchangeMgr->NewContext(session, this);
229 VerifyOrReturnError(exchangeContext != nullptr, err = CHIP_ERROR_NO_MEMORY);
234 CHIP_ERROR MessageCounterSyncMgr::SendMsgCounterSyncReq(SecureSessionHandle session)
236 CHIP_ERROR err = CHIP_NO_ERROR;
238 Messaging::ExchangeContext * exchangeContext = nullptr;
239 Transport::PeerConnectionState * state = nullptr;
240 System::PacketBufferHandle msgBuf;
241 Messaging::SendFlags sendFlags;
242 uint8_t challenge[kMsgCounterChallengeSize];
244 state = mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(session);
245 VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
247 // Create and initialize new exchange.
248 err = NewMsgCounterSyncExchange(session, exchangeContext);
251 // Allocate a buffer for the null message.
252 msgBuf = MessagePacketBuffer::New(kMsgCounterChallengeSize);
253 VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
255 // Generate a 64-bit random number to uniquely identify the request.
256 err = DRBG_get_bytes(challenge, kMsgCounterChallengeSize);
259 // Store generated Challenge value to ExchangeContext to resolve synchronization response.
260 exchangeContext->SetChallenge(challenge);
262 memcpy(msgBuf->Start(), challenge, kMsgCounterChallengeSize);
263 msgBuf->SetDataLength(kMsgCounterChallengeSize);
265 sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck, true).Set(Messaging::SendMessageFlags::kExpectResponse, true);
267 // Arm a timer to enforce that a MsgCounterSyncRsp is received before kMsgCounterSyncTimeout.
268 exchangeContext->SetResponseTimeout(kMsgCounterSyncTimeout);
270 // Send the message counter synchronization request in a Secure Channel Protocol::MsgCounterSyncReq message.
271 err = exchangeContext->SendMessageImpl(Protocols::SecureChannel::Id,
272 static_cast<uint8_t>(Protocols::SecureChannel::MsgType::MsgCounterSyncReq),
273 std::move(msgBuf), sendFlags);
276 state->SetMsgCounterSyncInProgress(true);
279 if (err != CHIP_NO_ERROR)
281 ChipLogError(ExchangeManager, "Failed to send message counter synchronization request with error:%s", ErrorStr(err));
287 CHIP_ERROR MessageCounterSyncMgr::SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, SecureSessionHandle session)
289 CHIP_ERROR err = CHIP_NO_ERROR;
290 Transport::PeerConnectionState * state = nullptr;
291 System::PacketBufferHandle msgBuf;
292 uint8_t * msg = nullptr;
294 state = mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(session);
295 VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
297 // Allocate new buffer.
298 msgBuf = System::PacketBufferHandle::New(kMsgCounterSyncRespMsgSize);
299 VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
301 msg = msgBuf->Start();
303 // Let's construct the message using BufBound
305 Encoding::LittleEndian::BufferWriter bbuf(msg, kMsgCounterSyncRespMsgSize);
307 // Write the message id (counter) field.
308 bbuf.Put32(state->GetSendMessageIndex());
310 // Fill in the random value
311 bbuf.Put(exchangeContext->GetChallenge(), kMsgCounterChallengeSize);
313 VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY);
316 // Set message length.
317 msgBuf->SetDataLength(kMsgCounterSyncRespMsgSize);
319 // Send message counter synchronization response message.
320 err = exchangeContext->SendMessageImpl(Protocols::SecureChannel::Id,
321 static_cast<uint8_t>(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp),
322 std::move(msgBuf), Messaging::SendFlags(Messaging::SendMessageFlags::kNoAutoRequestAck));
325 if (err != CHIP_NO_ERROR)
327 ChipLogError(ExchangeManager, "Failed to send message counter synchronization response with error:%s", ErrorStr(err));
333 void MessageCounterSyncMgr::HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader,
334 System::PacketBufferHandle msgBuf)
336 CHIP_ERROR err = CHIP_NO_ERROR;
338 const uint8_t * req = msgBuf->Start();
339 size_t reqlen = msgBuf->DataLength();
341 ChipLogDetail(ExchangeManager, "Received MsgCounterSyncReq request");
343 VerifyOrExit(packetHeader.GetSourceNodeId().HasValue(), err = CHIP_ERROR_INVALID_ARGUMENT);
344 VerifyOrExit(ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()), err = CHIP_ERROR_WRONG_KEY_TYPE);
345 VerifyOrExit(req != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE);
346 VerifyOrExit(reqlen == kMsgCounterChallengeSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
348 // Store the 64-bit value sent in the Challenge filed of the MsgCounterSyncReq.
349 exchangeContext->SetChallenge(req);
351 // Respond with MsgCounterSyncResp
352 err = SendMsgCounterSyncResp(exchangeContext, { packetHeader.GetSourceNodeId().Value(), packetHeader.GetEncryptionKeyID(), 0 });
355 if (err != CHIP_NO_ERROR)
357 ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncReq message with error:%s", ErrorStr(err));
360 if (exchangeContext != nullptr)
361 exchangeContext->Close();
366 void MessageCounterSyncMgr::HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext,
367 const PacketHeader & packetHeader, System::PacketBufferHandle msgBuf)
369 CHIP_ERROR err = CHIP_NO_ERROR;
371 Transport::PeerConnectionState * state = nullptr;
372 NodeId peerNodeId = 0;
373 uint32_t syncCounter = 0;
374 uint8_t challenge[kMsgCounterChallengeSize];
376 const uint8_t * resp = msgBuf->Start();
377 size_t resplen = msgBuf->DataLength();
379 ChipLogDetail(ExchangeManager, "Received MsgCounterSyncResp response");
381 // Find an active connection to the specified peer node
382 state = mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(exchangeContext->GetSecureSessionHandle());
383 VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
385 state->SetMsgCounterSyncInProgress(false);
387 VerifyOrExit(msgBuf->DataLength() == kMsgCounterSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
388 VerifyOrExit(ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()), err = CHIP_ERROR_WRONG_KEY_TYPE);
390 // Store the 64-bit value sent in the Challenge filed of the MsgCounterSyncReq.
391 VerifyOrExit(resp != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE);
392 VerifyOrExit(resplen == kMsgCounterSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
394 syncCounter = chip::Encoding::LittleEndian::Read32(resp);
395 VerifyOrExit(syncCounter != 0, err = CHIP_ERROR_READ_FAILED);
397 memcpy(challenge, resp, kMsgCounterChallengeSize);
399 // Verify that the response field matches the expected Challenge field for the exchange.
400 VerifyOrExit(memcmp(exchangeContext->GetChallenge(), challenge, kMsgCounterChallengeSize) == 0,
401 err = CHIP_ERROR_INVALID_SIGNATURE);
403 VerifyOrExit(packetHeader.GetSourceNodeId().HasValue(), err = CHIP_ERROR_INVALID_ARGUMENT);
404 peerNodeId = packetHeader.GetSourceNodeId().Value();
406 // Process all queued ougoing and incomming group messages after message counter synchronization is completed.
407 RetransPendingGroupMsgs(peerNodeId);
408 ProcessPendingGroupMsgs(peerNodeId);
411 if (err != CHIP_NO_ERROR)
413 ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncResp message with error:%s", ErrorStr(err));
416 if (exchangeContext != nullptr)
417 exchangeContext->Close();
422 } // namespace Messaging