/src/connectedhomeip/src/protocols/secure_channel/MessageCounterManager.cpp
Line | Count | Source |
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 | | CHIP_ERROR MessageCounterManager::Init(Messaging::ExchangeManager * exchangeMgr) |
44 | 2.24k | { |
45 | 2.24k | VerifyOrReturnError(exchangeMgr != nullptr, CHIP_ERROR_INCORRECT_STATE); |
46 | 2.24k | mExchangeMgr = exchangeMgr; |
47 | | |
48 | 2.24k | ReturnErrorOnFailure( |
49 | 2.24k | mExchangeMgr->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, this)); |
50 | | |
51 | 2.24k | return CHIP_NO_ERROR; |
52 | 2.24k | } |
53 | | |
54 | | void MessageCounterManager::Shutdown() |
55 | 2.24k | { |
56 | 2.24k | if (mExchangeMgr != nullptr) |
57 | 2.24k | { |
58 | 2.24k | TEMPORARY_RETURN_IGNORED mExchangeMgr->UnregisterUnsolicitedMessageHandlerForType( |
59 | 2.24k | Protocols::SecureChannel::MsgType::MsgCounterSyncReq); |
60 | 2.24k | mExchangeMgr->CloseAllContextsForDelegate(this); |
61 | 2.24k | mExchangeMgr = nullptr; |
62 | 2.24k | } |
63 | 2.24k | } |
64 | | |
65 | | CHIP_ERROR MessageCounterManager::StartSync(const SessionHandle & session, Transport::SecureSession * state) |
66 | 0 | { |
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 | 0 | { |
71 | 0 | ReturnErrorOnFailure(SendMsgCounterSyncReq(session, state)); |
72 | 0 | } |
73 | | |
74 | 0 | return CHIP_NO_ERROR; |
75 | 0 | } |
76 | | |
77 | | CHIP_ERROR MessageCounterManager::QueueReceivedMessageAndStartSync(const PacketHeader & packetHeader, const SessionHandle & session, |
78 | | Transport::SecureSession * state, |
79 | | const Transport::PeerAddress & peerAddress, |
80 | | System::PacketBufferHandle && msgBuf) |
81 | 0 | { |
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 | 0 | } |
92 | | |
93 | | CHIP_ERROR MessageCounterManager::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) |
94 | 0 | { |
95 | | // MessageCounterManager do not use an extra context to handle messages |
96 | 0 | newDelegate = this; |
97 | 0 | return CHIP_NO_ERROR; |
98 | 0 | } |
99 | | |
100 | | CHIP_ERROR MessageCounterManager::OnMessageReceived(Messaging::ExchangeContext * exchangeContext, |
101 | | const PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf) |
102 | 0 | { |
103 | 0 | if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq)) |
104 | 0 | { |
105 | 0 | return HandleMsgCounterSyncReq(exchangeContext, std::move(msgBuf)); |
106 | 0 | } |
107 | 0 | if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp)) |
108 | 0 | { |
109 | 0 | return HandleMsgCounterSyncResp(exchangeContext, std::move(msgBuf)); |
110 | 0 | } |
111 | 0 | return CHIP_NO_ERROR; |
112 | 0 | } |
113 | | |
114 | | void MessageCounterManager::OnResponseTimeout(Messaging::ExchangeContext * exchangeContext) |
115 | 0 | { |
116 | 0 | if (exchangeContext->HasSessionHandle()) |
117 | 0 | { |
118 | 0 | exchangeContext->GetSessionHandle()->AsSecureSession()->GetSessionMessageCounter().GetPeerMessageCounter().SyncFailed(); |
119 | 0 | } |
120 | 0 | else |
121 | 0 | { |
122 | 0 | ChipLogError(SecureChannel, "MCSP Timeout! On a already released session."); |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | CHIP_ERROR MessageCounterManager::AddToReceiveTable(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress, |
127 | | System::PacketBufferHandle && msgBuf) |
128 | 0 | { |
129 | 0 | ReturnErrorOnFailure(packetHeader.EncodeBeforeData(msgBuf)); |
130 | | |
131 | 0 | for (ReceiveTableEntry & entry : mReceiveTable) |
132 | 0 | { |
133 | 0 | if (entry.msgBuf.IsNull()) |
134 | 0 | { |
135 | 0 | entry.peerAddress = peerAddress; |
136 | 0 | entry.msgBuf = std::move(msgBuf); |
137 | |
|
138 | 0 | return CHIP_NO_ERROR; |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 0 | ChipLogError(SecureChannel, "MCSP ReceiveTable Already Full"); |
143 | 0 | return CHIP_ERROR_NO_MEMORY; |
144 | 0 | } |
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 | | void MessageCounterManager::ProcessPendingMessages(NodeId peerNodeId) |
154 | 0 | { |
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 | 0 | { |
161 | 0 | if (!entry.msgBuf.IsNull()) |
162 | 0 | { |
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 | 0 | { |
168 | 0 | ChipLogError(SecureChannel, "MessageCounterManager::ProcessPendingMessages: Failed to decode PacketHeader"); |
169 | 0 | entry.msgBuf = nullptr; |
170 | 0 | continue; |
171 | 0 | } |
172 | | |
173 | 0 | if (packetHeader.GetSourceNodeId().HasValue() && packetHeader.GetSourceNodeId().Value() == peerNodeId) |
174 | 0 | { |
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 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | CHIP_ERROR MessageCounterManager::SendMsgCounterSyncReq(const SessionHandle & session, Transport::SecureSession * state) |
186 | 0 | { |
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 | 0 | err = exchangeContext->SendMessage(Protocols::SecureChannel::MsgType::MsgCounterSyncReq, std::move(msgBuf), sendFlags)); |
215 | | |
216 | 0 | exit: |
217 | 0 | if (err != CHIP_NO_ERROR) |
218 | 0 | { |
219 | 0 | if (exchangeContext != nullptr) |
220 | 0 | { |
221 | 0 | exchangeContext->Close(); |
222 | 0 | } |
223 | 0 | state->GetSessionMessageCounter().GetPeerMessageCounter().SyncFailed(); |
224 | 0 | ChipLogError(SecureChannel, "Failed to send message counter synchronization request with error:%" CHIP_ERROR_FORMAT, |
225 | 0 | err.Format()); |
226 | 0 | } |
227 | |
|
228 | 0 | return err; |
229 | 0 | } |
230 | | |
231 | | CHIP_ERROR MessageCounterManager::SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, |
232 | | FixedByteSpan<kChallengeSize> challenge) |
233 | 0 | { |
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 | | CHIP_ERROR MessageCounterManager::HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext, |
251 | | System::PacketBufferHandle && msgBuf) |
252 | 0 | { |
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 | 0 | { |
269 | 0 | ChipLogError(SecureChannel, "Failed to handle MsgCounterSyncReq message with error:%" CHIP_ERROR_FORMAT, err.Format()); |
270 | 0 | } |
271 | |
|
272 | 0 | return err; |
273 | 0 | } |
274 | | |
275 | | CHIP_ERROR MessageCounterManager::HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, |
276 | | System::PacketBufferHandle && msgBuf) |
277 | 0 | { |
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 | 0 | err = |
299 | 0 | exchangeContext->GetSessionHandle()->AsSecureSession()->GetSessionMessageCounter().GetPeerMessageCounter().VerifyChallenge( |
300 | 0 | 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 | 0 | { |
309 | 0 | ChipLogError(SecureChannel, "Failed to handle MsgCounterSyncResp message with error:%" CHIP_ERROR_FORMAT, err.Format()); |
310 | 0 | } |
311 | |
|
312 | 0 | return err; |
313 | 0 | } |
314 | | |
315 | | } // namespace secure_channel |
316 | | } // namespace chip |