Matter SDK Coverage Report
Current view: top level - protocols/secure_channel - PairingSession.cpp (source / functions) Coverage Total Hit
Test: SHA:704d97f9c619242ad76fcf75aeabc67802fa72d4 Lines: 93.9 % 131 123
Test Date: 2026-05-18 07:37:39 Functions: 100.0 % 11 11

            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/SpecificationDefinedRevisions.h>
      22              : #include <lib/core/CHIPConfig.h>
      23              : #include <lib/core/TLVTypes.h>
      24              : #include <lib/support/SafeInt.h>
      25              : #include <lib/support/TypeTraits.h>
      26              : #include <platform/CHIPDeviceEvent.h>
      27              : #include <platform/PlatformManager.h>
      28              : #include <transport/SessionManager.h>
      29              : 
      30              : namespace chip {
      31              : 
      32           60 : CHIP_ERROR PairingSession::AllocateSecureSession(SessionManager & sessionManager, const ScopedNodeId & sessionEvictionHint)
      33              : {
      34           60 :     auto handle = sessionManager.AllocateSession(GetSecureSessionType(), sessionEvictionHint);
      35           60 :     VerifyOrReturnError(handle.HasValue(), CHIP_ERROR_NO_MEMORY);
      36           60 :     VerifyOrReturnError(mSecureSessionHolder.GrabPairingSession(handle.Value()), CHIP_ERROR_INTERNAL);
      37           60 :     mSessionManager = &sessionManager;
      38           60 :     mSystemLayer    = sessionManager.SystemLayer();
      39           60 :     return CHIP_NO_ERROR;
      40           60 : }
      41              : 
      42           30 : CHIP_ERROR PairingSession::ActivateSecureSession(const Transport::PeerAddress & peerAddress)
      43              : {
      44              :     // Prepare SecureSession fields, including key derivation, first, before activation
      45           30 :     Transport::SecureSession * secureSession = mSecureSessionHolder->AsSecureSession();
      46           30 :     ReturnErrorOnFailure(DeriveSecureSession(secureSession->GetCryptoContext()));
      47              : 
      48           30 :     uint16_t peerSessionId = GetPeerSessionId();
      49           30 :     secureSession->SetPeerAddress(peerAddress);
      50           30 :     secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue);
      51              : 
      52              :     // Call Activate last, otherwise errors on anything after would lead to
      53              :     // a partially valid session.
      54           30 :     secureSession->Activate(GetLocalScopedNodeId(), GetPeer(), GetPeerCATs(), peerSessionId, GetRemoteSessionParameters());
      55              : 
      56           30 :     ChipLogDetail(Inet, "New secure session activated for device " ChipLogFormatScopedNodeId ", LSID:%d PSID:%d!",
      57              :                   ChipLogValueScopedNodeId(GetPeer()), secureSession->GetLocalSessionId(), peerSessionId);
      58              : 
      59           30 :     return CHIP_NO_ERROR;
      60              : }
      61              : 
      62           30 : void PairingSession::Finish()
      63              : {
      64           30 :     Transport::PeerAddress address = mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->GetPeerAddress();
      65              : 
      66              : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
      67           30 :     if (address.GetTransportType() == Transport::Type::kTcp)
      68              :     {
      69              :         // Fetch the connection for the unauthenticated session used to set up
      70              :         // the secure session.
      71            0 :         auto conn = mExchangeCtxt.Value()->GetSessionHandle()->AsUnauthenticatedSession()->GetTCPConnection();
      72              : 
      73              :         // Associate the connection with the secure session being activated.
      74            0 :         mSecureSessionHolder->AsSecureSession()->SetTCPConnection(conn);
      75            0 :     }
      76              : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
      77              :     // Discard the exchange so that Clear() doesn't try closing it. The exchange will handle that.
      78           30 :     DiscardExchange();
      79              : 
      80           30 :     CHIP_ERROR err = ActivateSecureSession(address);
      81           60 :     if (err == CHIP_NO_ERROR)
      82              :     {
      83           30 :         VerifyOrDie(mSecureSessionHolder);
      84           30 :         DeviceLayer::ChipDeviceEvent event{ .Type = DeviceLayer::DeviceEventType::kSecureSessionEstablished };
      85           30 :         event.SecureSessionEstablished.TransportType = to_underlying(address.GetTransportType());
      86           30 :         event.SecureSessionEstablished.SecureSessionType =
      87           30 :             to_underlying(mSecureSessionHolder->AsSecureSession()->GetSecureSessionType());
      88           30 :         event.SecureSessionEstablished.LocalSessionId = mSecureSessionHolder->AsSecureSession()->GetLocalSessionId();
      89           30 :         event.SecureSessionEstablished.PeerNodeId     = mSecureSessionHolder->GetPeer().GetNodeId();
      90           30 :         event.SecureSessionEstablished.FabricIndex    = mSecureSessionHolder->GetPeer().GetFabricIndex();
      91           60 :         if (DeviceLayer::PlatformMgr().PostEvent(&event) != CHIP_NO_ERROR)
      92              :         {
      93            0 :             ChipLogError(SecureChannel, "Failed to post Secure Session established event");
      94              :         }
      95              :         // Make sure to null out mDelegate so we don't send it any other
      96              :         // notifications.
      97           30 :         auto * delegate = mDelegate;
      98           30 :         mDelegate       = nullptr;
      99           30 :         delegate->OnSessionEstablished(mSecureSessionHolder.Get().Value());
     100              :     }
     101              :     else
     102              :     {
     103            0 :         NotifySessionEstablishmentError(err);
     104              :     }
     105           30 : }
     106              : 
     107           38 : void PairingSession::DiscardExchange()
     108              : {
     109           38 :     if (mExchangeCtxt.HasValue())
     110              :     {
     111              :         // Make sure the exchange doesn't try to notify us when it closes,
     112              :         // since we might be dead by then.
     113           38 :         mExchangeCtxt.Value()->SetDelegate(nullptr);
     114              : 
     115              :         // Null out mExchangeCtxt so that Clear() doesn't try closing it.  The
     116              :         // exchange will handle that.
     117           38 :         mExchangeCtxt.ClearValue();
     118              :     }
     119           38 : }
     120              : 
     121           56 : CHIP_ERROR PairingSession::EncodeSessionParameters(TLV::Tag tag, const ReliableMessageProtocolConfig & mrpLocalConfig,
     122              :                                                    TLV::TLVWriter & tlvWriter)
     123              : {
     124              :     TLV::TLVType mrpParamsContainer;
     125           56 :     ReturnErrorOnFailure(tlvWriter.StartContainer(tag, TLV::kTLVType_Structure, mrpParamsContainer));
     126           56 :     ReturnErrorOnFailure(
     127              :         tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionIdleInterval), mrpLocalConfig.mIdleRetransTimeout.count()));
     128           56 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveInterval),
     129              :                                        mrpLocalConfig.mActiveRetransTimeout.count()));
     130           56 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSessionActiveThreshold),
     131              :                                        mrpLocalConfig.mActiveThresholdTime.count()));
     132              : 
     133           56 :     uint16_t dataModel = Revision::kDataModelRevision;
     134           56 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kDataModelRevision), dataModel));
     135              : 
     136           56 :     uint16_t interactionModel = Revision::kInteractionModelRevision;
     137           56 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kInteractionModelRevision), interactionModel));
     138              : 
     139           56 :     uint32_t specVersion = Revision::kSpecificationVersion;
     140           56 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kSpecificationVersion), specVersion));
     141              : 
     142           56 :     uint16_t maxPathsPerInvoke = CHIP_CONFIG_MAX_PATHS_PER_INVOKE;
     143           56 :     ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(SessionParameters::Tag::kMaxPathsPerInvoke), maxPathsPerInvoke));
     144           56 :     return tlvWriter.EndContainer(mrpParamsContainer);
     145              : }
     146              : 
     147           51 : CHIP_ERROR PairingSession::DecodeSessionParametersIfPresent(TLV::Tag expectedTag, TLV::ContiguousBufferTLVReader & tlvReader,
     148              :                                                             SessionParameters & outSessionParameters)
     149              : {
     150           51 :     CHIP_ERROR err = CHIP_NO_ERROR;
     151              : 
     152              :     // The MRP parameters are optional.
     153           51 :     if (tlvReader.GetTag() != expectedTag)
     154              :     {
     155            1 :         return CHIP_NO_ERROR;
     156              :     }
     157              : 
     158           50 :     TLV::TLVType containerType = TLV::kTLVType_Structure;
     159           50 :     ReturnErrorOnFailure(tlvReader.EnterContainer(containerType));
     160              : 
     161           50 :     ReturnErrorOnFailure(tlvReader.Next());
     162              : 
     163           50 :     ChipLogDetail(SecureChannel, "Found MRP parameters in the message");
     164              : 
     165              :     // All TLV elements in the structure are optional. If the first element is present, process it and move
     166              :     // the TLV reader to the next element.
     167           50 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionIdleInterval)
     168              :     {
     169              :         uint32_t idleRetransTimeout;
     170           50 :         ReturnErrorOnFailure(tlvReader.Get(idleRetransTimeout));
     171           50 :         outSessionParameters.SetMRPIdleRetransTimeout(System::Clock::Milliseconds32(idleRetransTimeout));
     172              : 
     173              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     174           50 :         SuccessOrExit(err = tlvReader.Next());
     175              :     }
     176              : 
     177           50 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveInterval)
     178              :     {
     179              :         uint32_t activeRetransTimeout;
     180           50 :         ReturnErrorOnFailure(tlvReader.Get(activeRetransTimeout));
     181           50 :         outSessionParameters.SetMRPActiveRetransTimeout(System::Clock::Milliseconds32(activeRetransTimeout));
     182              : 
     183              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     184           50 :         SuccessOrExit(err = tlvReader.Next());
     185              :     }
     186              : 
     187           50 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSessionActiveThreshold)
     188              :     {
     189              :         uint16_t activeThresholdTime;
     190           50 :         ReturnErrorOnFailure(tlvReader.Get(activeThresholdTime));
     191           50 :         outSessionParameters.SetMRPActiveThresholdTime(System::Clock::Milliseconds16(activeThresholdTime));
     192              : 
     193              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     194           50 :         SuccessOrExit(err = tlvReader.Next());
     195              :     }
     196              : 
     197           47 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kDataModelRevision)
     198              :     {
     199              :         uint16_t dataModelRevision;
     200           47 :         ReturnErrorOnFailure(tlvReader.Get(dataModelRevision));
     201           47 :         outSessionParameters.SetDataModelRevision(dataModelRevision);
     202              : 
     203              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     204           47 :         SuccessOrExit(err = tlvReader.Next());
     205              :     }
     206              : 
     207           47 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kInteractionModelRevision)
     208              :     {
     209              :         uint16_t interactionModelRevision;
     210           47 :         ReturnErrorOnFailure(tlvReader.Get(interactionModelRevision));
     211           47 :         outSessionParameters.SetInteractionModelRevision(interactionModelRevision);
     212              : 
     213              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     214           47 :         SuccessOrExit(err = tlvReader.Next());
     215              :     }
     216              : 
     217           47 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kSpecificationVersion)
     218              :     {
     219              :         uint32_t specificationVersion;
     220           47 :         ReturnErrorOnFailure(tlvReader.Get(specificationVersion));
     221           47 :         outSessionParameters.SetSpecificationVersion(specificationVersion);
     222              : 
     223              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     224           47 :         SuccessOrExit(err = tlvReader.Next());
     225              :     }
     226              : 
     227           47 :     if (TLV::TagNumFromTag(tlvReader.GetTag()) == SessionParameters::Tag::kMaxPathsPerInvoke)
     228              :     {
     229              :         uint16_t maxPathsPerInvoke;
     230           47 :         ReturnErrorOnFailure(tlvReader.Get(maxPathsPerInvoke));
     231           47 :         outSessionParameters.SetMaxPathsPerInvoke(maxPathsPerInvoke);
     232              : 
     233              :         // The next element is optional. If it's not present, return CHIP_NO_ERROR.
     234           47 :         SuccessOrExit(err = tlvReader.Next());
     235              :     }
     236              : 
     237              :     // Future proofing - Don't error out if there are other tags
     238            0 : exit:
     239          102 :     if (err == CHIP_END_OF_TLV || err == CHIP_NO_ERROR)
     240              :     {
     241           50 :         return tlvReader.ExitContainer(containerType);
     242              :     }
     243            0 :     return err;
     244              : }
     245              : 
     246            4 : bool PairingSession::IsSessionEstablishmentInProgress()
     247              : {
     248            4 :     if (!mSecureSessionHolder)
     249              :     {
     250            0 :         return false;
     251              :     }
     252              : 
     253            4 :     Transport::SecureSession * secureSession = mSecureSessionHolder->AsSecureSession();
     254            4 :     return secureSession->IsEstablishing();
     255              : }
     256              : 
     257          903 : void PairingSession::Clear()
     258              : {
     259              :     // Clear acts like the destructor of PairingSession. If it is called during
     260              :     // the middle of pairing, that means we should terminate the exchange. For the
     261              :     // normal path, the exchange should already be discarded before calling Clear.
     262          903 :     if (mExchangeCtxt.HasValue())
     263              :     {
     264              :         // The only time we reach this is when we are getting destroyed in the
     265              :         // middle of our handshake. In that case, there is no point in trying to
     266              :         // do MRP resends of the last message we sent. So, abort the exchange
     267              :         // instead of just closing it.
     268            6 :         mExchangeCtxt.Value()->Abort();
     269            6 :         mExchangeCtxt.ClearValue();
     270              :     }
     271          903 :     mSecureSessionHolder.Release();
     272          903 :     mPeerSessionId.ClearValue();
     273          903 :     mSessionManager = nullptr;
     274          903 : }
     275              : 
     276           18 : void PairingSession::NotifySessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage)
     277              : {
     278           18 :     if (mDelegate == nullptr)
     279              :     {
     280              :         // Already notified success or error.
     281            6 :         return;
     282              :     }
     283              : 
     284           12 :     auto * delegate = mDelegate;
     285           12 :     mDelegate       = nullptr;
     286           12 :     delegate->OnSessionEstablishmentError(error, stage);
     287              : }
     288              : 
     289           17 : void PairingSession::OnSessionReleased()
     290              : {
     291           17 :     if (mRole == CryptoContext::SessionRole::kInitiator)
     292              :     {
     293            8 :         NotifySessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED);
     294            8 :         return;
     295              :     }
     296              : 
     297              :     // Send the error notification async, because our delegate is likely to want
     298              :     // to create a new session to listen for new connection attempts, and doing
     299              :     // that under an OnSessionReleased notification is not safe.
     300              :     //
     301              :     // We capture the delegate by value and null mDelegate immediately, mirroring
     302              :     // what NotifySessionEstablishmentError does on the synchronous path.  This
     303              :     // means the lambda holds no reference to 'this' at all, so the PairingSession
     304              :     // lifetime is irrelevant — the callback is safe even if the object is freed
     305              :     // or recycled from a pool before the event loop drains this work item.
     306              :     //
     307              :     // mSystemLayer is captured from the session manager at AllocateSecureSession()
     308              :     // and intentionally not cleared by Clear(), so it remains valid here even
     309              :     // though mSessionManager has already been nulled.  Using it directly avoids
     310              :     // any dependency on DeviceLayer (which may not be present on all platforms).
     311            9 :     auto * delegate = mDelegate;
     312            9 :     mDelegate       = nullptr;
     313              : 
     314            9 :     if (delegate == nullptr || mSystemLayer == nullptr)
     315              :     {
     316            6 :         return;
     317              :     }
     318              : 
     319            3 :     TEMPORARY_RETURN_IGNORED mSystemLayer->ScheduleLambda([delegate]() {
     320            3 :         ChipLogError(Inet, "ASYNC Session establishment failed");
     321            3 :         delegate->OnSessionEstablishmentError(CHIP_ERROR_CONNECTION_ABORTED, SessionEstablishmentStage::kNotInKeyExchange);
     322            3 :     });
     323              : }
     324              : 
     325              : } // namespace chip
        

Generated by: LCOV version 2.0-1