| /* |
| * |
| * Copyright (c) 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. |
| */ |
| |
| /** |
| * @file |
| * Open IoT SDK key-value storage base on flash TDBStore. |
| */ |
| |
| #include <platform/internal/CHIPDeviceLayerInternal.h> |
| |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CHIPMemString.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| #include <platform/openiotsdk/KVBlockDeviceStore.h> |
| #include <platform/openiotsdk/OpenIoTSDKPort.h> |
| |
| namespace chip { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| // *** CAUTION ***: Changing the names or namespaces of these values will *break* existing devices. |
| |
| // NVS namespaces used to store device configuration information. |
| #define CHIP_CONFIG_FACTORY_PREFIX "chip-factory-" |
| #define CHIP_CONFIG_CONFIG_PREFIX "chip-config-" |
| #define CHIP_CONFIG_COUNTER_PREFIX "chip-counters-" |
| |
| #define FACTORY_KEY(key) CHIP_CONFIG_FACTORY_PREFIX key |
| #define CONFIG_KEY(key) CHIP_CONFIG_CONFIG_PREFIX key |
| #define COUNTER_KEY(key) CHIP_CONFIG_COUNTER_PREFIX key |
| |
| // Keys stored in the chip-factory namespace |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_SerialNum = { FACTORY_KEY("serial-num") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_MfrDeviceId = { FACTORY_KEY("device-id") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_MfrDeviceCert = { FACTORY_KEY("device-cert") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_MfrDeviceICACerts = { FACTORY_KEY("device-ca-certs") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_MfrDevicePrivateKey = { FACTORY_KEY("device-key") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_HardwareVersion = { FACTORY_KEY("hardware-ver") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_ManufacturingDate = { FACTORY_KEY("mfg-date") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_SetupPinCode = { FACTORY_KEY("pin-code") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_SetupDiscriminator = { FACTORY_KEY("discriminator") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_Spake2pIterationCount = { FACTORY_KEY("iteration-count") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_Spake2pSalt = { FACTORY_KEY("salt") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_Spake2pVerifier = { FACTORY_KEY("verifier") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_VendorId = { FACTORY_KEY("vendor-id") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_ProductId = { FACTORY_KEY("product-id") }; |
| |
| // Keys stored in the chip-config namespace |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_ServiceConfig = { CONFIG_KEY("service-config") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_PairedAccountId = { CONFIG_KEY("account-id") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_ServiceId = { CONFIG_KEY("service-id") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_LastUsedEpochKeyId = { CONFIG_KEY("last-ek-id") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_FailSafeArmed = { CONFIG_KEY("fail-safe-armed") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_WiFiStationSecType = { CONFIG_KEY("sta-sec-type") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_RegulatoryLocation = { CONFIG_KEY("regulatory-location") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_CountryCode = { CONFIG_KEY("country-code") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_LocationCapability = { CONFIG_KEY("location-capability") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kConfigKey_UniqueId = { CONFIG_KEY("unique-id") }; |
| |
| // Keys stored in the Chip-counters namespace |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kCounterKey_RebootCount = { COUNTER_KEY("reboot-count") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kCounterKey_UpTime = { COUNTER_KEY("up-time") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kCounterKey_TotalOperationalHours = { COUNTER_KEY("total-operational-hours") }; |
| const KVBlockDeviceStore::Key KVBlockDeviceStore::kCounterKey_BootReason = { COUNTER_KEY("boot-reason") }; |
| |
| using iotsdk::storage::kv_status; |
| using iotsdk::storage::KVStore; |
| |
| iotsdk::storage::TDBStore * KVBlockDeviceStore::tdb = nullptr; |
| |
| CHIP_ERROR KVBlockDeviceStore::Init(void) |
| { |
| if (tdb) |
| { |
| return CHIP_NO_ERROR; |
| } |
| |
| // Create a TDBStore using the underlying storage |
| tdb = new iotsdk::storage::TDBStore(GetBlockDevice()); |
| |
| if (!tdb) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| // KVStore uses dual stage initialization so we can handle any errors |
| // Call the `init` method to setup the TDBStore |
| kv_status err = tdb->init(); |
| if (err != kv_status::OK) |
| { |
| delete tdb; |
| // zero tdb as we use it keep track of init |
| tdb = nullptr; |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::Shutdown(void) |
| { |
| if (!tdb) |
| { |
| return CHIP_NO_ERROR; |
| } |
| |
| tdb->deinit(); |
| delete tdb; |
| tdb = nullptr; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValue(Key key, bool & val) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (!ConfigValueExists(key)) |
| { |
| return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; |
| } |
| |
| size_t actual_size = 0; |
| kv_status err = tdb->get(key, reinterpret_cast<void *>(&val), sizeof(val), &actual_size); |
| |
| if (err != kv_status::OK) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (actual_size != sizeof(val)) |
| { |
| return CHIP_ERROR_BAD_REQUEST; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValue(Key key, uint32_t & val) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (!ConfigValueExists(key)) |
| { |
| return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; |
| } |
| |
| size_t actual_size = 0; |
| kv_status err = tdb->get(key, reinterpret_cast<void *>(&val), sizeof(val), &actual_size); |
| |
| if (err != kv_status::OK) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (actual_size != sizeof(val)) |
| { |
| return CHIP_ERROR_BAD_REQUEST; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValue(Key key, uint64_t & val) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (!ConfigValueExists(key)) |
| { |
| return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; |
| } |
| |
| size_t actual_size = 0; |
| kv_status err = tdb->get(key, &val, sizeof(val), &actual_size); |
| |
| if (err != kv_status::OK) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (actual_size != sizeof(val)) |
| { |
| return CHIP_ERROR_BAD_REQUEST; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen) |
| { |
| CHIP_ERROR err = ReadConfigValueBin(key, reinterpret_cast<uint8_t *>(buf), bufSize, outLen); |
| // Note: The system expect the trailing null to be added. |
| if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| if (outLen >= bufSize) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| buf[outLen] = 0; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen) |
| { |
| return ReadConfigValueBin(key, buf, bufSize, outLen, 0); |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen, size_t offset) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (!ConfigValueExists(key)) |
| { |
| return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; |
| } |
| |
| KVStore::info_t info; |
| |
| kv_status err = tdb->get_info(key, &info); |
| if (err != kv_status::OK) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| err = tdb->get(key, reinterpret_cast<void *>(buf), bufSize, &outLen, offset); |
| if (err != kv_status::OK) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| if (bufSize < info.size - offset) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ReadConfigValueCounter(Key counterId, uint32_t & value) |
| { |
| char key[50] = { 0 }; |
| auto err = ConstructCounterKey(counterId, key, sizeof(key)); |
| if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| |
| return ReadConfigValue(key, value); |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValue(Key key, bool val) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| kv_status err = tdb->set(key, reinterpret_cast<const void *>(&val), sizeof(val), 0); |
| return err == kv_status::OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValue(Key key, uint32_t val) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| kv_status err = tdb->set(key, reinterpret_cast<const void *>(&val), sizeof(val), 0); |
| return err == kv_status::OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValue(Key key, uint64_t val) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| kv_status err = tdb->set(key, reinterpret_cast<void *>(&val), sizeof(val), 0); |
| return err == kv_status::OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValueStr(Key key, const char * str) |
| { |
| return WriteConfigValueBin(key, reinterpret_cast<const uint8_t *>(str), (str != nullptr) ? strlen(str) : 0); |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValueStr(Key key, const char * str, size_t strLen) |
| { |
| return WriteConfigValueBin(key, reinterpret_cast<const uint8_t *>(str), (strLen > 0) ? strLen : 1); |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| // Two different behavior: If the pointer is not null, the value is updated |
| // or create. If the pointer is null, the key is removed if it exist. |
| if (data != nullptr) |
| { |
| kv_status err = tdb->set(key, reinterpret_cast<const void *>(data), dataLen, 0); |
| return err == kv_status::OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; |
| } |
| else if (ConfigValueExists(key)) |
| { |
| return ClearConfigValue(key); |
| } |
| else |
| { |
| // Nothing to do, data is null and the key does not exist. |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::WriteConfigValueCounter(Key counterId, uint32_t value) |
| { |
| char key[50] = { 0 }; |
| auto err = ConstructCounterKey(counterId, key, sizeof(key)); |
| if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| |
| return WriteConfigValue(key, value); |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ClearConfigValue(Key key) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| kv_status err = tdb->remove(key); |
| if (err == kv_status::ITEM_NOT_FOUND) |
| { |
| return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; |
| } |
| return err == kv_status::OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::FactoryResetConfig() |
| { |
| // tdb->reset is not used, we want to preserve other setting and factory |
| // configuration |
| auto err = ClearNamespace(CHIP_CONFIG_CONFIG_PREFIX); |
| if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| return ClearNamespace(CHIP_CONFIG_COUNTER_PREFIX); |
| } |
| |
| void KVBlockDeviceStore::RunConfigUnitTest() |
| { |
| // Run common unit test. |
| ::chip::DeviceLayer::Internal::RunConfigUnitTest<KVBlockDeviceStore>(); |
| } |
| |
| bool KVBlockDeviceStore::ConfigValueExists(Key key) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return false; |
| } |
| |
| KVStore::info_t info; |
| kv_status err = tdb->get_info(key, &info); |
| return err == kv_status::OK ? true : false; |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ConstructCounterKey(Key id, char * buf, size_t bufSize) |
| { |
| auto length = snprintf(buf, bufSize - 1, CHIP_CONFIG_COUNTER_PREFIX "%s", id); |
| if (length < 0) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| else if ((size_t) length > (bufSize - 1)) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| else |
| { |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| CHIP_ERROR KVBlockDeviceStore::ClearNamespace(const char * ns) |
| { |
| if (Init() != CHIP_NO_ERROR) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| iotsdk::storage::KVStore::iterator_t it; |
| char key[iotsdk::storage::KVStore::MAX_KEY_SIZE]; |
| kv_status err = tdb->iterator_open(&it, ns); |
| if (err != kv_status::OK) |
| { |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| while (true) |
| { |
| err = tdb->iterator_next(it, key, sizeof(key)); |
| if (err == kv_status::OK) |
| { |
| tdb->remove(key); |
| memset(key, 0, sizeof(key)); |
| } |
| else if (err == kv_status::ITEM_NOT_FOUND) |
| { |
| break; |
| } |
| else |
| { |
| (void) tdb->iterator_close(it); |
| return CHIP_ERROR_INTERNAL; |
| } |
| } |
| |
| err = tdb->iterator_close(it); |
| return err == kv_status::OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; |
| } |
| |
| KVBlockDeviceStoreKeyBuilder::KVBlockDeviceStoreKeyBuilder(const char * key) |
| { |
| // Check sign by sign if key contains illegal characters |
| // Each illegal character will be replaced by '!' + capital encoded letter value |
| char * out = buffer + strlen(buffer); |
| char * illegal_ptr; |
| while ((out < buffer + sizeof(buffer) - 3) && *key) // 2 chars for potential illegal char + 1 for \0 |
| { |
| illegal_ptr = strchr(illegalCharacters, *key); |
| if (illegal_ptr) |
| { |
| *out++ = '!'; |
| *out++ = static_cast<char>('A' + (int) (illegal_ptr - illegalCharacters)); |
| } |
| else |
| { |
| *out++ = *key; |
| } |
| key++; |
| } |
| valid = true; |
| } |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace chip |