/* * * Copyright (c) 2020-2022 Project CHIP Authors * Copyright (c) 2019-2020 Google LLC. * Copyright (c) 2018 Nest Labs, Inc. * All rights reserved. * * 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. */ /** * @file * Utilities for interacting with multiple file partitions and maps * key-value config calls to the correct partition. */ #include #include #include #include #include #include namespace chip { namespace DeviceLayer { namespace Internal { static ChipLinuxStorage gChipLinuxFactoryStorage; static ChipLinuxStorage gChipLinuxConfigStorage; static ChipLinuxStorage gChipLinuxCountersStorage; // *** CAUTION ***: Changing the names or namespaces of these values will *break* existing devices. // NVS namespaces used to store device configuration information. const char PosixConfig::kConfigNamespace_ChipFactory[] = "chip-factory"; const char PosixConfig::kConfigNamespace_ChipConfig[] = "chip-config"; const char PosixConfig::kConfigNamespace_ChipCounters[] = "chip-counters"; // Keys stored in the Chip-factory namespace const PosixConfig::Key PosixConfig::kConfigKey_SerialNum = { kConfigNamespace_ChipFactory, "serial-num" }; const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceId = { kConfigNamespace_ChipFactory, "device-id" }; const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceCert = { kConfigNamespace_ChipFactory, "device-cert" }; const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceICACerts = { kConfigNamespace_ChipFactory, "device-ca-certs" }; const PosixConfig::Key PosixConfig::kConfigKey_MfrDevicePrivateKey = { kConfigNamespace_ChipFactory, "device-key" }; const PosixConfig::Key PosixConfig::kConfigKey_HardwareVersion = { kConfigNamespace_ChipFactory, "hardware-ver" }; const PosixConfig::Key PosixConfig::kConfigKey_ManufacturingDate = { kConfigNamespace_ChipFactory, "mfg-date" }; const PosixConfig::Key PosixConfig::kConfigKey_SetupPinCode = { kConfigNamespace_ChipFactory, "pin-code" }; const PosixConfig::Key PosixConfig::kConfigKey_SetupDiscriminator = { kConfigNamespace_ChipFactory, "discriminator" }; const PosixConfig::Key PosixConfig::kConfigKey_Spake2pIterationCount = { kConfigNamespace_ChipFactory, "iteration-count" }; const PosixConfig::Key PosixConfig::kConfigKey_Spake2pSalt = { kConfigNamespace_ChipFactory, "salt" }; const PosixConfig::Key PosixConfig::kConfigKey_Spake2pVerifier = { kConfigNamespace_ChipFactory, "verifier" }; const PosixConfig::Key PosixConfig::kConfigKey_VendorId = { kConfigNamespace_ChipFactory, "vendor-id" }; const PosixConfig::Key PosixConfig::kConfigKey_ProductId = { kConfigNamespace_ChipFactory, "product-id" }; // Keys stored in the Chip-config namespace const PosixConfig::Key PosixConfig::kConfigKey_FabricId = { kConfigNamespace_ChipConfig, "fabric-id" }; const PosixConfig::Key PosixConfig::kConfigKey_ServiceConfig = { kConfigNamespace_ChipConfig, "service-config" }; const PosixConfig::Key PosixConfig::kConfigKey_PairedAccountId = { kConfigNamespace_ChipConfig, "account-id" }; const PosixConfig::Key PosixConfig::kConfigKey_ServiceId = { kConfigNamespace_ChipConfig, "service-id" }; const PosixConfig::Key PosixConfig::kConfigKey_FabricSecret = { kConfigNamespace_ChipConfig, "fabric-secret" }; const PosixConfig::Key PosixConfig::kConfigKey_GroupKeyIndex = { kConfigNamespace_ChipConfig, "group-key-index" }; const PosixConfig::Key PosixConfig::kConfigKey_LastUsedEpochKeyId = { kConfigNamespace_ChipConfig, "last-ek-id" }; const PosixConfig::Key PosixConfig::kConfigKey_FailSafeArmed = { kConfigNamespace_ChipConfig, "fail-safe-armed" }; const PosixConfig::Key PosixConfig::kConfigKey_RegulatoryLocation = { kConfigNamespace_ChipConfig, "regulatory-location" }; const PosixConfig::Key PosixConfig::kConfigKey_CountryCode = { kConfigNamespace_ChipConfig, "country-code" }; const PosixConfig::Key PosixConfig::kConfigKey_Breadcrumb = { kConfigNamespace_ChipConfig, "breadcrumb" }; const PosixConfig::Key PosixConfig::kConfigKey_LocationCapability = { kConfigNamespace_ChipConfig, "location-capability" }; const PosixConfig::Key PosixConfig::kConfigKey_UniqueId = { kConfigNamespace_ChipFactory, "unique-id" }; // Keys stored in the Chip-counters namespace const PosixConfig::Key PosixConfig::kCounterKey_RebootCount = { kConfigNamespace_ChipCounters, "reboot-count" }; const PosixConfig::Key PosixConfig::kCounterKey_UpTime = { kConfigNamespace_ChipCounters, "up-time" }; const PosixConfig::Key PosixConfig::kCounterKey_TotalOperationalHours = { kConfigNamespace_ChipCounters, "total-operational-hours" }; const PosixConfig::Key PosixConfig::kCounterKey_BootReason = { kConfigNamespace_ChipCounters, "boot-reason" }; // Prefix used for NVS keys that contain Chip group encryption keys. const char PosixConfig::kGroupKeyNamePrefix[] = "gk-"; ChipLinuxStorage * PosixConfig::GetStorageForNamespace(Key key) { if (strcmp(key.Namespace, kConfigNamespace_ChipFactory) == 0) return &gChipLinuxFactoryStorage; if (strcmp(key.Namespace, kConfigNamespace_ChipConfig) == 0) return &gChipLinuxConfigStorage; if (strcmp(key.Namespace, kConfigNamespace_ChipCounters) == 0) return &gChipLinuxCountersStorage; return nullptr; } CHIP_ERROR PosixConfig::Init() { return PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH); } CHIP_ERROR PosixConfig::ReadConfigValue(Key key, bool & val) { CHIP_ERROR err; ChipLinuxStorage * storage; uint32_t intVal; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ReadValue(key.Name, intVal); if (err == CHIP_ERROR_KEY_NOT_FOUND) { err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } SuccessOrExit(err); val = (intVal != 0); exit: return err; } CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint16_t & val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ReadValue(key.Name, val); if (err == CHIP_ERROR_KEY_NOT_FOUND) { err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } SuccessOrExit(err); exit: return err; } CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint32_t & val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ReadValue(key.Name, val); if (err == CHIP_ERROR_KEY_NOT_FOUND) { err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } SuccessOrExit(err); exit: return err; } CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint64_t & val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); // Special case the MfrDeviceId value, optionally allowing it to be read as a blob containing // a 64-bit big-endian integer, instead of a u64 value. if (key == kConfigKey_MfrDeviceId) { uint8_t deviceIdBytes[sizeof(uint64_t)]; size_t deviceIdLen = sizeof(deviceIdBytes); size_t deviceIdOutLen; err = storage->ReadValueBin(key.Name, deviceIdBytes, deviceIdLen, deviceIdOutLen); if (err == CHIP_NO_ERROR) { VerifyOrExit(deviceIdOutLen == sizeof(deviceIdBytes), err = CHIP_ERROR_INCORRECT_STATE); val = Encoding::BigEndian::Get64(deviceIdBytes); ExitNow(); } } err = storage->ReadValue(key.Name, val); if (err == CHIP_ERROR_KEY_NOT_FOUND) { err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } SuccessOrExit(err); exit: return err; } CHIP_ERROR PosixConfig::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ReadValueStr(key.Name, buf, bufSize, outLen); if (err == CHIP_ERROR_KEY_NOT_FOUND) { outLen = 0; err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } else if (err == CHIP_ERROR_BUFFER_TOO_SMALL) { err = (buf == nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL; } SuccessOrExit(err); exit: return err; } CHIP_ERROR PosixConfig::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ReadValueBin(key.Name, buf, bufSize, outLen); if (err == CHIP_ERROR_KEY_NOT_FOUND) { outLen = 0; err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } else if (err == CHIP_ERROR_BUFFER_TOO_SMALL) { err = (buf == nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL; } SuccessOrExit(err); exit: return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, bool val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->WriteValue(key.Name, val); SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %s", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), val ? "true" : "false"); exit: return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint16_t val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->WriteValue(key.Name, val); SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %u (0x%X)", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), val, val); exit: return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint32_t val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->WriteValue(key.Name, val); SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu32 " (0x%" PRIX32 ")", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), val, val); exit: return err; } CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint64_t val) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->WriteValue(key.Name, val); SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu64 " (0x%" PRIX64 ")", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), val, val); exit: return err; } CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str) { CHIP_ERROR err; ChipLinuxStorage * storage; if (str != nullptr) { storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->WriteValueStr(key.Name, str); SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS set: %s/%s = \"%s\"", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), str); } else { err = ClearConfigValue(key); SuccessOrExit(err); } exit: return err; } CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str, size_t strLen) { #if CHIP_CONFIG_MEMORY_MGMT_MALLOC CHIP_ERROR err; char * strCopy = nullptr; if (str != nullptr) { strCopy = strndup(str, strLen); VerifyOrExit(strCopy != nullptr, err = CHIP_ERROR_NO_MEMORY); } err = PosixConfig::WriteConfigValueStr(key, strCopy); exit: if (strCopy != nullptr) { free(strCopy); } return err; #else #error "Unsupported CHIP_CONFIG_MEMORY_MGMT configuration" #endif } CHIP_ERROR PosixConfig::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) { CHIP_ERROR err; ChipLinuxStorage * storage; if (data != nullptr) { storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->WriteValueBin(key.Name, data, dataLen); SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS set: %s/%s = (blob length %u)", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), static_cast(dataLen)); } else { err = ClearConfigValue(key); SuccessOrExit(err); } exit: return err; } CHIP_ERROR PosixConfig::ClearConfigValue(Key key) { CHIP_ERROR err; ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ClearValue(key.Name); if (err == CHIP_ERROR_KEY_NOT_FOUND) { ExitNow(err = CHIP_NO_ERROR); } SuccessOrExit(err); // Commit the value to the persistent store. err = storage->Commit(); SuccessOrExit(err); ChipLogProgress(DeviceLayer, "NVS erase: %s/%s", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name)); exit: return err; } bool PosixConfig::ConfigValueExists(Key key) { ChipLinuxStorage * storage; storage = GetStorageForNamespace(key); if (storage == nullptr) return false; return storage->HasValue(key.Name); } CHIP_ERROR PosixConfig::EnsureNamespace(const char * ns) { CHIP_ERROR err = CHIP_NO_ERROR; ChipLinuxStorage * storage = nullptr; if (strcmp(ns, kConfigNamespace_ChipFactory) == 0) { storage = &gChipLinuxFactoryStorage; err = storage->Init(CHIP_DEFAULT_FACTORY_PATH); } else if (strcmp(ns, kConfigNamespace_ChipConfig) == 0) { storage = &gChipLinuxConfigStorage; err = storage->Init(CHIP_DEFAULT_CONFIG_PATH); } else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0) { storage = &gChipLinuxCountersStorage; err = storage->Init(CHIP_DEFAULT_DATA_PATH); } SuccessOrExit(err); exit: return err; } CHIP_ERROR PosixConfig::ClearNamespace(const char * ns) { CHIP_ERROR err = CHIP_NO_ERROR; ChipLinuxStorage * storage = nullptr; if (strcmp(ns, kConfigNamespace_ChipConfig) == 0) { storage = &gChipLinuxConfigStorage; } else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0) { storage = &gChipLinuxCountersStorage; } VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); err = storage->ClearAll(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err)); } SuccessOrExit(err); err = storage->Commit(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err)); } exit: return err; } CHIP_ERROR PosixConfig::FactoryResetConfig() { CHIP_ERROR err = CHIP_NO_ERROR; ChipLinuxStorage * storage; ChipLogProgress(DeviceLayer, "Performing factory reset configuration"); storage = &gChipLinuxConfigStorage; if (storage == nullptr) { ChipLogError(DeviceLayer, "Storage get failed"); err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } SuccessOrExit(err); err = storage->ClearAll(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err)); } SuccessOrExit(err); err = storage->Commit(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err)); } exit: return err; } CHIP_ERROR PosixConfig::FactoryResetCounters() { CHIP_ERROR err = CHIP_NO_ERROR; ChipLinuxStorage * storage; ChipLogProgress(DeviceLayer, "Performing factory reset counters"); storage = &gChipLinuxCountersStorage; if (storage == nullptr) { ChipLogError(DeviceLayer, "Storage get failed"); err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; } SuccessOrExit(err); err = storage->ClearAll(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err)); } SuccessOrExit(err); err = storage->Commit(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err)); } exit: return err; } void PosixConfig::RunConfigUnitTest() { // Run common unit test. ::chip::DeviceLayer::Internal::RunConfigUnitTest(); } } // namespace Internal } // namespace DeviceLayer } // namespace chip