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 the CHIP CASE Session object that provides
22 : * APIs for constructing a secure session using a certificate from the device's
23 : * operational credentials.
24 : */
25 :
26 : #pragma once
27 :
28 : #include <credentials/CHIPCert.h>
29 : #include <credentials/CertificateValidityPolicy.h>
30 : #include <credentials/FabricTable.h>
31 : #include <credentials/GroupDataProvider.h>
32 : #include <crypto/CHIPCryptoPAL.h>
33 : #include <lib/core/ScopedNodeId.h>
34 : #include <lib/core/TLV.h>
35 : #include <lib/support/Base64.h>
36 : #include <lib/support/CHIPMem.h>
37 : #include <messaging/ExchangeContext.h>
38 : #include <messaging/ExchangeDelegate.h>
39 : #include <messaging/ReliableMessageProtocolConfig.h>
40 : #include <protocols/secure_channel/CASEDestinationId.h>
41 : #include <protocols/secure_channel/Constants.h>
42 : #include <protocols/secure_channel/PairingSession.h>
43 : #include <protocols/secure_channel/SessionEstablishmentExchangeDispatch.h>
44 : #include <protocols/secure_channel/SessionResumptionStorage.h>
45 : #include <system/SystemClock.h>
46 : #include <system/SystemPacketBuffer.h>
47 : #include <transport/CryptoContext.h>
48 : #include <transport/raw/MessageHeader.h>
49 : #include <transport/raw/PeerAddress.h>
50 :
51 : namespace chip {
52 :
53 : // TODO: temporary derive from Messaging::UnsolicitedMessageHandler, actually the CASEServer should be the umh, it will be fixed
54 : // when implementing concurrent CASE session.
55 : class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler,
56 : public Messaging::ExchangeDelegate,
57 : public FabricTable::Delegate,
58 : public PairingSession
59 : {
60 : public:
61 : ~CASESession() override;
62 :
63 1 : Transport::SecureSession::Type GetSecureSessionType() const override { return Transport::SecureSession::Type::kCASE; }
64 72 : ScopedNodeId GetPeer() const override { return ScopedNodeId(mPeerNodeId, GetFabricIndex()); }
65 16 : ScopedNodeId GetLocalScopedNodeId() const override { return ScopedNodeId(mLocalNodeId, GetFabricIndex()); }
66 16 : CATValues GetPeerCATs() const override { return mPeerCATs; };
67 :
68 : /**
69 : * @brief
70 : * Initialize using configured fabrics and wait for session establishment requests (as a responder).
71 : *
72 : * @param sessionManager session manager from which to allocate a secure session object
73 : * @param fabricTable Table of fabrics that are currently configured on the device
74 : * @param policy Optional application-provided certificate validity policy
75 : * @param delegate Callback object
76 : * @param previouslyEstablishedPeer If a session had previously been established successfully to a peer, this should
77 : * be set to its scoped node-id. Else, this should be initialized to a
78 : * default-constructed ScopedNodeId().
79 : * @param mrpLocalConfig MRP configuration to encode into Sigma2. If not provided, it won't be encoded.
80 : *
81 : * @return CHIP_ERROR The result of initialization
82 : */
83 : CHIP_ERROR PrepareForSessionEstablishment(SessionManager & sessionManager, FabricTable * fabricTable,
84 : SessionResumptionStorage * sessionResumptionStorage,
85 : Credentials::CertificateValidityPolicy * policy,
86 : SessionEstablishmentDelegate * delegate,
87 : const ScopedNodeId & previouslyEstablishedPeer,
88 : Optional<ReliableMessageProtocolConfig> mrpLocalConfig);
89 :
90 : /**
91 : * @brief
92 : * Create and send session establishment request (as an initiator) using device's operational credentials.
93 : *
94 : * @param sessionManager session manager from which to allocate a secure session object
95 : * @param fabricTable The fabric table that contains a fabric in common with the peer
96 : * @param peerScopedNodeId Node to which we want to establish a session
97 : * @param exchangeCtxt The exchange context to send and receive messages with the peer
98 : * @param policy Optional application-provided certificate validity policy
99 : * @param delegate Callback object
100 : *
101 : * @return CHIP_ERROR The result of initialization
102 : */
103 : CHIP_ERROR
104 : EstablishSession(SessionManager & sessionManager, FabricTable * fabricTable, ScopedNodeId peerScopedNodeId,
105 : Messaging::ExchangeContext * exchangeCtxt, SessionResumptionStorage * sessionResumptionStorage,
106 : Credentials::CertificateValidityPolicy * policy, SessionEstablishmentDelegate * delegate,
107 : Optional<ReliableMessageProtocolConfig> mrpLocalConfig);
108 :
109 : /**
110 : * @brief Set the Group Data Provider which will be used to look up IPKs
111 : *
112 : * The GroupDataProvider set MUST have key sets available through `GetIpkKeySet` method
113 : * for the FabricIndex that is associated with the CASESession's FabricInfo.
114 : *
115 : * @param groupDataProvider - Pointer to the group data provider (if nullptr, will error at start of
116 : * establishment, not here).
117 : */
118 1 : void SetGroupDataProvider(Credentials::GroupDataProvider * groupDataProvider) { mGroupDataProvider = groupDataProvider; }
119 :
120 : /**
121 : * Parse a sigma1 message. This function will return success only if the
122 : * message passes schema checks. Specifically:
123 : * * The tags come in order.
124 : * * The required tags are present.
125 : * * The values for the tags that are present satisfy schema requirements
126 : * (e.g. constraints on octet string lengths)
127 : * * Either resumptionID and initiatorResume1MIC are both present or both
128 : * absent.
129 : *
130 : * On success, the initiatorRandom, initiatorSessionId, destinationId,
131 : * initiatorEphPubKey outparams will be set to the corresponding values in
132 : * the message.
133 : *
134 : * On success, either the resumptionRequested outparam will be set to true
135 : * and the resumptionID and initiatorResumeMIC outparams will be set to
136 : * valid values, or the resumptionRequested outparam will be set to false.
137 : */
138 : CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ByteSpan & initiatorRandom, uint16_t & initiatorSessionId,
139 : ByteSpan & destinationId, ByteSpan & initiatorEphPubKey, bool & resumptionRequested,
140 : ByteSpan & resumptionId, ByteSpan & initiatorResumeMIC);
141 :
142 : /**
143 : * @brief
144 : * Derive a secure session from the established session. The API will return error if called before session is established.
145 : *
146 : * @param session Reference to the secure session that will be initialized once session establishment is complete
147 : * @return CHIP_ERROR The result of session derivation
148 : */
149 : CHIP_ERROR DeriveSecureSession(CryptoContext & session) const override;
150 :
151 : //// UnsolicitedMessageHandler Implementation ////
152 3 : CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override
153 : {
154 3 : newDelegate = this;
155 3 : return CHIP_NO_ERROR;
156 : }
157 :
158 : //// ExchangeDelegate Implementation ////
159 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
160 : System::PacketBufferHandle && payload) override;
161 : void OnResponseTimeout(Messaging::ExchangeContext * ec) override;
162 23 : Messaging::ExchangeMessageDispatch & GetMessageDispatch() override { return SessionEstablishmentExchangeDispatch::Instance(); }
163 :
164 : //// SessionDelegate ////
165 : void OnSessionReleased() override;
166 :
167 : //// FabricTable::Delegate Implementation ////
168 0 : void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override
169 : {
170 : (void) fabricTable;
171 0 : InvalidateIfPendingEstablishmentOnFabric(fabricIndex);
172 0 : }
173 6 : void OnFabricUpdated(const chip::FabricTable & fabricTable, chip::FabricIndex fabricIndex) override
174 : {
175 : (void) fabricTable;
176 6 : InvalidateIfPendingEstablishmentOnFabric(fabricIndex);
177 6 : }
178 :
179 88 : FabricIndex GetFabricIndex() const { return mFabricIndex; }
180 :
181 : // Compute our Sigma1 response timeout. This can give consumers an idea of
182 : // how long it will take to detect that our Sigma1 did not get through.
183 : static System::Clock::Timeout ComputeSigma1ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig);
184 :
185 : // Compute our Sigma2 response timeout. This can give consumers an idea of
186 : // how long it will take to detect that our Sigma1 did not get through.
187 : static System::Clock::Timeout ComputeSigma2ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig);
188 :
189 : // TODO: remove Clear, we should create a new instance instead reset the old instance.
190 : /** @brief This function zeroes out and resets the memory used by the object.
191 : **/
192 : void Clear();
193 :
194 : enum class State : uint8_t
195 : {
196 : kInitialized = 0,
197 : kSentSigma1 = 1,
198 : kSentSigma2 = 2,
199 : kSentSigma3 = 3,
200 : kSentSigma1Resume = 4,
201 : kSentSigma2Resume = 5,
202 : kFinished = 6,
203 : kFinishedViaResume = 7,
204 : kSendSigma3Pending = 8,
205 : kHandleSigma3Pending = 9,
206 : };
207 :
208 8 : State GetState() { return mState; }
209 :
210 : // Returns true if the CASE session handshake was stuck due to failing to schedule work on the Matter thread.
211 : // If this function returns true, the CASE session has been reset and is ready for a new session establishment.
212 : bool InvokeBackgroundWorkWatchdog();
213 :
214 : private:
215 : friend class TestCASESession;
216 :
217 : using AutoReleaseSessionKey = Crypto::AutoReleaseSymmetricKey<Crypto::Aes128KeyHandle>;
218 :
219 : /*
220 : * Initialize the object given a reference to the SessionManager, certificate validity policy and a delegate which will be
221 : * notified of any further progress on this session.
222 : *
223 : * If we're either establishing or finished establishing a session to a peer in either initiator or responder
224 : * roles, the node id of that peer should be provided in sessionEvictionHint. Else, it should be initialized
225 : * to a default-constructed ScopedNodeId().
226 : *
227 : */
228 : CHIP_ERROR Init(SessionManager & sessionManager, Credentials::CertificateValidityPolicy * policy,
229 : SessionEstablishmentDelegate * delegate, const ScopedNodeId & sessionEvictionHint);
230 :
231 : // On success, sets mIpk to the correct value for outgoing Sigma1 based on internal state
232 : CHIP_ERROR RecoverInitiatorIpk();
233 : // On success, sets locally maching mFabricInfo in internal state to the entry matched by
234 : // destinationId/initiatorRandom from processing of Sigma1, and sets mIpk to the right IPK.
235 : CHIP_ERROR FindLocalNodeFromDestinationId(const ByteSpan & destinationId, const ByteSpan & initiatorRandom);
236 :
237 : CHIP_ERROR SendSigma1();
238 : CHIP_ERROR HandleSigma1_and_SendSigma2(System::PacketBufferHandle && msg);
239 : CHIP_ERROR HandleSigma1(System::PacketBufferHandle && msg);
240 : CHIP_ERROR TryResumeSession(SessionResumptionStorage::ConstResumptionIdView resumptionId, ByteSpan resume1MIC,
241 : ByteSpan initiatorRandom);
242 : CHIP_ERROR SendSigma2();
243 : CHIP_ERROR HandleSigma2_and_SendSigma3(System::PacketBufferHandle && msg);
244 : CHIP_ERROR HandleSigma2(System::PacketBufferHandle && msg);
245 : CHIP_ERROR HandleSigma2Resume(System::PacketBufferHandle && msg);
246 :
247 : struct SendSigma3Data;
248 : CHIP_ERROR SendSigma3a();
249 : static CHIP_ERROR SendSigma3b(SendSigma3Data & data, bool & cancel);
250 : CHIP_ERROR SendSigma3c(SendSigma3Data & data, CHIP_ERROR status);
251 :
252 : struct HandleSigma3Data;
253 : CHIP_ERROR HandleSigma3a(System::PacketBufferHandle && msg);
254 : static CHIP_ERROR HandleSigma3b(HandleSigma3Data & data, bool & cancel);
255 : CHIP_ERROR HandleSigma3c(HandleSigma3Data & data, CHIP_ERROR status);
256 :
257 : CHIP_ERROR SendSigma2Resume();
258 :
259 : CHIP_ERROR DeriveSigmaKey(const ByteSpan & salt, const ByteSpan & info, AutoReleaseSessionKey & key) const;
260 : CHIP_ERROR ConstructSaltSigma2(const ByteSpan & rand, const Crypto::P256PublicKey & pubkey, const ByteSpan & ipk,
261 : MutableByteSpan & salt);
262 : CHIP_ERROR ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey,
263 : const ByteSpan & receiverPubKey, uint8_t * tbsData, size_t & tbsDataLen);
264 : CHIP_ERROR ConstructSaltSigma3(const ByteSpan & ipk, MutableByteSpan & salt);
265 :
266 : CHIP_ERROR ConstructSigmaResumeKey(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID, const ByteSpan & skInfo,
267 : const ByteSpan & nonce, AutoReleaseSessionKey & resumeKey);
268 :
269 : CHIP_ERROR GenerateSigmaResumeMIC(const ByteSpan & initiatorRandom, const ByteSpan & resumptionID, const ByteSpan & skInfo,
270 : const ByteSpan & nonce, MutableByteSpan & resumeMIC);
271 : CHIP_ERROR ValidateSigmaResumeMIC(const ByteSpan & resumeMIC, const ByteSpan & initiatorRandom, const ByteSpan & resumptionID,
272 : const ByteSpan & skInfo, const ByteSpan & nonce);
273 :
274 : void OnSuccessStatusReport() override;
275 : CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode) override;
276 :
277 : void AbortPendingEstablish(CHIP_ERROR err);
278 :
279 : CHIP_ERROR GetHardcodedTime();
280 :
281 : CHIP_ERROR SetEffectiveTime();
282 :
283 : CHIP_ERROR ValidateReceivedMessage(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
284 : const System::PacketBufferHandle & msg);
285 :
286 : void InvalidateIfPendingEstablishmentOnFabric(FabricIndex fabricIndex);
287 :
288 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
289 : void SetStopSigmaHandshakeAt(Optional<State> state) { mStopHandshakeAtState = state; }
290 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
291 :
292 : Crypto::Hash_SHA256_stream mCommissioningHash;
293 : Crypto::P256PublicKey mRemotePubKey;
294 : Crypto::P256Keypair * mEphemeralKey = nullptr;
295 : Crypto::P256ECDHDerivedSecret mSharedSecret;
296 : Credentials::ValidationContext mValidContext;
297 : Credentials::GroupDataProvider * mGroupDataProvider = nullptr;
298 :
299 : uint8_t mMessageDigest[Crypto::kSHA256_Hash_Length];
300 : uint8_t mIPK[kIPKSize];
301 :
302 : SessionResumptionStorage * mSessionResumptionStorage = nullptr;
303 :
304 : FabricTable * mFabricsTable = nullptr;
305 : FabricIndex mFabricIndex = kUndefinedFabricIndex;
306 : NodeId mPeerNodeId = kUndefinedNodeId;
307 : NodeId mLocalNodeId = kUndefinedNodeId;
308 : CATValues mPeerCATs;
309 :
310 : SessionResumptionStorage::ResumptionIdStorage mResumeResumptionId; // ResumptionId which is used to resume this session
311 : SessionResumptionStorage::ResumptionIdStorage mNewResumptionId; // ResumptionId which is stored to resume future session
312 : // Sigma1 initiator random, maintained to be reused post-Sigma1, such as when generating Sigma2 S2RK key
313 : uint8_t mInitiatorRandom[kSigmaParamRandomNumberSize];
314 :
315 : template <class DATA>
316 : class WorkHelper;
317 : Platform::SharedPtr<WorkHelper<SendSigma3Data>> mSendSigma3Helper;
318 : Platform::SharedPtr<WorkHelper<HandleSigma3Data>> mHandleSigma3Helper;
319 :
320 : State mState;
321 :
322 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
323 : Optional<State> mStopHandshakeAtState = Optional<State>::Missing();
324 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
325 :
326 : SessionEstablishmentStage MapCASEStateToSessionEstablishmentStage(State caseState);
327 : };
328 :
329 : } // namespace chip
|