Line data Source code
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/SecureSessionTable.h>
45 : #include <transport/Session.h>
46 : #include <transport/SessionDelegate.h>
47 : #include <transport/SessionHolder.h>
48 : #include <transport/SessionMessageDelegate.h>
49 : #include <transport/TransportMgr.h>
50 : #include <transport/UnauthenticatedSessionTable.h>
51 : #include <transport/raw/Base.h>
52 : #include <transport/raw/PeerAddress.h>
53 : #include <transport/raw/Tuple.h>
54 :
55 : namespace chip {
56 :
57 : /**
58 : * @brief
59 : * Tracks ownership of a encrypted packet buffer.
60 : *
61 : * EncryptedPacketBufferHandle is a kind of PacketBufferHandle class and used to hold a packet buffer
62 : * object whose payload has already been encrypted.
63 : */
64 : class EncryptedPacketBufferHandle final : private System::PacketBufferHandle
65 : {
66 : public:
67 9540 : EncryptedPacketBufferHandle() {}
68 : EncryptedPacketBufferHandle(EncryptedPacketBufferHandle && aBuffer) : PacketBufferHandle(std::move(aBuffer)) {}
69 :
70 9585 : void operator=(EncryptedPacketBufferHandle && aBuffer) { PacketBufferHandle::operator=(std::move(aBuffer)); }
71 :
72 : using System::PacketBufferHandle::IsNull;
73 : // Pass-through to HasChainedBuffer on our underlying buffer without
74 : // exposing operator->
75 : bool HasChainedBuffer() const { return (*this)->HasChainedBuffer(); }
76 :
77 : uint32_t GetMessageCounter() const;
78 :
79 : /**
80 : * Creates a copy of the data in this packet.
81 : *
82 : * Does NOT support chained buffers.
83 : *
84 : * @returns empty handle on allocation failure.
85 : */
86 : EncryptedPacketBufferHandle CloneData() { return EncryptedPacketBufferHandle(PacketBufferHandle::CloneData()); }
87 :
88 : #ifdef CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API
89 : /**
90 : * Extracts the (unencrypted) packet header from this encrypted packet
91 : * buffer. Returns error if a packet header cannot be extracted (e.g. if
92 : * there are not enough bytes in this packet buffer). After this call the
93 : * buffer does not have a packet header. This API is meant for
94 : * unit tests only. The CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API define
95 : * should not be defined normally.
96 : */
97 : CHIP_ERROR ExtractPacketHeader(PacketHeader & aPacketHeader) { return aPacketHeader.DecodeAndConsume(*this); }
98 :
99 : /**
100 : * Inserts a new (unencrypted) packet header in the encrypted packet buffer
101 : * based on the given PacketHeader. This API is meant for
102 : * unit tests only. The CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API define
103 : * should not be defined normally.
104 : */
105 : CHIP_ERROR InsertPacketHeader(const PacketHeader & aPacketHeader) { return aPacketHeader.EncodeBeforeData(*this); }
106 : #endif // CHIP_ENABLE_TEST_ENCRYPTED_BUFFER_API
107 :
108 9585 : static EncryptedPacketBufferHandle MarkEncrypted(PacketBufferHandle && aBuffer)
109 : {
110 9585 : return EncryptedPacketBufferHandle(std::move(aBuffer));
111 : }
112 :
113 : /**
114 : * Get a handle to the data that allows mutating the bytes. This should
115 : * only be used if absolutely necessary, because EncryptedPacketBufferHandle
116 : * represents a buffer that we want to resend as-is.
117 : */
118 9619 : PacketBufferHandle CastToWritable() const { return PacketBufferHandle::Retain(); }
119 :
120 : private:
121 9539 : EncryptedPacketBufferHandle(PacketBufferHandle && aBuffer) : PacketBufferHandle(std::move(aBuffer)) {}
122 : };
123 :
124 : class DLL_EXPORT SessionManager : public TransportMgrDelegate, public FabricTable::Delegate
125 : {
126 : public:
127 : SessionManager();
128 : ~SessionManager() override;
129 :
130 : /**
131 : * @brief
132 : * This function takes the payload and returns an encrypted message which can be sent multiple times.
133 : *
134 : * @details
135 : * It does the following:
136 : * 1. Encrypt the msgBuf
137 : * 2. construct the packet header
138 : * 3. Encode the packet header and prepend it to message.
139 : * Returns a encrypted message in encryptedMessage.
140 : */
141 : CHIP_ERROR PrepareMessage(const SessionHandle & session, PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf,
142 : EncryptedPacketBufferHandle & encryptedMessage);
143 :
144 : /**
145 : * @brief
146 : * Send a prepared message to a currently connected peer.
147 : */
148 : CHIP_ERROR SendPreparedMessage(const SessionHandle & session, const EncryptedPacketBufferHandle & preparedMessage);
149 :
150 : /// @brief Set the delegate for handling incoming messages. There can be only one message delegate (probably the
151 : /// ExchangeManager)
152 226 : void SetMessageDelegate(SessionMessageDelegate * cb) { mCB = cb; }
153 :
154 : // Test-only: create a session on the fly.
155 : CHIP_ERROR InjectPaseSessionWithTestKey(SessionHolder & sessionHolder, uint16_t localSessionId, NodeId peerNodeId,
156 : uint16_t peerSessionId, FabricIndex fabricIndex,
157 : const Transport::PeerAddress & peerAddress, CryptoContext::SessionRole role);
158 : CHIP_ERROR InjectCaseSessionWithTestKey(SessionHolder & sessionHolder, uint16_t localSessionId, uint16_t peerSessionId,
159 : NodeId localNodeId, NodeId peerNodeId, FabricIndex fabric,
160 : const Transport::PeerAddress & peerAddress, CryptoContext::SessionRole role,
161 : const CATValues & cats = CATValues{});
162 :
163 : /**
164 : * @brief
165 : * Allocate a secure session and non-colliding session ID in the secure
166 : * session table.
167 : *
168 : * If we're either establishing or just finished establishing a session to a peer in either initiator or responder
169 : * roles, the node id of that peer should be provided in sessionEvictionHint. Else, it should be initialized
170 : * to a default-constructed ScopedNodeId().
171 : *
172 : * @return SessionHandle with a reference to a SecureSession, else NullOptional on failure
173 : */
174 : CHECK_RETURN_VALUE
175 : Optional<SessionHandle> AllocateSession(Transport::SecureSession::Type secureSessionType,
176 : const ScopedNodeId & sessionEvictionHint);
177 :
178 : /**
179 : * A set of templated helper function that call a provided lambda
180 : * on all sessions in the underlying session table that match the provided
181 : * query criteria.
182 : *
183 : */
184 :
185 : /**
186 : * Call the provided lambda on sessions whose remote side match the provided ScopedNodeId.
187 : *
188 : */
189 : template <typename Function>
190 7 : void ForEachMatchingSession(const ScopedNodeId & node, Function && function)
191 : {
192 7 : mSecureSessions.ForEachSession([&](auto * session) {
193 28 : if (session->GetPeer() == node)
194 : {
195 7 : function(session);
196 : }
197 :
198 28 : return Loop::Continue;
199 : });
200 7 : }
201 :
202 : /**
203 : * Call the provided lambda on sessions that match the provided fabric index.
204 : *
205 : */
206 : template <typename Function>
207 2 : void ForEachMatchingSession(FabricIndex fabricIndex, Function && function)
208 : {
209 2 : mSecureSessions.ForEachSession([&](auto * session) {
210 16 : if (session->GetFabricIndex() == fabricIndex)
211 : {
212 6 : function(session);
213 : }
214 :
215 16 : return Loop::Continue;
216 : });
217 2 : }
218 :
219 : /**
220 : * Call the provided lambda on all sessions whose remote side match the logical fabric
221 : * associated with the provided ScopedNodeId and target the same logical remote node.
222 : *
223 : * *NOTE* This is identical in behavior to ForEachMatchingSession(const ScopedNodeId ..)
224 : * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide
225 : * on the same logical fabric (i.e root public key + fabric ID tuple).
226 : * This can ONLY happen if multiple controller instances on the same fabric is permitted
227 : * and each is assigned a unique fabric index.
228 : */
229 : template <typename Function>
230 0 : CHIP_ERROR ForEachMatchingSessionOnLogicalFabric(const ScopedNodeId & node, Function && function)
231 : {
232 0 : Crypto::P256PublicKey targetPubKey;
233 :
234 0 : auto * targetFabric = mFabricTable->FindFabricWithIndex(node.GetFabricIndex());
235 0 : VerifyOrReturnError(targetFabric != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
236 :
237 0 : auto err = targetFabric->FetchRootPubkey(targetPubKey);
238 0 : VerifyOrDie(err == CHIP_NO_ERROR);
239 :
240 0 : mSecureSessions.ForEachSession([&](auto * session) {
241 0 : Crypto::P256PublicKey comparePubKey;
242 :
243 : //
244 : // It's entirely possible to either come across a PASE session OR, a CASE session
245 : // that has yet to be activated (i.e a CASEServer holding onto a SecureSession object
246 : // waiting for a Sigma1 message to arrive). Let's skip those.
247 : //
248 0 : if (!session->IsCASESession() || session->GetFabricIndex() == kUndefinedFabricIndex)
249 : {
250 0 : return Loop::Continue;
251 : }
252 :
253 0 : auto * compareFabric = mFabricTable->FindFabricWithIndex(session->GetFabricIndex());
254 0 : VerifyOrDie(compareFabric != nullptr);
255 :
256 0 : err = compareFabric->FetchRootPubkey(comparePubKey);
257 0 : VerifyOrDie(err == CHIP_NO_ERROR);
258 :
259 0 : if (comparePubKey.Matches(targetPubKey) && targetFabric->GetFabricId() == compareFabric->GetFabricId() &&
260 0 : session->GetPeerNodeId() == node.GetNodeId())
261 : {
262 0 : function(session);
263 : }
264 :
265 0 : return Loop::Continue;
266 0 : });
267 :
268 0 : return CHIP_NO_ERROR;
269 0 : }
270 :
271 : /**
272 : * Call the provided lambda on all sessions that match the logical fabric
273 : * associated with the provided fabric index.
274 : *
275 : * *NOTE* This is identical in behavior to ForEachMatchingSession(FabricIndex ..)
276 : * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide
277 : * on the same logical fabric (i.e root public key + fabric ID tuple).
278 : * This can ONLY happen if multiple controller instances on the same fabric is permitted
279 : * and each is assigned a unique fabric index.
280 : */
281 : template <typename Function>
282 0 : CHIP_ERROR ForEachMatchingSessionOnLogicalFabric(FabricIndex fabricIndex, Function && function)
283 : {
284 0 : Crypto::P256PublicKey targetPubKey;
285 :
286 0 : auto * targetFabric = mFabricTable->FindFabricWithIndex(fabricIndex);
287 0 : VerifyOrReturnError(targetFabric != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
288 :
289 0 : auto err = targetFabric->FetchRootPubkey(targetPubKey);
290 0 : VerifyOrDie(err == CHIP_NO_ERROR);
291 :
292 0 : mSecureSessions.ForEachSession([&](auto * session) {
293 0 : Crypto::P256PublicKey comparePubKey;
294 :
295 : //
296 : // It's entirely possible to either come across a PASE session OR, a CASE session
297 : // that has yet to be activated (i.e a CASEServer holding onto a SecureSession object
298 : // waiting for a Sigma1 message to arrive). Let's skip those.
299 : //
300 0 : if (!session->IsCASESession() || session->GetFabricIndex() == kUndefinedFabricIndex)
301 : {
302 0 : return Loop::Continue;
303 : }
304 :
305 0 : auto * compareFabric = mFabricTable->FindFabricWithIndex(session->GetFabricIndex());
306 0 : VerifyOrDie(compareFabric != nullptr);
307 :
308 0 : err = compareFabric->FetchRootPubkey(comparePubKey);
309 0 : VerifyOrDie(err == CHIP_NO_ERROR);
310 :
311 0 : if (comparePubKey.Matches(targetPubKey) && targetFabric->GetFabricId() == compareFabric->GetFabricId())
312 : {
313 0 : function(session);
314 : }
315 :
316 0 : return Loop::Continue;
317 0 : });
318 :
319 0 : return CHIP_NO_ERROR;
320 0 : }
321 :
322 : void ExpireAllSessions(const ScopedNodeId & node);
323 : void ExpireAllSessionsForFabric(FabricIndex fabricIndex);
324 :
325 : /**
326 : * Expire all sessions whose remote side matches the logical fabric
327 : * associated with the provided ScopedNodeId and target the same logical remote node.
328 : *
329 : * *NOTE* This is identical in behavior to ExpireAllSessions(const ScopedNodeId ..)
330 : * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide
331 : * on the same logical fabric (i.e root public key + fabric ID tuple). This can ONLY happen
332 : * if multiple controller instances on the same fabric is permitted and each is assigned
333 : * a unique fabric index.
334 : *
335 : */
336 : CHIP_ERROR ExpireAllSessionsOnLogicalFabric(const ScopedNodeId & node);
337 :
338 : /**
339 : * Expire all sessions whose remote side matches the logical fabric
340 : * associated with the provided fabric index.
341 : *
342 : * *NOTE* This is identical in behavior to ExpireAllSessExpireAllSessionsForFabricions(FabricIndex ..)
343 : * EXCEPT if there are multiple FabricInfo instances in the FabricTable that collide
344 : * on the same logical fabric (i.e root public key + fabric ID tuple). This can ONLY happen
345 : * if multiple controller instances on the same fabric is permitted and each is assigned
346 : * a unique fabric index.
347 : *
348 : */
349 : CHIP_ERROR ExpireAllSessionsOnLogicalFabric(FabricIndex fabricIndex);
350 :
351 : void ExpireAllPASESessions();
352 :
353 : /**
354 : * @brief
355 : * Marks all active sessions that match provided arguments as defunct.
356 : *
357 : * @param node Scoped node ID of the active sessions we should mark as defunct.
358 : * @param type Type of session we are looking to mark as defunct. If matching
359 : * against all types of sessions is desired, NullOptional should
360 : * be passed into type.
361 : */
362 : void MarkSessionsAsDefunct(const ScopedNodeId & node, const Optional<Transport::SecureSession::Type> & type);
363 :
364 : /**
365 : * @brief
366 : * Update all CASE sessions that match `node` with the provided transport peer address.
367 : *
368 : * @param node Scoped node ID of the active sessions we want to update.
369 : * @param addr Transport peer address that we want to update to.
370 : */
371 : void UpdateAllSessionsPeerAddress(const ScopedNodeId & node, const Transport::PeerAddress & addr);
372 :
373 : /**
374 : * @brief
375 : * Return the System Layer pointer used by current SessionManager.
376 : */
377 17662 : System::Layer * SystemLayer() { return mSystemLayer; }
378 :
379 : /**
380 : * @brief
381 : * Initialize a Secure Session Manager
382 : *
383 : * @param systemLayer System layer to use
384 : * @param transportMgr Transport to use
385 : * @param messageCounterManager The message counter manager
386 : * @param storageDelegate Persistent storage implementation
387 : * @param fabricTable Fabric table to hold information about joined fabrics
388 : * @param sessionKeystore Session keystore for management of symmetric encryption keys
389 : */
390 : CHIP_ERROR Init(System::Layer * systemLayer, TransportMgrBase * transportMgr,
391 : Transport::MessageCounterManagerInterface * messageCounterManager,
392 : chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable,
393 : Crypto::SessionKeystore & sessionKeystore);
394 :
395 : /**
396 : * @brief
397 : * Shutdown the Secure Session Manager. This terminates this instance
398 : * of the object and reset it's state.
399 : */
400 : void Shutdown();
401 :
402 : /**
403 : * @brief Notification that a fabric was removed.
404 : */
405 : void FabricRemoved(FabricIndex fabricIndex);
406 :
407 : TransportMgrBase * GetTransportManager() const { return mTransportMgr; }
408 : Transport::SecureSessionTable & GetSecureSessions() { return mSecureSessions; }
409 :
410 : /**
411 : * @brief
412 : * Handle received secure message. Implements TransportMgrDelegate
413 : *
414 : * @param source the source address of the package
415 : * @param msgBuf the buffer containing a full CHIP message (except for the optional length field).
416 : */
417 : void OnMessageReceived(const Transport::PeerAddress & source, System::PacketBufferHandle && msgBuf) override;
418 :
419 0 : Optional<SessionHandle> CreateUnauthenticatedSession(const Transport::PeerAddress & peerAddress,
420 : const ReliableMessageProtocolConfig & config)
421 : {
422 : // Allocate ephemeralInitiatorNodeID in Operational Node ID range
423 : NodeId ephemeralInitiatorNodeID;
424 : do
425 : {
426 0 : ephemeralInitiatorNodeID = static_cast<NodeId>(Crypto::GetRandU64());
427 0 : } while (!IsOperationalNodeId(ephemeralInitiatorNodeID));
428 0 : return mUnauthenticatedSessions.AllocInitiator(ephemeralInitiatorNodeID, peerAddress, config);
429 : }
430 :
431 : //
432 : // Find an existing secure session given a peer's scoped NodeId and a type of session to match against.
433 : // If matching against all types of sessions is desired, NullOptional should be passed into type.
434 : //
435 : // If a valid session is found, an Optional<SessionHandle> with the value set to the SessionHandle of the session
436 : // is returned. Otherwise, an Optional<SessionHandle> with no value set is returned.
437 : //
438 : //
439 : Optional<SessionHandle> FindSecureSessionForNode(ScopedNodeId peerNodeId,
440 : const Optional<Transport::SecureSession::Type> & type = NullOptional);
441 :
442 : using SessionHandleCallback = bool (*)(void * context, SessionHandle & sessionHandle);
443 : CHIP_ERROR ForEachSessionHandle(void * context, SessionHandleCallback callback);
444 :
445 : //// FabricTable::Delegate Implementation ////
446 5 : void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override
447 : {
448 : (void) fabricTable;
449 5 : this->FabricRemoved(fabricIndex);
450 5 : }
451 :
452 18754 : FabricTable * GetFabricTable() const { return mFabricTable; }
453 :
454 152 : Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; }
455 :
456 : private:
457 : /**
458 : * The State of a secure transport object.
459 : */
460 : enum class State
461 : {
462 : kNotReady, /**< State before initialization. */
463 : kInitialized, /**< State when the object is ready connect to other peers. */
464 : };
465 :
466 : enum class EncryptionState
467 : {
468 : kPayloadIsEncrypted,
469 : kPayloadIsUnencrypted,
470 : };
471 :
472 : System::Layer * mSystemLayer = nullptr;
473 : FabricTable * mFabricTable = nullptr;
474 : Crypto::SessionKeystore * mSessionKeystore = nullptr;
475 : Transport::UnauthenticatedSessionTable<CHIP_CONFIG_UNAUTHENTICATED_CONNECTION_POOL_SIZE> mUnauthenticatedSessions;
476 : Transport::SecureSessionTable mSecureSessions;
477 : State mState; // < Initialization state of the object
478 : chip::Transport::GroupOutgoingCounters mGroupClientCounter;
479 :
480 : SessionMessageDelegate * mCB = nullptr;
481 :
482 : TransportMgrBase * mTransportMgr = nullptr;
483 : Transport::MessageCounterManagerInterface * mMessageCounterManager = nullptr;
484 :
485 : GlobalUnencryptedMessageCounter mGlobalUnencryptedMessageCounter;
486 :
487 : /**
488 : * @brief Parse, decrypt, validate, and dispatch a secure unicast message.
489 : *
490 : * @param[in] partialPacketHeader The partial PacketHeader of the message after processing with DecodeFixed.
491 : * If the message decrypts successfully, this will be filled with a fully decoded PacketHeader.
492 : * @param[in] peerAddress The PeerAddress of the message as provided by the receiving Transport Endpoint.
493 : * @param msg The full message buffer, including header fields.
494 : */
495 : void SecureUnicastMessageDispatch(const PacketHeader & partialPacketHeader, const Transport::PeerAddress & peerAddress,
496 : System::PacketBufferHandle && msg);
497 :
498 : /**
499 : * @brief Parse, decrypt, validate, and dispatch a secure group message.
500 : *
501 : * @param partialPacketHeader The partial PacketHeader of the message once processed with DecodeFixed.
502 : * @param peerAddress The PeerAddress of the message as provided by the receiving Transport Endpoint.
503 : * @param msg The full message buffer, including header fields.
504 : */
505 : void SecureGroupMessageDispatch(const PacketHeader & partialPacketHeader, const Transport::PeerAddress & peerAddress,
506 : System::PacketBufferHandle && msg);
507 :
508 : /**
509 : * @brief Parse, decrypt, validate, and dispatch an unsecured message.
510 : *
511 : * @param partialPacketHeader The partial PacketHeader of the message after processing with DecodeFixed.
512 : * @param peerAddress The PeerAddress of the message as provided by the receiving Transport Endpoint.
513 : * @param msg The full message buffer, including header fields.
514 : */
515 : void UnauthenticatedMessageDispatch(const PacketHeader & partialPacketHeader, const Transport::PeerAddress & peerAddress,
516 : System::PacketBufferHandle && msg);
517 :
518 : void OnReceiveError(CHIP_ERROR error, const Transport::PeerAddress & source);
519 :
520 9587 : static bool IsControlMessage(PayloadHeader & payloadHeader)
521 : {
522 19174 : return payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncReq) ||
523 19174 : payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::MsgCounterSyncRsp);
524 : }
525 : };
526 :
527 : namespace MessagePacketBuffer {
528 : /**
529 : * Maximum size of a message footer, in bytes.
530 : */
531 : inline constexpr uint16_t kMaxFooterSize = kMaxTagLen;
532 :
533 : /**
534 : * Allocates a packet buffer with space for message headers and footers.
535 : *
536 : * Fails and returns \c nullptr if no memory is available, or if the size requested is too large.
537 : *
538 : * @param[in] aAvailableSize Minimum number of octets to for application data.
539 : *
540 : * @return On success, a PacketBufferHandle to the allocated buffer. On fail, \c nullptr.
541 : */
542 1556 : inline System::PacketBufferHandle New(size_t aAvailableSize)
543 : {
544 : static_assert(System::PacketBuffer::kMaxSize > kMaxFooterSize, "inadequate capacity");
545 1556 : if (aAvailableSize > System::PacketBuffer::kMaxSize - kMaxFooterSize)
546 : {
547 0 : return System::PacketBufferHandle();
548 : }
549 1556 : return System::PacketBufferHandle::New(aAvailableSize + kMaxFooterSize);
550 : }
551 :
552 : /**
553 : * Allocates a packet buffer with initial contents.
554 : *
555 : * @param[in] aData Initial buffer contents.
556 : * @param[in] aDataSize Size of initial buffer contents.
557 : *
558 : * @return On success, a PacketBufferHandle to the allocated buffer. On fail, \c nullptr.
559 : */
560 0 : inline System::PacketBufferHandle NewWithData(const void * aData, size_t aDataSize)
561 : {
562 0 : return System::PacketBufferHandle::NewWithData(aData, aDataSize, kMaxFooterSize);
563 : }
564 :
565 : /**
566 : * Check whether a packet buffer has enough space for a message footer.
567 : *
568 : * @returns true if there is space, false otherwise.
569 : */
570 2 : inline bool HasFooterSpace(const System::PacketBufferHandle & aBuffer)
571 : {
572 2 : return aBuffer->AvailableDataLength() >= kMaxFooterSize;
573 : }
574 :
575 : } // namespace MessagePacketBuffer
576 :
577 : } // namespace chip
|