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