Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : *
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
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
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.
16 : */
17 :
18 : /**
19 : * @file
20 : * This file implements the CHIP message counter messages in secure channel protocol.
21 : *
22 : */
23 :
24 : #include <protocols/secure_channel/MessageCounterManager.h>
25 :
26 : #include <lib/core/CHIPCore.h>
27 : #include <lib/core/CHIPEncoding.h>
28 : #include <lib/core/CHIPKeyIds.h>
29 : #include <lib/support/BufferWriter.h>
30 : #include <lib/support/CodeUtils.h>
31 : #include <lib/support/logging/CHIPLogging.h>
32 : #include <messaging/ExchangeContext.h>
33 : #include <messaging/ExchangeMgr.h>
34 : #include <messaging/Flags.h>
35 : #include <protocols/Protocols.h>
36 : #include <protocols/secure_channel/Constants.h>
37 :
38 : namespace chip {
39 : namespace secure_channel {
40 :
41 : constexpr System::Clock::Timeout MessageCounterManager::kSyncTimeout;
42 :
43 427 : CHIP_ERROR MessageCounterManager::Init(Messaging::ExchangeManager * exchangeMgr)
44 : {
45 427 : VerifyOrReturnError(exchangeMgr != nullptr, CHIP_ERROR_INCORRECT_STATE);
46 427 : mExchangeMgr = exchangeMgr;
47 :
48 427 : ReturnErrorOnFailure(
49 : mExchangeMgr->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, this));
50 :
51 427 : return CHIP_NO_ERROR;
52 : }
53 :
54 427 : void MessageCounterManager::Shutdown()
55 : {
56 427 : if (mExchangeMgr != nullptr)
57 : {
58 427 : TEMPORARY_RETURN_IGNORED mExchangeMgr->UnregisterUnsolicitedMessageHandlerForType(
59 : Protocols::SecureChannel::MsgType::MsgCounterSyncReq);
60 427 : mExchangeMgr->CloseAllContextsForDelegate(this);
61 427 : mExchangeMgr = nullptr;
62 : }
63 427 : }
64 :
65 0 : CHIP_ERROR MessageCounterManager::StartSync(const SessionHandle & session, Transport::SecureSession * state)
66 : {
67 : // Initiate message counter synchronization if no message counter synchronization is in progress.
68 0 : Transport::PeerMessageCounter & counter = state->GetSessionMessageCounter().GetPeerMessageCounter();
69 0 : if (!counter.IsSynchronizing() && !counter.IsSynchronized())
70 : {
71 0 : ReturnErrorOnFailure(SendMsgCounterSyncReq(session, state));
72 : }
73 :
74 0 : return CHIP_NO_ERROR;
75 : }
76 :
77 0 : CHIP_ERROR MessageCounterManager::QueueReceivedMessageAndStartSync(const PacketHeader & packetHeader, const SessionHandle & session,
78 : Transport::SecureSession * state,
79 : const Transport::PeerAddress & peerAddress,
80 : System::PacketBufferHandle && msgBuf)
81 : {
82 : // Queue the message to be reprocessed when sync completes.
83 0 : ReturnErrorOnFailure(AddToReceiveTable(packetHeader, peerAddress, std::move(msgBuf)));
84 0 : ReturnErrorOnFailure(StartSync(session, state));
85 :
86 : // After the message that triggers message counter synchronization is stored, and a message counter
87 : // synchronization exchange is initiated, we need to return immediately and re-process the original message
88 : // when the synchronization is completed.
89 :
90 0 : return CHIP_NO_ERROR;
91 : }
92 :
93 0 : CHIP_ERROR MessageCounterManager::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate)
94 : {
95 : // MessageCounterManager do not use an extra context to handle messages
96 0 : newDelegate = this;
97 0 : return CHIP_NO_ERROR;
98 : }
99 :
100 0 : CHIP_ERROR MessageCounterManager::OnMessageReceived(Messaging::ExchangeContext * exchangeContext,
101 : const PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf)
102 : {
103 0 : if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq))
104 : {
105 0 : return HandleMsgCounterSyncReq(exchangeContext, std::move(msgBuf));
106 : }
107 0 : if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp))
108 : {
109 0 : return HandleMsgCounterSyncResp(exchangeContext, std::move(msgBuf));
110 : }
111 0 : return CHIP_NO_ERROR;
112 : }
113 :
114 0 : void MessageCounterManager::OnResponseTimeout(Messaging::ExchangeContext * exchangeContext)
115 : {
116 0 : if (exchangeContext->HasSessionHandle())
117 : {
118 0 : exchangeContext->GetSessionHandle()->AsSecureSession()->GetSessionMessageCounter().GetPeerMessageCounter().SyncFailed();
119 : }
120 : else
121 : {
122 0 : ChipLogError(SecureChannel, "MCSP Timeout! On a already released session.");
123 : }
124 0 : }
125 :
126 0 : CHIP_ERROR MessageCounterManager::AddToReceiveTable(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress,
127 : System::PacketBufferHandle && msgBuf)
128 : {
129 0 : ReturnErrorOnFailure(packetHeader.EncodeBeforeData(msgBuf));
130 :
131 0 : for (ReceiveTableEntry & entry : mReceiveTable)
132 : {
133 0 : if (entry.msgBuf.IsNull())
134 : {
135 0 : entry.peerAddress = peerAddress;
136 0 : entry.msgBuf = std::move(msgBuf);
137 :
138 0 : return CHIP_NO_ERROR;
139 : }
140 : }
141 :
142 0 : ChipLogError(SecureChannel, "MCSP ReceiveTable Already Full");
143 0 : return CHIP_ERROR_NO_MEMORY;
144 : }
145 :
146 : /**
147 : * Reprocess all pending messages that were encrypted with application
148 : * group key and were addressed to the specified node id.
149 : *
150 : * @param[in] peerNodeId Node ID of the destination node.
151 : *
152 : */
153 0 : void MessageCounterManager::ProcessPendingMessages(NodeId peerNodeId)
154 : {
155 0 : auto * sessionManager = mExchangeMgr->GetSessionManager();
156 :
157 : // Find all receive entries matching peerNodeId. Note that everything in
158 : // this table was using an application group key; that's why it was added.
159 0 : for (ReceiveTableEntry & entry : mReceiveTable)
160 : {
161 0 : if (!entry.msgBuf.IsNull())
162 : {
163 0 : PacketHeader packetHeader;
164 0 : uint16_t headerSize = 0;
165 :
166 0 : if (packetHeader.Decode((entry.msgBuf)->Start(), (entry.msgBuf)->DataLength(), &headerSize) != CHIP_NO_ERROR)
167 : {
168 0 : ChipLogError(SecureChannel, "MessageCounterManager::ProcessPendingMessages: Failed to decode PacketHeader");
169 0 : entry.msgBuf = nullptr;
170 0 : continue;
171 : }
172 :
173 0 : if (packetHeader.GetSourceNodeId().HasValue() && packetHeader.GetSourceNodeId().Value() == peerNodeId)
174 : {
175 : // Reprocess message.
176 0 : sessionManager->OnMessageReceived(entry.peerAddress, std::move(entry.msgBuf));
177 :
178 : // Explicitly free any buffer owned by this handle.
179 0 : entry.msgBuf = nullptr;
180 : }
181 : }
182 : }
183 0 : }
184 :
185 0 : CHIP_ERROR MessageCounterManager::SendMsgCounterSyncReq(const SessionHandle & session, Transport::SecureSession * state)
186 : {
187 0 : CHIP_ERROR err = CHIP_NO_ERROR;
188 :
189 0 : Messaging::ExchangeContext * exchangeContext = nullptr;
190 0 : System::PacketBufferHandle msgBuf;
191 0 : Messaging::SendFlags sendFlags;
192 :
193 0 : exchangeContext = mExchangeMgr->NewContext(session, this);
194 0 : VerifyOrExit(exchangeContext != nullptr, err = CHIP_ERROR_NO_MEMORY);
195 :
196 0 : msgBuf = MessagePacketBuffer::New(kChallengeSize);
197 0 : VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
198 :
199 : // Generate a 64-bit random number to uniquely identify the request.
200 0 : SuccessOrExit(err = Crypto::DRBG_get_bytes(msgBuf->Start(), kChallengeSize));
201 :
202 0 : msgBuf->SetDataLength(kChallengeSize);
203 :
204 : // Store generated Challenge value to message counter context to resolve synchronization response.
205 0 : state->GetSessionMessageCounter().GetPeerMessageCounter().SyncStarting(FixedByteSpan<kChallengeSize>(msgBuf->Start()));
206 :
207 0 : sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck).Set(Messaging::SendMessageFlags::kExpectResponse);
208 :
209 : // Arm a timer to enforce that a MsgCounterSyncRsp is received before kSyncTimeout.
210 0 : exchangeContext->SetResponseTimeout(kSyncTimeout);
211 :
212 : // Send the message counter synchronization request in a Secure Channel Protocol::MsgCounterSyncReq message.
213 0 : SuccessOrExit(
214 : err = exchangeContext->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, std::move(msgBuf), sendFlags));
215 :
216 0 : exit:
217 0 : if (err != CHIP_NO_ERROR)
218 : {
219 0 : if (exchangeContext != nullptr)
220 : {
221 0 : exchangeContext->Close();
222 : }
223 0 : state->GetSessionMessageCounter().GetPeerMessageCounter().SyncFailed();
224 0 : ChipLogError(SecureChannel, "Failed to send message counter synchronization request with error:%" CHIP_ERROR_FORMAT,
225 : err.Format());
226 : }
227 :
228 0 : return err;
229 0 : }
230 :
231 0 : CHIP_ERROR MessageCounterManager::SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext,
232 : FixedByteSpan<kChallengeSize> challenge)
233 : {
234 0 : System::PacketBufferHandle msgBuf;
235 0 : VerifyOrDie(exchangeContext->HasSessionHandle());
236 :
237 0 : VerifyOrReturnError(exchangeContext->GetSessionHandle()->IsGroupSession(), CHIP_ERROR_INVALID_ARGUMENT);
238 :
239 : // NOTE: not currently implemented. When implementing, the following should be done:
240 : // - allocate a new buffer: MessagePacketBuffer::New
241 : // - setup payload and place the local message counter + challange in it
242 : // - exchangeContext->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp, ...)
243 : //
244 : // You can view the history of this file for a partial implementation that got
245 : // removed due to it using non-group sessions.
246 :
247 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
248 0 : }
249 :
250 0 : CHIP_ERROR MessageCounterManager::HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext,
251 : System::PacketBufferHandle && msgBuf)
252 : {
253 0 : CHIP_ERROR err = CHIP_NO_ERROR;
254 :
255 0 : uint8_t * req = msgBuf->Start();
256 0 : size_t reqlen = msgBuf->DataLength();
257 :
258 0 : ChipLogDetail(SecureChannel, "Received MsgCounterSyncReq request");
259 :
260 0 : VerifyOrExit(req != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE);
261 0 : VerifyOrExit(reqlen == kChallengeSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
262 :
263 : // Respond with MsgCounterSyncResp
264 0 : err = SendMsgCounterSyncResp(exchangeContext, FixedByteSpan<kChallengeSize>(req));
265 :
266 0 : exit:
267 0 : if (err != CHIP_NO_ERROR)
268 : {
269 0 : ChipLogError(SecureChannel, "Failed to handle MsgCounterSyncReq message with error:%" CHIP_ERROR_FORMAT, err.Format());
270 : }
271 :
272 0 : return err;
273 : }
274 :
275 0 : CHIP_ERROR MessageCounterManager::HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext,
276 : System::PacketBufferHandle && msgBuf)
277 : {
278 0 : CHIP_ERROR err = CHIP_NO_ERROR;
279 :
280 0 : uint32_t syncCounter = 0;
281 :
282 0 : const uint8_t * resp = msgBuf->Start();
283 0 : size_t resplen = msgBuf->DataLength();
284 :
285 0 : ChipLogDetail(SecureChannel, "Received MsgCounterSyncResp response");
286 :
287 0 : VerifyOrDie(exchangeContext->HasSessionHandle());
288 :
289 0 : VerifyOrExit(msgBuf->DataLength() == kSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
290 :
291 0 : VerifyOrExit(resp != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE);
292 0 : VerifyOrExit(resplen == kSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
293 :
294 0 : syncCounter = chip::Encoding::LittleEndian::Read32(resp);
295 0 : VerifyOrExit(syncCounter != 0, err = CHIP_ERROR_READ_FAILED);
296 :
297 : // Verify that the response field matches the expected Challenge field for the exchange.
298 : err =
299 0 : exchangeContext->GetSessionHandle()->AsSecureSession()->GetSessionMessageCounter().GetPeerMessageCounter().VerifyChallenge(
300 : syncCounter, FixedByteSpan<kChallengeSize>(resp));
301 0 : SuccessOrExit(err);
302 :
303 : // Process all queued incoming messages after message counter synchronization is completed.
304 0 : ProcessPendingMessages(exchangeContext->GetSessionHandle()->AsSecureSession()->GetPeerNodeId());
305 :
306 0 : exit:
307 0 : if (err != CHIP_NO_ERROR)
308 : {
309 0 : ChipLogError(SecureChannel, "Failed to handle MsgCounterSyncResp message with error:%" CHIP_ERROR_FORMAT, err.Format());
310 : }
311 :
312 0 : return err;
313 : }
314 :
315 : } // namespace secure_channel
316 : } // namespace chip
|