/src/connectedhomeip/src/transport/SessionManager.h
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2020-2021 Project CHIP Authors |
4 | | * All rights reserved. |
5 | | * |
6 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | * you may not use this file except in compliance with the License. |
8 | | * You may obtain a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, software |
13 | | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | * See the License for the specific language governing permissions and |
16 | | * limitations under the License. |
17 | | */ |
18 | | |
19 | | /** |
20 | | * @file |
21 | | * This file defines a secure transport layer which adds encryption to data |
22 | | * sent over a transport. |
23 | | * |
24 | | */ |
25 | | |
26 | | #pragma once |
27 | | |
28 | | #include <utility> |
29 | | |
30 | | #include <credentials/FabricTable.h> |
31 | | #include <crypto/RandUtils.h> |
32 | | #include <crypto/SessionKeystore.h> |
33 | | #include <inet/IPAddress.h> |
34 | | #include <lib/core/CHIPCore.h> |
35 | | #include <lib/core/CHIPPersistentStorageDelegate.h> |
36 | | #include <lib/support/CodeUtils.h> |
37 | | #include <lib/support/DLLUtil.h> |
38 | | #include <messaging/ReliableMessageProtocolConfig.h> |
39 | | #include <protocols/secure_channel/Constants.h> |
40 | | #include <transport/CryptoContext.h> |
41 | | #include <transport/GroupPeerMessageCounter.h> |
42 | | #include <transport/GroupSession.h> |
43 | | #include <transport/MessageCounterManagerInterface.h> |
44 | | #include <transport/MessageStats.h> |
45 | | #include <transport/SecureSessionTable.h> |
46 | | #include <transport/Session.h> |
47 | | #include <transport/SessionDelegate.h> |
48 | | #include <transport/SessionHolder.h> |
49 | | #include <transport/SessionMessageDelegate.h> |
50 | | #include <transport/TransportMgr.h> |
51 | | #include <transport/UnauthenticatedSessionTable.h> |
52 | | #include <transport/raw/Base.h> |
53 | | #include <transport/raw/PeerAddress.h> |
54 | | #include <transport/raw/Tuple.h> |
55 | | |
56 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
57 | | #include <transport/SessionConnectionDelegate.h> |
58 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
59 | | |
60 | | namespace chip { |
61 | | |
62 | | /* |
63 | | * This enum indicates whether a session needs to be established over a |
64 | | * suitable transport that meets certain payload size requirements for |
65 | | * transmitted messages. |
66 | | * |
67 | | */ |
68 | | enum class TransportPayloadCapability : uint8_t |
69 | | { |
70 | | kMRPPayload, // Transport requires the maximum payload size to fit within a single |
71 | | // IPv6 packet(1280 bytes). |
72 | | kLargePayload, // Transport needs to handle payloads larger than the single IPv6 |
73 | | // packet, as supported by MRP. The transport of choice, in this |
74 | | // case, is TCP. |
75 | | kMRPOrTCPCompatiblePayload // This option provides the ability to use MRP |
76 | | // as the preferred transport, but use a large |
77 | | // payload transport if that is already |
78 | | // available. |
79 | | }; |
80 | | /** |
81 | | * @brief |
82 | | * Tracks ownership of a encrypted packet buffer. |
83 | | * |
84 | | * EncryptedPacketBufferHandle is a kind of PacketBufferHandle class and used to hold a packet buffer |
85 | | * object whose payload has already been encrypted. |
86 | | */ |
87 | | class EncryptedPacketBufferHandle final : private System::PacketBufferHandle |
88 | | { |
89 | | public: |
90 | 0 | EncryptedPacketBufferHandle() {} |
91 | 0 | EncryptedPacketBufferHandle(EncryptedPacketBufferHandle && aBuffer) : PacketBufferHandle(std::move(aBuffer)) {} |
92 | | |
93 | 0 | void operator=(EncryptedPacketBufferHandle && aBuffer) { PacketBufferHandle::operator=(std::move(aBuffer)); } |
94 | | |
95 | | using System::PacketBufferHandle::IsNull; |
96 | | // Pass-through to HasChainedBuffer on our underlying buffer without |
97 | | // exposing operator-> |
98 | 0 | bool HasChainedBuffer() const { return (*this)->HasChainedBuffer(); } |
99 | | |
100 | | uint32_t GetMessageCounter() const; |
101 | | |
102 | | /** |
103 | | * Creates a copy of the data in this packet. |
104 | | * |
105 | | * Does NOT support chained buffers. |
106 | | * |
107 | | * @returns empty handle on allocation failure. |
108 | | */ |
109 | 0 | EncryptedPacketBufferHandle CloneData() { return EncryptedPacketBufferHandle(PacketBufferHandle::CloneData()); } |
110 | | |
111 | | #ifdef CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API |
112 | | /** |
113 | | * Extracts the (unencrypted) packet header from this encrypted packet |
114 | | * buffer. Returns error if a packet header cannot be extracted (e.g. if |
115 | | * there are not enough bytes in this packet buffer). After this call the |
116 | | * buffer does not have a packet header. This API is meant for |
117 | | * unit tests only. The CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API define |
118 | | * should not be defined normally. |
119 | | */ |
120 | | CHIP_ERROR ExtractPacketHeader(PacketHeader & aPacketHeader) { return aPacketHeader.DecodeAndConsume(*this); } |
121 | | |
122 | | /** |
123 | | * Inserts a new (unencrypted) packet header in the encrypted packet buffer |
124 | | * based on the given PacketHeader. This API is meant for |
125 | | * unit tests only. The CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API define |
126 | | * should not be defined normally. |
127 | | */ |
128 | | CHIP_ERROR InsertPacketHeader(const PacketHeader & aPacketHeader) { return aPacketHeader.EncodeBeforeData(*this); } |
129 | | #endif // CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API |
130 | | |
131 | | static EncryptedPacketBufferHandle MarkEncrypted(PacketBufferHandle && aBuffer) |
132 | 0 | { |
133 | 0 | return EncryptedPacketBufferHandle(std::move(aBuffer)); |
134 | 0 | } |
135 | | |
136 | | /** |
137 | | * Get a handle to the data that allows mutating the bytes. This should |
138 | | * only be used if absolutely necessary, because EncryptedPacketBufferHandle |
139 | | * represents a buffer that we want to resend as-is. |
140 | | */ |
141 | 0 | PacketBufferHandle CastToWritable() const { return PacketBufferHandle::Retain(); } |
142 | | |
143 | | private: |
144 | 0 | EncryptedPacketBufferHandle(PacketBufferHandle && aBuffer) : PacketBufferHandle(std::move(aBuffer)) {} |
145 | | }; |
146 | | |
147 | | class DLL_EXPORT SessionManager : public TransportMgrDelegate, public FabricTable::Delegate |
148 | | { |
149 | | public: |
150 | | SessionManager(); |
151 | | ~SessionManager() override; |
152 | | |
153 | | /** |
154 | | * @brief |
155 | | * This function takes the payload and returns an encrypted message which can be sent multiple times. |
156 | | * Every successful call to this function MUST be followed by a send attempt with the prepared message. |
157 | | * |
158 | | * @details |
159 | | * It does the following: |
160 | | * 1. Encrypt the msgBuf |
161 | | * 2. construct the packet header |
162 | | * 3. Encode the packet header and prepend it to message. |
163 | | * Returns a encrypted message in encryptedMessage. |
164 | | */ |
165 | | CHIP_ERROR PrepareMessage(const SessionHandle & session, PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf, |
166 | | EncryptedPacketBufferHandle & encryptedMessage); |
167 | | |
168 | | /** |
169 | | * @brief |
170 | | * Send a prepared message to a currently connected peer. |
171 | | */ |
172 | | CHIP_ERROR SendPreparedMessage(const SessionHandle & session, const EncryptedPacketBufferHandle & preparedMessage); |
173 | | |
174 | | /// @brief Set the delegate for handling incoming messages. There can be only one message delegate (probably the |
175 | | /// ExchangeManager) |
176 | 0 | void SetMessageDelegate(SessionMessageDelegate * cb) { mCB = cb; } |
177 | | |
178 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
179 | 0 | void SetConnectionDelegate(SessionConnectionDelegate * cb) { mConnDelegate = cb; } |
180 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
181 | | |
182 | | // Test-only: create a session on the fly. |
183 | | CHIP_ERROR InjectPaseSessionWithTestKey(SessionHolder & sessionHolder, uint16_t localSessionId, NodeId peerNodeId, |
184 | | uint16_t peerSessionId, FabricIndex fabricIndex, |
185 | | const Transport::PeerAddress & peerAddress, CryptoContext::SessionRole role); |
186 | | CHIP_ERROR InjectCaseSessionWithTestKey(SessionHolder & sessionHolder, uint16_t localSessionId, uint16_t peerSessionId, |
187 | | NodeId localNodeId, NodeId peerNodeId, FabricIndex fabric, |
188 | | const Transport::PeerAddress & peerAddress, CryptoContext::SessionRole role, |
189 | | const CATValues & cats = CATValues{}); |
190 | | |
191 | | /** |
192 | | * @brief |
193 | | * Allocate a secure session and non-colliding session ID in the secure |
194 | | * session table. |
195 | | * |
196 | | * If we're either establishing or just finished establishing a session to a peer in either initiator or responder |
197 | | * roles, the node id of that peer should be provided in sessionEvictionHint. Else, it should be initialized |
198 | | * to a default-constructed ScopedNodeId(). |
199 | | * |
200 | | * @return SessionHandle with a reference to a SecureSession, else NullOptional on failure |
201 | | */ |
202 | | CHECK_RETURN_VALUE |
203 | | Optional<SessionHandle> AllocateSession(Transport::SecureSession::Type secureSessionType, |
204 | | const ScopedNodeId & sessionEvictionHint); |
205 | | |
206 | | /** |
207 | | * A set of templated helper function that call a provided lambda |
208 | | * on all sessions in the underlying session table that match the provided |
209 | | * query criteria. |
210 | | * |
211 | | */ |
212 | | |
213 | | /** |
214 | | * Call the provided lambda on sessions whose remote side match the provided ScopedNodeId. |
215 | | * |
216 | | */ |
217 | | template <typename Function> |
218 | | void ForEachMatchingSession(const ScopedNodeId & node, Function && function) |
219 | 0 | { |
220 | 0 | mSecureSessions.ForEachSession([&](auto * session) { |
221 | 0 | if (session->GetPeer() == node) |
222 | 0 | { |
223 | 0 | function(session); |
224 | 0 | } |
225 | |
|
226 | 0 | return Loop::Continue; |
227 | 0 | }); Unexecuted instantiation: SessionManager.cpp:auto chip::SessionManager::ForEachMatchingSession<chip::SessionManager::ExpireAllSessions(chip::ScopedNodeId const&)::$_0>(chip::ScopedNodeId const&, chip::SessionManager::ExpireAllSessions(chip::ScopedNodeId const&)::$_0&&)::{lambda(auto:1*)#1}::operator()<chip::Transport::SecureSession>(chip::Transport::SecureSession*) constUnexecuted instantiation: ReadClient.cpp:auto chip::SessionManager::ForEachMatchingSession<chip::app::ReadClient::TriggerResubscriptionForLivenessTimeout(chip::ChipError)::$_0>(chip::ScopedNodeId const&, chip::app::ReadClient::TriggerResubscriptionForLivenessTimeout(chip::ChipError)::$_0&&)::{lambda(auto:1*)#1}::operator()<chip::Transport::SecureSession>(chip::Transport::SecureSession*) const |
228 | 0 | } Unexecuted instantiation: SessionManager.cpp:void chip::SessionManager::ForEachMatchingSession<chip::SessionManager::ExpireAllSessions(chip::ScopedNodeId const&)::$_0>(chip::ScopedNodeId const&, chip::SessionManager::ExpireAllSessions(chip::ScopedNodeId const&)::$_0&&) Unexecuted instantiation: ReadClient.cpp:void chip::SessionManager::ForEachMatchingSession<chip::app::ReadClient::TriggerResubscriptionForLivenessTimeout(chip::ChipError)::$_0>(chip::ScopedNodeId const&, chip::app::ReadClient::TriggerResubscriptionForLivenessTimeout(chip::ChipError)::$_0&&) |
229 | | |
230 | | /** |
231 | | * Call the provided lambda on sessions that match the provided fabric index. |
232 | | * |
233 | | */ |
234 | | template <typename Function> |
235 | | void ForEachMatchingSession(FabricIndex fabricIndex, Function && function) |
236 | 0 | { |
237 | 0 | mSecureSessions.ForEachSession([&](auto * session) { |
238 | 0 | if (session->GetFabricIndex() == fabricIndex) |
239 | 0 | { |
240 | 0 | function(session); |
241 | 0 | } |
242 | |
|
243 | 0 | return Loop::Continue; |
244 | 0 | }); |
245 | 0 | } |
246 | | |
247 | | /** |
248 | | * Call the provided lambda on all sessions whose remote side match the logical fabric |
249 | | * associated with the provided ScopedNodeId and target the same logical remote node. |
250 | | * |
251 | | * *NOTE* This is identical in behavior to ForEachMatchingSession(const ScopedNodeId ..) |
252 | | * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide |
253 | | * on the same logical fabric (i.e root public key + fabric ID tuple). |
254 | | * This can ONLY happen if multiple controller instances on the same fabric is permitted |
255 | | * and each is assigned a unique fabric index. |
256 | | */ |
257 | | template <typename Function> |
258 | | CHIP_ERROR ForEachMatchingSessionOnLogicalFabric(const ScopedNodeId & node, Function && function) |
259 | 0 | { |
260 | 0 | Crypto::P256PublicKey targetPubKey; |
261 | |
|
262 | 0 | auto * targetFabric = mFabricTable->FindFabricWithIndex(node.GetFabricIndex()); |
263 | 0 | VerifyOrReturnError(targetFabric != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX); |
264 | | |
265 | 0 | auto err = targetFabric->FetchRootPubkey(targetPubKey); |
266 | 0 | VerifyOrDie(err == CHIP_NO_ERROR); |
267 | | |
268 | 0 | mSecureSessions.ForEachSession([&](auto * session) { |
269 | 0 | Crypto::P256PublicKey comparePubKey; |
270 | | |
271 | | // |
272 | | // It's entirely possible to either come across a PASE session OR, a CASE session |
273 | | // that has yet to be activated (i.e a CASEServer holding onto a SecureSession object |
274 | | // waiting for a Sigma1 message to arrive). Let's skip those. |
275 | | // |
276 | 0 | if (!session->IsCASESession() || session->GetFabricIndex() == kUndefinedFabricIndex) |
277 | 0 | { |
278 | 0 | return Loop::Continue; |
279 | 0 | } |
280 | | |
281 | 0 | auto * compareFabric = mFabricTable->FindFabricWithIndex(session->GetFabricIndex()); |
282 | 0 | VerifyOrDie(compareFabric != nullptr); |
283 | | |
284 | 0 | err = compareFabric->FetchRootPubkey(comparePubKey); |
285 | 0 | VerifyOrDie(err == CHIP_NO_ERROR); |
286 | | |
287 | 0 | if (comparePubKey.Matches(targetPubKey) && targetFabric->GetFabricId() == compareFabric->GetFabricId() && |
288 | 0 | session->GetPeerNodeId() == node.GetNodeId()) |
289 | 0 | { |
290 | 0 | function(session); |
291 | 0 | } |
292 | |
|
293 | 0 | return Loop::Continue; |
294 | 0 | }); |
295 | |
|
296 | 0 | return CHIP_NO_ERROR; |
297 | 0 | } |
298 | | |
299 | | /** |
300 | | * Call the provided lambda on all sessions that match the logical fabric |
301 | | * associated with the provided fabric index. |
302 | | * |
303 | | * *NOTE* This is identical in behavior to ForEachMatchingSession(FabricIndex ..) |
304 | | * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide |
305 | | * on the same logical fabric (i.e root public key + fabric ID tuple). |
306 | | * This can ONLY happen if multiple controller instances on the same fabric is permitted |
307 | | * and each is assigned a unique fabric index. |
308 | | */ |
309 | | template <typename Function> |
310 | | CHIP_ERROR ForEachMatchingSessionOnLogicalFabric(FabricIndex fabricIndex, Function && function) |
311 | 0 | { |
312 | 0 | Crypto::P256PublicKey targetPubKey; |
313 | |
|
314 | 0 | auto * targetFabric = mFabricTable->FindFabricWithIndex(fabricIndex); |
315 | 0 | VerifyOrReturnError(targetFabric != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX); |
316 | | |
317 | 0 | SuccessOrDie(targetFabric->FetchRootPubkey(targetPubKey)); |
318 | | |
319 | 0 | mSecureSessions.ForEachSession([&](auto * session) { |
320 | 0 | Crypto::P256PublicKey comparePubKey; |
321 | | |
322 | | // |
323 | | // It's entirely possible to either come across a PASE session OR, a CASE session |
324 | | // that has yet to be activated (i.e a CASEServer holding onto a SecureSession object |
325 | | // waiting for a Sigma1 message to arrive). Let's skip those. |
326 | | // |
327 | 0 | if (!session->IsCASESession() || session->GetFabricIndex() == kUndefinedFabricIndex) |
328 | 0 | { |
329 | 0 | return Loop::Continue; |
330 | 0 | } |
331 | | |
332 | 0 | auto * compareFabric = mFabricTable->FindFabricWithIndex(session->GetFabricIndex()); |
333 | 0 | VerifyOrDie(compareFabric != nullptr); |
334 | | |
335 | 0 | SuccessOrDie(compareFabric->FetchRootPubkey(comparePubKey)); |
336 | | |
337 | 0 | if (comparePubKey.Matches(targetPubKey) && targetFabric->GetFabricId() == compareFabric->GetFabricId()) |
338 | 0 | { |
339 | 0 | function(session); |
340 | 0 | } |
341 | |
|
342 | 0 | return Loop::Continue; |
343 | 0 | }); |
344 | |
|
345 | 0 | return CHIP_NO_ERROR; |
346 | 0 | } |
347 | | |
348 | | /** |
349 | | * Expire all sessions for a given peer, as identified by a specific fabric |
350 | | * index and node ID. |
351 | | */ |
352 | | void ExpireAllSessions(const ScopedNodeId & node); |
353 | | |
354 | | /** |
355 | | * Expire all sessions associated with the given fabric index. |
356 | | * |
357 | | * *NOTE* This is generally all sessions for a given fabric _EXCEPT_ if there are multiple |
358 | | * FabricInfo instances in the FabricTable that collide on the same logical fabric (i.e |
359 | | * root public key + fabric ID tuple). This can ONLY happen if multiple controller |
360 | | * instances on the same fabric is permitted and each is assigned a unique fabric index. |
361 | | */ |
362 | | void ExpireAllSessionsForFabric(FabricIndex fabricIndex); |
363 | | |
364 | | /** |
365 | | * Expire all sessions whose remote side matches the logical fabric |
366 | | * associated with the provided ScopedNodeId and target the same logical remote node. |
367 | | * |
368 | | * *NOTE* This is identical in behavior to ExpireAllSessions(const ScopedNodeId ..) |
369 | | * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide |
370 | | * on the same logical fabric (i.e root public key + fabric ID tuple). This can ONLY happen |
371 | | * if multiple controller instances on the same fabric is permitted and each is assigned |
372 | | * a unique fabric index. |
373 | | * |
374 | | */ |
375 | | CHIP_ERROR ExpireAllSessionsOnLogicalFabric(const ScopedNodeId & node); |
376 | | |
377 | | /** |
378 | | * Expire all sessions whose remote side matches the logical fabric |
379 | | * associated with the provided fabric index. |
380 | | * |
381 | | * *NOTE* This is identical in behavior to ExpireAllSessExpireAllSessionsForFabricions(FabricIndex ..) |
382 | | * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide |
383 | | * on the same logical fabric (i.e root public key + fabric ID tuple). This can ONLY happen |
384 | | * if multiple controller instances on the same fabric is permitted and each is assigned |
385 | | * a unique fabric index. |
386 | | * |
387 | | */ |
388 | | CHIP_ERROR ExpireAllSessionsOnLogicalFabric(FabricIndex fabricIndex); |
389 | | |
390 | | void ExpireAllPASESessions(); |
391 | | |
392 | | /** |
393 | | * Expire all secure sessions. See documentation for Shutdown on when it's |
394 | | * appropriate to use this. |
395 | | */ |
396 | | void ExpireAllSecureSessions(); |
397 | | |
398 | | /** |
399 | | * @brief |
400 | | * Marks all active sessions that match provided arguments as defunct. |
401 | | * |
402 | | * @param node Scoped node ID of the active sessions we should mark as defunct. |
403 | | * @param type Type of session we are looking to mark as defunct. If matching |
404 | | * against all types of sessions is desired, NullOptional should |
405 | | * be passed into type. |
406 | | */ |
407 | | void MarkSessionsAsDefunct(const ScopedNodeId & node, const Optional<Transport::SecureSession::Type> & type); |
408 | | |
409 | | /** |
410 | | * @brief |
411 | | * Update all CASE sessions that match `node` with the provided transport peer address. |
412 | | * |
413 | | * @param node Scoped node ID of the active sessions we want to update. |
414 | | * @param addr Transport peer address that we want to update to. |
415 | | */ |
416 | | void UpdateAllSessionsPeerAddress(const ScopedNodeId & node, const Transport::PeerAddress & addr); |
417 | | |
418 | | /** |
419 | | * @brief |
420 | | * Return the System Layer pointer used by current SessionManager. |
421 | | */ |
422 | 0 | System::Layer * SystemLayer() { return mSystemLayer; } |
423 | | |
424 | | /** |
425 | | * @brief |
426 | | * Initialize a Secure Session Manager |
427 | | * |
428 | | * @param systemLayer System layer to use |
429 | | * @param transportMgr Transport to use |
430 | | * @param messageCounterManager The message counter manager |
431 | | * @param storageDelegate Persistent storage implementation |
432 | | * @param fabricTable Fabric table to hold information about joined fabrics |
433 | | * @param sessionKeystore Session keystore for management of symmetric encryption keys |
434 | | */ |
435 | | CHIP_ERROR Init(System::Layer * systemLayer, TransportMgrBase * transportMgr, |
436 | | Transport::MessageCounterManagerInterface * messageCounterManager, |
437 | | chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable, |
438 | | Crypto::SessionKeystore & sessionKeystore); |
439 | | |
440 | | /** |
441 | | * @brief |
442 | | * Shutdown the Secure Session Manager. This terminates this instance |
443 | | * of the object and reset it's state. |
444 | | * |
445 | | * The proper order of shutdown for SessionManager is as follows: |
446 | | * |
447 | | * 1) Call ExpireAllSecureSessions() on the SessionManager, and ensure that any unauthenticated |
448 | | * sessions (e.g. CASEServer and CASESessionManager instances, or anything that does PASE |
449 | | * handshakes) are released. |
450 | | * 2) Shut down the exchange manager, so that it's no longer referencing |
451 | | * the to-be-shut-down SessionManager. |
452 | | * 3) Shut down the SessionManager. |
453 | | */ |
454 | | void Shutdown(); |
455 | | |
456 | | /** |
457 | | * @brief Notification that a fabric was removed. |
458 | | */ |
459 | | void FabricRemoved(FabricIndex fabricIndex); |
460 | | |
461 | 0 | TransportMgrBase * GetTransportManager() const { return mTransportMgr; } |
462 | 0 | Transport::SecureSessionTable & GetSecureSessions() { return mSecureSessions; } |
463 | | |
464 | | /** |
465 | | * @brief |
466 | | * Handle received secure message. Implements TransportMgrDelegate |
467 | | * |
468 | | * @param source the source address of the package |
469 | | * @param msgBuf the buffer containing a full CHIP message (except for the optional length field). |
470 | | * @param ctxt pointer to additional context on the underlying transport. For TCP, it is a pointer |
471 | | * to the underlying connection object. |
472 | | */ |
473 | | void OnMessageReceived(const Transport::PeerAddress & source, System::PacketBufferHandle && msgBuf, |
474 | | Transport::MessageTransportContext * ctxt = nullptr) override; |
475 | | |
476 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
477 | | CHIP_ERROR TCPConnect(const Transport::PeerAddress & peerAddress, Transport::AppTCPConnectionCallbackCtxt * appState, |
478 | | Transport::ActiveTCPConnectionHandle & peerConnState); |
479 | | |
480 | | void HandleConnectionReceived(Transport::ActiveTCPConnectionState & conn) override; |
481 | | |
482 | | void HandleConnectionAttemptComplete(Transport::ActiveTCPConnectionHandle & conn, CHIP_ERROR conErr) override; |
483 | | |
484 | | void HandleConnectionClosed(Transport::ActiveTCPConnectionState & conn, CHIP_ERROR conErr) override; |
485 | | |
486 | | // Functors for callbacks into higher layers |
487 | | using OnTCPConnectionReceivedCallback = void (*)(Transport::ActiveTCPConnectionHandle & conn); |
488 | | |
489 | | using OnTCPConnectionCompleteCallback = void (*)(Transport::ActiveTCPConnectionHandle & conn, CHIP_ERROR conErr); |
490 | | |
491 | | using OnTCPConnectionClosedCallback = void (*)(Transport::ActiveTCPConnectionState & conn, CHIP_ERROR conErr); |
492 | | |
493 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
494 | | |
495 | | Optional<SessionHandle> CreateUnauthenticatedSession(const Transport::PeerAddress & peerAddress, |
496 | | const ReliableMessageProtocolConfig & config) |
497 | 0 | { |
498 | | // Allocate ephemeralInitiatorNodeID in Operational Node ID range |
499 | 0 | NodeId ephemeralInitiatorNodeID; |
500 | 0 | do |
501 | 0 | { |
502 | 0 | ephemeralInitiatorNodeID = static_cast<NodeId>(Crypto::GetRandU64()); |
503 | 0 | } while (!IsOperationalNodeId(ephemeralInitiatorNodeID)); |
504 | 0 | return mUnauthenticatedSessions.AllocInitiator(ephemeralInitiatorNodeID, peerAddress, config); |
505 | 0 | } |
506 | | |
507 | | // |
508 | | // Find an existing secure session given a peer's scoped NodeId and a type of session to match against. |
509 | | // If matching against all types of sessions is desired, NullOptional should be passed into type. |
510 | | // |
511 | | // If a valid session is found, an Optional<SessionHandle> with the value set to the SessionHandle of the session |
512 | | // is returned. Otherwise, an Optional<SessionHandle> with no value set is returned. |
513 | | // |
514 | | // |
515 | | Optional<SessionHandle> |
516 | | FindSecureSessionForNode(ScopedNodeId peerNodeId, const Optional<Transport::SecureSession::Type> & type = NullOptional, |
517 | | TransportPayloadCapability transportPayloadCapability = TransportPayloadCapability::kMRPPayload); |
518 | | |
519 | | using SessionHandleCallback = bool (*)(void * context, SessionHandle & sessionHandle); |
520 | | CHIP_ERROR ForEachSessionHandle(void * context, SessionHandleCallback callback); |
521 | | |
522 | | //// FabricTable::Delegate Implementation //// |
523 | | void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override |
524 | 0 | { |
525 | 0 | (void) fabricTable; |
526 | 0 | this->FabricRemoved(fabricIndex); |
527 | 0 | } |
528 | | |
529 | 0 | FabricTable * GetFabricTable() const { return mFabricTable; } |
530 | | |
531 | 0 | Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; } |
532 | | |
533 | 0 | MessageStats GetMessageStats() const { return mMessageStats; } |
534 | | |
535 | | private: |
536 | | /** |
537 | | * The State of a secure transport object. |
538 | | */ |
539 | | enum class State |
540 | | { |
541 | | kNotReady, /**< State before initialization. */ |
542 | | kInitialized, /**< State when the object is ready connect to other peers. */ |
543 | | }; |
544 | | |
545 | | enum class EncryptionState |
546 | | { |
547 | | kPayloadIsEncrypted, |
548 | | kPayloadIsUnencrypted, |
549 | | }; |
550 | | |
551 | | System::Layer * mSystemLayer = nullptr; |
552 | | FabricTable * mFabricTable = nullptr; |
553 | | Crypto::SessionKeystore * mSessionKeystore = nullptr; |
554 | | Transport::UnauthenticatedSessionTable<CHIP_CONFIG_UNAUTHENTICATED_CONNECTION_POOL_SIZE> mUnauthenticatedSessions; |
555 | | Transport::SecureSessionTable mSecureSessions; |
556 | | State mState; // < Initialization state of the object |
557 | | chip::Transport::GroupOutgoingCounters mGroupClientCounter; |
558 | | MessageStats mMessageStats; |
559 | | |
560 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
561 | | OnTCPConnectionReceivedCallback mConnReceivedCb = nullptr; |
562 | | OnTCPConnectionCompleteCallback mConnCompleteCb = nullptr; |
563 | | OnTCPConnectionClosedCallback mConnClosedCb = nullptr; |
564 | | |
565 | | // Hold the TCPConnection callback context for the receiver application in the SessionManager. |
566 | | // On receipt of a connection from a peer, the SessionManager |
567 | | Transport::AppTCPConnectionCallbackCtxt * mServerTCPConnCbCtxt = nullptr; |
568 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
569 | | |
570 | | SessionMessageDelegate * mCB = nullptr; |
571 | | |
572 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
573 | | SessionConnectionDelegate * mConnDelegate = nullptr; |
574 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
575 | | |
576 | | TransportMgrBase * mTransportMgr = nullptr; |
577 | | Transport::MessageCounterManagerInterface * mMessageCounterManager = nullptr; |
578 | | |
579 | | GlobalUnencryptedMessageCounter mGlobalUnencryptedMessageCounter; |
580 | | |
581 | | /** |
582 | | * @brief Parse, decrypt, validate, and dispatch a secure unicast message. |
583 | | * |
584 | | * @param[in] partialPacketHeader The partial PacketHeader of the message after processing with DecodeFixed. |
585 | | * If the message decrypts successfully, this will be filled with a fully decoded PacketHeader. |
586 | | * @param[in] peerAddress The PeerAddress of the message as provided by the receiving Transport Endpoint. |
587 | | * @param msg The full message buffer, including header fields. |
588 | | * @param ctxt The pointer to additional context on the underlying transport. For TCP, it is a pointer |
589 | | * to the underlying connection object. |
590 | | */ |
591 | | void SecureUnicastMessageDispatch(const PacketHeader & partialPacketHeader, const Transport::PeerAddress & peerAddress, |
592 | | System::PacketBufferHandle && msg, Transport::MessageTransportContext * ctxt = nullptr); |
593 | | |
594 | | /** |
595 | | * @brief Parse, decrypt, validate, and dispatch a secure group message. |
596 | | * |
597 | | * @param partialPacketHeader The partial PacketHeader of the message once processed with DecodeFixed. |
598 | | * @param peerAddress The PeerAddress of the message as provided by the receiving Transport Endpoint. |
599 | | * @param msg The full message buffer, including header fields. |
600 | | */ |
601 | | void SecureGroupMessageDispatch(const PacketHeader & partialPacketHeader, const Transport::PeerAddress & peerAddress, |
602 | | System::PacketBufferHandle && msg); |
603 | | |
604 | | /** |
605 | | * @brief Parse, decrypt, validate, and dispatch an unsecured message. |
606 | | * |
607 | | * @param partialPacketHeader The partial PacketHeader of the message after processing with DecodeFixed. |
608 | | * @param peerAddress The PeerAddress of the message as provided by the receiving Transport Endpoint. |
609 | | * @param msg The full message buffer, including header fields. |
610 | | * @param ctxt The pointer to additional context on the underlying transport. For TCP, it is a pointer |
611 | | * to the underlying connection object. |
612 | | */ |
613 | | void UnauthenticatedMessageDispatch(const PacketHeader & partialPacketHeader, const Transport::PeerAddress & peerAddress, |
614 | | System::PacketBufferHandle && msg, Transport::MessageTransportContext * ctxt = nullptr); |
615 | | |
616 | | void OnReceiveError(CHIP_ERROR error, const Transport::PeerAddress & source); |
617 | | |
618 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
619 | | void MarkSecureSessionOverTCPForEviction(Transport::ActiveTCPConnectionState & conn, CHIP_ERROR conErr); |
620 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
621 | | |
622 | | static bool IsControlMessage(PayloadHeader & payloadHeader) |
623 | 0 | { |
624 | 0 | return payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq) || |
625 | 0 | payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp); |
626 | 0 | } |
627 | | |
628 | | void CountMessagesReceived(const SessionHandle & sessionHandle, const PayloadHeader & payloadHeader); |
629 | | void CountMessagesSent(const SessionHandle & sessionHandle, const PayloadHeader & payloadHeader); |
630 | | }; |
631 | | |
632 | | namespace MessagePacketBuffer { |
633 | | /** |
634 | | * Maximum size of a message footer, in bytes. |
635 | | */ |
636 | | inline constexpr uint16_t kMaxFooterSize = kMaxTagLen; |
637 | | |
638 | | /** |
639 | | * Allocates a packet buffer with space for message headers and footers. |
640 | | * |
641 | | * Fails and returns \c nullptr if no memory is available, or if the size requested is too large. |
642 | | * |
643 | | * @param[in] aAvailableSize Minimum number of octets to for application data. |
644 | | * |
645 | | * @return On success, a PacketBufferHandle to the allocated buffer. On fail, \c nullptr. |
646 | | */ |
647 | | inline System::PacketBufferHandle New(size_t aAvailableSize) |
648 | 0 | { |
649 | 0 | static_assert(System::PacketBuffer::kMaxSize > kMaxFooterSize, "inadequate capacity"); |
650 | 0 | if (aAvailableSize > System::PacketBuffer::kMaxSize - kMaxFooterSize) |
651 | 0 | { |
652 | 0 | return System::PacketBufferHandle(); |
653 | 0 | } |
654 | 0 | return System::PacketBufferHandle::New(aAvailableSize + kMaxFooterSize); |
655 | 0 | } |
656 | | |
657 | | /** |
658 | | * Allocates a packet buffer with initial contents. |
659 | | * |
660 | | * @param[in] aData Initial buffer contents. |
661 | | * @param[in] aDataSize Size of initial buffer contents. |
662 | | * |
663 | | * @return On success, a PacketBufferHandle to the allocated buffer. On fail, \c nullptr. |
664 | | */ |
665 | | inline System::PacketBufferHandle NewWithData(const void * aData, size_t aDataSize) |
666 | 0 | { |
667 | 0 | return System::PacketBufferHandle::NewWithData(aData, aDataSize, kMaxFooterSize); |
668 | 0 | } |
669 | | |
670 | | /** |
671 | | * Check whether a packet buffer has enough space for a message footer. |
672 | | * |
673 | | * @returns true if there is space, false otherwise. |
674 | | */ |
675 | | inline bool HasFooterSpace(const System::PacketBufferHandle & aBuffer) |
676 | 0 | { |
677 | 0 | return aBuffer->AvailableDataLength() >= kMaxFooterSize; |
678 | 0 | } |
679 | | |
680 | | } // namespace MessagePacketBuffer |
681 | | |
682 | | } // namespace chip |