diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 4770c9608..122f60985 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (68) +#define OPENTHREAD_API_VERSION (69) /** * @addtogroup api-instance diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h index 4a4208a8a..cb519939e 100644 --- a/include/openthread/platform/settings.h +++ b/include/openthread/platform/settings.h @@ -51,6 +51,28 @@ extern "C" { * */ +/** + * This enumeration defines the keys of settings. + * + * Note: When adding a new setings key, if the settings corresponding to the key contains security sensitive + * information, the developer MUST add the key to the array `kCriticalKeys`. + * + */ +enum +{ + OT_SETTINGS_KEY_ACTIVE_DATASET = 0x0001, ///< Active Operational Dataset. + OT_SETTINGS_KEY_PENDING_DATASET = 0x0002, ///< Pending Operational Dataset. + OT_SETTINGS_KEY_NETWORK_INFO = 0x0003, ///< Thread network information. + OT_SETTINGS_KEY_PARENT_INFO = 0x0004, ///< Parent information. + OT_SETTINGS_KEY_CHILD_INFO = 0x0005, ///< Child information. + OT_SETTINGS_KEY_RESERVED = 0x0006, ///< Reserved (previously auto-start). + OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY = 0x0007, ///< SLAAC key to generate semantically opaque IID. + OT_SETTINGS_KEY_DAD_INFO = 0x0008, ///< Duplicate Address Detection (DAD) information. + OT_SETTINGS_KEY_OMR_PREFIX = 0x0009, ///< Off-mesh routable (OMR) prefix. + OT_SETTINGS_KEY_ON_LINK_PREFIX = 0x000a, ///< On-link prefix for infrastructure link. + OT_SETTINGS_KEY_SRP_ECDSA_KEY = 0x000b, ///< SRP client ECDSA public/private key pair. +}; + /** * Performs any initialization for the settings subsystem, if necessary. * @@ -67,6 +89,18 @@ void otPlatSettingsInit(otInstance *aInstance); */ void otPlatSettingsDeinit(otInstance *aInstance); +/** + * This function sets the critical keys that should be stored in the secure area. + * + * Note that the memory pointed by @p aKeys MUST not be released before @p aInstance is destroyed. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKeys A pointer to an array containing the list of critical keys. + * @param[in] aKeysLength The number of entries in the @p aKeys array. + * + */ +void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength); + /// Fetches the value of a setting /** This function fetches the value of the setting identified * by aKey and write it to the memory pointed to by aValue. diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp index 12a3f4aa1..10759acb5 100644 --- a/src/core/common/settings.cpp +++ b/src/core/common/settings.cpp @@ -43,6 +43,9 @@ #include "thread/mle.hpp" namespace ot { +// This array contains critical keys that should be stored in the secure area. +static const uint16_t kCriticalKeys[] = {SettingsBase::kKeyActiveDataset, SettingsBase::kKeyPendingDataset, + SettingsBase::kKeySrpEcdsaKey}; // LCOV_EXCL_START @@ -122,6 +125,11 @@ void SettingsDriver::Deinit(void) otPlatSettingsDeinit(&GetInstance()); } +void SettingsDriver::SetCriticalKeys(const uint16_t *aKeys, uint16_t aKeysLength) +{ + otPlatSettingsSetCriticalKeys(&GetInstance(), aKeys, aKeysLength); +} + otError SettingsDriver::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { return otPlatSettingsAdd(&GetInstance(), aKey, aValue, aValueLength); @@ -164,6 +172,12 @@ void SettingsDriver::Deinit(void) { } +void SettingsDriver::SetCriticalKeys(const uint16_t *aKeys, uint16_t aKeysLength) +{ + OT_UNUSED_VARIABLE(aKeys); + OT_UNUSED_VARIABLE(aKeysLength); +} + otError SettingsDriver::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { return mFlash.Add(aKey, aValue, aValueLength); @@ -194,6 +208,7 @@ void SettingsDriver::Wipe(void) void Settings::Init(void) { Get().Init(); + Get().SetCriticalKeys(kCriticalKeys, OT_ARRAY_LENGTH(kCriticalKeys)); } void Settings::Deinit(void) @@ -579,3 +594,13 @@ otError Settings::Delete(Key aKey) } } // namespace ot + +//--------------------------------------------------------------------------------------------------------------------- +// Default/weak implementation of settings platform APIs + +OT_TOOL_WEAK void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aKeys); + OT_UNUSED_VARIABLE(aKeysLength); +} diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp index e863db5d5..ede647d22 100644 --- a/src/core/common/settings.hpp +++ b/src/core/common/settings.hpp @@ -36,6 +36,8 @@ #include "openthread-core-config.h" +#include + #include "common/clearable.hpp" #include "common/encoding.hpp" #include "common/equatable.hpp" @@ -77,6 +79,15 @@ public: */ void Deinit(void); + /** + * This method sets the critical keys that should be stored in a secure area. + * + * @param[in] aKeys A pointer to an array containing the list of critical keys. + * @param[in] aKeysLength The number of entries in the @p aKeys array. + * + */ + void SetCriticalKeys(const uint16_t *aKeys, uint16_t aKeysLength); + /** * This method adds a value to @p aKey. * @@ -584,17 +595,17 @@ public: */ enum Key { - kKeyActiveDataset = 0x0001, ///< Active Operational Dataset - kKeyPendingDataset = 0x0002, ///< Pending Operational Dataset - kKeyNetworkInfo = 0x0003, ///< Thread network information - kKeyParentInfo = 0x0004, ///< Parent information - kKeyChildInfo = 0x0005, ///< Child information - kKeyReserved = 0x0006, ///< Reserved (previously auto-start) - kKeySlaacIidSecretKey = 0x0007, ///< Secret key used by SLAAC module for generating semantically opaque IID - kKeyDadInfo = 0x0008, ///< Duplicate Address Detection (DAD) information. - kKeyOmrPrefix = 0x0009, ///< Off-mesh routable (OMR) prefix. - kKeyOnLinkPrefix = 0x000a, ///< On-link prefix for infrastructure link. - kKeySrpEcdsaKey = 0x000b, ///< SRP client ECDSA public/private key pair. + kKeyActiveDataset = OT_SETTINGS_KEY_ACTIVE_DATASET, + kKeyPendingDataset = OT_SETTINGS_KEY_PENDING_DATASET, + kKeyNetworkInfo = OT_SETTINGS_KEY_NETWORK_INFO, + kKeyParentInfo = OT_SETTINGS_KEY_PARENT_INFO, + kKeyChildInfo = OT_SETTINGS_KEY_CHILD_INFO, + kKeyReserved = OT_SETTINGS_KEY_RESERVED, + kKeySlaacIidSecretKey = OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY, + kKeyDadInfo = OT_SETTINGS_KEY_DAD_INFO, + kKeyOmrPrefix = OT_SETTINGS_KEY_OMR_PREFIX, + kKeyOnLinkPrefix = OT_SETTINGS_KEY_ON_LINK_PREFIX, + kKeySrpEcdsaKey = OT_SETTINGS_KEY_SRP_ECDSA_KEY, }; protected: diff --git a/src/posix/platform/include/openthread/platform/secure_settings.h b/src/posix/platform/include/openthread/platform/secure_settings.h new file mode 100644 index 000000000..90ce5c312 --- /dev/null +++ b/src/posix/platform/include/openthread/platform/secure_settings.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file includes platform abstraction for secure non-volatile storage of settings. + */ + +#ifndef OPENTHREAD_POSIX_SECURE_SETTINGS_H_ +#define OPENTHREAD_POSIX_SECURE_SETTINGS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup plat-settings + * + * @brief + * This module includes the platform abstraction for secure non-volatile storage of settings. + * + * @{ + * + */ + +/** + * This function performs any initialization for the secure settings subsystem, if necessary. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void otPosixSecureSettingsInit(otInstance *aInstance); + +/** + * This function performs any de-initialization for the secure settings subsystem, if necessary. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void otPosixSecureSettingsDeinit(otInstance *aInstance); + +/** + * This function fetches the value of the setting identified by aKey and write it to the memory pointed to by aValue. + * It then writes the length to the integer pointed to by aValueLength. The initial value of aValueLength is the + * maximum number of bytes to be written to aValue. + * + * This function can be used to check for the existence of a key without fetching the value by setting aValue and + * aValueLength to NULL. You can also check the length of the setting without fetching it by setting only aValue + * to NULL. + * + * Note that the underlying storage implementation is not required to maintain the order of settings with multiple + * values. The order of such values MAY change after ANY write operation to the store. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the specific item to get. + * @param[out] aValue A pointer to where the value of the setting should be written. May be set to NULL if + * just testing for the presence or length of a setting. + * @param[inout] aValueLength A pointer to the length of the value. When called, this pointer should point to an + * integer containing the maximum value size that can be written to aValue. At return, + * the actual length of the setting is written. This may be set to NULL if performing + * a presence check. + * + * @retval OT_ERROR_NONE The given setting was found and fetched successfully. + * @retval OT_ERROR_NOT_FOUND The given setting was not found in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * + */ +otError otPosixSecureSettingsGet(otInstance *aInstance, + uint16_t aKey, + int aIndex, + uint8_t * aValue, + uint16_t * aValueLength); + +/** + * This function sets or replaces the value of a setting identified by aKey. If there was more than one value + * previously associated with aKey, then they are all deleted and replaced with this single entry. + * + * Calling this function successfully may cause unrelated settings with multiple values to be reordered. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the setting to change. + * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL if + * aValueLength is non-zero. + * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * + * @retval OT_ERROR_NONE The given setting was changed or staged. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @retval OT_ERROR_NO_BUFS No space remaining to store the given setting. + * + */ +otError otPosixSecureSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); + +/** + * This function adds the value to a setting identified by aKey, without replacing any existing values. + * + * Note that the underlying implementation is not required to maintain the order of the items associated with a + * specific key. The added value may be added to the end, the beginning, or even somewhere in the middle. The order + * of any pre-existing values may also change. + * + * Calling this function successfully may cause unrelated settings with multiple values to be reordered. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the setting to change. + * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL + * if aValueLength is non-zero. + * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * + * @retval OT_ERROR_NONE The given setting was added or staged to be added. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @retval OT_ERROR_NO_BUFS No space remaining to store the given setting. + * + */ +otError otPosixSecureSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); + +/** + * This function deletes a specific value from the setting identified by aKey from the secure settings store. + * + * Note that the underlying implementation is not required to maintain the order of the items associated with a + * specific key. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this aKey will be removed. + * + * @retval OT_ERROR_NONE The given key and index was found and removed successfully. + * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * + */ +otError otPosixSecureSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex); + +/** + * This function deletes all settings from the secure settings store, resetting it to its initial factory state. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void otPosixSecureSettingsWipe(otInstance *aInstance); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_POSIX_SECURE_SETTINGS_H_ diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h index 713c97fe5..5d5552799 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -140,6 +140,17 @@ #define OPENTHREAD_POSIX_CONFIG_MAX_MULTICAST_FORWARDING_CACHE_TABLE (OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS * 10) #endif +/** + * @def OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + * + * Define as 1 to enable the secure settings. When defined to 1, the platform MUST implement the otPosixSecureSetting* + * APIs defined in 'src/posix/platform/include/openthread/platform/secure_settings.h'. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE +#define OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE 0 +#endif + #ifdef __APPLE__ /** diff --git a/src/posix/platform/settings.cpp b/src/posix/platform/settings.cpp index ec6aa7984..c2750ffe4 100644 --- a/src/posix/platform/settings.cpp +++ b/src/posix/platform/settings.cpp @@ -47,6 +47,9 @@ #include #include #include +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE +#include +#endif #include "common/code_utils.hpp" #include "common/encoding.hpp" @@ -57,6 +60,34 @@ static int sSettingsFd = -1; static otError platformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd); +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE +static const uint16_t *sKeys = nullptr; +static uint16_t sKeysLength = 0; + +void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + sKeys = aKeys; + sKeysLength = aKeysLength; +} + +static bool isCriticalKey(uint16_t aKey) +{ + bool ret = false; + + VerifyOrExit(sKeys != nullptr); + + for (uint16_t i = 0; i < sKeysLength; i++) + { + VerifyOrExit(aKey != sKeys[i], ret = true); + } + +exit: + return ret; +} +#endif + static void getSettingsFileName(otInstance *aInstance, char aFileName[kMaxFileNameSize], bool aSwap) { const char *offset = getenv("PORT_OFFSET"); @@ -137,6 +168,10 @@ void otPlatSettingsInit(otInstance *aInstance) { otError error = OT_ERROR_NONE; +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + otPosixSecureSettingsInit(aInstance); +#endif + { struct stat st; @@ -182,6 +217,10 @@ void otPlatSettingsDeinit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + otPosixSecureSettingsDeinit(aInstance); +#endif + assert(sSettingsFd != -1); VerifyOrDie(close(sSettingsFd) == 0, OT_EXIT_ERROR_ERRNO); } @@ -194,6 +233,13 @@ otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint const off_t size = lseek(sSettingsFd, 0, SEEK_END); off_t offset = lseek(sSettingsFd, 0, SEEK_SET); +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + if (isCriticalKey(aKey)) + { + ExitNow(error = otPosixSecureSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength)); + } +#endif + VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE); while (offset < size) @@ -245,7 +291,15 @@ exit: otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { - int swapFd = -1; + int swapFd = -1; + otError error = OT_ERROR_NONE; + +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + if (isCriticalKey(aKey)) + { + ExitNow(error = otPosixSecureSettingsSet(aInstance, aKey, aValue, aValueLength)); + } +#endif switch (platformSettingsDelete(aInstance, aKey, -1, &swapFd)) { @@ -265,15 +319,26 @@ otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *a swapPersist(aInstance, swapFd); - return OT_ERROR_NONE; +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE +exit: +#endif + return error; } otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { OT_UNUSED_VARIABLE(aInstance); - off_t size = lseek(sSettingsFd, 0, SEEK_END); - int swapFd = swapOpen(aInstance); + otError error = OT_ERROR_NONE; + off_t size = lseek(sSettingsFd, 0, SEEK_END); + int swapFd = swapOpen(aInstance); + +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + if (isCriticalKey(aKey)) + { + ExitNow(error = otPosixSecureSettingsAdd(aInstance, aKey, aValue, aValueLength)); + } +#endif if (size > 0) { @@ -288,12 +353,28 @@ otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *a swapPersist(aInstance, swapFd); - return OT_ERROR_NONE; +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE +exit: +#endif + return error; } otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) { - return platformSettingsDelete(aInstance, aKey, aIndex, nullptr); + otError error; + +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + if (isCriticalKey(aKey)) + { + error = otPosixSecureSettingsDelete(aInstance, aKey, aIndex); + } + else +#endif + { + error = platformSettingsDelete(aInstance, aKey, aIndex, nullptr); + } + + return error; } /** @@ -393,6 +474,10 @@ exit: void otPlatSettingsWipe(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); +#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE + otPosixSecureSettingsWipe(aInstance); +#endif + VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO); }