/* * * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @brief Defines a table of fabrics that have provisioned the device. */ #include "LastKnownGoodTime.h" #include #include #include #include namespace chip { namespace { // Tags for Last Known Good Time. constexpr TLV::Tag kLastKnownGoodChipEpochSecondsTag = TLV::ContextTag(0); } // anonymous namespace void LastKnownGoodTime::LogTime(const char * msg, System::Clock::Seconds32 chipEpochTime) { #if CHIP_PROGRESS_LOGGING char buf[26] = { 0 }; // strlen("00000-000-000T000:000:000") == 25 uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; ChipEpochToCalendarTime(chipEpochTime.count(), year, month, day, hour, minute, second); snprintf(buf, sizeof(buf), "%04u-%02u-%02uT%02u:%02u:%02u", year, month, day, hour, minute, second); ChipLogProgress(TimeService, "%s%s", StringOrNullMarker(msg), buf); #else IgnoreUnusedVariable(msg); IgnoreUnusedVariable(chipEpochTime); #endif } CHIP_ERROR LastKnownGoodTime::LoadLastKnownGoodChipEpochTime(System::Clock::Seconds32 & lastKnownGoodChipEpochTime) const { uint8_t buf[LastKnownGoodTimeTLVMaxSize()]; uint16_t size = sizeof(buf); uint32_t seconds; ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf, size)); TLV::ContiguousBufferTLVReader reader; reader.Init(buf, size); ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); TLV::TLVType containerType; ReturnErrorOnFailure(reader.EnterContainer(containerType)); ReturnErrorOnFailure(reader.Next(kLastKnownGoodChipEpochSecondsTag)); ReturnErrorOnFailure(reader.Get(seconds)); lastKnownGoodChipEpochTime = System::Clock::Seconds32(seconds); return CHIP_NO_ERROR; } CHIP_ERROR LastKnownGoodTime::StoreLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime) const { uint8_t buf[LastKnownGoodTimeTLVMaxSize()]; TLV::TLVWriter writer; writer.Init(buf); TLV::TLVType outerType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType)); ReturnErrorOnFailure(writer.Put(kLastKnownGoodChipEpochSecondsTag, lastKnownGoodChipEpochTime.count())); ReturnErrorOnFailure(writer.EndContainer(outerType)); const auto length = writer.GetLengthWritten(); VerifyOrReturnError(CanCastTo(length), CHIP_ERROR_BUFFER_TOO_SMALL); ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf, static_cast(length))); return CHIP_NO_ERROR; } CHIP_ERROR LastKnownGoodTime::Init(PersistentStorageDelegate * storage) { CHIP_ERROR err = CHIP_NO_ERROR; mStorage = storage; // 3.5.6.1 Last Known Good UTC Time: // // "A Node’s initial out-of-box Last Known Good UTC time SHALL be the // compile-time of the firmware." System::Clock::Seconds32 buildTime; SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime)); System::Clock::Seconds32 storedLastKnownGoodChipEpochTime; err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime); VerifyOrExit(err == CHIP_NO_ERROR || err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND, ;); if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) { ChipLogProgress(TimeService, "Last Known Good Time: [unknown]"); } else { LogTime("Last Known Good Time: ", storedLastKnownGoodChipEpochTime); } if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND || buildTime > storedLastKnownGoodChipEpochTime) { // If we have no value in persistence, or the firmware build time is // later than the value in persistence, set last known good time to the // firmware build time and write back. LogTime("Setting Last Known Good Time to firmware build time ", buildTime); mLastKnownGoodChipEpochTime.SetValue(buildTime); SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(buildTime)); } else { mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime); } exit: if (err != CHIP_NO_ERROR) { ChipLogError(TimeService, "Failed to init Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format()); } return err; } CHIP_ERROR LastKnownGoodTime::SetLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime, System::Clock::Seconds32 notBefore) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE); LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value()); LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime); // Verify that the passed value is not earlier than the firmware build time. System::Clock::Seconds32 buildTime; SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime)); VerifyOrExit(lastKnownGoodChipEpochTime >= buildTime, err = CHIP_ERROR_INVALID_ARGUMENT); // Verify that the passed value is not earlier than the passed NotBefore time. VerifyOrExit(lastKnownGoodChipEpochTime >= notBefore, err = CHIP_ERROR_INVALID_ARGUMENT); // Passed value is valid. Capture it and write back to persistence. // // Note that we are purposefully overwriting any previous last known // good time that may have been stored as part of a fail-safe context. // This is intentional: we don't promise not to change last known good // time during the fail-safe timer. For instance, if the platform has a // new, better time source, it is legal to capture it. If we do, we should // both overwrite last known good time and discard the previous value stored // for fail safe recovery, as together these comprise a transaction. By // overwriting both, we are fully superseding that transaction with our // own, which does not to have a revert feature. SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime)); mLastKnownGoodChipEpochTime.SetValue(lastKnownGoodChipEpochTime); exit: if (err != CHIP_NO_ERROR) { ChipLogError(TimeService, "Failed to update Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format()); } else { LogTime("Updating Last Known Good Time to ", lastKnownGoodChipEpochTime); } return err; } CHIP_ERROR LastKnownGoodTime::UpdatePendingLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE); LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value()); LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime); if (lastKnownGoodChipEpochTime > mLastKnownGoodChipEpochTime.Value()) { LogTime("Updating pending Last Known Good Time to ", lastKnownGoodChipEpochTime); mLastKnownGoodChipEpochTime.SetValue(lastKnownGoodChipEpochTime); } else { ChipLogProgress(TimeService, "Retaining current Last Known Good Time"); } exit: return err; } CHIP_ERROR LastKnownGoodTime::CommitPendingLastKnownGoodChipEpochTime() { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE); LogTime("Committing Last Known Good Time to storage: ", mLastKnownGoodChipEpochTime.Value()); SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(mLastKnownGoodChipEpochTime.Value())); exit: if (err != CHIP_NO_ERROR) { ChipLogError(TimeService, "Failed to commit Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format()); } return err; } CHIP_ERROR LastKnownGoodTime::RevertPendingLastKnownGoodChipEpochTime() { CHIP_ERROR err = CHIP_NO_ERROR; System::Clock::Seconds32 storedLastKnownGoodChipEpochTime; VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE); LogTime("Pending Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value()); SuccessOrExit(err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime)); LogTime("Previous Last Known Good Time: ", storedLastKnownGoodChipEpochTime); mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime); exit: if (err != CHIP_NO_ERROR) { // We do not expect to arrive here unless the kvstore broke or some // other code removed our value from persistence. However, clearing the // in-memory value is the right thing to do for all cases. This will // prevent other code from consuming an invalid time. ChipLogError(TimeService, "Clearing Last Known Good Time; failed to load a previous value from persistence: %" CHIP_ERROR_FORMAT, err.Format()); mLastKnownGoodChipEpochTime.ClearValue(); } else { ChipLogProgress(TimeService, "Reverted Last Known Good Time to previous value"); } return err; } } // namespace chip