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 1550 : SecureSession(SecureSessionTable & table, Type secureSessionType, uint16_t localSessionId, NodeId localNodeId,
72 : NodeId peerNodeId, CATValues peerCATs, uint16_t peerSessionId, FabricIndex fabric,
73 1550 : const ReliableMessageProtocolConfig & config) :
74 1550 : mTable(table),
75 1550 : mState(State::kEstablishing), mSecureSessionType(secureSessionType), mLocalNodeId(localNodeId), mPeerNodeId(peerNodeId),
76 1550 : mPeerCATs(peerCATs), mLocalSessionId(localSessionId), mPeerSessionId(peerSessionId), mRemoteSessionParams(config)
77 : {
78 1550 : MoveToState(State::kActive);
79 1550 : Retain(); // Put the test session in Active state. This ref is released inside MarkForEviction
80 1550 : SetFabricIndex(fabric);
81 1550 : ChipLogDetail(Inet, "SecureSession[%p]: Allocated for Test Type:%d LSID:%d", this, to_underlying(mSecureSessionType),
82 : mLocalSessionId);
83 1550 : }
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 135300 : ~SecureSession() override
108 135300 : {
109 135300 : ChipLogDetail(Inet, "SecureSession[%p]: Released - Type:%d LSID:%d", this, to_underlying(mSecureSessionType),
110 : mLocalSessionId);
111 135300 : }
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 27773 : 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 13873 : 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 702291 : Session::SessionType GetSessionType() const override { return Session::SessionType::kSecure; }
152 :
153 30402 : ScopedNodeId GetPeer() const override { return ScopedNodeId(mPeerNodeId, GetFabricIndex()); }
154 :
155 13847 : ScopedNodeId GetLocalScopedNodeId() const override { return ScopedNodeId(mLocalNodeId, GetFabricIndex()); }
156 :
157 : Access::SubjectDescriptor GetSubjectDescriptor() const override;
158 :
159 : bool IsCommissioningSession() const override;
160 :
161 57266 : bool AllowsMRP() const override { return (GetPeerAddress().GetTransportType() == Transport::Type::kUdp); }
162 :
163 15856 : bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }
164 :
165 8723 : System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
166 : {
167 8723 : switch (mPeerAddress.GetTransportType())
168 : {
169 8676 : case Transport::Type::kUdp: {
170 8676 : const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
171 8676 : return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
172 17352 : GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
173 8676 : isFirstMessageOnExchange);
174 : }
175 0 : case Transport::Type::kTcp:
176 0 : return System::Clock::Seconds16(30);
177 0 : case Transport::Type::kBle:
178 0 : return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
179 47 : default:
180 47 : break;
181 : }
182 47 : return System::Clock::Timeout();
183 : }
184 :
185 9106 : System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
186 : bool isFirstMessageOnExchange) const override
187 : {
188 9106 : switch (mPeerAddress.GetTransportType())
189 : {
190 9059 : case Transport::Type::kUdp: {
191 9059 : const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
192 9059 : const auto & defaultMRRPConfig = GetDefaultMRPConfig();
193 9059 : const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
194 18118 : return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
195 18118 : ourLastActivity, localMRPConfig.mActiveThresholdTime, isFirstMessageOnExchange);
196 : }
197 0 : case Transport::Type::kTcp:
198 0 : return System::Clock::Seconds16(30);
199 0 : case Transport::Type::kBle:
200 0 : return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
201 47 : default:
202 47 : break;
203 : }
204 47 : return System::Clock::Timeout();
205 : }
206 :
207 114612 : const PeerAddress & GetPeerAddress() const { return mPeerAddress; }
208 1542 : void SetPeerAddress(const PeerAddress & address) { mPeerAddress = address; }
209 :
210 37019 : Type GetSecureSessionType() const { return mSecureSessionType; }
211 13795 : bool IsCASESession() const { return GetSecureSessionType() == Type::kCASE; }
212 9271 : bool IsPASESession() const { return GetSecureSessionType() == Type::kPASE; }
213 26188 : NodeId GetPeerNodeId() const { return mPeerNodeId; }
214 13735 : NodeId GetLocalNodeId() const { return mLocalNodeId; }
215 :
216 106 : const CATValues & GetPeerCATs() const { return mPeerCATs; }
217 :
218 : void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; }
219 :
220 37814 : const SessionParameters & GetRemoteSessionParameters() const override { return mRemoteSessionParams; }
221 :
222 61538 : uint16_t GetLocalSessionId() const { return mLocalSessionId; }
223 13845 : uint16_t GetPeerSessionId() const { return mPeerSessionId; }
224 :
225 : // Called when AddNOC has gone through sufficient success that we need to switch the
226 : // session to reflect a new fabric if it was a PASE session
227 : CHIP_ERROR AdoptFabricIndex(FabricIndex fabricIndex)
228 : {
229 : // It's not legal to augment session type for non-PASE
230 : if (mSecureSessionType != Type::kPASE)
231 : {
232 : return CHIP_ERROR_INVALID_ARGUMENT;
233 : }
234 : SetFabricIndex(fabricIndex);
235 : return CHIP_NO_ERROR;
236 : }
237 :
238 282 : System::Clock::Timestamp GetLastActivityTime() const { return mLastActivityTime; }
239 22585 : System::Clock::Timestamp GetLastPeerActivityTime() const { return mLastPeerActivityTime; }
240 27725 : void MarkActive() { mLastActivityTime = System::SystemClock().GetMonotonicTimestamp(); }
241 13847 : void MarkActiveRx()
242 : {
243 13847 : mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
244 13847 : MarkActive();
245 :
246 13847 : if (mState == State::kDefunct)
247 : {
248 0 : MoveToState(State::kActive);
249 : }
250 13847 : }
251 :
252 10 : void SetCaseCommissioningSessionStatus(bool isCaseCommissioningSession)
253 : {
254 10 : VerifyOrDie(GetSecureSessionType() == Type::kCASE);
255 10 : mIsCaseCommissioningSession = isCaseCommissioningSession;
256 10 : }
257 :
258 13901 : bool IsPeerActive() const
259 : {
260 27802 : return ((System::SystemClock().GetMonotonicTimestamp() - GetLastPeerActivityTime()) <
261 27802 : GetRemoteMRPConfig().mActiveThresholdTime);
262 : }
263 :
264 2136 : System::Clock::Timestamp GetMRPBaseTimeout() const override
265 : {
266 2136 : return IsPeerActive() ? GetRemoteMRPConfig().mActiveRetransTimeout : GetRemoteMRPConfig().mIdleRetransTimeout;
267 : }
268 :
269 29121 : CryptoContext & GetCryptoContext() { return mCryptoContext; }
270 :
271 0 : const CryptoContext & GetCryptoContext() const { return mCryptoContext; }
272 :
273 42774 : SessionMessageCounter & GetSessionMessageCounter() { return mSessionMessageCounter; }
274 :
275 : // This should be a private API, only meant to be called by SecureSessionTable
276 : // Session holders to this session may shift to the target session regarding SessionDelegate::GetNewSessionHandlingPolicy.
277 : // It requires that the target sessoin is also a CASE session, having the same peer and CATs as this session.
278 : void NewerSessionAvailable(const SessionHandle & session);
279 :
280 : private:
281 : enum class State : uint8_t
282 : {
283 : //
284 : // Denotes a secure session object that is internally
285 : // reserved by the stack before and during session establishment.
286 : //
287 : // Although the stack can tolerate eviction of these (releasing one
288 : // out from under the holder would exhibit as CHIP_ERROR_INCORRECT_STATE
289 : // during CASE or PASE), intent is that we should not and would leave
290 : // these untouched until CASE or PASE complete.
291 : //
292 : // In this state, the reference count is held by the PairingSession.
293 : //
294 : kEstablishing = 1,
295 :
296 : //
297 : // The session is active, ready for use. When transitioning to this state via Activate, the
298 : // reference count is incremented by 1, and will subsequently be decremented
299 : // by 1 when MarkForEviction is called. This ensures the session remains resident
300 : // and active for future use even if there currently are no references to it.
301 : //
302 : kActive = 2,
303 :
304 : //
305 : // The session is temporarily disabled due to suspicion of a loss of synchronization
306 : // with the session state on the peer (e.g transport failure).
307 : // In this state, no new outbound exchanges can be created. However, if we receive valid messages
308 : // again on this session, we CAN mark this session as being active again.
309 : //
310 : // Transitioning to this state does not detach any existing SessionHolders.
311 : //
312 : // In addition to any existing SessionHolders holding a reference to this session, the SessionManager
313 : // maintains a reference as well to the session that will only be relinquished when MarkForEviction is called.
314 : //
315 : kDefunct = 3,
316 :
317 : //
318 : // The session has been marked for eviction and is pending deallocation. All SessionHolders would have already
319 : // been detached in a previous call to MarkForEviction. Future SessionHolders will not be able to attach to
320 : // this session.
321 : //
322 : // When all SessionHandles go out of scope, the session will be released automatically.
323 : //
324 : kPendingEviction = 4,
325 : };
326 :
327 : const char * StateToString(State state) const;
328 : void MoveToState(State targetState);
329 :
330 : friend class SecureSessionDeleter;
331 : friend class TestSecureSessionTable;
332 :
333 : SecureSessionTable & mTable;
334 : State mState;
335 : const Type mSecureSessionType;
336 : bool mIsCaseCommissioningSession = false;
337 : NodeId mLocalNodeId = kUndefinedNodeId;
338 : NodeId mPeerNodeId = kUndefinedNodeId;
339 : CATValues mPeerCATs = CATValues{};
340 : const uint16_t mLocalSessionId;
341 : uint16_t mPeerSessionId = 0;
342 :
343 : PeerAddress mPeerAddress;
344 :
345 : /// Timestamp of last tx or rx. @see SessionTimestamp in the spec
346 : System::Clock::Timestamp mLastActivityTime = System::SystemClock().GetMonotonicTimestamp();
347 :
348 : /// Timestamp of last rx. @see ActiveTimestamp in the spec
349 : System::Clock::Timestamp mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
350 :
351 : SessionParameters mRemoteSessionParams;
352 : CryptoContext mCryptoContext;
353 : SessionMessageCounter mSessionMessageCounter;
354 : };
355 :
356 : } // namespace Transport
357 : } // namespace chip
|