LCOV - code coverage report
Current view: top level - protocols/secure_channel - PairingSession.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 115 120 95.8 %
Date: 2024-02-15 08:20:41 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021 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             : #include <protocols/secure_channel/PairingSession.h>
      20             : 
      21             : #include <app/DataModelRevision.h>
      22             : #include <app/InteractionModelRevision.h>
      23             : #include <app/SpecificationVersion.h>
      24             : #include <lib/core/CHIPConfig.h>
      25             : #include <lib/core/TLVTypes.h>
      26             : #include <lib/support/SafeInt.h>
      27             : 
      28             : namespace chip {
      29             : 
      30          54 : CHIP_ERROR PairingSession::AllocateSecureSession(SessionManager & sessionManager, const ScopedNodeId & sessionEvictionHint)
      31             : {
      32          54 :     auto handle = sessionManager.AllocateSession(GetSecureSessionType(), sessionEvictionHint);
      33          54 :     VerifyOrReturnError(handle.HasValue(), CHIP_ERROR_NO_MEMORY);
      34          54 :     VerifyOrReturnError(mSecureSessionHolder.GrabPairingSession(handle.Value()), CHIP_ERROR_INTERNAL);
      35          54 :     mSessionManager = &sessionManager;
      36          54 :     return CHIP_NO_ERROR;
      37          54 : }
      38             : 
      39          26 : CHIP_ERROR PairingSession::ActivateSecureSession(const Transport::PeerAddress & peerAddress)
      40             : {
      41             :     // Prepare SecureSession fields, including key derivation, first, before activation
      42          26 :     Transport::SecureSession * secureSession = mSecureSessionHolder->AsSecureSession();
      43          26 :     ReturnErrorOnFailure(DeriveSecureSession(secureSession->GetCryptoContext()));
      44             : 
      45          26 :     uint16_t peerSessionId = GetPeerSessionId();
      46          26 :     secureSession->SetPeerAddress(peerAddress);
      47          26 :     secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue);
      48             : 
      49             :     // Call Activate last, otherwise errors on anything after would lead to
      50             :     // a partially valid session.
      51          26 :     secureSession->Activate(GetLocalScopedNodeId(), GetPeer(), GetPeerCATs(), peerSessionId, GetRemoteSessionParameters());
      52             : 
      53          26 :     ChipLogDetail(Inet, "New secure session activated for device " ChipLogFormatScopedNodeId ", LSID:%d PSID:%d!",
      54             :                   ChipLogValueScopedNodeId(GetPeer()), secureSession->GetLocalSessionId(), peerSessionId);
      55             : 
      56          26 :     return CHIP_NO_ERROR;
      57             : }
      58             : 
      59          26 : void PairingSession::Finish()
      60             : {
      61          26 :     Transport::PeerAddress address = mExchangeCtxt->GetSessionHandle()->AsUnauthenticatedSession()->GetPeerAddress();
      62             : 
      63             :     // Discard the exchange so that Clear() doesn't try closing it. The exchange will handle that.
      64          26 :     DiscardExchange();
      65             : 
      66          26 :     CHIP_ERROR err = ActivateSecureSession(address);
      67          26 :     if (err == CHIP_NO_ERROR)
      68             :     {
      69          26 :         VerifyOrDie(mSecureSessionHolder);
      70             :         // Make sure to null out mDelegate so we don't send it any other
      71             :         // notifications.
      72          26 :         auto * delegate = mDelegate;
      73          26 :         mDelegate       = nullptr;
      74          26 :         delegate->OnSessionEstablished(mSecureSessionHolder.Get().Value());
      75             :     }
      76             :     else
      77             :     {
      78           0 :         NotifySessionEstablishmentError(err);
      79             :     }
      80          26 : }
      81             : 
      82          30 : void PairingSession::DiscardExchange()
      83             : {
      84          30 :     if (mExchangeCtxt != nullptr)
      85             :     {
      86             :         // Make sure the exchange doesn't try to notify us when it closes,
      87             :         // since we might be dead by then.
      88          30 :         mExchangeCtxt->SetDelegate(nullptr);
      89             :         // Null out mExchangeCtxt so that Clear() doesn't try closing it.  The
      90             :         // exchange will handle that.
      91          30 :         mExchangeCtxt = nullptr;
      92             :     }
      93          30 : }
      94             : 
      95          36 : CHIP_ERROR PairingSession::EncodeSessionParameters(TLV::Tag tag, const Optional<ReliableMessageProtocolConfig> & providedMrpConfig,
      96             :                                                    TLV::TLVWriter & tlvWriter)
      97             : {
      98             :     // TODO: https://github.com/project-chip/connectedhomeip/issues/30456. Based on the spec we need to send values here now,
      99             :     // but it is not entirely clear what we should be sending here when `providedMrpConfig.HasValue() == false`. For now we
     100             :     // are sending the default MRP config values.
     101          36 :     ReliableMessageProtocolConfig mrpLocalConfig = GetDefaultMRPConfig();
     102          36 :     if (providedMrpConfig.HasValue())
     103             :     {
     104           7 :         mrpLocalConfig = providedMrpConfig.Value();
     105             :     }
     106             :     TLV::TLVType mrpParamsContainer;
     107          36 :     ReturnErrorOnFailure(tlvWriter.StartContainer(tag, TLV::kTLVType_Structure, mrpParamsContainer));
     108          36 :     ReturnErrorOnFailure(
     109             :         tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionIdleInterval), mrpLocalConfig.mIdleRetransTimeout.count()));
     110          36 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveInterval),
     111             :                                        mrpLocalConfig.mActiveRetransTimeout.count()));
     112          36 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveThreshold),
     113             :                                        mrpLocalConfig.mActiveThresholdTime.count()));
     114             : 
     115          36 :     uint16_t dataModel = CHIP_DEVICE_DATA_MODEL_REVISION;
     116          36 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kDataModelRevision), dataModel));
     117             : 
     118          36 :     uint16_t interactionModel = CHIP_DEVICE_INTERACTION_MODEL_REVISION;
     119          36 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kInteractionModelRevision), interactionModel));
     120             : 
     121          36 :     uint32_t specVersion = CHIP_DEVICE_SPECIFICATION_VERSION;
     122          36 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSpecificationVersion), specVersion));
     123             : 
     124          36 :     uint16_t maxPathsPerInvoke = CHIP_CONFIG_MAX_PATHS_PER_INVOKE;
     125          36 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kMaxPathsPerInvoke), maxPathsPerInvoke));
     126          36 :     return tlvWriter.EndContainer(mrpParamsContainer);
     127             : }
     128             : 
     129          31 : CHIP_ERROR PairingSession::DecodeMRPParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader)
     130             : {
     131          31 :     CHIP_ERROR err = CHIP_NO_ERROR;
     132             : 
     133             :     // The MRP parameters are optional.
     134          31 :     if (tlvReader.GetTag() != expectedTag)
     135             :     {
     136           1 :         return CHIP_NO_ERROR;
     137             :     }
     138             : 
     139          30 :     TLV::TLVType containerType = TLV::kTLVType_Structure;
     140          30 :     ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
     141             : 
     142          30 :     ReturnErrorOnFailure(tlvReader.Next());
     143             : 
     144          30 :     ChipLogDetail(SecureChannel, "Found MRP parameters in the message");
     145             : 
     146             :     // All TLV elements in the structure are optional. If the first element is present, process it and move
     147             :     // the TLV reader to the next element.
     148          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionIdleInterval)
     149             :     {
     150             :         uint32_t idleRetransTimeout;
     151          30 :         ReturnErrorOnFailure(tlvReader.Get(idleRetransTimeout));
     152          30 :         mRemoteSessionParams.SetMRPIdleRetransTimeout(System::Clock::Milliseconds32(idleRetransTimeout));
     153             : 
     154             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     155          30 :         SuccessOrExit(err = tlvReader.Next());
     156             :     }
     157             : 
     158          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveInterval)
     159             :     {
     160             :         uint32_t activeRetransTimeout;
     161          30 :         ReturnErrorOnFailure(tlvReader.Get(activeRetransTimeout));
     162          30 :         mRemoteSessionParams.SetMRPActiveRetransTimeout(System::Clock::Milliseconds32(activeRetransTimeout));
     163             : 
     164             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     165          30 :         SuccessOrExit(err = tlvReader.Next());
     166             :     }
     167             : 
     168          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveThreshold)
     169             :     {
     170             :         uint16_t activeThresholdTime;
     171          30 :         ReturnErrorOnFailure(tlvReader.Get(activeThresholdTime));
     172          30 :         mRemoteSessionParams.SetMRPActiveThresholdTime(System::Clock::Milliseconds16(activeThresholdTime));
     173             : 
     174             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     175          30 :         SuccessOrExit(err = tlvReader.Next());
     176             :     }
     177             : 
     178          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kDataModelRevision)
     179             :     {
     180             :         uint16_t dataModelRevision;
     181          30 :         ReturnErrorOnFailure(tlvReader.Get(dataModelRevision));
     182          30 :         mRemoteSessionParams.SetDataModelRevision(dataModelRevision);
     183             : 
     184             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     185          30 :         SuccessOrExit(err = tlvReader.Next());
     186             :     }
     187             : 
     188          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kInteractionModelRevision)
     189             :     {
     190             :         uint16_t interactionModelRevision;
     191          30 :         ReturnErrorOnFailure(tlvReader.Get(interactionModelRevision));
     192          30 :         mRemoteSessionParams.SetInteractionModelRevision(interactionModelRevision);
     193             : 
     194             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     195          30 :         SuccessOrExit(err = tlvReader.Next());
     196             :     }
     197             : 
     198          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSpecificationVersion)
     199             :     {
     200             :         uint32_t specificationVersion;
     201          30 :         ReturnErrorOnFailure(tlvReader.Get(specificationVersion));
     202          30 :         mRemoteSessionParams.SetSpecificationVersion(specificationVersion);
     203             : 
     204             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     205          30 :         SuccessOrExit(err = tlvReader.Next());
     206             :     }
     207             : 
     208          30 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kMaxPathsPerInvoke)
     209             :     {
     210             :         uint16_t maxPathsPerInvoke;
     211          30 :         ReturnErrorOnFailure(tlvReader.Get(maxPathsPerInvoke));
     212          30 :         mRemoteSessionParams.SetMaxPathsPerInvoke(maxPathsPerInvoke);
     213             : 
     214             :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     215          30 :         SuccessOrExit(err = tlvReader.Next());
     216             :     }
     217             : 
     218             :     // Future proofing - Don't error out if there are other tags
     219           0 : exit:
     220          30 :     if (err == CHIP_END_OF_TLV)
     221             :     {
     222          30 :         return tlvReader.ExitContainer(containerType);
     223             :     }
     224           0 :     return err;
     225             : }
     226             : 
     227           4 : bool PairingSession::IsSessionEstablishmentInProgress()
     228             : {
     229           4 :     if (!mSecureSessionHolder)
     230             :     {
     231           0 :         return false;
     232             :     }
     233             : 
     234           4 :     Transport::SecureSession * secureSession = mSecureSessionHolder->AsSecureSession();
     235           4 :     return secureSession->IsEstablishing();
     236             : }
     237             : 
     238         215 : void PairingSession::Clear()
     239             : {
     240             :     // Clear acts like the destructor if PairingSession, if it is call during
     241             :     // middle of a pairing, means we should terminate the exchange. For normal
     242             :     // path, the exchange should already be discarded before calling Clear.
     243         215 :     if (mExchangeCtxt != nullptr)
     244             :     {
     245             :         // The only time we reach this is if we are getting destroyed in the
     246             :         // middle of our handshake.  In that case, there is no point trying to
     247             :         // do MRP resends of the last message we sent, so abort the exchange
     248             :         // instead of just closing it.
     249           6 :         mExchangeCtxt->Abort();
     250           6 :         mExchangeCtxt = nullptr;
     251             :     }
     252             : 
     253         215 :     mSecureSessionHolder.Release();
     254         215 :     mPeerSessionId.ClearValue();
     255         215 :     mSessionManager = nullptr;
     256         215 : }
     257             : 
     258          18 : void PairingSession::NotifySessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage)
     259             : {
     260          18 :     if (mDelegate == nullptr)
     261             :     {
     262             :         // Already notified success or error.
     263          10 :         return;
     264             :     }
     265             : 
     266           8 :     auto * delegate = mDelegate;
     267           8 :     mDelegate       = nullptr;
     268           8 :     delegate->OnSessionEstablishmentError(error, stage);
     269             : }
     270             : 
     271          13 : void PairingSession::OnSessionReleased()
     272             : {
     273          13 :     if (mRole == CryptoContext::SessionRole::kInitiator)
     274             :     {
     275           5 :         NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
     276           5 :         return;
     277             :     }
     278             : 
     279             :     // Send the error notification async, because our delegate is likely to want
     280             :     // to create a new session to listen for new connection attempts, and doing
     281             :     // that under an OnSessionReleased notification is not safe.
     282           8 :     if (!mSessionManager)
     283             :     {
     284           0 :         return;
     285             :     }
     286             : 
     287           8 :     mSessionManager->SystemLayer()->ScheduleWork(
     288           7 :         [](auto * systemLayer, auto * appState) -> void {
     289           7 :             ChipLogError(Inet, "ASYNC CASE Session establishment failed");
     290           7 :             auto * _this = static_cast<PairingSession *>(appState);
     291           7 :             _this->NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
     292           7 :         },
     293             :         this);
     294             : }
     295             : 
     296             : } // namespace chip

Generated by: LCOV version 1.14