95219e5af0b098d38372331907ab967bfea9b5b0
[platform/upstream/connectedhomeip.git] / src / transport / SecureSessionMgr.cpp
1 /*
2  *
3  *    Copyright (c) 2020-2021 Project CHIP Authors
4  *    Copyright (c) 2013-2017 Nest Labs, Inc.
5  *    All rights reserved.
6  *
7  *    Licensed under the Apache License, Version 2.0 (the "License");
8  *    you may not use this file except in compliance with the License.
9  *    You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *    Unless required by applicable law or agreed to in writing, software
14  *    distributed under the License is distributed on an "AS IS" BASIS,
15  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *    See the License for the specific language governing permissions and
17  *    limitations under the License.
18  */
19
20 /**
21  *    @file
22  *      This file implements the CHIP Connection object that maintains a UDP connection.
23  *      TODO This class should be extended to support TCP as well...
24  *
25  */
26
27 #include "SecureSessionMgr.h"
28
29 #include <inttypes.h>
30 #include <string.h>
31
32 #include <core/CHIPKeyIds.h>
33 #include <platform/CHIPDeviceLayer.h>
34 #include <support/CodeUtils.h>
35 #include <support/ReturnMacros.h>
36 #include <support/SafeInt.h>
37 #include <support/logging/CHIPLogging.h>
38 #include <transport/AdminPairingTable.h>
39 #include <transport/PASESession.h>
40 #include <transport/RendezvousSession.h>
41 #include <transport/SecureMessageCodec.h>
42 #include <transport/SecureSessionMgr.h>
43 #include <transport/TransportMgr.h>
44
45 #include <inttypes.h>
46
47 namespace chip {
48
49 using System::PacketBufferHandle;
50 using Transport::PeerAddress;
51 using Transport::PeerConnectionState;
52
53 // Maximum length of application data that can be encrypted as one block.
54 // The limit is derived from IPv6 MTU (1280 bytes) - expected header overheads.
55 // This limit would need additional reviews once we have formalized Secure Transport header.
56 //
57 // TODO: this should be checked within the transport message sending instead of the session management layer.
58 static const size_t kMax_SecureSDU_Length = 1024;
59
60 SecureSessionMgr::SecureSessionMgr() : mState(State::kNotReady) {}
61
62 SecureSessionMgr::~SecureSessionMgr()
63 {
64     CancelExpiryTimer();
65 }
66
67 CHIP_ERROR SecureSessionMgr::Init(NodeId localNodeId, System::Layer * systemLayer, TransportMgrBase * transportMgr,
68                                   Transport::AdminPairingTable * admins)
69 {
70     VerifyOrReturnError(mState == State::kNotReady, CHIP_ERROR_INCORRECT_STATE);
71     VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
72
73     mState        = State::kInitialized;
74     mLocalNodeId  = localNodeId;
75     mSystemLayer  = systemLayer;
76     mTransportMgr = transportMgr;
77     mAdmins       = admins;
78
79     ChipLogProgress(Inet, "local node id is %llu\n", mLocalNodeId);
80
81     ScheduleExpiryTimer();
82
83     mTransportMgr->SetSecureSessionMgr(this);
84
85     return CHIP_NO_ERROR;
86 }
87
88 Transport::Type SecureSessionMgr::GetTransportType(NodeId peerNodeId)
89 {
90     PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(peerNodeId, nullptr);
91
92     if (state)
93     {
94         return state->GetPeerAddress().GetTransportType();
95     }
96
97     return Transport::Type::kUndefined;
98 }
99
100 CHIP_ERROR SecureSessionMgr::SendMessage(SecureSessionHandle session, System::PacketBufferHandle && msgBuf)
101 {
102     PayloadHeader unusedPayloadHeader;
103     return SendMessage(session, unusedPayloadHeader, std::move(msgBuf));
104 }
105
106 CHIP_ERROR SecureSessionMgr::SendMessage(SecureSessionHandle session, PayloadHeader & payloadHeader,
107                                          System::PacketBufferHandle && msgBuf, EncryptedPacketBufferHandle * bufferRetainSlot)
108 {
109     PacketHeader unusedPacketHeader;
110     return SendMessage(session, payloadHeader, unusedPacketHeader, std::move(msgBuf), bufferRetainSlot,
111                        EncryptionState::kPayloadIsUnencrypted);
112 }
113
114 CHIP_ERROR SecureSessionMgr::SendEncryptedMessage(SecureSessionHandle session, EncryptedPacketBufferHandle msgBuf,
115                                                   EncryptedPacketBufferHandle * bufferRetainSlot)
116 {
117     VerifyOrReturnError(!msgBuf.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
118     VerifyOrReturnError(!msgBuf->HasChainedBuffer(), CHIP_ERROR_INVALID_MESSAGE_LENGTH);
119     VerifyOrReturnError(msgBuf->TotalLength() < kMax_SecureSDU_Length, CHIP_ERROR_INVALID_MESSAGE_LENGTH);
120
121     // Advancing the start to encrypted header, since the transport will attach the packet header on top of it
122     PacketHeader packetHeader;
123     ReturnErrorOnFailure(packetHeader.DecodeAndConsume(msgBuf));
124
125     PayloadHeader payloadHeader;
126     return SendMessage(session, payloadHeader, packetHeader, std::move(msgBuf), bufferRetainSlot,
127                        EncryptionState::kPayloadIsEncrypted);
128 }
129
130 CHIP_ERROR SecureSessionMgr::SendMessage(SecureSessionHandle session, PayloadHeader & payloadHeader, PacketHeader & packetHeader,
131                                          System::PacketBufferHandle msgBuf, EncryptedPacketBufferHandle * bufferRetainSlot,
132                                          EncryptionState encryptionState)
133 {
134     CHIP_ERROR err              = CHIP_NO_ERROR;
135     PeerConnectionState * state = nullptr;
136     uint8_t * msgStart          = nullptr;
137     uint16_t msgLen             = 0;
138     uint16_t headerSize         = 0;
139     NodeId localNodeId          = mLocalNodeId;
140
141     Transport::AdminPairingInfo * admin = nullptr;
142
143     // Hold the reference to encrypted message in stack variable.
144     // In case of any failures, the reference is not returned, and this stack variable
145     // will automatically free the reference on returning from the function.
146     EncryptedPacketBufferHandle encryptedMsg;
147
148     VerifyOrExit(mState == State::kInitialized, err = CHIP_ERROR_INCORRECT_STATE);
149
150     VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_INVALID_ARGUMENT);
151     VerifyOrExit(!msgBuf->HasChainedBuffer(), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
152     VerifyOrExit(msgBuf->TotalLength() < kMax_SecureSDU_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
153
154     // Find an active connection to the specified peer node
155     state = GetPeerConnectionState(session);
156     VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
157
158     // This marks any connection where we send data to as 'active'
159     mPeerConnections.MarkConnectionActive(state);
160     admin = mAdmins->FindAdmin(state->GetAdminId());
161     VerifyOrExit(admin != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
162     localNodeId = admin->GetNodeId();
163
164     if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq) ||
165         payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp))
166     {
167         packetHeader.SetSecureSessionControlMsg(true);
168     }
169
170     if (encryptionState == EncryptionState::kPayloadIsUnencrypted)
171     {
172         err = SecureMessageCodec::Encode(localNodeId, state, payloadHeader, packetHeader, msgBuf);
173         SuccessOrExit(err);
174     }
175
176     // The start of buffer points to the beginning of the encrypted header, and the length of buffer
177     // contains both the encrypted header and encrypted data.
178     // Locally store the start and length of the retained buffer after accounting for the size of packet header.
179     headerSize = packetHeader.EncodeSizeBytes();
180
181     msgStart = static_cast<uint8_t *>(msgBuf->Start() - headerSize);
182     msgLen   = static_cast<uint16_t>(msgBuf->DataLength() + headerSize);
183
184     // Retain the packet buffer in case it's needed for retransmissions.
185     if (bufferRetainSlot != nullptr)
186     {
187         encryptedMsg        = msgBuf.Retain();
188         encryptedMsg.mMsgId = packetHeader.GetMessageId();
189     }
190
191     ChipLogProgress(Inet, "Sending msg from %llu to %llu", localNodeId, state->GetPeerNodeId());
192
193     if (state->GetTransport() != nullptr)
194     {
195         ChipLogProgress(Inet, "Sending secure msg on connection specific transport");
196         err = state->GetTransport()->SendMessage(packetHeader, state->GetPeerAddress(), std::move(msgBuf));
197     }
198     else
199     {
200         ChipLogProgress(Inet, "Sending secure msg on generic transport");
201         err = mTransportMgr->SendMessage(packetHeader, state->GetPeerAddress(), std::move(msgBuf));
202     }
203     ChipLogProgress(Inet, "Secure msg send status %d", err);
204     SuccessOrExit(err);
205
206     if (bufferRetainSlot != nullptr)
207     {
208         // Rewind the start and len of the buffer back to pre-send state for following possible retransmition.
209         encryptedMsg->SetStart(msgStart);
210         encryptedMsg->SetDataLength(msgLen);
211
212         (*bufferRetainSlot) = std::move(encryptedMsg);
213     }
214
215 exit:
216     if (!msgBuf.IsNull())
217     {
218         const char * errStr = ErrorStr(err);
219         if (state == nullptr)
220         {
221             ChipLogError(Inet, "Secure transport could not find a valid PeerConnection: %s", errStr);
222         }
223     }
224
225     return err;
226 }
227
228 CHIP_ERROR SecureSessionMgr::NewPairing(const Optional<Transport::PeerAddress> & peerAddr, NodeId peerNodeId, PASESession * pairing,
229                                         PairingDirection direction, Transport::AdminId admin, Transport::Base * transport)
230 {
231     uint16_t peerKeyId          = pairing->GetPeerKeyId();
232     uint16_t localKeyId         = pairing->GetLocalKeyId();
233     PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(Optional<NodeId>::Value(peerNodeId), peerKeyId, nullptr);
234
235     // Find any existing connection with the same node and key ID
236     if (state && (state->GetAdminId() == Transport::kUndefinedAdminId || state->GetAdminId() == admin))
237     {
238         mPeerConnections.MarkConnectionExpired(
239             state, [this](const Transport::PeerConnectionState & state1) { HandleConnectionExpired(state1); });
240     }
241
242     ChipLogDetail(Inet, "New pairing for device %llu, key %d!!", peerNodeId, peerKeyId);
243
244     state = nullptr;
245     ReturnErrorOnFailure(
246         mPeerConnections.CreateNewPeerConnectionState(Optional<NodeId>::Value(peerNodeId), peerKeyId, localKeyId, &state));
247
248     state->SetAdminId(admin);
249     state->SetTransport(transport);
250
251     if (peerAddr.HasValue() && peerAddr.Value().GetIPAddress() != Inet::IPAddress::Any)
252     {
253         state->SetPeerAddress(peerAddr.Value());
254     }
255     else if (peerAddr.HasValue() &&
256              (peerAddr.Value().GetTransportType() == Transport::Type::kTcp ||
257               peerAddr.Value().GetTransportType() == Transport::Type::kUdp))
258     {
259         return CHIP_ERROR_INVALID_ARGUMENT;
260     }
261
262     if (state != nullptr)
263     {
264         switch (direction)
265         {
266         case PairingDirection::kInitiator:
267             ReturnErrorOnFailure(pairing->DeriveSecureSession(reinterpret_cast<const uint8_t *>(kSpake2pI2RSessionInfo),
268                                                               strlen(kSpake2pI2RSessionInfo), state->GetSenderSecureSession()));
269
270             ReturnErrorOnFailure(pairing->DeriveSecureSession(reinterpret_cast<const uint8_t *>(kSpake2pR2ISessionInfo),
271                                                               strlen(kSpake2pR2ISessionInfo), state->GetReceiverSecureSession()));
272
273             break;
274         case PairingDirection::kResponder:
275             ReturnErrorOnFailure(pairing->DeriveSecureSession(reinterpret_cast<const uint8_t *>(kSpake2pR2ISessionInfo),
276                                                               strlen(kSpake2pR2ISessionInfo), state->GetSenderSecureSession()));
277             ReturnErrorOnFailure(pairing->DeriveSecureSession(reinterpret_cast<const uint8_t *>(kSpake2pI2RSessionInfo),
278                                                               strlen(kSpake2pI2RSessionInfo), state->GetReceiverSecureSession()));
279
280             break;
281         default:
282             return CHIP_ERROR_INVALID_ARGUMENT;
283         };
284
285         if (mCB != nullptr)
286         {
287             mCB->OnNewConnection({ state->GetPeerNodeId(), state->GetPeerKeyID(), admin }, this);
288         }
289     }
290
291     return CHIP_NO_ERROR;
292 }
293
294 void SecureSessionMgr::ScheduleExpiryTimer()
295 {
296     CHIP_ERROR err =
297         mSystemLayer->StartTimer(CHIP_PEER_CONNECTION_TIMEOUT_CHECK_FREQUENCY_MS, SecureSessionMgr::ExpiryTimerCallback, this);
298
299     VerifyOrDie(err == CHIP_NO_ERROR);
300 }
301
302 void SecureSessionMgr::CancelExpiryTimer()
303 {
304     if (mSystemLayer != nullptr)
305     {
306         mSystemLayer->CancelTimer(SecureSessionMgr::ExpiryTimerCallback, this);
307     }
308 }
309
310 void SecureSessionMgr::OnMessageReceived(const PacketHeader & packetHeader, const PeerAddress & peerAddress,
311                                          System::PacketBufferHandle msg)
312 {
313     CHIP_ERROR err = CHIP_NO_ERROR;
314
315     PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(packetHeader.GetEncryptionKeyID(), nullptr);
316
317     PacketBufferHandle origMsg;
318     PayloadHeader payloadHeader;
319
320     Transport::AdminPairingInfo * admin = nullptr;
321
322     VerifyOrExit(!msg.IsNull(), ChipLogError(Inet, "Secure transport received NULL packet, discarding"));
323
324     if (state == nullptr)
325     {
326         ChipLogError(Inet, "Data received on an unknown connection (%d). Dropping it!!", packetHeader.GetEncryptionKeyID());
327         ExitNow(err = CHIP_ERROR_KEY_NOT_FOUND_FROM_PEER);
328     }
329
330     admin = mAdmins->FindAdmin(state->GetAdminId());
331     VerifyOrExit(admin != nullptr, ChipLogError(Inet, "Secure transport received packet for unknown admin pairing, discarding"));
332     if (packetHeader.GetDestinationNodeId().HasValue())
333     {
334         VerifyOrExit(
335             admin->GetNodeId() == packetHeader.GetDestinationNodeId().Value(),
336             ChipLogError(Inet, "Secure transport received message, but destination node ID doesn't match our node ID, discarding"));
337     }
338     mPeerConnections.MarkConnectionActive(state);
339
340     // Decode the message
341     VerifyOrReturn(CHIP_NO_ERROR == SecureMessageCodec::Decode(state, payloadHeader, packetHeader, msg));
342
343     if (state->GetPeerNodeId() == kUndefinedNodeId && packetHeader.GetSourceNodeId().HasValue())
344     {
345         state->SetPeerNodeId(packetHeader.GetSourceNodeId().Value());
346     }
347
348     // TODO: once mDNS address resolution is available reconsider if this is required
349     // This updates the peer address once a packet is received from a new address
350     // and serves as a way to auto-detect peer changing IPs.
351     if (state->GetPeerAddress() != peerAddress)
352     {
353         state->SetPeerAddress(peerAddress);
354     }
355
356     if (!state->IsPeerMsgCounterSynced())
357     {
358         // For all control messages, the first authenticated message counter from an unsynchronized peer is trusted
359         // and used to seed subsequent message counter based replay protection.
360         if (packetHeader.IsSecureSessionControlMsg())
361         {
362             state->SetPeerMessageIndex(packetHeader.GetMessageId());
363         }
364
365         // For all group messages, Set flag if peer group key message counter is not synchronized.
366         if (ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()))
367         {
368             const_cast<PacketHeader &>(packetHeader).SetPeerGroupMsgIdNotSynchronized(true);
369         }
370     }
371
372     if (mCB != nullptr)
373     {
374         mCB->OnMessageReceived(packetHeader, payloadHeader, { state->GetPeerNodeId(), state->GetPeerKeyID(), state->GetAdminId() },
375                                std::move(msg), this);
376     }
377
378 exit:
379     if (err != CHIP_NO_ERROR && mCB != nullptr)
380     {
381         mCB->OnReceiveError(err, peerAddress, this);
382     }
383 }
384
385 void SecureSessionMgr::HandleConnectionExpired(const Transport::PeerConnectionState & state)
386 {
387     char addr[Transport::PeerAddress::kMaxToStringSize];
388     state.GetPeerAddress().ToString(addr);
389
390     ChipLogDetail(Inet, "Connection from '%s' expired", addr);
391
392     if (mCB != nullptr)
393     {
394         mCB->OnConnectionExpired({ state.GetPeerNodeId(), state.GetPeerKeyID(), state.GetAdminId() }, this);
395     }
396
397     mTransportMgr->Disconnect(state.GetPeerAddress());
398 }
399
400 void SecureSessionMgr::ExpiryTimerCallback(System::Layer * layer, void * param, System::Error error)
401 {
402     SecureSessionMgr * mgr = reinterpret_cast<SecureSessionMgr *>(param);
403 #if CHIP_CONFIG_SESSION_REKEYING
404     // TODO(#2279): session expiration is currently disabled until rekeying is supported
405     // the #ifdef should be removed after that.
406     mgr->mPeerConnections.ExpireInactiveConnections(
407         CHIP_PEER_CONNECTION_TIMEOUT_MS,
408         [this](const Transport::PeerConnectionState & state1) { HandleConnectionExpired(state1); });
409 #endif
410     mgr->ScheduleExpiryTimer(); // re-schedule the oneshot timer
411 }
412
413 PeerConnectionState * SecureSessionMgr::GetPeerConnectionState(SecureSessionHandle session)
414 {
415     return mPeerConnections.FindPeerConnectionState(Optional<NodeId>::Value(session.mPeerNodeId), session.mPeerKeyId, nullptr);
416 }
417
418 } // namespace chip