LCOV - code coverage report
Current view: top level - credentials - LastKnownGoodTime.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 90 93 96.8 %
Date: 2024-02-15 08:20:41 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021-2022 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             : /**
      19             :  * @brief Defines a table of fabrics that have provisioned the device.
      20             :  */
      21             : 
      22             : #include "LastKnownGoodTime.h"
      23             : 
      24             : #include <lib/support/BufferWriter.h>
      25             : #include <lib/support/DefaultStorageKeyAllocator.h>
      26             : #include <lib/support/SafeInt.h>
      27             : #include <platform/ConfigurationManager.h>
      28             : 
      29             : namespace chip {
      30             : 
      31             : namespace {
      32             : // Tags for Last Known Good Time.
      33             : constexpr TLV::Tag kLastKnownGoodChipEpochSecondsTag = TLV::ContextTag(0);
      34             : } // anonymous namespace
      35             : 
      36        3088 : void LastKnownGoodTime::LogTime(const char * msg, System::Clock::Seconds32 chipEpochTime)
      37             : {
      38             : #if CHIP_PROGRESS_LOGGING
      39        3088 :     char buf[26] = { 0 }; // strlen("00000-000-000T000:000:000") == 25
      40             :     uint16_t year;
      41             :     uint8_t month;
      42             :     uint8_t day;
      43             :     uint8_t hour;
      44             :     uint8_t minute;
      45             :     uint8_t second;
      46        3088 :     ChipEpochToCalendarTime(chipEpochTime.count(), year, month, day, hour, minute, second);
      47        3088 :     snprintf(buf, sizeof(buf), "%04u-%02u-%02uT%02u:%02u:%02u", year, month, day, hour, minute, second);
      48        3088 :     ChipLogProgress(TimeService, "%s%s", StringOrNullMarker(msg), buf);
      49             : #else
      50             :     IgnoreUnusedVariable(msg);
      51             :     IgnoreUnusedVariable(chipEpochTime);
      52             : #endif
      53        3088 : }
      54             : 
      55         755 : CHIP_ERROR LastKnownGoodTime::LoadLastKnownGoodChipEpochTime(System::Clock::Seconds32 & lastKnownGoodChipEpochTime) const
      56             : {
      57             :     uint8_t buf[LastKnownGoodTimeTLVMaxSize()];
      58         755 :     uint16_t size = sizeof(buf);
      59             :     uint32_t seconds;
      60         755 :     ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf, size));
      61         405 :     TLV::ContiguousBufferTLVReader reader;
      62         405 :     reader.Init(buf, size);
      63         405 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
      64             :     TLV::TLVType containerType;
      65         405 :     ReturnErrorOnFailure(reader.EnterContainer(containerType));
      66         405 :     ReturnErrorOnFailure(reader.Next(kLastKnownGoodChipEpochSecondsTag));
      67         405 :     ReturnErrorOnFailure(reader.Get(seconds));
      68         405 :     lastKnownGoodChipEpochTime = System::Clock::Seconds32(seconds);
      69         405 :     return CHIP_NO_ERROR;
      70             : }
      71             : 
      72         981 : CHIP_ERROR LastKnownGoodTime::StoreLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime) const
      73             : {
      74             :     uint8_t buf[LastKnownGoodTimeTLVMaxSize()];
      75         981 :     TLV::TLVWriter writer;
      76         981 :     writer.Init(buf);
      77             :     TLV::TLVType outerType;
      78         981 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
      79         981 :     ReturnErrorOnFailure(writer.Put(kLastKnownGoodChipEpochSecondsTag, lastKnownGoodChipEpochTime.count()));
      80         981 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
      81         981 :     const auto length = writer.GetLengthWritten();
      82         981 :     VerifyOrReturnError(CanCastTo<uint16_t>(length), CHIP_ERROR_BUFFER_TOO_SMALL);
      83         981 :     ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf,
      84             :                                                    static_cast<uint16_t>(length)));
      85         981 :     return CHIP_NO_ERROR;
      86             : }
      87             : 
      88         367 : CHIP_ERROR LastKnownGoodTime::Init(PersistentStorageDelegate * storage)
      89             : {
      90         367 :     CHIP_ERROR err = CHIP_NO_ERROR;
      91         367 :     mStorage       = storage;
      92             :     // 3.5.6.1 Last Known Good UTC Time:
      93             :     //
      94             :     // "A Node’s initial out-of-box Last Known Good UTC time SHALL be the
      95             :     // compile-time of the firmware."
      96             :     System::Clock::Seconds32 buildTime;
      97         367 :     SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime));
      98             :     System::Clock::Seconds32 storedLastKnownGoodChipEpochTime;
      99         367 :     err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime);
     100         367 :     VerifyOrExit(err == CHIP_NO_ERROR || err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND, ;);
     101         367 :     if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     102             :     {
     103         348 :         ChipLogProgress(TimeService, "Last Known Good Time: [unknown]");
     104             :     }
     105             :     else
     106             :     {
     107          19 :         LogTime("Last Known Good Time: ", storedLastKnownGoodChipEpochTime);
     108             :     }
     109         367 :     if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND || buildTime > storedLastKnownGoodChipEpochTime)
     110             :     {
     111             :         // If we have no value in persistence, or the firmware build time is
     112             :         // later than the value in persistence, set last known good time to the
     113             :         // firmware build time and write back.
     114         348 :         LogTime("Setting Last Known Good Time to firmware build time ", buildTime);
     115         348 :         mLastKnownGoodChipEpochTime.SetValue(buildTime);
     116         348 :         SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(buildTime));
     117             :     }
     118             :     else
     119             :     {
     120          19 :         mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime);
     121             :     }
     122         367 : exit:
     123         367 :     if (err != CHIP_NO_ERROR)
     124             :     {
     125           0 :         ChipLogError(TimeService, "Failed to init Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
     126             :     }
     127         367 :     return err;
     128             : }
     129             : 
     130           8 : CHIP_ERROR LastKnownGoodTime::SetLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime,
     131             :                                                             System::Clock::Seconds32 notBefore)
     132             : {
     133           8 :     CHIP_ERROR err = CHIP_NO_ERROR;
     134           8 :     VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
     135           8 :     LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
     136           8 :     LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime);
     137             : 
     138             :     // Verify that the passed value is not earlier than the firmware build time.
     139             :     System::Clock::Seconds32 buildTime;
     140           8 :     SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime));
     141           8 :     VerifyOrExit(lastKnownGoodChipEpochTime >= buildTime, err = CHIP_ERROR_INVALID_ARGUMENT);
     142             :     // Verify that the passed value is not earlier than the passed NotBefore time.
     143           5 :     VerifyOrExit(lastKnownGoodChipEpochTime >= notBefore, err = CHIP_ERROR_INVALID_ARGUMENT);
     144             : 
     145             :     // Passed value is valid.  Capture it and write back to persistence.
     146             :     //
     147             :     // Note that we are purposefully overwriting any previous last known
     148             :     // good time that may have been stored as part of a fail-safe context.
     149             :     // This is intentional: we don't promise not to change last known good
     150             :     // time during the fail-safe timer.  For instance, if the platform has a
     151             :     // new, better time source, it is legal to capture it.  If we do, we should
     152             :     // both overwrite last known good time and discard the previous value stored
     153             :     // for fail safe recovery, as together these comprise a transaction.  By
     154             :     // overwriting both, we are fully superseding that transaction with our
     155             :     // own, which does not to have a revert feature.
     156           4 :     SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime));
     157           4 :     mLastKnownGoodChipEpochTime.SetValue(lastKnownGoodChipEpochTime);
     158           8 : exit:
     159           8 :     if (err != CHIP_NO_ERROR)
     160             :     {
     161           4 :         ChipLogError(TimeService, "Failed to update Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
     162             :     }
     163             :     else
     164             :     {
     165           4 :         LogTime("Updating Last Known Good Time to ", lastKnownGoodChipEpochTime);
     166             :     }
     167           8 :     return err;
     168             : }
     169             : 
     170         640 : CHIP_ERROR LastKnownGoodTime::UpdatePendingLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime)
     171             : {
     172         640 :     CHIP_ERROR err = CHIP_NO_ERROR;
     173         640 :     VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
     174         640 :     LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
     175         640 :     LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime);
     176         640 :     if (lastKnownGoodChipEpochTime > mLastKnownGoodChipEpochTime.Value())
     177             :     {
     178          18 :         LogTime("Updating pending Last Known Good Time to ", lastKnownGoodChipEpochTime);
     179          18 :         mLastKnownGoodChipEpochTime.SetValue(lastKnownGoodChipEpochTime);
     180             :     }
     181             :     else
     182             :     {
     183         622 :         ChipLogProgress(TimeService, "Retaining current Last Known Good Time");
     184             :     }
     185           0 : exit:
     186         640 :     return err;
     187             : }
     188             : 
     189         629 : CHIP_ERROR LastKnownGoodTime::CommitPendingLastKnownGoodChipEpochTime()
     190             : {
     191         629 :     CHIP_ERROR err = CHIP_NO_ERROR;
     192         629 :     VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
     193         629 :     LogTime("Committing Last Known Good Time to storage: ", mLastKnownGoodChipEpochTime.Value());
     194         629 :     SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(mLastKnownGoodChipEpochTime.Value()));
     195         629 : exit:
     196         629 :     if (err != CHIP_NO_ERROR)
     197             :     {
     198           0 :         ChipLogError(TimeService, "Failed to commit Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
     199             :     }
     200         629 :     return err;
     201             : }
     202             : 
     203         388 : CHIP_ERROR LastKnownGoodTime::RevertPendingLastKnownGoodChipEpochTime()
     204             : {
     205         388 :     CHIP_ERROR err = CHIP_NO_ERROR;
     206             :     System::Clock::Seconds32 storedLastKnownGoodChipEpochTime;
     207         388 :     VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
     208         388 :     LogTime("Pending Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
     209         388 :     SuccessOrExit(err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime));
     210         386 :     LogTime("Previous Last Known Good Time: ", storedLastKnownGoodChipEpochTime);
     211         386 :     mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime);
     212         388 : exit:
     213         388 :     if (err != CHIP_NO_ERROR)
     214             :     {
     215             :         // We do not expect to arrive here unless the kvstore broke or some
     216             :         // other code removed our value from persistence.  However, clearing the
     217             :         // in-memory value is the right thing to do for all cases.  This will
     218             :         // prevent other code from consuming an invalid time.
     219           2 :         ChipLogError(TimeService,
     220             :                      "Clearing Last Known Good Time; failed to load a previous value from persistence: %" CHIP_ERROR_FORMAT,
     221             :                      err.Format());
     222           2 :         mLastKnownGoodChipEpochTime.ClearValue();
     223             :     }
     224             :     else
     225             :     {
     226         386 :         ChipLogProgress(TimeService, "Reverted Last Known Good Time to previous value");
     227             :     }
     228         388 :     return err;
     229             : }
     230             : 
     231             : } // namespace chip

Generated by: LCOV version 1.14