Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / messaging / MessageCounterSync.cpp
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 Secure Channel protocol.
21  *
22  */
23
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>
35
36 namespace chip {
37 namespace Messaging {
38
39 CHIP_ERROR MessageCounterSyncMgr::Init(Messaging::ExchangeManager * exchangeMgr)
40 {
41     CHIP_ERROR err = CHIP_NO_ERROR;
42
43     VerifyOrReturnError(exchangeMgr != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
44     mExchangeMgr = exchangeMgr;
45
46     // Register to receive unsolicited Secure Channel Request messages from the exchange manager.
47     err = mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::SecureChannel::Id, this);
48
49     ReturnErrorOnFailure(err);
50
51     return err;
52 }
53
54 void MessageCounterSyncMgr::Shutdown()
55 {
56     if (mExchangeMgr != nullptr)
57     {
58         mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::SecureChannel::Id);
59         mExchangeMgr = nullptr;
60     }
61 }
62
63 void MessageCounterSyncMgr::OnMessageReceived(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader,
64                                               const PayloadHeader & payloadHeader, System::PacketBufferHandle msgBuf)
65 {
66     if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq))
67     {
68         HandleMsgCounterSyncReq(exchangeContext, packetHeader, std::move(msgBuf));
69     }
70     else if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp))
71     {
72         HandleMsgCounterSyncResp(exchangeContext, packetHeader, std::move(msgBuf));
73     }
74 }
75
76 void MessageCounterSyncMgr::OnResponseTimeout(Messaging::ExchangeContext * exchangeContext)
77 {
78     Transport::PeerConnectionState * state =
79         mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(exchangeContext->GetSecureSessionHandle());
80
81     if (state != nullptr)
82     {
83         state->SetMsgCounterSyncInProgress(false);
84     }
85     else
86     {
87         ChipLogError(ExchangeManager, "Timed out! Failed to clear message counter synchronization status.");
88     }
89
90     // Close the exchange if MsgCounterSyncRsp is not received before kMsgCounterSyncTimeout.
91     if (exchangeContext != nullptr)
92         exchangeContext->Close();
93 }
94
95 CHIP_ERROR MessageCounterSyncMgr::AddToRetransmissionTable(Protocols::Id protocolId, uint8_t msgType, const SendFlags & sendFlags,
96                                                            System::PacketBufferHandle msgBuf,
97                                                            Messaging::ExchangeContext * exchangeContext)
98 {
99     bool added     = false;
100     CHIP_ERROR err = CHIP_NO_ERROR;
101
102     VerifyOrReturnError(exchangeContext != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
103
104     for (RetransTableEntry & entry : mRetransTable)
105     {
106         // Entries are in use if they have an exchangeContext.
107         if (entry.exchangeContext == nullptr)
108         {
109             entry.protocolId      = protocolId;
110             entry.msgType         = msgType;
111             entry.msgBuf          = std::move(msgBuf);
112             entry.exchangeContext = exchangeContext;
113             entry.exchangeContext->Retain();
114             added = true;
115
116             break;
117         }
118     }
119
120     if (!added)
121     {
122         ChipLogError(ExchangeManager, "MCSP RetransTable Already Full");
123         err = CHIP_ERROR_NO_MEMORY;
124     }
125
126     return err;
127 }
128
129 /**
130  *  Retransmit all pending messages that were encrypted with application
131  *  group key and were addressed to the specified node.
132  *
133  *  @param[in] peerNodeId    Node ID of the destination node.
134  *
135  */
136 void MessageCounterSyncMgr::RetransPendingGroupMsgs(NodeId peerNodeId)
137 {
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)
141     {
142         if (entry.exchangeContext != nullptr && entry.exchangeContext->GetSecureSession().GetPeerNodeId() == peerNodeId)
143         {
144             // Retramsmit message.
145             CHIP_ERROR err =
146                 entry.exchangeContext->SendMessage(entry.protocolId, entry.msgType, std::move(entry.msgBuf), entry.sendFlags);
147
148             if (err != CHIP_NO_ERROR)
149             {
150                 ChipLogError(ExchangeManager, "Failed to resend cached group message to node: %d with error:%s", peerNodeId,
151                              ErrorStr(err));
152             }
153
154             entry.exchangeContext->Release();
155             entry.exchangeContext = nullptr;
156         }
157     }
158 }
159
160 CHIP_ERROR MessageCounterSyncMgr::AddToReceiveTable(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
161                                                     const SecureSessionHandle & session, System::PacketBufferHandle msgBuf)
162 {
163     bool added     = false;
164     CHIP_ERROR err = CHIP_NO_ERROR;
165
166     for (ReceiveTableEntry & entry : mReceiveTable)
167     {
168         // Entries are in use if they have a message buffer.
169         if (entry.msgBuf.IsNull())
170         {
171             entry.packetHeader  = packetHeader;
172             entry.payloadHeader = payloadHeader;
173             entry.session       = session;
174             entry.msgBuf        = std::move(msgBuf);
175             added               = true;
176
177             break;
178         }
179     }
180
181     if (!added)
182     {
183         ChipLogError(ExchangeManager, "MCSP ReceiveTable Already Full");
184         err = CHIP_ERROR_NO_MEMORY;
185     }
186
187     return err;
188 }
189
190 /**
191  *  Reprocess all pending messages that were encrypted with application
192  *  group key and were addressed to the specified node id.
193  *
194  *  @param[in] peerNodeId    Node ID of the destination node.
195  *
196  */
197 void MessageCounterSyncMgr::ProcessPendingGroupMsgs(NodeId peerNodeId)
198 {
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)
202     {
203         if (!entry.msgBuf.IsNull() && entry.session.GetPeerNodeId() == peerNodeId)
204         {
205             // Reprocess message.
206             mExchangeMgr->HandleGroupMessageReceived(entry.packetHeader, entry.payloadHeader, entry.session,
207                                                      std::move(entry.msgBuf));
208
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;
214         }
215     }
216 }
217
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)
221 {
222     CHIP_ERROR err = CHIP_NO_ERROR;
223
224     // Message counter synchronization protocol is only applicable for application group keys.
225     VerifyOrReturnError(ChipKeyId::IsAppGroupKey(session.GetPeerKeyId()), err = CHIP_ERROR_INVALID_ARGUMENT);
226
227     // Create new exchange context.
228     exchangeContext = mExchangeMgr->NewContext(session, this);
229     VerifyOrReturnError(exchangeContext != nullptr, err = CHIP_ERROR_NO_MEMORY);
230
231     return err;
232 }
233
234 CHIP_ERROR MessageCounterSyncMgr::SendMsgCounterSyncReq(SecureSessionHandle session)
235 {
236     CHIP_ERROR err = CHIP_NO_ERROR;
237
238     Messaging::ExchangeContext * exchangeContext = nullptr;
239     Transport::PeerConnectionState * state       = nullptr;
240     System::PacketBufferHandle msgBuf;
241     Messaging::SendFlags sendFlags;
242     uint8_t challenge[kMsgCounterChallengeSize];
243
244     state = mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(session);
245     VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
246
247     // Create and initialize new exchange.
248     err = NewMsgCounterSyncExchange(session, exchangeContext);
249     SuccessOrExit(err);
250
251     // Allocate a buffer for the null message.
252     msgBuf = MessagePacketBuffer::New(kMsgCounterChallengeSize);
253     VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
254
255     // Generate a 64-bit random number to uniquely identify the request.
256     err = DRBG_get_bytes(challenge, kMsgCounterChallengeSize);
257     SuccessOrExit(err);
258
259     // Store generated Challenge value to ExchangeContext to resolve synchronization response.
260     exchangeContext->SetChallenge(challenge);
261
262     memcpy(msgBuf->Start(), challenge, kMsgCounterChallengeSize);
263     msgBuf->SetDataLength(kMsgCounterChallengeSize);
264
265     sendFlags.Set(Messaging::SendMessageFlags::kNoAutoRequestAck, true).Set(Messaging::SendMessageFlags::kExpectResponse, true);
266
267     // Arm a timer to enforce that a MsgCounterSyncRsp is received before kMsgCounterSyncTimeout.
268     exchangeContext->SetResponseTimeout(kMsgCounterSyncTimeout);
269
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);
274     SuccessOrExit(err);
275
276     state->SetMsgCounterSyncInProgress(true);
277
278 exit:
279     if (err != CHIP_NO_ERROR)
280     {
281         ChipLogError(ExchangeManager, "Failed to send message counter synchronization request with error:%s", ErrorStr(err));
282     }
283
284     return err;
285 }
286
287 CHIP_ERROR MessageCounterSyncMgr::SendMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext, SecureSessionHandle session)
288 {
289     CHIP_ERROR err                         = CHIP_NO_ERROR;
290     Transport::PeerConnectionState * state = nullptr;
291     System::PacketBufferHandle msgBuf;
292     uint8_t * msg = nullptr;
293
294     state = mExchangeMgr->GetSessionMgr()->GetPeerConnectionState(session);
295     VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
296
297     // Allocate new buffer.
298     msgBuf = System::PacketBufferHandle::New(kMsgCounterSyncRespMsgSize);
299     VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
300
301     msg = msgBuf->Start();
302
303     // Let's construct the message using BufBound
304     {
305         Encoding::LittleEndian::BufferWriter bbuf(msg, kMsgCounterSyncRespMsgSize);
306
307         // Write the message id (counter) field.
308         bbuf.Put32(state->GetSendMessageIndex());
309
310         // Fill in the random value
311         bbuf.Put(exchangeContext->GetChallenge(), kMsgCounterChallengeSize);
312
313         VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY);
314     }
315
316     // Set message length.
317     msgBuf->SetDataLength(kMsgCounterSyncRespMsgSize);
318
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));
323
324 exit:
325     if (err != CHIP_NO_ERROR)
326     {
327         ChipLogError(ExchangeManager, "Failed to send message counter synchronization response with error:%s", ErrorStr(err));
328     }
329
330     return err;
331 }
332
333 void MessageCounterSyncMgr::HandleMsgCounterSyncReq(Messaging::ExchangeContext * exchangeContext, const PacketHeader & packetHeader,
334                                                     System::PacketBufferHandle msgBuf)
335 {
336     CHIP_ERROR err = CHIP_NO_ERROR;
337
338     const uint8_t * req = msgBuf->Start();
339     size_t reqlen       = msgBuf->DataLength();
340
341     ChipLogDetail(ExchangeManager, "Received MsgCounterSyncReq request");
342
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);
347
348     // Store the 64-bit value sent in the Challenge filed of the MsgCounterSyncReq.
349     exchangeContext->SetChallenge(req);
350
351     // Respond with MsgCounterSyncResp
352     err = SendMsgCounterSyncResp(exchangeContext, { packetHeader.GetSourceNodeId().Value(), packetHeader.GetEncryptionKeyID(), 0 });
353
354 exit:
355     if (err != CHIP_NO_ERROR)
356     {
357         ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncReq message with error:%s", ErrorStr(err));
358     }
359
360     if (exchangeContext != nullptr)
361         exchangeContext->Close();
362
363     return;
364 }
365
366 void MessageCounterSyncMgr::HandleMsgCounterSyncResp(Messaging::ExchangeContext * exchangeContext,
367                                                      const PacketHeader & packetHeader, System::PacketBufferHandle msgBuf)
368 {
369     CHIP_ERROR err = CHIP_NO_ERROR;
370
371     Transport::PeerConnectionState * state = nullptr;
372     NodeId peerNodeId                      = 0;
373     uint32_t syncCounter                   = 0;
374     uint8_t challenge[kMsgCounterChallengeSize];
375
376     const uint8_t * resp = msgBuf->Start();
377     size_t resplen       = msgBuf->DataLength();
378
379     ChipLogDetail(ExchangeManager, "Received MsgCounterSyncResp response");
380
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);
384
385     state->SetMsgCounterSyncInProgress(false);
386
387     VerifyOrExit(msgBuf->DataLength() == kMsgCounterSyncRespMsgSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
388     VerifyOrExit(ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()), err = CHIP_ERROR_WRONG_KEY_TYPE);
389
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);
393
394     syncCounter = chip::Encoding::LittleEndian::Read32(resp);
395     VerifyOrExit(syncCounter != 0, err = CHIP_ERROR_READ_FAILED);
396
397     memcpy(challenge, resp, kMsgCounterChallengeSize);
398
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);
402
403     VerifyOrExit(packetHeader.GetSourceNodeId().HasValue(), err = CHIP_ERROR_INVALID_ARGUMENT);
404     peerNodeId = packetHeader.GetSourceNodeId().Value();
405
406     // Process all queued ougoing and incomming group messages after message counter synchronization is completed.
407     RetransPendingGroupMsgs(peerNodeId);
408     ProcessPendingGroupMsgs(peerNodeId);
409
410 exit:
411     if (err != CHIP_NO_ERROR)
412     {
413         ChipLogError(ExchangeManager, "Failed to handle MsgCounterSyncResp message with error:%s", ErrorStr(err));
414     }
415
416     if (exchangeContext != nullptr)
417         exchangeContext->Close();
418
419     return;
420 }
421
422 } // namespace Messaging
423 } // namespace chip