3 * Copyright (c) 2020-2021 Project CHIP Authors
4 * Copyright (c) 2013-2017 Nest Labs, Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
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...
27 #include "SecureSessionMgr.h"
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>
49 using System::PacketBufferHandle;
50 using Transport::PeerAddress;
51 using Transport::PeerConnectionState;
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.
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;
60 SecureSessionMgr::SecureSessionMgr() : mState(State::kNotReady) {}
62 SecureSessionMgr::~SecureSessionMgr()
67 CHIP_ERROR SecureSessionMgr::Init(NodeId localNodeId, System::Layer * systemLayer, TransportMgrBase * transportMgr,
68 Transport::AdminPairingTable * admins)
70 VerifyOrReturnError(mState == State::kNotReady, CHIP_ERROR_INCORRECT_STATE);
71 VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
73 mState = State::kInitialized;
74 mLocalNodeId = localNodeId;
75 mSystemLayer = systemLayer;
76 mTransportMgr = transportMgr;
79 ChipLogProgress(Inet, "local node id is %llu\n", mLocalNodeId);
81 ScheduleExpiryTimer();
83 mTransportMgr->SetSecureSessionMgr(this);
88 Transport::Type SecureSessionMgr::GetTransportType(NodeId peerNodeId)
90 PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(peerNodeId, nullptr);
94 return state->GetPeerAddress().GetTransportType();
97 return Transport::Type::kUndefined;
100 CHIP_ERROR SecureSessionMgr::SendMessage(SecureSessionHandle session, System::PacketBufferHandle && msgBuf)
102 PayloadHeader unusedPayloadHeader;
103 return SendMessage(session, unusedPayloadHeader, std::move(msgBuf));
106 CHIP_ERROR SecureSessionMgr::SendMessage(SecureSessionHandle session, PayloadHeader & payloadHeader,
107 System::PacketBufferHandle && msgBuf, EncryptedPacketBufferHandle * bufferRetainSlot)
109 PacketHeader unusedPacketHeader;
110 return SendMessage(session, payloadHeader, unusedPacketHeader, std::move(msgBuf), bufferRetainSlot,
111 EncryptionState::kPayloadIsUnencrypted);
114 CHIP_ERROR SecureSessionMgr::SendEncryptedMessage(SecureSessionHandle session, EncryptedPacketBufferHandle msgBuf,
115 EncryptedPacketBufferHandle * bufferRetainSlot)
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);
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));
125 PayloadHeader payloadHeader;
126 return SendMessage(session, payloadHeader, packetHeader, std::move(msgBuf), bufferRetainSlot,
127 EncryptionState::kPayloadIsEncrypted);
130 CHIP_ERROR SecureSessionMgr::SendMessage(SecureSessionHandle session, PayloadHeader & payloadHeader, PacketHeader & packetHeader,
131 System::PacketBufferHandle msgBuf, EncryptedPacketBufferHandle * bufferRetainSlot,
132 EncryptionState encryptionState)
134 CHIP_ERROR err = CHIP_NO_ERROR;
135 PeerConnectionState * state = nullptr;
136 uint8_t * msgStart = nullptr;
138 uint16_t headerSize = 0;
139 NodeId localNodeId = mLocalNodeId;
141 Transport::AdminPairingInfo * admin = nullptr;
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;
148 VerifyOrExit(mState == State::kInitialized, err = CHIP_ERROR_INCORRECT_STATE);
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);
154 // Find an active connection to the specified peer node
155 state = GetPeerConnectionState(session);
156 VerifyOrExit(state != nullptr, err = CHIP_ERROR_NOT_CONNECTED);
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();
164 if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq) ||
165 payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp))
167 packetHeader.SetSecureSessionControlMsg(true);
170 if (encryptionState == EncryptionState::kPayloadIsUnencrypted)
172 err = SecureMessageCodec::Encode(localNodeId, state, payloadHeader, packetHeader, msgBuf);
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();
181 msgStart = static_cast<uint8_t *>(msgBuf->Start() - headerSize);
182 msgLen = static_cast<uint16_t>(msgBuf->DataLength() + headerSize);
184 // Retain the packet buffer in case it's needed for retransmissions.
185 if (bufferRetainSlot != nullptr)
187 encryptedMsg = msgBuf.Retain();
188 encryptedMsg.mMsgId = packetHeader.GetMessageId();
191 ChipLogProgress(Inet, "Sending msg from %llu to %llu", localNodeId, state->GetPeerNodeId());
193 if (state->GetTransport() != nullptr)
195 ChipLogProgress(Inet, "Sending secure msg on connection specific transport");
196 err = state->GetTransport()->SendMessage(packetHeader, state->GetPeerAddress(), std::move(msgBuf));
200 ChipLogProgress(Inet, "Sending secure msg on generic transport");
201 err = mTransportMgr->SendMessage(packetHeader, state->GetPeerAddress(), std::move(msgBuf));
203 ChipLogProgress(Inet, "Secure msg send status %d", err);
206 if (bufferRetainSlot != nullptr)
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);
212 (*bufferRetainSlot) = std::move(encryptedMsg);
216 if (!msgBuf.IsNull())
218 const char * errStr = ErrorStr(err);
219 if (state == nullptr)
221 ChipLogError(Inet, "Secure transport could not find a valid PeerConnection: %s", errStr);
228 CHIP_ERROR SecureSessionMgr::NewPairing(const Optional<Transport::PeerAddress> & peerAddr, NodeId peerNodeId, PASESession * pairing,
229 PairingDirection direction, Transport::AdminId admin, Transport::Base * transport)
231 uint16_t peerKeyId = pairing->GetPeerKeyId();
232 uint16_t localKeyId = pairing->GetLocalKeyId();
233 PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(Optional<NodeId>::Value(peerNodeId), peerKeyId, nullptr);
235 // Find any existing connection with the same node and key ID
236 if (state && (state->GetAdminId() == Transport::kUndefinedAdminId || state->GetAdminId() == admin))
238 mPeerConnections.MarkConnectionExpired(
239 state, [this](const Transport::PeerConnectionState & state1) { HandleConnectionExpired(state1); });
242 ChipLogDetail(Inet, "New pairing for device %llu, key %d!!", peerNodeId, peerKeyId);
245 ReturnErrorOnFailure(
246 mPeerConnections.CreateNewPeerConnectionState(Optional<NodeId>::Value(peerNodeId), peerKeyId, localKeyId, &state));
248 state->SetAdminId(admin);
249 state->SetTransport(transport);
251 if (peerAddr.HasValue() && peerAddr.Value().GetIPAddress() != Inet::IPAddress::Any)
253 state->SetPeerAddress(peerAddr.Value());
255 else if (peerAddr.HasValue() &&
256 (peerAddr.Value().GetTransportType() == Transport::Type::kTcp ||
257 peerAddr.Value().GetTransportType() == Transport::Type::kUdp))
259 return CHIP_ERROR_INVALID_ARGUMENT;
262 if (state != nullptr)
266 case PairingDirection::kInitiator:
267 ReturnErrorOnFailure(pairing->DeriveSecureSession(reinterpret_cast<const uint8_t *>(kSpake2pI2RSessionInfo),
268 strlen(kSpake2pI2RSessionInfo), state->GetSenderSecureSession()));
270 ReturnErrorOnFailure(pairing->DeriveSecureSession(reinterpret_cast<const uint8_t *>(kSpake2pR2ISessionInfo),
271 strlen(kSpake2pR2ISessionInfo), state->GetReceiverSecureSession()));
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()));
282 return CHIP_ERROR_INVALID_ARGUMENT;
287 mCB->OnNewConnection({ state->GetPeerNodeId(), state->GetPeerKeyID(), admin }, this);
291 return CHIP_NO_ERROR;
294 void SecureSessionMgr::ScheduleExpiryTimer()
297 mSystemLayer->StartTimer(CHIP_PEER_CONNECTION_TIMEOUT_CHECK_FREQUENCY_MS, SecureSessionMgr::ExpiryTimerCallback, this);
299 VerifyOrDie(err == CHIP_NO_ERROR);
302 void SecureSessionMgr::CancelExpiryTimer()
304 if (mSystemLayer != nullptr)
306 mSystemLayer->CancelTimer(SecureSessionMgr::ExpiryTimerCallback, this);
310 void SecureSessionMgr::OnMessageReceived(const PacketHeader & packetHeader, const PeerAddress & peerAddress,
311 System::PacketBufferHandle msg)
313 CHIP_ERROR err = CHIP_NO_ERROR;
315 PeerConnectionState * state = mPeerConnections.FindPeerConnectionState(packetHeader.GetEncryptionKeyID(), nullptr);
317 PacketBufferHandle origMsg;
318 PayloadHeader payloadHeader;
320 Transport::AdminPairingInfo * admin = nullptr;
322 VerifyOrExit(!msg.IsNull(), ChipLogError(Inet, "Secure transport received NULL packet, discarding"));
324 if (state == nullptr)
326 ChipLogError(Inet, "Data received on an unknown connection (%d). Dropping it!!", packetHeader.GetEncryptionKeyID());
327 ExitNow(err = CHIP_ERROR_KEY_NOT_FOUND_FROM_PEER);
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())
335 admin->GetNodeId() == packetHeader.GetDestinationNodeId().Value(),
336 ChipLogError(Inet, "Secure transport received message, but destination node ID doesn't match our node ID, discarding"));
338 mPeerConnections.MarkConnectionActive(state);
340 // Decode the message
341 VerifyOrReturn(CHIP_NO_ERROR == SecureMessageCodec::Decode(state, payloadHeader, packetHeader, msg));
343 if (state->GetPeerNodeId() == kUndefinedNodeId && packetHeader.GetSourceNodeId().HasValue())
345 state->SetPeerNodeId(packetHeader.GetSourceNodeId().Value());
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)
353 state->SetPeerAddress(peerAddress);
356 if (!state->IsPeerMsgCounterSynced())
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())
362 state->SetPeerMessageIndex(packetHeader.GetMessageId());
365 // For all group messages, Set flag if peer group key message counter is not synchronized.
366 if (ChipKeyId::IsAppGroupKey(packetHeader.GetEncryptionKeyID()))
368 const_cast<PacketHeader &>(packetHeader).SetPeerGroupMsgIdNotSynchronized(true);
374 mCB->OnMessageReceived(packetHeader, payloadHeader, { state->GetPeerNodeId(), state->GetPeerKeyID(), state->GetAdminId() },
375 std::move(msg), this);
379 if (err != CHIP_NO_ERROR && mCB != nullptr)
381 mCB->OnReceiveError(err, peerAddress, this);
385 void SecureSessionMgr::HandleConnectionExpired(const Transport::PeerConnectionState & state)
387 char addr[Transport::PeerAddress::kMaxToStringSize];
388 state.GetPeerAddress().ToString(addr);
390 ChipLogDetail(Inet, "Connection from '%s' expired", addr);
394 mCB->OnConnectionExpired({ state.GetPeerNodeId(), state.GetPeerKeyID(), state.GetAdminId() }, this);
397 mTransportMgr->Disconnect(state.GetPeerAddress());
400 void SecureSessionMgr::ExpiryTimerCallback(System::Layer * layer, void * param, System::Error error)
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); });
410 mgr->ScheduleExpiryTimer(); // re-schedule the oneshot timer
413 PeerConnectionState * SecureSessionMgr::GetPeerConnectionState(SecureSessionHandle session)
415 return mPeerConnections.FindPeerConnectionState(Optional<NodeId>::Value(session.mPeerNodeId), session.mPeerKeyId, nullptr);