Matter SDK Coverage Report
Current view: top level - transport - PeerMessageCounter.h (source / functions) Coverage Total Hit
Test: SHA:97920baf58e6bf4a18be41e121e08f13676f36b0 Lines: 77.4 % 137 106
Test Date: 2025-08-17 07:11:11 Functions: 79.2 % 24 19

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2021 Project CHIP Authors
       3              :  *
       4              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       5              :  *    you may not use this file except in compliance with the License.
       6              :  *    You may obtain a copy of the License at
       7              :  *
       8              :  *        http://www.apache.org/licenses/LICENSE-2.0
       9              :  *
      10              :  *    Unless required by applicable law or agreed to in writing, software
      11              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      12              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13              :  *    See the License for the specific language governing permissions and
      14              :  *    limitations under the License.
      15              :  */
      16              : 
      17              : /**
      18              :  *    @file
      19              :  *      This file defines the CHIP message counters of remote nodes.
      20              :  *
      21              :  */
      22              : #pragma once
      23              : 
      24              : #include <array>
      25              : #include <bitset>
      26              : 
      27              : #include <lib/support/Span.h>
      28              : 
      29              : namespace chip {
      30              : namespace Transport {
      31              : 
      32              : class PeerMessageCounter
      33              : {
      34              : public:
      35              :     static constexpr size_t kChallengeSize      = 8;
      36              :     static constexpr uint32_t kInitialSyncValue = 0;
      37              : 
      38       170601 :     PeerMessageCounter() : mStatus(Status::NotSynced) {}
      39       170054 :     ~PeerMessageCounter() { Reset(); }
      40              : 
      41       173530 :     void Reset()
      42              :     {
      43       173530 :         switch (mStatus)
      44              :         {
      45       170093 :         case Status::NotSynced:
      46       170093 :             break;
      47            0 :         case Status::SyncInProcess:
      48            0 :             mSyncInProcess.~SyncInProcess();
      49            0 :             break;
      50         3437 :         case Status::Synced:
      51         3437 :             mSynced.~Synced();
      52         3437 :             break;
      53              :         }
      54       173530 :         mStatus = Status::NotSynced;
      55       173530 :     }
      56              : 
      57            0 :     bool IsSynchronizing() { return mStatus == Status::SyncInProcess; }
      58            0 :     bool IsSynchronized() { return mStatus == Status::Synced; }
      59              : 
      60            0 :     void SyncStarting(FixedByteSpan<kChallengeSize> challenge)
      61              :     {
      62            0 :         VerifyOrDie(mStatus == Status::NotSynced);
      63            0 :         mStatus = Status::SyncInProcess;
      64            0 :         new (&mSyncInProcess) SyncInProcess();
      65            0 :         ::memcpy(mSyncInProcess.mChallenge.data(), challenge.data(), kChallengeSize);
      66            0 :     }
      67              : 
      68            0 :     void SyncFailed() { Reset(); }
      69              : 
      70            0 :     CHIP_ERROR VerifyChallenge(uint32_t counter, FixedByteSpan<kChallengeSize> challenge)
      71              :     {
      72            0 :         if (mStatus != Status::SyncInProcess)
      73              :         {
      74            0 :             return CHIP_ERROR_INCORRECT_STATE;
      75              :         }
      76            0 :         if (::memcmp(mSyncInProcess.mChallenge.data(), challenge.data(), kChallengeSize) != 0)
      77              :         {
      78            0 :             return CHIP_ERROR_INVALID_ARGUMENT;
      79              :         }
      80              : 
      81            0 :         mSyncInProcess.~SyncInProcess();
      82            0 :         mStatus = Status::Synced;
      83            0 :         new (&mSynced) Synced();
      84            0 :         mSynced.mMaxCounter = counter;
      85            0 :         mSynced.mWindow.reset(); // reset all bits, accept all packets in the window
      86            0 :         return CHIP_NO_ERROR;
      87              :     }
      88              : 
      89              :     /**
      90              :      * @brief Implementation of spec 4.5.4.2
      91              :      *
      92              :      * For encrypted messages of Group Session Type, any arriving message with a counter in the range
      93              :      * [(max_message_counter + 1) to (max_message_counter + 2^31 - 1)] (modulo 2^32) SHALL be considered
      94              :      * new, and cause the max_message_counter value to be updated. Messages with counters from
      95              :      * [(max_message_counter - 2^31) to (max_message_counter - MSG_COUNTER_WINDOW_SIZE - 1)] (modulo 2^
      96              :      * 32) SHALL be considered duplicate. Message counters within the range of the bitmap SHALL be
      97              :      * considered duplicate if the corresponding bit offset is set to true.
      98              :      *
      99              :      */
     100        18959 :     CHIP_ERROR VerifyGroup(uint32_t counter) const
     101              :     {
     102        18959 :         if (mStatus != Status::Synced)
     103              :         {
     104            0 :             return CHIP_ERROR_INCORRECT_STATE;
     105              :         }
     106              : 
     107        18959 :         Position pos = ClassifyWithRollover(counter);
     108        18959 :         return VerifyPositionEncrypted(pos, counter);
     109              :     }
     110              : 
     111        19549 :     CHIP_ERROR VerifyOrTrustFirstGroup(uint32_t counter)
     112              :     {
     113        19549 :         switch (mStatus)
     114              :         {
     115          590 :         case Status::NotSynced: {
     116              :             // Trust and set the counter when not synced
     117          590 :             SetCounter(counter);
     118          590 :             return CHIP_NO_ERROR;
     119              :         }
     120        18959 :         case Status::Synced: {
     121        18959 :             return VerifyGroup(counter);
     122              :         }
     123            0 :         default:
     124            0 :             VerifyOrDie(false);
     125              :             return CHIP_ERROR_INTERNAL;
     126              :         }
     127              :     }
     128              : 
     129              :     /**
     130              :      * @brief
     131              :      *    With the group counter verified and the packet MIC also verified by the secure key, we can trust the packet and adjust
     132              :      *    counter states.
     133              :      *
     134              :      * @pre counter has been verified via VerifyGroup or VerifyOrTrustFirstGroup
     135              :      */
     136         1406 :     void CommitGroup(uint32_t counter) { CommitWithRollover(counter); }
     137              : 
     138        27345 :     CHIP_ERROR VerifyEncryptedUnicast(uint32_t counter) const
     139              :     {
     140        27345 :         if (mStatus != Status::Synced)
     141              :         {
     142            0 :             return CHIP_ERROR_INCORRECT_STATE;
     143              :         }
     144              : 
     145        27345 :         Position pos = ClassifyWithoutRollover(counter);
     146        27345 :         return VerifyPositionEncrypted(pos, counter);
     147              :     }
     148              : 
     149              :     /**
     150              :      * @brief
     151              :      *    With the counter verified and the packet MIC also verified by the secure key, we can trust the packet and adjust
     152              :      *    counter states.
     153              :      *
     154              :      * @pre counter has been verified via VerifyEncryptedUnicast
     155              :      */
     156        15753 :     void CommitEncryptedUnicast(uint32_t counter) { CommitWithoutRollover(counter); }
     157              : 
     158         2987 :     CHIP_ERROR VerifyUnencrypted(uint32_t counter)
     159              :     {
     160         2987 :         switch (mStatus)
     161              :         {
     162          631 :         case Status::NotSynced: {
     163              :             // Trust and set the counter when not synced
     164          631 :             SetCounter(counter);
     165          631 :             return CHIP_NO_ERROR;
     166              :         }
     167         2356 :         case Status::Synced: {
     168         2356 :             Position pos = ClassifyWithRollover(counter);
     169         2356 :             return VerifyPositionUnencrypted(pos, counter);
     170              :         }
     171            0 :         default: {
     172            0 :             VerifyOrDie(false);
     173              :             return CHIP_ERROR_INTERNAL;
     174              :         }
     175              :         }
     176              :     }
     177              : 
     178              :     /**
     179              :      * @brief
     180              :      *    With the unencrypted counter verified we can trust the packet and adjust
     181              :      *    counter states.
     182              :      *
     183              :      * @pre counter has been verified via VerifyUnencrypted
     184              :      */
     185         1499 :     void CommitUnencrypted(uint32_t counter) { CommitWithRollover(counter); }
     186              : 
     187         3438 :     void SetCounter(uint32_t value)
     188              :     {
     189         3438 :         Reset();
     190         3438 :         mStatus = Status::Synced;
     191         3438 :         new (&mSynced) Synced();
     192         3438 :         mSynced.mMaxCounter = value;
     193         3438 :         mSynced.mWindow.reset();
     194         3438 :     }
     195              : 
     196              :     uint32_t GetCounter() const { return mSynced.mMaxCounter; }
     197              : 
     198              : private:
     199              :     // Counter position indicator with respect to our current
     200              :     // mSynced.mMaxCounter.
     201              :     enum class Position
     202              :     {
     203              :         BeforeWindow,
     204              :         InWindow,
     205              :         MaxCounter,
     206              :         FutureCounter,
     207              :     };
     208              : 
     209              :     // Classify an incoming counter value's position.  Must be used only if
     210              :     // mStatus is Status::Synced.
     211        43098 :     Position ClassifyWithoutRollover(uint32_t counter) const
     212              :     {
     213        43098 :         if (counter > mSynced.mMaxCounter)
     214              :         {
     215        31504 :             return Position::FutureCounter;
     216              :         }
     217              : 
     218        11594 :         return ClassifyNonFutureCounter(counter);
     219              :     }
     220              : 
     221              :     /**
     222              :      * Classify an incoming counter value's position for the cases when counters
     223              :      * are allowed to roll over.  Must be used only if mStatus is
     224              :      * Status::Synced.
     225              :      *
     226              :      * This can be used as the basis for implementing section 4.5.4.2 in the
     227              :      * spec:
     228              :      *
     229              :      * For encrypted messages of Group Session Type, any arriving message with a counter in the range
     230              :      * [(max_message_counter + 1) to (max_message_counter + 2^31 - 1)] (modulo 2^32) SHALL be considered
     231              :      * new, and cause the max_message_counter value to be updated. Messages with counters from
     232              :      * [(max_message_counter - 2^31) to (max_message_counter - MSG_COUNTER_WINDOW_SIZE - 1)] (modulo 2^
     233              :      * 32) SHALL be considered duplicate. Message counters within the range of the bitmap SHALL be
     234              :      * considered duplicate if the corresponding bit offset is set to true.
     235              :      */
     236        24220 :     Position ClassifyWithRollover(uint32_t counter) const
     237              :     {
     238        24220 :         uint32_t counterIncrease               = counter - mSynced.mMaxCounter;
     239        24220 :         constexpr uint32_t futureCounterWindow = (static_cast<uint32_t>(1 << 31)) - 1;
     240              : 
     241        24220 :         if (counterIncrease >= 1 && counterIncrease <= futureCounterWindow)
     242              :         {
     243         2467 :             return Position::FutureCounter;
     244              :         }
     245              : 
     246        21753 :         return ClassifyNonFutureCounter(counter);
     247              :     }
     248              : 
     249              :     /**
     250              :      * Classify a counter that's known to not be future counter.  This works
     251              :      * identically whether we are doing rollover or not.
     252              :      */
     253        33347 :     Position ClassifyNonFutureCounter(uint32_t counter) const
     254              :     {
     255        33347 :         if (counter == mSynced.mMaxCounter)
     256              :         {
     257         1807 :             return Position::MaxCounter;
     258              :         }
     259              : 
     260        31540 :         uint32_t offset = mSynced.mMaxCounter - counter;
     261        31540 :         if (offset <= CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE)
     262              :         {
     263         3273 :             return Position::InWindow;
     264              :         }
     265              : 
     266        28267 :         return Position::BeforeWindow;
     267              :     }
     268              : 
     269              :     /**
     270              :      * Given an encrypted (group or unicast) counter position and the counter
     271              :      * value, verify whether we should accept it.
     272              :      */
     273        46304 :     CHIP_ERROR VerifyPositionEncrypted(Position position, uint32_t counter) const
     274              :     {
     275        46304 :         switch (position)
     276              :         {
     277        16370 :         case Position::FutureCounter:
     278        16370 :             return CHIP_NO_ERROR;
     279         1680 :         case Position::InWindow: {
     280         1680 :             uint32_t offset = mSynced.mMaxCounter - counter;
     281         1680 :             if (mSynced.mWindow.test(offset - 1))
     282              :             {
     283          520 :                 return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     284              :             }
     285         1160 :             return CHIP_NO_ERROR;
     286              :         }
     287        28254 :         default: {
     288              :             // Equal to max counter, or before window.
     289        28254 :             return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     290              :         }
     291              :         }
     292              :     }
     293              : 
     294              :     /**
     295              :      * Given an unencrypted counter position and value, verify whether we should
     296              :      * accept it.
     297              :      */
     298         2356 :     CHIP_ERROR VerifyPositionUnencrypted(Position position, uint32_t counter) const
     299              :     {
     300         2356 :         switch (position)
     301              :         {
     302            0 :         case Position::MaxCounter:
     303            0 :             return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     304         1096 :         case Position::InWindow: {
     305         1096 :             uint32_t offset = mSynced.mMaxCounter - counter;
     306         1096 :             if (mSynced.mWindow.test(offset - 1))
     307              :             {
     308          304 :                 return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     309              :             }
     310          792 :             return CHIP_NO_ERROR;
     311              :         }
     312         1260 :         default: {
     313              :             // Future counter or before window; all of these are accepted.  The
     314              :             // before-window case is accepted because the peer may have reset
     315              :             // and is using a new randomized initial value.
     316         1260 :             return CHIP_NO_ERROR;
     317              :         }
     318              :         }
     319              :     }
     320              : 
     321         2905 :     void CommitWithRollover(uint32_t counter)
     322              :     {
     323         2905 :         Position pos = ClassifyWithRollover(counter);
     324         2905 :         CommitWithPosition(pos, counter);
     325         2905 :     }
     326              : 
     327        15753 :     void CommitWithoutRollover(uint32_t counter)
     328              :     {
     329        15753 :         Position pos = ClassifyWithoutRollover(counter);
     330        15753 :         CommitWithPosition(pos, counter);
     331        15753 :     }
     332              : 
     333              :     /**
     334              :      * Commit a counter value that is known to be at the given position with
     335              :      * respect to our max counter.
     336              :      */
     337        18658 :     void CommitWithPosition(Position position, uint32_t counter)
     338              :     {
     339        18658 :         switch (position)
     340              :         {
     341          497 :         case Position::InWindow: {
     342          497 :             uint32_t offset = mSynced.mMaxCounter - counter;
     343          497 :             mSynced.mWindow.set(offset - 1);
     344          497 :             break;
     345              :         }
     346         1220 :         case Position::MaxCounter: {
     347              :             // Nothing to do
     348         1220 :             break;
     349              :         }
     350        16941 :         default: {
     351              :             // Since we are committing, this becomes a new max-counter value.
     352        16941 :             uint32_t shift      = counter - mSynced.mMaxCounter;
     353        16941 :             mSynced.mMaxCounter = counter;
     354        16941 :             if (shift > CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE)
     355              :             {
     356         1715 :                 mSynced.mWindow.reset();
     357              :             }
     358              :             else
     359              :             {
     360        15226 :                 mSynced.mWindow <<= shift;
     361        15226 :                 mSynced.mWindow.set(shift - 1);
     362              :             }
     363        16941 :             break;
     364              :         }
     365              :         }
     366        18658 :     }
     367              : 
     368              :     enum class Status
     369              :     {
     370              :         NotSynced,     // No state associated
     371              :         SyncInProcess, // mSyncInProcess will be active
     372              :         Synced,        // mSynced will be active
     373              :     } mStatus;
     374              : 
     375              :     struct SyncInProcess
     376              :     {
     377              :         std::array<uint8_t, kChallengeSize> mChallenge;
     378              :     };
     379              : 
     380              :     struct Synced
     381              :     {
     382              :         /*
     383              :          *  Past <--                --> Future
     384              :          *          MaxCounter - 1
     385              :          *                 |
     386              :          *                 v
     387              :          *  | <-- mWindow -->|
     388              :          *  |[n]|  ...   |[0]|
     389              :          */
     390              :         uint32_t mMaxCounter = 0; // The most recent counter we have seen
     391              :         std::bitset<CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE> mWindow;
     392              :     };
     393              : 
     394              :     // We should use std::variant here when migrated to C++17
     395              :     union
     396              :     {
     397              :         SyncInProcess mSyncInProcess;
     398              :         Synced mSynced;
     399              :     };
     400              : };
     401              : 
     402              : } // namespace Transport
     403              : } // namespace chip
        

Generated by: LCOV version 2.0-1