Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 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 common interface to access various types of secure
22 : * pairing sessions (e.g. PASE, CASE)
23 : *
24 : */
25 :
26 : #pragma once
27 :
28 : #include <lib/core/CHIPError.h>
29 : #include <lib/core/Optional.h>
30 : #include <lib/core/TLV.h>
31 : #include <messaging/ExchangeContext.h>
32 : #include <messaging/SessionParameters.h>
33 : #include <protocols/secure_channel/Constants.h>
34 : #include <protocols/secure_channel/SessionEstablishmentDelegate.h>
35 : #include <protocols/secure_channel/StatusReport.h>
36 : #include <transport/CryptoContext.h>
37 : #include <transport/SecureSession.h>
38 :
39 : namespace chip {
40 :
41 : class SessionManager;
42 :
43 : class DLL_EXPORT PairingSession : public SessionDelegate
44 : {
45 : public:
46 2 : PairingSession() : mSecureSessionHolder(*this) {}
47 34 : virtual ~PairingSession() { Clear(); }
48 :
49 : virtual Transport::SecureSession::Type GetSecureSessionType() const = 0;
50 : virtual ScopedNodeId GetPeer() const = 0;
51 : virtual ScopedNodeId GetLocalScopedNodeId() const = 0;
52 : virtual CATValues GetPeerCATs() const = 0;
53 :
54 : // Implement SessionDelegate
55 1 : NewSessionHandlingPolicy GetNewSessionHandlingPolicy() override { return NewSessionHandlingPolicy::kStayAtOldSession; }
56 : void OnSessionReleased() override;
57 :
58 112 : Optional<uint16_t> GetLocalSessionId() const
59 : {
60 112 : Optional<uint16_t> localSessionId;
61 112 : VerifyOrExit(mSecureSessionHolder, localSessionId = NullOptional);
62 112 : VerifyOrExit(mSecureSessionHolder->GetSessionType() == Transport::Session::SessionType::kSecure,
63 : localSessionId = Optional<uint16_t>::Missing());
64 112 : localSessionId.SetValue(mSecureSessionHolder->AsSecureSession()->GetLocalSessionId());
65 112 : exit:
66 112 : return localSessionId;
67 : }
68 :
69 : /**
70 : * Copy the underlying session (if present) into a SessionHandle that a caller can use to
71 : * obtain a reference to the session.
72 : */
73 14 : Optional<SessionHandle> CopySecureSession()
74 : {
75 14 : if (mSecureSessionHolder)
76 : {
77 14 : VerifyOrDie(mSecureSessionHolder->GetSessionType() == Transport::Session::SessionType::kSecure);
78 14 : return MakeOptional<SessionHandle>(*mSecureSessionHolder->AsSecureSession());
79 : }
80 :
81 0 : return Optional<SessionHandle>::Missing();
82 : }
83 :
84 26 : uint16_t GetPeerSessionId() const
85 : {
86 26 : VerifyOrDie(mPeerSessionId.HasValue());
87 26 : return mPeerSessionId.Value();
88 : }
89 :
90 : bool IsValidPeerSessionId() const { return mPeerSessionId.HasValue(); }
91 :
92 : /**
93 : * @brief
94 : * Derive a secure session from the paired session. The API will return error if called before pairing is established.
95 : *
96 : * @param session Reference to the secure session that will be initialized once pairing is complete
97 : * @return CHIP_ERROR The result of session derivation
98 : */
99 : virtual CHIP_ERROR DeriveSecureSession(CryptoContext & session) = 0;
100 :
101 0 : const ReliableMessageProtocolConfig & GetRemoteMRPConfig() const { return mRemoteSessionParams.GetMRPConfig(); }
102 55 : const SessionParameters & GetRemoteSessionParameters() const { return mRemoteSessionParams; }
103 0 : void SetRemoteMRPConfig(const ReliableMessageProtocolConfig & config) { mRemoteSessionParams.SetMRPConfig(config); }
104 :
105 : /**
106 : * Encode the Session Parameters using the provided TLV tag.
107 : */
108 : static CHIP_ERROR EncodeSessionParameters(TLV::Tag tag, const ReliableMessageProtocolConfig & mrpLocalConfig,
109 : TLV::TLVWriter & tlvWriter);
110 :
111 : protected:
112 : /**
113 : * Allocate a secure session object from the passed session manager for the
114 : * pending session establishment operation.
115 : *
116 : * @param sessionManager Session manager from which to allocate a secure session object
117 : * @param sessionEvictionHint If we're either establishing or just finished establishing a session to a peer in either
118 : * initiator or responder roles, the node id of that peer should be provided in this argument. Else, it should be initialized to
119 : * a default-constructed ScopedNodeId().
120 : *
121 : * @return CHIP_ERROR The outcome of the allocation attempt
122 : */
123 : CHIP_ERROR AllocateSecureSession(SessionManager & sessionManager, const ScopedNodeId & sessionEvictionHint = ScopedNodeId());
124 :
125 : CHIP_ERROR ActivateSecureSession(const Transport::PeerAddress & peerAddress);
126 :
127 : void Finish();
128 :
129 : void DiscardExchange(); // Clear our reference to our exchange context pointer so that it can close itself at some later time.
130 :
131 30 : void SetPeerSessionId(uint16_t id) { mPeerSessionId.SetValue(id); }
132 :
133 9 : void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; }
134 :
135 0 : virtual void OnSuccessStatusReport() {}
136 :
137 : // Handle a failure StatusReport message from the server. protocolData will
138 : // depend on exactly what the generalCode/protocolCode are.
139 0 : virtual CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode,
140 : Optional<uintptr_t> protocolData)
141 : {
142 0 : return CHIP_ERROR_INTERNAL;
143 : }
144 :
145 15 : void SendStatusReport(Optional<Messaging::ExchangeHandle> & exchangeCtxt, uint16_t protocolCode)
146 : {
147 15 : Protocols::SecureChannel::GeneralStatusCode generalCode = (protocolCode == Protocols::SecureChannel::kProtocolCodeSuccess)
148 15 : ? Protocols::SecureChannel::GeneralStatusCode::kSuccess
149 : : Protocols::SecureChannel::GeneralStatusCode::kFailure;
150 :
151 15 : ChipLogDetail(SecureChannel, "Sending status report. Protocol code %d, exchange %d", protocolCode,
152 : exchangeCtxt.Value()->GetExchangeId());
153 :
154 15 : Protocols::SecureChannel::StatusReport statusReport(generalCode, Protocols::SecureChannel::Id, protocolCode);
155 :
156 15 : auto handle = System::PacketBufferHandle::New(statusReport.Size());
157 15 : VerifyOrReturn(!handle.IsNull(), ChipLogError(SecureChannel, "Failed to allocate status report message"));
158 15 : Encoding::LittleEndian::PacketBufferWriter bbuf(std::move(handle));
159 :
160 15 : statusReport.WriteToBuffer(bbuf);
161 :
162 15 : System::PacketBufferHandle msg = bbuf.Finalize();
163 15 : VerifyOrReturn(!msg.IsNull(), ChipLogError(SecureChannel, "Failed to allocate status report message"));
164 :
165 15 : CHIP_ERROR err = exchangeCtxt.Value()->SendMessage(Protocols::SecureChannel::MsgType::StatusReport, std::move(msg));
166 15 : if (err != CHIP_NO_ERROR)
167 : {
168 0 : ChipLogError(SecureChannel, "Failed to send status report message: %" CHIP_ERROR_FORMAT, err.Format());
169 : }
170 15 : }
171 :
172 15 : CHIP_ERROR HandleStatusReport(System::PacketBufferHandle && msg, bool successExpected)
173 : {
174 15 : Protocols::SecureChannel::StatusReport report;
175 15 : ReturnErrorOnFailure(report.Parse(std::move(msg)));
176 15 : VerifyOrReturnError(report.GetProtocolId() == Protocols::SecureChannel::Id, CHIP_ERROR_INVALID_ARGUMENT);
177 :
178 28 : if (report.GetGeneralCode() == Protocols::SecureChannel::GeneralStatusCode::kSuccess &&
179 28 : report.GetProtocolCode() == Protocols::SecureChannel::kProtocolCodeSuccess && successExpected)
180 : {
181 13 : OnSuccessStatusReport();
182 13 : return CHIP_NO_ERROR;
183 : }
184 :
185 2 : Optional<uintptr_t> protocolData;
186 3 : if (report.GetGeneralCode() == Protocols::SecureChannel::GeneralStatusCode::kBusy &&
187 1 : report.GetProtocolCode() == Protocols::SecureChannel::kProtocolCodeBusy)
188 : {
189 1 : if (!report.GetProtocolData().IsNull())
190 : {
191 1 : Encoding::LittleEndian::Reader reader(report.GetProtocolData()->Start(), report.GetProtocolData()->DataLength());
192 :
193 1 : uint16_t minimumWaitTime = 0;
194 1 : CHIP_ERROR waitTimeErr = reader.Read16(&minimumWaitTime).StatusCode();
195 1 : if (waitTimeErr != CHIP_NO_ERROR)
196 : {
197 0 : ChipLogError(SecureChannel, "Failed to read the minimum wait time: %" CHIP_ERROR_FORMAT, waitTimeErr.Format());
198 : }
199 : else
200 : {
201 1 : ChipLogProgress(SecureChannel, "Received busy status report with minimum wait time: %u ms", minimumWaitTime);
202 1 : protocolData.Emplace(minimumWaitTime);
203 : }
204 : }
205 : }
206 :
207 : // It's very important that we propagate the return value from
208 : // OnFailureStatusReport out to the caller. Make sure we return it directly.
209 2 : return OnFailureStatusReport(report.GetGeneralCode(), report.GetProtocolCode(), protocolData);
210 15 : }
211 :
212 : /**
213 : * Try to decode the current element (pointed by the TLV reader) as Session parameters (which include MRP parameters).
214 : * If the Session parameters are found, outparam sessionParameters is updated with the decoded values.
215 : *
216 : * Session parameters are optional. So, if the TLV reader is not pointing to the Session parameters,
217 : * the function is a noop.
218 : *
219 : * If the parameters are present, but TLV reader fails to correctly parse it, the function will
220 : * return the corresponding error.
221 : */
222 : static CHIP_ERROR DecodeSessionParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader,
223 : SessionParameters & sessionParameters);
224 :
225 : bool IsSessionEstablishmentInProgress();
226 :
227 : // TODO: remove Clear, we should create a new instance instead reset the old instance.
228 : void Clear();
229 :
230 : /**
231 : * Notify our delegate about a session establishment error and the stage when the error occurs
232 : * if we have not already notified it of an error or success before.
233 : *
234 : * @param error The error code to report.
235 : * @param stage The stage of the session when the error occurs, defaults to kNotInKeyExchange.
236 : */
237 : void NotifySessionEstablishmentError(CHIP_ERROR error,
238 : SessionEstablishmentStage stage = SessionEstablishmentStage::kNotInKeyExchange);
239 :
240 : protected:
241 : CryptoContext::SessionRole mRole;
242 : SessionHolderWithDelegate mSecureSessionHolder;
243 : // mSessionManager is set if we actually allocate a secure session, so we
244 : // can clean it up later as needed.
245 : SessionManager * mSessionManager = nullptr;
246 : Optional<Messaging::ExchangeHandle> mExchangeCtxt = NullOptional;
247 : SessionEstablishmentDelegate * mDelegate = nullptr;
248 :
249 : // mLocalMRPConfig is our config which is sent to the other end and used by the peer session.
250 : // mRemoteSessionParams is received from other end and set to our session.
251 : // It is set the first time that session establishment is initiated.
252 : Optional<ReliableMessageProtocolConfig> mLocalMRPConfig;
253 : SessionParameters mRemoteSessionParams;
254 :
255 : private:
256 : Optional<uint16_t> mPeerSessionId;
257 : };
258 :
259 : } // namespace chip
|