Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-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 : * @brief Defines state relevant for an active connection to a peer.
20 : */
21 :
22 : #pragma once
23 :
24 : #include <app/util/basic-types.h>
25 : #include <ble/Ble.h>
26 : #include <lib/core/ReferenceCounted.h>
27 : #include <messaging/ReliableMessageProtocolConfig.h>
28 : #include <transport/CryptoContext.h>
29 : #include <transport/Session.h>
30 : #include <transport/SessionMessageCounter.h>
31 : #include <transport/raw/PeerAddress.h>
32 :
33 : namespace chip {
34 : namespace Transport {
35 :
36 : class SecureSessionTable;
37 : class SecureSessionDeleter
38 : {
39 : public:
40 : static void Release(SecureSession * entry);
41 : };
42 :
43 : /**
44 : * Defines state of a peer connection at a transport layer.
45 : *
46 : * Information contained within the state:
47 : * - SecureSessionType represents CASE or PASE session
48 : * - PeerAddress represents how to talk to the peer
49 : * - PeerNodeId is the unique ID of the peer
50 : * - PeerCATs represents CASE Authenticated Tags
51 : * - SendMessageIndex is an ever increasing index for sending messages
52 : * - LastActivityTime is a monotonic timestamp of when this connection was
53 : * last used. Inactive connections can expire.
54 : * - CryptoContext contains the encryption context of a connection
55 : */
56 : class SecureSession : public Session, public ReferenceCounted<SecureSession, SecureSessionDeleter, 0, uint16_t>
57 : {
58 : public:
59 : /**
60 : * @brief
61 : * Defines SecureSession Type. Currently supported types are PASE and CASE.
62 : */
63 : enum class Type : uint8_t
64 : {
65 : kPASE = 1,
66 : kCASE = 2,
67 : };
68 :
69 : // Test-only: inject a session in Active state.
70 : // TODO: Tests should allocate a pending session and then call Activate(), just like non-test code does.
71 1522 : SecureSession(SecureSessionTable & table, Type secureSessionType, uint16_t localSessionId, NodeId localNodeId,
72 : NodeId peerNodeId, CATValues peerCATs, uint16_t peerSessionId, FabricIndex fabric,
73 1522 : const ReliableMessageProtocolConfig & config) :
74 1522 : mTable(table),
75 1522 : mState(State::kEstablishing), mSecureSessionType(secureSessionType), mLocalNodeId(localNodeId), mPeerNodeId(peerNodeId),
76 1522 : mPeerCATs(peerCATs), mLocalSessionId(localSessionId), mPeerSessionId(peerSessionId), mRemoteSessionParams(config)
77 : {
78 1522 : MoveToState(State::kActive);
79 1522 : Retain(); // Put the test session in Active state. This ref is released inside MarkForEviction
80 1522 : SetFabricIndex(fabric);
81 1522 : ChipLogDetail(Inet, "SecureSession[%p]: Allocated for Test Type:%d LSID:%d", this, to_underlying(mSecureSessionType),
82 : mLocalSessionId);
83 1522 : }
84 :
85 : /**
86 : * @brief
87 : * Construct a secure session object to associate with a pending secure
88 : * session establishment attempt. The object for the pending session
89 : * receives a local session ID, but no other state.
90 : */
91 133750 : SecureSession(SecureSessionTable & table, Type secureSessionType, uint16_t localSessionId) :
92 133750 : mTable(table), mState(State::kEstablishing), mSecureSessionType(secureSessionType), mLocalSessionId(localSessionId)
93 : {
94 133750 : ChipLogDetail(Inet, "SecureSession[%p]: Allocated Type:%d LSID:%d", this, to_underlying(mSecureSessionType),
95 : mLocalSessionId);
96 133750 : }
97 :
98 : /**
99 : * @brief
100 : * Activate a pending Secure Session that had been reserved during CASE or
101 : * PASE, setting internal state according to the parameters used and
102 : * discovered during session establishment.
103 : */
104 : void Activate(const ScopedNodeId & localNode, const ScopedNodeId & peerNode, CATValues peerCATs, uint16_t peerSessionId,
105 : const SessionParameters & sessionParameters);
106 :
107 135272 : ~SecureSession() override
108 135272 : {
109 135272 : ChipLogDetail(Inet, "SecureSession[%p]: Released - Type:%d LSID:%d", this, to_underlying(mSecureSessionType),
110 : mLocalSessionId);
111 135272 : }
112 :
113 : SecureSession(SecureSession &&) = delete;
114 : SecureSession(const SecureSession &) = delete;
115 : SecureSession & operator=(const SecureSession &) = delete;
116 : SecureSession & operator=(SecureSession &&) = delete;
117 :
118 : void Retain() override;
119 : void Release() override;
120 :
121 22521 : bool IsActiveSession() const override { return mState == State::kActive; }
122 56 : bool IsEstablishing() const { return mState == State::kEstablishing; }
123 16 : bool IsPendingEviction() const { return mState == State::kPendingEviction; }
124 10106 : bool IsDefunct() const { return mState == State::kDefunct; }
125 84 : const char * GetStateStr() const { return StateToString(mState); }
126 :
127 : /*
128 : * This marks the session for eviction. It will first detach all SessionHolders attached to this
129 : * session by calling 'OnSessionReleased' on each of them. This will force them to release their reference
130 : * to the session. If there are no more references left, the session will then be de-allocated.
131 : *
132 : * Once marked for eviction, the session SHALL NOT ever become active again.
133 : *
134 : */
135 : void MarkForEviction();
136 :
137 : /*
138 : * This marks a previously active session as defunct to temporarily prevent it from being used with
139 : * new exchanges to send or receive messages on this session. This should be called when there is suspicion of
140 : * a loss-of-sync with the session state on the associated peer. This could arise if there is evidence
141 : * of transport failure.
142 : *
143 : * If messages are received thereafter on this session, the session SHALL be put back into the Active state.
144 : *
145 : * This SHALL only be callable on an active session.
146 : * This SHALL NOT detach any existing SessionHolders.
147 : *
148 : */
149 : void MarkAsDefunct();
150 :
151 582708 : Session::SessionType GetSessionType() const override { return Session::SessionType::kSecure; }
152 :
153 22863 : ScopedNodeId GetPeer() const override { return ScopedNodeId(mPeerNodeId, GetFabricIndex()); }
154 :
155 10080 : ScopedNodeId GetLocalScopedNodeId() const override { return ScopedNodeId(mLocalNodeId, GetFabricIndex()); }
156 :
157 : Access::SubjectDescriptor GetSubjectDescriptor() const override;
158 :
159 : bool IsCommissioningSession() const override;
160 :
161 41833 : bool AllowsMRP() const override
162 : {
163 42044 : return ((GetPeerAddress().GetTransportType() == Transport::Type::kUdp) ||
164 42044 : (GetPeerAddress().GetTransportType() == Transport::Type::kWiFiPAF));
165 : }
166 :
167 12086 : bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }
168 :
169 6640 : System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
170 : {
171 6640 : switch (mPeerAddress.GetTransportType())
172 : {
173 6593 : case Transport::Type::kUdp: {
174 6593 : const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
175 6593 : return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
176 13186 : GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
177 6593 : isFirstMessageOnExchange);
178 : }
179 0 : case Transport::Type::kTcp:
180 0 : return System::Clock::Seconds16(30);
181 0 : case Transport::Type::kBle:
182 0 : return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
183 47 : default:
184 47 : break;
185 : }
186 47 : return System::Clock::Timeout();
187 : }
188 :
189 7020 : System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
190 : bool isFirstMessageOnExchange) const override
191 : {
192 7020 : switch (mPeerAddress.GetTransportType())
193 : {
194 6973 : case Transport::Type::kUdp: {
195 6973 : const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
196 6973 : const auto & defaultMRRPConfig = GetDefaultMRPConfig();
197 6973 : const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
198 13946 : return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
199 13946 : ourLastActivity, localMRPConfig.mActiveThresholdTime, isFirstMessageOnExchange);
200 : }
201 0 : case Transport::Type::kTcp:
202 0 : return System::Clock::Seconds16(30);
203 0 : case Transport::Type::kBle:
204 0 : return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
205 47 : default:
206 47 : break;
207 : }
208 47 : return System::Clock::Timeout();
209 : }
210 :
211 84319 : const PeerAddress & GetPeerAddress() const { return mPeerAddress; }
212 1514 : void SetPeerAddress(const PeerAddress & address) { mPeerAddress = address; }
213 :
214 27783 : Type GetSecureSessionType() const { return mSecureSessionType; }
215 10029 : bool IsCASESession() const { return GetSecureSessionType() == Type::kCASE; }
216 7568 : bool IsPASESession() const { return GetSecureSessionType() == Type::kPASE; }
217 19003 : NodeId GetPeerNodeId() const { return mPeerNodeId; }
218 9968 : NodeId GetLocalNodeId() const { return mLocalNodeId; }
219 :
220 106 : const CATValues & GetPeerCATs() const { return mPeerCATs; }
221 :
222 : void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; }
223 :
224 27232 : const SessionParameters & GetRemoteSessionParameters() const override { return mRemoteSessionParams; }
225 :
226 44767 : uint16_t GetLocalSessionId() const { return mLocalSessionId; }
227 10078 : uint16_t GetPeerSessionId() const { return mPeerSessionId; }
228 :
229 : // Called when AddNOC has gone through sufficient success that we need to switch the
230 : // session to reflect a new fabric if it was a PASE session
231 : CHIP_ERROR AdoptFabricIndex(FabricIndex fabricIndex)
232 : {
233 : // It's not legal to augment session type for non-PASE
234 : if (mSecureSessionType != Type::kPASE)
235 : {
236 : return CHIP_ERROR_INVALID_ARGUMENT;
237 : }
238 : SetFabricIndex(fabricIndex);
239 : return CHIP_NO_ERROR;
240 : }
241 :
242 282 : System::Clock::Timestamp GetLastActivityTime() const { return mLastActivityTime; }
243 16735 : System::Clock::Timestamp GetLastPeerActivityTime() const { return mLastPeerActivityTime; }
244 20191 : void MarkActive() { mLastActivityTime = System::SystemClock().GetMonotonicTimestamp(); }
245 10080 : void MarkActiveRx()
246 : {
247 10080 : mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
248 10080 : MarkActive();
249 :
250 10080 : if (mState == State::kDefunct)
251 : {
252 0 : MoveToState(State::kActive);
253 : }
254 10080 : }
255 :
256 10 : void SetCaseCommissioningSessionStatus(bool isCaseCommissioningSession)
257 : {
258 10 : VerifyOrDie(GetSecureSessionType() == Type::kCASE);
259 10 : mIsCaseCommissioningSession = isCaseCommissioningSession;
260 10 : }
261 :
262 10134 : bool IsPeerActive() const
263 : {
264 20268 : return ((System::SystemClock().GetMonotonicTimestamp() - GetLastPeerActivityTime()) <
265 20268 : GetRemoteMRPConfig().mActiveThresholdTime);
266 : }
267 :
268 1775 : System::Clock::Timestamp GetMRPBaseTimeout() const override
269 : {
270 1775 : return IsPeerActive() ? GetRemoteMRPConfig().mActiveRetransTimeout : GetRemoteMRPConfig().mIdleRetransTimeout;
271 : }
272 :
273 21559 : CryptoContext & GetCryptoContext() { return mCryptoContext; }
274 :
275 0 : const CryptoContext & GetCryptoContext() const { return mCryptoContext; }
276 :
277 31445 : SessionMessageCounter & GetSessionMessageCounter() { return mSessionMessageCounter; }
278 :
279 : // This should be a private API, only meant to be called by SecureSessionTable
280 : // Session holders to this session may shift to the target session regarding SessionDelegate::GetNewSessionHandlingPolicy.
281 : // It requires that the target sessoin is also a CASE session, having the same peer and CATs as this session.
282 : void NewerSessionAvailable(const SessionHandle & session);
283 :
284 : private:
285 : enum class State : uint8_t
286 : {
287 : //
288 : // Denotes a secure session object that is internally
289 : // reserved by the stack before and during session establishment.
290 : //
291 : // Although the stack can tolerate eviction of these (releasing one
292 : // out from under the holder would exhibit as CHIP_ERROR_INCORRECT_STATE
293 : // during CASE or PASE), intent is that we should not and would leave
294 : // these untouched until CASE or PASE complete.
295 : //
296 : // In this state, the reference count is held by the PairingSession.
297 : //
298 : kEstablishing = 1,
299 :
300 : //
301 : // The session is active, ready for use. When transitioning to this state via Activate, the
302 : // reference count is incremented by 1, and will subsequently be decremented
303 : // by 1 when MarkForEviction is called. This ensures the session remains resident
304 : // and active for future use even if there currently are no references to it.
305 : //
306 : kActive = 2,
307 :
308 : //
309 : // The session is temporarily disabled due to suspicion of a loss of synchronization
310 : // with the session state on the peer (e.g transport failure).
311 : // In this state, no new outbound exchanges can be created. However, if we receive valid messages
312 : // again on this session, we CAN mark this session as being active again.
313 : //
314 : // Transitioning to this state does not detach any existing SessionHolders.
315 : //
316 : // In addition to any existing SessionHolders holding a reference to this session, the SessionManager
317 : // maintains a reference as well to the session that will only be relinquished when MarkForEviction is called.
318 : //
319 : kDefunct = 3,
320 :
321 : //
322 : // The session has been marked for eviction and is pending deallocation. All SessionHolders would have already
323 : // been detached in a previous call to MarkForEviction. Future SessionHolders will not be able to attach to
324 : // this session.
325 : //
326 : // When all SessionHandles go out of scope, the session will be released automatically.
327 : //
328 : kPendingEviction = 4,
329 : };
330 :
331 : const char * StateToString(State state) const;
332 : void MoveToState(State targetState);
333 :
334 : friend class SecureSessionDeleter;
335 : friend class TestSecureSessionTable;
336 :
337 : SecureSessionTable & mTable;
338 : State mState;
339 : const Type mSecureSessionType;
340 : bool mIsCaseCommissioningSession = false;
341 : NodeId mLocalNodeId = kUndefinedNodeId;
342 : NodeId mPeerNodeId = kUndefinedNodeId;
343 : CATValues mPeerCATs = CATValues{};
344 : const uint16_t mLocalSessionId;
345 : uint16_t mPeerSessionId = 0;
346 :
347 : PeerAddress mPeerAddress;
348 :
349 : /// Timestamp of last tx or rx. @see SessionTimestamp in the spec
350 : System::Clock::Timestamp mLastActivityTime = System::SystemClock().GetMonotonicTimestamp();
351 :
352 : /// Timestamp of last rx. @see ActiveTimestamp in the spec
353 : System::Clock::Timestamp mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
354 :
355 : SessionParameters mRemoteSessionParams;
356 : CryptoContext mCryptoContext;
357 : SessionMessageCounter mSessionMessageCounter;
358 : };
359 :
360 : } // namespace Transport
361 : } // namespace chip
|