Matter SDK Coverage Report
Current view: top level - transport - PeerMessageCounter.h (source / functions) Coverage Total Hit
Test: SHA:f84fe08d06f240e801b5d923f8a938a9938ca110 Lines: 73.7 % 137 101
Test Date: 2025-02-22 08:08:07 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       153723 :     PeerMessageCounter() : mStatus(Status::NotSynced) {}
      39       153723 :     ~PeerMessageCounter() { Reset(); }
      40              : 
      41        20741 :     void Reset()
      42              :     {
      43        20741 :         switch (mStatus)
      44              :         {
      45        19832 :         case Status::NotSynced:
      46        19832 :             break;
      47            0 :         case Status::SyncInProcess:
      48            0 :             mSyncInProcess.~SyncInProcess();
      49            0 :             break;
      50          909 :         case Status::Synced:
      51          909 :             mSynced.~Synced();
      52          909 :             break;
      53              :         }
      54        20741 :         mStatus = Status::NotSynced;
      55        20741 :     }
      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            3 :     CHIP_ERROR VerifyGroup(uint32_t counter) const
     101              :     {
     102            3 :         if (mStatus != Status::Synced)
     103              :         {
     104            0 :             return CHIP_ERROR_INCORRECT_STATE;
     105              :         }
     106              : 
     107            3 :         Position pos = ClassifyWithRollover(counter);
     108            3 :         return VerifyPositionEncrypted(pos, counter);
     109              :     }
     110              : 
     111            4 :     CHIP_ERROR VerifyOrTrustFirstGroup(uint32_t counter)
     112              :     {
     113            4 :         switch (mStatus)
     114              :         {
     115            1 :         case Status::NotSynced: {
     116              :             // Trust and set the counter when not synced
     117            1 :             SetCounter(counter);
     118            1 :             return CHIP_NO_ERROR;
     119              :         }
     120            3 :         case Status::Synced: {
     121            3 :             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            3 :     void CommitGroup(uint32_t counter) { CommitWithRollover(counter); }
     137              : 
     138         9966 :     CHIP_ERROR VerifyEncryptedUnicast(uint32_t counter) const
     139              :     {
     140         9966 :         if (mStatus != Status::Synced)
     141              :         {
     142            0 :             return CHIP_ERROR_INCORRECT_STATE;
     143              :         }
     144              : 
     145         9966 :         Position pos = ClassifyWithoutRollover(counter);
     146         9966 :         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         9958 :     void CommitEncryptedUnicast(uint32_t counter) { CommitWithoutRollover(counter); }
     157              : 
     158           98 :     CHIP_ERROR VerifyUnencrypted(uint32_t counter)
     159              :     {
     160           98 :         switch (mStatus)
     161              :         {
     162           43 :         case Status::NotSynced: {
     163              :             // Trust and set the counter when not synced
     164           43 :             SetCounter(counter);
     165           43 :             return CHIP_NO_ERROR;
     166              :         }
     167           55 :         case Status::Synced: {
     168           55 :             Position pos = ClassifyWithRollover(counter);
     169           55 :             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           98 :     void CommitUnencrypted(uint32_t counter) { CommitWithRollover(counter); }
     186              : 
     187         1515 :     void SetCounter(uint32_t value)
     188              :     {
     189         1515 :         Reset();
     190         1515 :         mStatus = Status::Synced;
     191         1515 :         new (&mSynced) Synced();
     192         1515 :         mSynced.mMaxCounter = value;
     193         1515 :         mSynced.mWindow.reset();
     194         1515 :     }
     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        19924 :     Position ClassifyWithoutRollover(uint32_t counter) const
     212              :     {
     213        19924 :         if (counter > mSynced.mMaxCounter)
     214              :         {
     215        19914 :             return Position::FutureCounter;
     216              :         }
     217              : 
     218           10 :         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          159 :     Position ClassifyWithRollover(uint32_t counter) const
     237              :     {
     238          159 :         uint32_t counterIncrease               = counter - mSynced.mMaxCounter;
     239          159 :         constexpr uint32_t futureCounterWindow = (static_cast<uint32_t>(1 << 31)) - 1;
     240              : 
     241          159 :         if (counterIncrease >= 1 && counterIncrease <= futureCounterWindow)
     242              :         {
     243          114 :             return Position::FutureCounter;
     244              :         }
     245              : 
     246           45 :         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           55 :     Position ClassifyNonFutureCounter(uint32_t counter) const
     254              :     {
     255           55 :         if (counter == mSynced.mMaxCounter)
     256              :         {
     257           50 :             return Position::MaxCounter;
     258              :         }
     259              : 
     260            5 :         uint32_t offset = mSynced.mMaxCounter - counter;
     261            5 :         if (offset <= CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE)
     262              :         {
     263            3 :             return Position::InWindow;
     264              :         }
     265              : 
     266            2 :         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         9969 :     CHIP_ERROR VerifyPositionEncrypted(Position position, uint32_t counter) const
     274              :     {
     275         9969 :         switch (position)
     276              :         {
     277         9959 :         case Position::FutureCounter:
     278         9959 :             return CHIP_NO_ERROR;
     279            2 :         case Position::InWindow: {
     280            2 :             uint32_t offset = mSynced.mMaxCounter - counter;
     281            2 :             if (mSynced.mWindow.test(offset - 1))
     282              :             {
     283            1 :                 return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     284              :             }
     285            1 :             return CHIP_NO_ERROR;
     286              :         }
     287            8 :         default: {
     288              :             // Equal to max counter, or before window.
     289            8 :             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           55 :     CHIP_ERROR VerifyPositionUnencrypted(Position position, uint32_t counter) const
     299              :     {
     300           55 :         switch (position)
     301              :         {
     302            0 :         case Position::MaxCounter:
     303            0 :             return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     304            0 :         case Position::InWindow: {
     305            0 :             uint32_t offset = mSynced.mMaxCounter - counter;
     306            0 :             if (mSynced.mWindow.test(offset - 1))
     307              :             {
     308            0 :                 return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED;
     309              :             }
     310            0 :             return CHIP_NO_ERROR;
     311              :         }
     312           55 :         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           55 :             return CHIP_NO_ERROR;
     317              :         }
     318              :         }
     319              :     }
     320              : 
     321          101 :     void CommitWithRollover(uint32_t counter)
     322              :     {
     323          101 :         Position pos = ClassifyWithRollover(counter);
     324          101 :         CommitWithPosition(pos, counter);
     325          101 :     }
     326              : 
     327         9958 :     void CommitWithoutRollover(uint32_t counter)
     328              :     {
     329         9958 :         Position pos = ClassifyWithoutRollover(counter);
     330         9958 :         CommitWithPosition(pos, counter);
     331         9958 :     }
     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        10059 :     void CommitWithPosition(Position position, uint32_t counter)
     338              :     {
     339        10059 :         switch (position)
     340              :         {
     341            1 :         case Position::InWindow: {
     342            1 :             uint32_t offset = mSynced.mMaxCounter - counter;
     343            1 :             mSynced.mWindow.set(offset - 1);
     344            1 :             break;
     345              :         }
     346           44 :         case Position::MaxCounter: {
     347              :             // Nothing to do
     348           44 :             break;
     349              :         }
     350        10014 :         default: {
     351              :             // Since we are committing, this becomes a new max-counter value.
     352        10014 :             uint32_t shift      = counter - mSynced.mMaxCounter;
     353        10014 :             mSynced.mMaxCounter = counter;
     354        10014 :             if (shift > CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE)
     355              :             {
     356          448 :                 mSynced.mWindow.reset();
     357              :             }
     358              :             else
     359              :             {
     360         9566 :                 mSynced.mWindow <<= shift;
     361         9566 :                 mSynced.mWindow.set(shift - 1);
     362              :             }
     363        10014 :             break;
     364              :         }
     365              :         }
     366        10059 :     }
     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