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