Matter SDK Coverage Report
Current view: top level - protocols/secure_channel - CASEServer.cpp (source / functions) Coverage Total Hit
Test: SHA:e98a48c2e59f85a25417956e1d105721433aa5d1 Lines: 87.3 % 71 62
Test Date: 2026-01-09 16:53:50 Functions: 87.5 % 8 7

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #include <protocols/secure_channel/CASEServer.h>
      19              : 
      20              : #include <lib/core/CHIPError.h>
      21              : #include <lib/support/CHIPFaultInjection.h>
      22              : #include <lib/support/CodeUtils.h>
      23              : #include <lib/support/SafeInt.h>
      24              : #include <lib/support/logging/CHIPLogging.h>
      25              : #include <tracing/macros.h>
      26              : #include <transport/SessionManager.h>
      27              : 
      28              : using namespace ::chip::Inet;
      29              : using namespace ::chip::Transport;
      30              : using namespace ::chip::Credentials;
      31              : 
      32              : namespace chip {
      33              : 
      34            7 : CHIP_ERROR CASEServer::ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, SessionManager * sessionManager,
      35              :                                                      FabricTable * fabrics, SessionResumptionStorage * sessionResumptionStorage,
      36              :                                                      Credentials::CertificateValidityPolicy * certificateValidityPolicy,
      37              :                                                      Credentials::GroupDataProvider * responderGroupDataProvider)
      38              : {
      39            7 :     VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      40            7 :     VerifyOrReturnError(sessionManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      41            7 :     VerifyOrReturnError(responderGroupDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      42              : 
      43            7 :     mSessionManager            = sessionManager;
      44            7 :     mSessionResumptionStorage  = sessionResumptionStorage;
      45            7 :     mCertificateValidityPolicy = certificateValidityPolicy;
      46            7 :     mFabrics                   = fabrics;
      47            7 :     mExchangeManager           = exchangeManager;
      48            7 :     mGroupDataProvider         = responderGroupDataProvider;
      49              : 
      50              :     // Set up the group state provider that persists across all handshakes.
      51            7 :     GetSession().SetGroupDataProvider(mGroupDataProvider);
      52              : 
      53            7 :     ChipLogProgress(Inet, "CASE Server enabling CASE session setups");
      54            7 :     TEMPORARY_RETURN_IGNORED mExchangeManager->RegisterUnsolicitedMessageHandlerForType(
      55              :         Protocols::SecureChannel::MsgType::CASE_Sigma1, this);
      56              : 
      57            7 :     PrepareForSessionEstablishment();
      58              : 
      59            7 :     return CHIP_NO_ERROR;
      60              : }
      61              : 
      62            7 : CHIP_ERROR CASEServer::InitCASEHandshake(Messaging::ExchangeContext * ec)
      63              : {
      64              :     MATTER_TRACE_SCOPE("InitCASEHandshake", "CASEServer");
      65            7 :     VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      66              : 
      67              :     // Hand over the exchange context to the CASE session.
      68            7 :     ec->SetDelegate(&GetSession());
      69              : 
      70            7 :     return CHIP_NO_ERROR;
      71              : }
      72              : 
      73            8 : CHIP_ERROR CASEServer::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate)
      74              : {
      75              :     // TODO: assign newDelegate to CASESession, let CASESession handle future messages.
      76            8 :     newDelegate = this;
      77            8 :     return CHIP_NO_ERROR;
      78              : }
      79              : 
      80            8 : CHIP_ERROR CASEServer::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
      81              :                                          System::PacketBufferHandle && payload)
      82              : {
      83              :     MATTER_TRACE_SCOPE("OnMessageReceived", "CASEServer");
      84              : 
      85            8 :     bool busy = GetSession().GetState() != CASESession::State::kInitialized;
      86            8 :     CHIP_FAULT_INJECT(FaultInjection::kFault_CASEServerBusy, busy = true);
      87            8 :     if (busy)
      88              :     {
      89              :         // We are in the middle of CASE handshake
      90              : 
      91              :         // Invoke watchdog to fix any stuck handshakes
      92            1 :         bool watchdogFired = GetSession().InvokeBackgroundWorkWatchdog();
      93            1 :         if (!watchdogFired)
      94              :         {
      95              :             // Handshake wasn't stuck, send the busy status report and let the existing handshake continue.
      96              : 
      97              :             // A successful CASE handshake can take several seconds and some may time out (30 seconds or more).
      98              : 
      99            1 :             System::Clock::Milliseconds16 delay = System::Clock::kZero;
     100            1 :             if (GetSession().GetState() == CASESession::State::kSentSigma2)
     101              :             {
     102              :                 // The delay should be however long we think it will take for
     103              :                 // that to time out.
     104            1 :                 auto sigma2Timeout = CASESession::ComputeSigma2ResponseTimeout(GetSession().GetRemoteMRPConfig());
     105            1 :                 if (sigma2Timeout < System::Clock::Milliseconds16::max())
     106              :                 {
     107            1 :                     delay = std::chrono::duration_cast<System::Clock::Milliseconds16>(sigma2Timeout);
     108              :                 }
     109              :                 else
     110              :                 {
     111              :                     // Avoid overflow issues, just wait for as long as we can to
     112              :                     // get close to our expected Sigma2 timeout.
     113            0 :                     delay = System::Clock::Milliseconds16::max();
     114              :                 }
     115              :             }
     116              :             else
     117              :             {
     118              :                 // For now, setting minimum wait time to 5000 milliseconds if we
     119              :                 // have no other information.
     120            0 :                 delay = System::Clock::Milliseconds16(5000);
     121              :             }
     122            1 :             CHIP_ERROR err = SendBusyStatusReport(ec, delay);
     123            2 :             if (err != CHIP_NO_ERROR)
     124              :             {
     125            0 :                 ChipLogError(Inet, "Failed to send the busy status report, err:%" CHIP_ERROR_FORMAT, err.Format());
     126              :             }
     127            1 :             return err;
     128              :         }
     129              :     }
     130              : 
     131            7 :     if (!ec->GetSessionHandle()->IsUnauthenticatedSession())
     132              :     {
     133            0 :         ChipLogError(Inet, "CASE Server received Sigma1 message %s EC %p", "over encrypted session. Ignoring.", ec);
     134            0 :         return CHIP_ERROR_INCORRECT_STATE;
     135              :     }
     136              : 
     137            7 :     ChipLogProgress(Inet, "CASE Server received Sigma1 message %s EC %p", ". Starting handshake.", ec);
     138              : 
     139            7 :     CHIP_ERROR err = InitCASEHandshake(ec);
     140            7 :     SuccessOrExit(err);
     141              : 
     142              :     // TODO - Enable multiple concurrent CASE session establishment
     143              :     // https://github.com/project-chip/connectedhomeip/issues/8342
     144              : 
     145            7 :     err = GetSession().OnMessageReceived(ec, payloadHeader, std::move(payload));
     146            7 :     SuccessOrExit(err);
     147              : 
     148            7 : exit:
     149              :     // CASESession::OnMessageReceived guarantees that it will call
     150              :     // OnSessionEstablishmentError if it returns error, so nothing else to do here.
     151            7 :     return err;
     152              : }
     153              : 
     154           14 : void CASEServer::PrepareForSessionEstablishment(const ScopedNodeId & previouslyEstablishedPeer)
     155              : {
     156           14 :     GetSession().Clear();
     157              : 
     158              :     //
     159              :     // This releases our reference to a previously pinned session. If that was a successfully established session and is now
     160              :     // active, this will have no effect (the session will remain in the session table).
     161              :     //
     162              :     // If we previously held a session still in the pairing state, it means PairingSession was owning that session. Since it
     163              :     // gave up its reference by the time we got here, releasing the pinned session here will actually result in it being
     164              :     // de-allocated since no one else is holding onto this session. This will mean that when we get to allocating a session below,
     165              :     // we'll at least have one free session available in the session table, and won't need to evict an arbitrary session.
     166              :     //
     167           14 :     mPinnedSecureSession.ClearValue();
     168              : 
     169              :     //
     170              :     // Indicate to the underlying CASE session to prepare for session establishment requests coming its way. This will
     171              :     // involve allocating a SecureSession that will be held until it's needed for the next CASE session handshake.
     172              :     //
     173              :     // Logically speaking, we're attempting to evict a session using details of the just-established session (to ensure
     174              :     // we're evicting sessions from the right fabric if needed) and then transferring the just established session into that
     175              :     // slot (and thereby free'ing up the slot for the next session attempt). However, this transfer isn't necessary - just
     176              :     // evicting a session will ensure it is available for the next attempt.
     177              :     //
     178              :     // This call can fail if we have run out memory to allocate SecureSessions. Continuing without taking any action
     179              :     // however will render this node deaf to future handshake requests, so it's better to die here to raise attention to the problem
     180              :     // / facilitate recovery.
     181              :     //
     182              :     // TODO(#17568): Once session eviction is actually in place, this call should NEVER fail and if so, is a logic bug.
     183              :     // Dying here on failure is even more appropriate then.
     184              :     //
     185           28 :     VerifyOrDie(GetSession().PrepareForSessionEstablishment(*mSessionManager, mFabrics, mSessionResumptionStorage,
     186              :                                                             mCertificateValidityPolicy, this, previouslyEstablishedPeer,
     187              :                                                             GetLocalMRPConfig()) == CHIP_NO_ERROR);
     188              : 
     189              :     //
     190              :     // PairingSession::mSecureSessionHolder is a weak-reference. If MarkForEviction is called on this session, the session is
     191              :     // going to get de-allocated from underneath us. This session that has just been allocated should *never* get evicted, and
     192              :     // remain available till the next hand-shake is received.
     193              :     //
     194              :     // TODO: Converting SessionHolder to a true weak-ref and making PairingSession hold a strong-ref (#18397) would avoid this
     195              :     // headache...
     196              :     //
     197              :     // Let's create a SessionHandle strong-reference to it to keep it resident.
     198              :     //
     199           14 :     mPinnedSecureSession = GetSession().CopySecureSession();
     200              : 
     201              :     //
     202              :     // If we've gotten this far, it means we have successfully allocated a SecureSession to back our next attempt. If we haven't,
     203              :     // there is a bug somewhere and we should raise attention to it by dying.
     204              :     //
     205           14 :     VerifyOrDie(mPinnedSecureSession.HasValue());
     206           14 : }
     207              : 
     208            0 : void CASEServer::OnSessionEstablishmentError(CHIP_ERROR err)
     209              : {
     210              :     MATTER_TRACE_SCOPE("OnSessionEstablishmentError", "CASEServer");
     211            0 :     ChipLogError(Inet, "CASE Session establishment failed: %" CHIP_ERROR_FORMAT, err.Format());
     212              : 
     213              :     MATTER_TRACE_SCOPE("CASEFail", "CASESession");
     214            0 :     PrepareForSessionEstablishment();
     215            0 : }
     216              : 
     217            7 : void CASEServer::OnSessionEstablished(const SessionHandle & session)
     218              : {
     219              :     MATTER_TRACE_SCOPE("OnSessionEstablished", "CASEServer");
     220            7 :     ChipLogProgress(Inet, "CASE Session established to peer: " ChipLogFormatScopedNodeId,
     221              :                     ChipLogValueScopedNodeId(session->GetPeer()));
     222            7 :     PrepareForSessionEstablishment(session->GetPeer());
     223            7 : }
     224              : 
     225            1 : CHIP_ERROR CASEServer::SendBusyStatusReport(Messaging::ExchangeContext * ec, System::Clock::Milliseconds16 minimumWaitTime)
     226              : {
     227              :     MATTER_TRACE_SCOPE("SendBusyStatusReport", "CASEServer");
     228            1 :     ChipLogProgress(Inet, "Already in the middle of CASE handshake, sending busy status report");
     229              : 
     230            1 :     System::PacketBufferHandle handle = Protocols::SecureChannel::StatusReport::MakeBusyStatusReportMessage(minimumWaitTime);
     231            1 :     VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
     232              : 
     233            1 :     ChipLogProgress(Inet, "Sending status report, exchange " ChipLogFormatExchange, ChipLogValueExchange(ec));
     234            1 :     return ec->SendMessage(Protocols::SecureChannel::MsgType::StatusReport, std::move(handle));
     235            1 : }
     236              : 
     237              : } // namespace chip
        

Generated by: LCOV version 2.0-1