diff --git a/src/include/platform/internal/GenericConfigurationManagerImpl.h b/src/include/platform/internal/GenericConfigurationManagerImpl.h new file mode 100644 index 00000000000000..c53cc2c266b4ac --- /dev/null +++ b/src/include/platform/internal/GenericConfigurationManagerImpl.h @@ -0,0 +1,165 @@ +/* + * + * + * + * 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 + * Provides an generic implementation of ConfigurationManager features + * for use on various platforms. + */ + +#ifndef GENERIC_CONFIGURATION_MANAGER_IMPL_H +#define GENERIC_CONFIGURATION_MANAGER_IMPL_H + +namespace chip { +namespace DeviceLayer { + +class ProvisioningDataSet; + +namespace Internal { + +/** + * Provides a generic implementation of ConfigurationManager features that works on multiple platforms. + * + * This template contains implementations of select features from the ConfigurationManager abstract + * interface that are suitable for use on all platforms. It is intended to be inherited (directly + * or indirectly) by the ConfigurationManagerImpl class, which also appears as the template's ImplClass + * parameter. + */ +template +class GenericConfigurationManagerImpl +{ +public: + + // ===== Methods that implement the ConfigurationManager abstract interface. + + CHIP_ERROR _Init(); + CHIP_ERROR _ConfigureChiptack(); + CHIP_ERROR _GetVendorId(uint16_t & vendorId); + CHIP_ERROR _GetProductId(uint16_t & productId); + CHIP_ERROR _GetProductRevision(uint16_t & productRev); + CHIP_ERROR _StoreProductRevision(uint16_t productRev); + CHIP_ERROR _GetFirmwareRevision(char * buf, size_t bufSize, size_t & outLen); + CHIP_ERROR _GetFirmwareBuildTime(uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, + uint8_t & hour, uint8_t & minute, uint8_t & second); + CHIP_ERROR _GetSerialNumber(char * buf, size_t bufSize, size_t & serialNumLen); + CHIP_ERROR _StoreSerialNumber(const char * serialNum, size_t serialNumLen); + CHIP_ERROR _GetPrimaryWiFiMACAddress(uint8_t * buf); + CHIP_ERROR _StorePrimaryWiFiMACAddress(const uint8_t * buf); + CHIP_ERROR _GetPrimary802154MACAddress(uint8_t * buf); + CHIP_ERROR _StorePrimary802154MACAddress(const uint8_t * buf); + CHIP_ERROR _GetManufacturingDate(uint16_t & year, uint8_t & month, uint8_t & dayOfMonth); + CHIP_ERROR _StoreManufacturingDate(const char * mfgDate, size_t mfgDateLen); + CHIP_ERROR _GetDeviceId(uint64_t & deviceId); + CHIP_ERROR _GetDeviceCertificate(uint8_t * buf, size_t bufSize, size_t & certLen); + CHIP_ERROR _GetDeviceIntermediateCACerts(uint8_t * buf, size_t bufSize, size_t & certsLen); + CHIP_ERROR _GetDevicePrivateKey(uint8_t * buf, size_t bufSize, size_t & keyLen); +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + CHIP_ERROR _StoreDeviceId(uint64_t deviceId); + CHIP_ERROR _StoreDeviceCertificate(const uint8_t * cert, size_t certLen); + CHIP_ERROR _StoreDeviceIntermediateCACerts(const uint8_t * certs, size_t certsLen); + CHIP_ERROR _StoreDevicePrivateKey(const uint8_t * key, size_t keyLen); + CHIP_ERROR _ClearOperationalDeviceCredentials(void); +#endif + CHIP_ERROR _GetManufacturerDeviceId(uint64_t & deviceId); + CHIP_ERROR _StoreManufacturerDeviceId(uint64_t deviceId); + CHIP_ERROR _GetManufacturerDeviceCertificate(uint8_t * buf, size_t bufSize, size_t & certLen); + CHIP_ERROR _StoreManufacturerDeviceCertificate(const uint8_t * cert, size_t certLen); + CHIP_ERROR _GetManufacturerDeviceIntermediateCACerts(uint8_t * buf, size_t bufSize, size_t & certsLen); + CHIP_ERROR _StoreManufacturerDeviceIntermediateCACerts(const uint8_t * certs, size_t certsLen); + CHIP_ERROR _GetManufacturerDevicePrivateKey(uint8_t * buf, size_t bufSize, size_t & keyLen); + CHIP_ERROR _StoreManufacturerDevicePrivateKey(const uint8_t * key, size_t keyLen); + CHIP_ERROR _GetPairingCode(char * buf, size_t bufSize, size_t & pairingCodeLen); + CHIP_ERROR _StorePairingCode(const char * pairingCode, size_t pairingCodeLen); + CHIP_ERROR _GetFabricId(uint64_t & fabricId); + CHIP_ERROR _StoreFabricId(uint64_t fabricId); + CHIP_ERROR _GetServiceId(uint64_t & serviceId); + CHIP_ERROR _GetServiceConfig(uint8_t * buf, size_t bufSize, size_t & serviceConfigLen); + CHIP_ERROR _StoreServiceConfig(const uint8_t * serviceConfig, size_t serviceConfigLen); + CHIP_ERROR _GetPairedAccountId(char * buf, size_t bufSize, size_t & accountIdLen); + CHIP_ERROR _StorePairedAccountId(const char * accountId, size_t accountIdLen); + CHIP_ERROR _StoreServiceProvisioningData(uint64_t serviceId, const uint8_t * serviceConfig, + size_t serviceConfigLen, const char * accountId, size_t accountIdLen); + CHIP_ERROR _ClearServiceProvisioningData(); + CHIP_ERROR _GetFailSafeArmed(bool & val); + CHIP_ERROR _SetFailSafeArmed(bool val); + CHIP_ERROR _GetDeviceDescriptor(::chip::Profiles::DeviceDescription::ChipeviceDescriptor & deviceDesc); + CHIP_ERROR _GetDeviceDescriptorTLV(uint8_t * buf, size_t bufSize, size_t & encodedLen); + CHIP_ERROR _GetQRCodeString(char * buf, size_t bufSize); + CHIP_ERROR _GetWiFiAPSSID(char * buf, size_t bufSize); + CHIP_ERROR _GetBLEDeviceIdentificationInfo(Ble::ChipBLEDeviceIdentificationInfo & deviceIdInfo); + bool _IsServiceProvisioned(); + bool _IsMemberOfFabric(); + bool _IsPairedToAccount(); + bool _IsFullyProvisioned(); + CHIP_ERROR _ComputeProvisioningHash(uint8_t * hashBuf, size_t hashBufSize); +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + bool _OperationalDeviceCredentialsProvisioned(); + void _UseManufacturerCredentialsAsOperational(bool val); +#endif + +protected: + + enum + { + kFlag_IsServiceProvisioned = 0x01, + kFlag_IsMemberOfFabric = 0x02, + kFlag_IsPairedToAccount = 0x04, + kFlag_OperationalDeviceCredentialsProvisioned = 0x08, + kFlag_UseManufacturerCredentialsAsOperational = 0x10, + }; + + uint8_t mFlags; + + void LogDeviceConfig(); + CHIP_ERROR PersistProvisioningData(ProvisioningDataSet & provData); + +private: + + ImplClass * Impl() { return static_cast(this); } + + static void HashLengthAndBase64Value(Platform::Security::SHA256 & hash, const uint8_t * val, uint16_t valLen); + +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + bool UseManufacturerCredentialsAsOperational(); +#endif +}; + +// Instruct the compiler to instantiate the template only when explicitly told to do so. +extern template class Internal::GenericConfigurationManagerImpl; + +template +inline CHIP_ERROR GenericConfigurationManagerImpl::_GetVendorId(uint16_t & vendorId) +{ + vendorId = (uint16_t)CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID; + return CHIP_NO_ERROR; +} + +template +inline CHIP_ERROR GenericConfigurationManagerImpl::_GetProductId(uint16_t & productId) +{ + productId = (uint16_t)CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID; + return CHIP_NO_ERROR; +} + + + + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_CONFIGURATION_MANAGER_IMPL_H diff --git a/src/include/platform/internal/GenericConfigurationManagerImpl.ipp b/src/include/platform/internal/GenericConfigurationManagerImpl.ipp new file mode 100644 index 00000000000000..143fa6612915ac --- /dev/null +++ b/src/include/platform/internal/GenericConfigurationManagerImpl.ipp @@ -0,0 +1,1154 @@ +/* + * + * + * + * 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 + * Contains non-inline method definitions for the + * GenericConfigurationManagerImpl<> template. + */ + +#ifndef GENERIC_CONFIGURATION_MANAGER_IMPL_IPP +#define GENERIC_CONFIGURATION_MANAGER_IMPL_IPP + +#include +#include +#include +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#include +#endif + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +// Fully instantiate the generic implementation class in whatever compilation unit includes this file. +template class GenericConfigurationManagerImpl; + +template +CHIP_ERROR GenericConfigurationManagerImpl::_Init() +{ + mFlags = 0; + + // Cache flags indicating whether the device is currently service provisioned, is a member of a fabric, + // is paired to an account, and/or provisioned with operational credentials. + SetFlag(mFlags, kFlag_IsServiceProvisioned, Impl()->ConfigValueExists(ImplClass::kConfigKey_ServiceConfig)); + SetFlag(mFlags, kFlag_IsMemberOfFabric, Impl()->ConfigValueExists(ImplClass::kConfigKey_FabricId)); + SetFlag(mFlags, kFlag_IsPairedToAccount, Impl()->ConfigValueExists(ImplClass::kConfigKey_PairedAccountId)); + SetFlag(mFlags, kFlag_OperationalDeviceCredentialsProvisioned, Impl()->ConfigValueExists(ImplClass::kConfigKey_OperationalDeviceCert)); + + return CHIP_NO_ERROR; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_ConfigureChipStack() +{ + CHIP_ERROR err; + size_t pairingCodeLen; + + static char sPairingCodeBuf[ConfigurationManager::kMaxPairingCodeLength + 1]; + + // Configure the CHIP FabricState object with the local node id. + err = Impl()->_GetDeviceId(FabricState.LocalNodeId); + SuccessOrExit(err); + + // Configure the FabricState object with the pairing code string, if present. + err = Impl()->_GetPairingCode(sPairingCodeBuf, sizeof(sPairingCodeBuf), pairingCodeLen); + if (err != CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + SuccessOrExit(err); + FabricState.PairingCode = sPairingCodeBuf; + } + + // If the device is a member of a CHIP fabric, configure the FabricState object with the fabric id. + err = Impl()->_GetFabricId(FabricState.FabricId); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + FabricState.FabricId = kFabricIdNotSpecified; + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + // Configure the FabricState object with a reference to the GroupKeyStore object. + FabricState.GroupKeyStore = Impl()->_GetGroupKeyStore(); + +#if CHIP_PROGRESS_LOGGING + + Impl()->LogDeviceConfig(); + +#if CHIP_DEVICE_CONFIG_LOG_PROVISIONING_HASH + { + uint8_t provHash[Platform::Security::SHA256::kHashLength]; + char provHashBase64[BASE64_ENCODED_LEN(sizeof(provHash)) + 1]; + err = Impl()->_ComputeProvisioningHash(provHash, sizeof(provHash)); + if (err == CHIP_NO_ERROR) + { + Base64Encode(provHash, sizeof(provHash), provHashBase64); + provHashBase64[sizeof(provHashBase64) - 1] = '\0'; + ChipLogProgress(DeviceLayer, "CHIP Provisioning Hash: %s", provHashBase64); + } + else + { + ChipLogError(DeviceLayer, "Error generating CHIP Provisioning Hash: %s", chip::ErrorStr(err)); + err = CHIP_NO_ERROR; + } + } +#endif // CHIP_DEVICE_CONFIG_LOG_PROVISIONING_HASH + +#endif // CHIP_PROGRESS_LOGGING + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetFirmwareRevision(char * buf, size_t bufSize, size_t & outLen) +{ +#ifdef CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION + if (CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION[0] != 0) + { + outLen = min(bufSize, sizeof(CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION) - 1); + memcpy(buf, CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION, outLen); + return CHIP_NO_ERROR; + } + else +#endif // CHIP_DEVICE_CONFIG_DEVICE_FIRMWARE_REVISION + { + outLen = 0; + return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; + } +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetFirmwareBuildTime(uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, + uint8_t & hour, uint8_t & minute, uint8_t & second) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // TODO: Allow build time to be overridden by compile-time config (e.g. CHIP_DEVICE_CONFIG_FIRMWARE_BUILD_TIME). + + err = ParseCompilerDateStr(__DATE__, year, month, dayOfMonth); + SuccessOrExit(err); + + err = Parse24HourTimeStr(__TIME__, hour, minute, second); + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetManufacturerDeviceId(uint64_t & deviceId) +{ + CHIP_ERROR err; + + err = Impl()->ReadConfigValue(ImplClass::kConfigKey_MfrDeviceId, deviceId); + +#if CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + deviceId = TestDeviceId; + err = CHIP_NO_ERROR; + } +#endif + + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreManufacturerDeviceId(uint64_t deviceId) +{ + return Impl()->WriteConfigValue(ImplClass::kConfigKey_MfrDeviceId, deviceId); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetSerialNumber(char * buf, size_t bufSize, size_t & serialNumLen) +{ + CHIP_ERROR err; + + err = Impl()->ReadConfigValueStr(ImplClass::kConfigKey_SerialNum, buf, bufSize, serialNumLen); +#ifdef CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER + if (CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER[0] != 0 && err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + VerifyOrExit(sizeof(CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER) <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(buf, CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER, sizeof(CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER)); + serialNumLen = sizeof(CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER) - 1; + ChipProgress(DeviceLayer, "Serial Number not found; using default: %s", CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER); + err = CHIP_NO_ERROR; + } +#endif // CHIP_DEVICE_CONFIG_USE_TEST_SERIAL_NUMBER + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreSerialNumber(const char * serialNum, size_t serialNumLen) +{ + return Impl()->WriteConfigValueStr(ImplClass::kConfigKey_SerialNum, serialNum, serialNumLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetPrimaryWiFiMACAddress(uint8_t * buf) +{ + return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StorePrimaryWiFiMACAddress(const uint8_t * buf) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetPrimary802154MACAddress(uint8_t * buf) +{ +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + return ThreadStackManager().GetPrimary802154MACAddress(buf); +#else + return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StorePrimary802154MACAddress(const uint8_t * buf) +{ + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +template +inline CHIP_ERROR GenericConfigurationManagerImpl::_GetProductRevision(uint16_t & productRev) +{ + CHIP_ERROR err; + uint32_t val; + + err = Impl()->ReadConfigValue(ImplClass::kConfigKey_ProductRevision, val); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + productRev = (uint16_t)CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_PRODUCT_REVISION; + err = CHIP_NO_ERROR; + } + else + { + productRev = (uint16_t)val; + } + + return err; +} + +template +inline CHIP_ERROR GenericConfigurationManagerImpl::_StoreProductRevision(uint16_t productRev) +{ + return Impl()->WriteConfigValue(ImplClass::kConfigKey_ProductRevision, (uint32_t)productRev); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetManufacturingDate(uint16_t& year, uint8_t& month, uint8_t& dayOfMonth) +{ + CHIP_ERROR err; + enum { + kDateStringLength = 10 // YYYY-MM-DD + }; + char dateStr[kDateStringLength + 1]; + size_t dateLen; + char *parseEnd; + + err = Impl()->ReadConfigValueStr(ImplClass::kConfigKey_ManufacturingDate, dateStr, sizeof(dateStr), dateLen); + SuccessOrExit(err); + + VerifyOrExit(dateLen == kDateStringLength, err = CHIP_ERROR_INVALID_ARGUMENT); + + year = strtoul(dateStr, &parseEnd, 10); + VerifyOrExit(parseEnd == dateStr + 4, err = CHIP_ERROR_INVALID_ARGUMENT); + + month = strtoul(dateStr + 5, &parseEnd, 10); + VerifyOrExit(parseEnd == dateStr + 7, err = CHIP_ERROR_INVALID_ARGUMENT); + + dayOfMonth = strtoul(dateStr + 8, &parseEnd, 10); + VerifyOrExit(parseEnd == dateStr + 10, err = CHIP_ERROR_INVALID_ARGUMENT); + +exit: + if (err != CHIP_NO_ERROR && err != CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + ChipLogError(DeviceLayer, "Invalid manufacturing date: %s", dateStr); + } + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreManufacturingDate(const char * mfgDate, size_t mfgDateLen) +{ + return Impl()->WriteConfigValueStr(ImplClass::kConfigKey_ManufacturingDate, mfgDate, mfgDateLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetManufacturerDeviceCertificate(uint8_t * buf, size_t bufSize, size_t & certLen) +{ + CHIP_ERROR err; + + err = Impl()->ReadConfigValueBin(ImplClass::kConfigKey_MfrDeviceCert, buf, bufSize, certLen); + +#if CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + certLen = TestDeviceCertLength; + VerifyOrExit(buf != NULL, err = CHIP_NO_ERROR); + VerifyOrExit(TestDeviceCertLength <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); + ChipLogProgress(DeviceLayer, "Device certificate not found; using default"); + memcpy(buf, TestDeviceCert, TestDeviceCertLength); + err = CHIP_NO_ERROR; + } + +#endif // CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreManufacturerDeviceCertificate(const uint8_t * cert, size_t certLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_MfrDeviceCert, cert, certLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetManufacturerDeviceIntermediateCACerts(uint8_t * buf, size_t bufSize, size_t & certsLen) +{ + CHIP_ERROR err; + + err = Impl()->ReadConfigValueBin(ImplClass::kConfigKey_MfrDeviceICACerts, buf, bufSize, certsLen); + +#if CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + certsLen = TestDeviceIntermediateCACertLength; + VerifyOrExit(buf != NULL, err = CHIP_NO_ERROR); + VerifyOrExit(TestDeviceIntermediateCACertLength <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); + ChipLogProgress(DeviceLayer, "Device certificate not found; using default"); + memcpy(buf, TestDeviceIntermediateCACert, TestDeviceIntermediateCACertLength); + err = CHIP_NO_ERROR; + } + +#endif // CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreManufacturerDeviceIntermediateCACerts(const uint8_t * certs, size_t certsLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_MfrDeviceICACerts, certs, certsLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetManufacturerDevicePrivateKey(uint8_t * buf, size_t bufSize, size_t & keyLen) +{ + CHIP_ERROR err; + + err = Impl()->ReadConfigValueBin(ImplClass::kConfigKey_MfrDevicePrivateKey, buf, bufSize, keyLen); + +#if CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + keyLen = TestDevicePrivateKeyLength; + VerifyOrExit(buf != NULL, err = CHIP_NO_ERROR); + VerifyOrExit(TestDevicePrivateKeyLength <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); + ChipLogProgress(DeviceLayer, "Device private key not found; using default"); + memcpy(buf, TestDevicePrivateKey, TestDevicePrivateKeyLength); + err = CHIP_NO_ERROR; + } + +#endif // CHIP_DEVICE_CONFIG_ENABLE_TEST_DEVICE_IDENTITY + + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreManufacturerDevicePrivateKey(const uint8_t * key, size_t keyLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_MfrDevicePrivateKey, key, keyLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetDeviceId(uint64_t & deviceId) +{ + CHIP_ERROR err; + +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + if (!UseManufacturerCredentialsAsOperational()) + { + err = Impl()->ReadConfigValue(ImplClass::kConfigKey_OperationalDeviceId, deviceId); + } + else +#endif + { + err = Impl()->_GetManufacturerDeviceId(deviceId); + } + + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetDeviceCertificate(uint8_t * buf, size_t bufSize, size_t & certLen) +{ + CHIP_ERROR err; + +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + if (!UseManufacturerCredentialsAsOperational()) + { + err = Impl()->ReadConfigValueBin(ImplClass::kConfigKey_OperationalDeviceCert, buf, bufSize, certLen); + } + else +#endif + { + err = Impl()->_GetManufacturerDeviceCertificate(buf, bufSize, certLen); + } + + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetDeviceIntermediateCACerts(uint8_t * buf, size_t bufSize, size_t & certsLen) +{ + CHIP_ERROR err; + +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + if (!UseManufacturerCredentialsAsOperational()) + { + err = Impl()->ReadConfigValueBin(ImplClass::kConfigKey_OperationalDeviceICACerts, buf, bufSize, certsLen); + } + else +#endif + { + err = Impl()->_GetManufacturerDeviceIntermediateCACerts(buf, bufSize, certsLen); + } + + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetDevicePrivateKey(uint8_t * buf, size_t bufSize, size_t & keyLen) +{ + CHIP_ERROR err; + +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + if (!UseManufacturerCredentialsAsOperational()) + { + err = Impl()->ReadConfigValueBin(ImplClass::kConfigKey_OperationalDevicePrivateKey, buf, bufSize, keyLen); + } + else +#endif + { + err = Impl()->_GetManufacturerDevicePrivateKey(buf, bufSize, keyLen); + } + + return err; +} + +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreDeviceId(uint64_t deviceId) +{ + return Impl()->WriteConfigValue(ImplClass::kConfigKey_OperationalDeviceId, deviceId); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreDeviceCertificate(const uint8_t * cert, size_t certLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_OperationalDeviceCert, cert, certLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreDeviceIntermediateCACerts(const uint8_t * certs, size_t certsLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_OperationalDeviceICACerts, certs, certsLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreDevicePrivateKey(const uint8_t * key, size_t keyLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_OperationalDevicePrivateKey, key, keyLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_ClearOperationalDeviceCredentials(void) +{ + Impl()->ClearConfigValue(ImplClass::kConfigKey_OperationalDeviceId); + Impl()->ClearConfigValue(ImplClass::kConfigKey_OperationalDeviceCert); + Impl()->ClearConfigValue(ImplClass::kConfigKey_OperationalDeviceICACerts); + Impl()->ClearConfigValue(ImplClass::kConfigKey_OperationalDevicePrivateKey); + + ClearFlag(mFlags, kFlag_OperationalDeviceCredentialsProvisioned); + + return CHIP_NO_ERROR; +} + +template +bool GenericConfigurationManagerImpl::_OperationalDeviceCredentialsProvisioned() +{ + return ::chip::GetFlag(mFlags, kFlag_OperationalDeviceCredentialsProvisioned); +} + +template +bool GenericConfigurationManagerImpl::UseManufacturerCredentialsAsOperational() +{ + return ::chip::GetFlag(mFlags, kFlag_UseManufacturerCredentialsAsOperational); +} + +template +void GenericConfigurationManagerImpl::_UseManufacturerCredentialsAsOperational(bool val) +{ + SetFlag(mFlags, kFlag_UseManufacturerCredentialsAsOperational, val); +} + +#endif // CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetPairingCode(char * buf, size_t bufSize, size_t & pairingCodeLen) +{ + CHIP_ERROR err; + + err = Impl()->ReadConfigValueStr(ImplClass::kConfigKey_PairingCode, buf, bufSize, pairingCodeLen); +#ifdef CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE + if (CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE[0] != 0 && err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + VerifyOrExit(sizeof(CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE) <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(buf, CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE, sizeof(CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE)); + pairingCodeLen = sizeof(CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE) - 1; + ChipLogProgress(DeviceLayer, "Pairing code not found; using default: %s", CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE); + err = CHIP_NO_ERROR; + } +#endif // CHIP_DEVICE_CONFIG_USE_TEST_PAIRING_CODE + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StorePairingCode(const char * pairingCode, size_t pairingCodeLen) +{ + return Impl()->WriteConfigValueStr(ImplClass::kConfigKey_PairingCode, pairingCode, pairingCodeLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetFabricId(uint64_t & fabricId) +{ + return Impl()->ReadConfigValue(ImplClass::kConfigKey_FabricId, fabricId); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreFabricId(uint64_t fabricId) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + if (fabricId != kFabricIdNotSpecified) + { + err = Impl()->WriteConfigValue(ImplClass::kConfigKey_FabricId, fabricId); + SuccessOrExit(err); + SetFlag(mFlags, kFlag_IsMemberOfFabric); + } + else + { + ClearFlag(mFlags, kFlag_IsMemberOfFabric); + err = Impl()->ClearConfigValue(ImplClass::kConfigKey_FabricId); + SuccessOrExit(err); + } + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetServiceId(uint64_t & serviceId) +{ + return Impl()->ReadConfigValue(ImplClass::kConfigKey_ServiceId, serviceId); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetServiceConfig(uint8_t * buf, size_t bufSize, size_t & serviceConfigLen) +{ + return Impl()->ReadConfigValueBin(ImplClass::kConfigKey_ServiceConfig, buf, bufSize, serviceConfigLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreServiceConfig(const uint8_t * serviceConfig, size_t serviceConfigLen) +{ + return Impl()->WriteConfigValueBin(ImplClass::kConfigKey_ServiceConfig, serviceConfig, serviceConfigLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetPairedAccountId(char * buf, size_t bufSize, size_t & accountIdLen) +{ + return Impl()->ReadConfigValueStr(ImplClass::kConfigKey_PairedAccountId, buf, bufSize, accountIdLen); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StorePairedAccountId(const char * accountId, size_t accountIdLen) +{ + CHIP_ERROR err; + + err = Impl()->WriteConfigValueStr(ImplClass::kConfigKey_PairedAccountId, accountId, accountIdLen); + SuccessOrExit(err); + + SetFlag(mFlags, kFlag_IsPairedToAccount, (accountId != NULL && accountIdLen != 0)); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_StoreServiceProvisioningData(uint64_t serviceId, + const uint8_t * serviceConfig, size_t serviceConfigLen, + const char * accountId, size_t accountIdLen) +{ + CHIP_ERROR err; + + err = Impl()->WriteConfigValue(ImplClass::kConfigKey_ServiceId, serviceId); + SuccessOrExit(err); + + err = _StoreServiceConfig(serviceConfig, serviceConfigLen); + SuccessOrExit(err); + + err = _StorePairedAccountId(accountId, accountIdLen); + SuccessOrExit(err); + + SetFlag(mFlags, kFlag_IsServiceProvisioned); + SetFlag(mFlags, kFlag_IsPairedToAccount, (accountId != NULL && accountIdLen != 0)); + +exit: + if (err != CHIP_NO_ERROR) + { + Impl()->ClearConfigValue(ImplClass::kConfigKey_ServiceId); + Impl()->ClearConfigValue(ImplClass::kConfigKey_ServiceConfig); + Impl()->ClearConfigValue(ImplClass::kConfigKey_PairedAccountId); + ClearFlag(mFlags, kFlag_IsServiceProvisioned); + ClearFlag(mFlags, kFlag_IsPairedToAccount); + } + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_ClearServiceProvisioningData() +{ + Impl()->ClearConfigValue(ImplClass::kConfigKey_ServiceId); + Impl()->ClearConfigValue(ImplClass::kConfigKey_ServiceConfig); + Impl()->ClearConfigValue(ImplClass::kConfigKey_PairedAccountId); + + // TODO: Move these behaviors out of configuration manager. + + // If necessary, post an event alerting other subsystems to the change in + // the account pairing state. + if (_IsPairedToAccount()) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kAccountPairingChange; + event.AccountPairingChange.IsPairedToAccount = false; + PlatformMgr().PostEvent(&event); + } + + // If necessary, post an event alerting other subsystems to the change in + // the service provisioning state. + if (_IsServiceProvisioned()) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kServiceProvisioningChange; + event.ServiceProvisioningChange.IsServiceProvisioned = false; + event.ServiceProvisioningChange.ServiceConfigUpdated = false; + PlatformMgr().PostEvent(&event); + } + + ClearFlag(mFlags, kFlag_IsServiceProvisioned); + ClearFlag(mFlags, kFlag_IsPairedToAccount); + + return CHIP_NO_ERROR; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetFailSafeArmed(bool & val) +{ + return Impl()->ReadConfigValue(ImplClass::kConfigKey_FailSafeArmed, val); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_SetFailSafeArmed(bool val) +{ + return Impl()->WriteConfigValue(ImplClass::kConfigKey_FailSafeArmed, val); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetDeviceDescriptor(::chip::Profiles::DeviceDescription::ChipDeviceDescriptor & deviceDesc) +{ + CHIP_ERROR err; + size_t outLen; + + deviceDesc.Clear(); + + deviceDesc.DeviceId = FabricState.LocalNodeId; + + deviceDesc.FabricId = FabricState.FabricId; + + err = Impl()->_GetVendorId(deviceDesc.VendorId); + SuccessOrExit(err); + + err = Impl()->_GetProductId(deviceDesc.ProductId); + SuccessOrExit(err); + + err = Impl()->_GetProductRevision(deviceDesc.ProductRevision); + SuccessOrExit(err); + + err = Impl()->_GetManufacturingDate(deviceDesc.ManufacturingDate.Year, deviceDesc.ManufacturingDate.Month, deviceDesc.ManufacturingDate.Day); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + err = Impl()->_GetPrimaryWiFiMACAddress(deviceDesc.PrimaryWiFiMACAddress); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND || err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + err = Impl()->_GetPrimary802154MACAddress(deviceDesc.Primary802154MACAddress); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND || err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + err = Impl()->_GetWiFiAPSSID(deviceDesc.RendezvousWiFiESSID, sizeof(deviceDesc.RendezvousWiFiESSID)); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND || err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + err = Impl()->_GetSerialNumber(deviceDesc.SerialNumber, sizeof(deviceDesc.SerialNumber), outLen); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND || err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + err = Impl()->_GetFirmwareRevision(deviceDesc.SoftwareVersion, sizeof(deviceDesc.SoftwareVersion), outLen); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND || err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetDeviceDescriptorTLV(uint8_t * buf, size_t bufSize, size_t & encodedLen) +{ + CHIP_ERROR err; + ::chip::Profiles::DeviceDescription::ChipDeviceDescriptor deviceDesc; + + err = Impl()->_GetDeviceDescriptor(deviceDesc); + SuccessOrExit(err); + + { + uint32_t tmp = 0; + err = ::chip::Profiles::DeviceDescription::ChipDeviceDescriptor::EncodeTLV(deviceDesc, buf, (uint32_t)bufSize, tmp); + SuccessOrExit(err); + encodedLen = tmp; + } + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetQRCodeString(char * buf, size_t bufSize) +{ + CHIP_ERROR err; + ::chip::Profiles::DeviceDescription::ChipDeviceDescriptor deviceDesc; + uint32_t encodedLen; + + err = Impl()->_GetDeviceDescriptor(deviceDesc); + SuccessOrExit(err); + + strncpy(deviceDesc.PairingCode, FabricState.PairingCode, ::chip::Profiles::DeviceDescription::ChipDeviceDescriptor::kMaxPairingCodeLength); + deviceDesc.PairingCode[::chip::Profiles::DeviceDescription::ChipDeviceDescriptor::kMaxPairingCodeLength] = 0; + + err = ::chip::Profiles::DeviceDescription::ChipDeviceDescriptor::EncodeText(deviceDesc, buf, (uint32_t)bufSize, encodedLen); + SuccessOrExit(err); + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetWiFiAPSSID(char * buf, size_t bufSize) +{ + CHIP_ERROR err; + +#ifdef CHIP_DEVICE_CONFIG_WIFI_AP_SSID_PREFIX + + uint8_t mac[6]; + + VerifyOrExit(bufSize >= sizeof(CHIP_DEVICE_CONFIG_WIFI_AP_SSID_PREFIX) + 4, err = CHIP_ERROR_BUFFER_TOO_SMALL); + + err = Impl()->_GetPrimaryWiFiMACAddress(mac); + SuccessOrExit(err); + + snprintf(buf, bufSize, "%s%02X%02X", CHIP_DEVICE_CONFIG_WIFI_AP_SSID_PREFIX, mac[4], mac[5]); + buf[bufSize - 1] = 0; + +#else // CHIP_DEVICE_CONFIG_WIFI_AP_SSID_PREFIX + + ExitNow(err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); + +#endif // CHIP_DEVICE_CONFIG_WIFI_AP_SSID_PREFIX + +exit: + return err; +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_GetBLEDeviceIdentificationInfo(Ble::ChipBLEDeviceIdentificationInfo & deviceIdInfo) +{ + CHIP_ERROR err; + uint16_t id; + + deviceIdInfo.Init(); + + err = Impl()->_GetVendorId(id); + SuccessOrExit(err); + deviceIdInfo.SetVendorId(id); + + err = Impl()->_GetProductId(id); + SuccessOrExit(err); + deviceIdInfo.SetProductId(id); + + deviceIdInfo.SetDeviceId(FabricState.LocalNodeId); + + deviceIdInfo.PairingStatus = Impl()->_IsPairedToAccount() + ? Ble::ChipBLEDeviceIdentificationInfo::kPairingStatus_Paired + : Ble::ChipBLEDeviceIdentificationInfo::kPairingStatus_Unpaired; + +exit: + return err; +} + +template +bool GenericConfigurationManagerImpl::_IsServiceProvisioned() +{ + return ::chip::GetFlag(mFlags, kFlag_IsServiceProvisioned); +} + +template +bool GenericConfigurationManagerImpl::_IsMemberOfFabric() +{ + return ::chip::GetFlag(mFlags, kFlag_IsMemberOfFabric); +} + +template +bool GenericConfigurationManagerImpl::_IsPairedToAccount() +{ + return ::chip::GetFlag(mFlags, kFlag_IsPairedToAccount); +} + +template +bool GenericConfigurationManagerImpl::_IsFullyProvisioned() +{ + return +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION + ConnectivityMgr().IsWiFiStationProvisioned() && +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + ConnectivityMgr().IsThreadProvisioned() && +#endif +#if !CHIP_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING + Impl()->IsPairedToAccount() && +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING + (!UseManufacturerCredentialsAsOperational() && _OperationalDeviceCredentialsProvisioned()) && +#endif + Impl()->IsMemberOfFabric(); +} + +template +CHIP_ERROR GenericConfigurationManagerImpl::_ComputeProvisioningHash(uint8_t * hashBuf, size_t hashBufSize) +{ + using HashAlgo = Platform::Security::SHA256; + + CHIP_ERROR err = CHIP_NO_ERROR; + HashAlgo hash; + uint8_t * dataBuf = NULL; + size_t dataBufSize; + constexpr uint16_t kLenFieldLen = 4; // 4 hex characters + + VerifyOrExit(hashBufSize >= HashAlgo::kHashLength, err = CHIP_ERROR_BUFFER_TOO_SMALL); + + // Compute a hash of the device's provisioning data. The generated hash value confirms to the form + // described in the CHIP Chip: Factory Provisioning Specification. + // + // A CHIP provisioning hash is a SHA-256 hash of an ASCII string with the following format: + // + // DDDDddddddddddddddddCCCCcccc…ccccIIIIiiii…iiiiKKKKkkkk…kkkkPPPPpppppp + // + // Where: + // dddddddddddddddd is the Chip node id for the device, encoded as a string of 16 uppercase hex digits. + // cccc…cccc is the device Chip certificate, in base-64 format. + // iiii…iiii is the device intermediate CA certificates, in base-64 format (if provisioned). + // kkkk…kkkk is the device private key, in base-64 format. + // pppppp is the device pairing code, as ASCII characters. + // DDDD is the length of the dddddddddddddddd field (the device id), represented as 4 uppercase hex digits. + // Because the device id is always the same size, this field is always '0010'. + // CCCC is the length of the cccc…cccc field (the device certificate), represented as 4 uppercase hex digits. + // IIII is the length of the iiii…iiii field (the device intermediate CA certificates), represented as 4 uppercase hex digits. + // KKKK is the length of the kkkk…kkkk field (the device private key), represented as 4 uppercase hex digits. + // PPPP is the length of the pppppp field (the device pairing code), represented as 4 uppercase hex digits. + + hash.Begin(); + + // Hash the device id + { + uint64_t deviceId; + constexpr uint16_t kDeviceIdLen = 16; // 16 hex characters + char inputBuf[kLenFieldLen + kDeviceIdLen + 1]; // +1 for terminator + + err = Impl()->_GetManufacturerDeviceId(deviceId); + SuccessOrExit(err); + + snprintf(inputBuf, sizeof(inputBuf), "0010%016" PRIX64, deviceId); + + hash.AddData((uint8_t *)inputBuf, kLenFieldLen + kDeviceIdLen); + } + + // Hash the device certificate + { + size_t certLen; + + // Determine the length of the device certificate. + err = Impl()->_GetManufacturerDeviceCertificate((uint8_t *)NULL, 0, certLen); + SuccessOrExit(err); + + // Create a temporary buffer to hold the certificate. (This will also be used for + // the private key). + dataBufSize = certLen; + dataBuf = (uint8_t *)Platform::Security::MemoryAlloc(dataBufSize); + VerifyOrExit(dataBuf != NULL, err = CHIP_ERROR_NO_MEMORY); + + // Read the certificate. + err = Impl()->_GetManufacturerDeviceCertificate(dataBuf, certLen, certLen); + SuccessOrExit(err); + + // Hash the length and value of the device certificate in base-64 form. + HashLengthAndBase64Value(hash, dataBuf, (uint16_t)certLen); + } + + // Hash the device intermediate CA certificates + if (Impl()->ConfigValueExists(ImplClass::kConfigKey_MfrDeviceICACerts)) + { + size_t certsLen; + + // Determine the length of the device intermediate CA certificates. + err = Impl()->_GetManufacturerDeviceIntermediateCACerts((uint8_t *)NULL, 0, certsLen); + SuccessOrExit(err); + + // Allocate larger buffer to hold the intermediate CA certificates. + // (This will also be used for the private key). + if (certsLen > dataBufSize) + { + Platform::Security::MemoryFree(dataBuf); + + dataBufSize = certsLen; + dataBuf = (uint8_t *)Platform::Security::MemoryAlloc(dataBufSize); + VerifyOrExit(dataBuf != NULL, err = CHIP_ERROR_NO_MEMORY); + } + + // Read the device intermediate CA certificates. + err = Impl()->_GetManufacturerDeviceIntermediateCACerts(dataBuf, certsLen, certsLen); + SuccessOrExit(err); + + // Hash the length and value of the device intermediate CA certificates in base-64 form. + HashLengthAndBase64Value(hash, dataBuf, (uint16_t)certsLen); + } + + // Hash the device private key + { + size_t keyLen; + + // Determine the length of the device private key. + err = Impl()->_GetManufacturerDevicePrivateKey((uint8_t *)NULL, 0, keyLen); + SuccessOrExit(err); + + // Read the private key. (Note that we presume the buffer allocated to hold the certificate + // is big enough to hold the private key. _GetDevicePrivateKey() will return an error in the + // unlikely event that this is not the case.) + err = Impl()->_GetManufacturerDevicePrivateKey(dataBuf, dataBufSize, keyLen); + SuccessOrExit(err); + + // Hash the length and value of the private key in base-64 form. + HashLengthAndBase64Value(hash, dataBuf, (uint16_t)keyLen); + } + + // Hash the device pairing code. If the device does not have a pairing code, hash a zero-length value. + { + char pairingCode[ConfigurationManager::kMaxPairingCodeLength + 1]; // +1 for terminator + char lenStr[kLenFieldLen + 1]; // +1 for terminator + size_t pairingCodeLen; + + err = Impl()->_GetPairingCode(pairingCode, sizeof(pairingCode), pairingCodeLen); + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + pairingCodeLen = 0; + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + snprintf(lenStr, sizeof(lenStr), "%04" PRIX16, (uint16_t)pairingCodeLen); + + hash.AddData((uint8_t *)lenStr, kLenFieldLen); + hash.AddData((uint8_t *)pairingCode, pairingCodeLen); + } + + hash.Finish(hashBuf); + +exit: + if (dataBuf != NULL) + { + Crypto::ClearSecretData(dataBuf, dataBufSize); + Platform::Security::MemoryFree(dataBuf); + } + return err; +} + +template +void GenericConfigurationManagerImpl::HashLengthAndBase64Value(Platform::Security::SHA256 & hash, const uint8_t * val, uint16_t valLen) +{ + constexpr uint16_t kInputBufSize = 80; + static_assert(kInputBufSize > 0 && kInputBufSize % 4 == 0, "kInputBufSize must be a positive multiple of 4"); + char inputBuf[kInputBufSize]; + constexpr uint16_t kMaxChunkLen = BASE64_MAX_DECODED_LEN(kInputBufSize); + + // Hash the length of the base-64 value as 4 hex digits. + snprintf(inputBuf, sizeof(inputBuf), "%04" PRIX16, (uint16_t)BASE64_ENCODED_LEN(valLen)); + hash.AddData((uint8_t *)inputBuf, 4); + + // Repeatedly encode and hash chunks of the value in base-64 format. + while (valLen > 0) + { + uint16_t chunkLen = (valLen > kMaxChunkLen) ? kMaxChunkLen : valLen; + uint16_t encodedLen = Base64Encode(val, chunkLen, inputBuf); + inputBuf[encodedLen] = 0; + hash.AddData((uint8_t *)inputBuf, encodedLen); + val += chunkLen; + valLen -= chunkLen; + } +} + +#if CHIP_PROGRESS_LOGGING + +template +void GenericConfigurationManagerImpl::LogDeviceConfig() +{ + CHIP_ERROR err; + + ChipLogProgress(DeviceLayer, "Device Configuration:"); + + ChipLogProgress(DeviceLayer, " Device Id: %016" PRIX64, FabricState.LocalNodeId); + + { + char serialNum[ConfigurationManager::kMaxSerialNumberLength + 1]; + size_t serialNumLen; + err = Impl()->_GetSerialNumber(serialNum, sizeof(serialNum), serialNumLen); + ChipLogProgress(DeviceLayer, " Serial Number: %s", (err == CHIP_NO_ERROR) ? serialNum : "(not set)"); + } + + { + uint16_t vendorId; + if (Impl()->_GetVendorId(vendorId) != CHIP_NO_ERROR) + { + vendorId = 0; + } + ChipLogProgress(DeviceLayer, " Vendor Id: %" PRIu16 " (0x%" PRIX16 ")%s", + vendorId, vendorId, (vendorId == kChipVendor_CHIPLabs) ? " (CHIP)" : ""); + } + + { + uint16_t productId; + if (Impl()->_GetProductId(productId) != CHIP_NO_ERROR) + { + productId = 0; + } + ChipLogProgress(DeviceLayer, " Product Id: %" PRIu16 " (0x%" PRIX16 ")", productId, productId); + } + + { + uint16_t productRev; + if (Impl()->_GetProductRevision(productRev) != CHIP_NO_ERROR) + { + productRev = 0; + } + ChipLogProgress(DeviceLayer, " Product Revision: %" PRIu16, productRev); + } + + { + uint16_t year; + uint8_t month, dayOfMonth; + err = Impl()->_GetManufacturingDate(year, month, dayOfMonth); + if (err == CHIP_NO_ERROR) + { + ChipLogProgress(DeviceLayer, " Manufacturing Date: %04" PRIu16 "/%02" PRIu8 "/%02" PRIu8, year, month, dayOfMonth); + } + else + { + ChipLogProgress(DeviceLayer, " Manufacturing Date: (not set)"); + } + } + + if (FabricState.FabricId != kFabricIdNotSpecified) + { + ChipLogProgress(DeviceLayer, " Fabric Id: %016" PRIX64, FabricState.FabricId); + } + else + { + ChipLogProgress(DeviceLayer, " Fabric Id: (none)"); + } + + ChipLogProgress(DeviceLayer, " Pairing Code: %s", (FabricState.PairingCode != NULL) ? FabricState.PairingCode : "(none)"); +} + +#endif // CHIP_PROGRESS_LOGGING + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip + +#endif // GENERIC_CONFIGURATION_MANAGER_IMPL_IPP diff --git a/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp b/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp index fce74373331c8e..a9d60ffb9bbe62 100644 --- a/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp +++ b/src/include/platform/internal/GenericConnectivityManagerImpl_Thread.ipp @@ -117,7 +117,7 @@ void GenericConnectivityManagerImpl_Thread::UpdateServiceConnectivity if (FabricState.FabricId != kFabricIdNotSpecified) { const uint64_t fabricGlobalId = ChipFabricIdToIPv6GlobalId(FabricState.FabricId); - IPAddress serviceAddr = IPAddress::MakeULA(fabricGlobalId, nl::chip::kChipSubnetId_Service, 1); + IPAddress serviceAddr = IPAddress::MakeULA(fabricGlobalId, chip::kChipSubnetId_Service, 1); haveServiceConnectivity = ThreadStackMgr().HaveRouteToAddress(serviceAddr); } diff --git a/src/inet/InetConfig.h b/src/inet/InetConfig.h index f02c7d5b1a90d0..17353e3e124891 100644 --- a/src/inet/InetConfig.h +++ b/src/inet/InetConfig.h @@ -25,7 +25,7 @@ * either use preprocessor definitions or create a project- * specific InetProjectConfig.h header and then assert * HAVE_INETPROJECTCONFIG_H via the package configuration tool - * via --with-weave-inet-project-includes=DIR where DIR is the + * via --with-chip-inet-project-includes=DIR where DIR is the * directory that contains the header. * * NOTE WELL: On some platforms, this header is included by C-language programs. diff --git a/src/lib/support/Base64.cpp b/src/lib/support/Base64.cpp new file mode 100644 index 00000000000000..bcd12328fe1885 --- /dev/null +++ b/src/lib/support/Base64.cpp @@ -0,0 +1,353 @@ +/* + * + * + * + * 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 + * Base-64 utility functions. + * + */ + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#include +#include + +#include "Base64.h" + +namespace chip { + +// Convert a value in the range 0..63 to its equivalent base64 character. +// Return '=' for any value >= 64. +static char Base64ValToChar(uint8_t val) +{ + if (val < 26) + return 'A' + val; + val -= 26; + if (val < 26) + return 'a' + val; + val -= 26; + if (val < 10) + return '0' + val; + if (val == 10) + return '+'; + if (val == 11) + return '/'; + return '='; +} + +// Convert a base64 character to a value in the range 0..63, or UINT8_MAX if the character is invalid. +static uint8_t Base64CharToVal(uint8_t c) +{ + if (c == 43) + return 62; + if (c == 47) + return 63; + // NOTE: c < 48 will fall through to return UINT8_MAX below. + c -= 48; + if (c < 10) + return c + 52; + c -= 17; + if (c < 26) + return c; + c -= 32; + if (c < 26) + return c + 26; + return UINT8_MAX; +} + +// Convert a value in the range 0..63 to its equivalent base64url character (see RFC-4648, section 5). +// Return '=' for any value >= 64. +static char Base64URLValToChar(uint8_t val) +{ + if (val < 26) + return 'A' + val; + val -= 26; + if (val < 26) + return 'a' + val; + val -= 26; + if (val < 10) + return '0' + val; + if (val == 10) + return '-'; + if (val == 11) + return '_'; + return '='; +} + +// Convert a base64url character to a value in the range 0..63, or UINT8_MAX if the character is invalid. +static uint8_t Base64URLCharToVal(uint8_t c) +{ + if (c == 45) + return 62; + if (c == 95) + return 63; + // NOTE: c < 48 will fall through to return UINT8_MAX below. + c -= 48; + if (c < 10) + return c + 52; + c -= 17; + if (c < 26) + return c; + c -= 32; + if (c < 26) + return c + 26; + return UINT8_MAX; +} + +uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out, Base64ValToCharFunct valToCharFunct) +{ + char * outStart = out; + + while (inLen > 0) + { + uint8_t val1, val2, val3, val4; + + val1 = *in >> 2; + val2 = (*in << 4) & 0x3F; + in++; + inLen--; + if (inLen > 0) + { + val2 |= *in >> 4; + val3 = (*in << 2) & 0x3F; + in++; + inLen--; + if (inLen > 0) + { + val3 |= *in >> 6; + val4 = *in & 0x3F; + in++; + inLen--; + } + else + val4 = UINT8_MAX; + } + else + val3 = val4 = UINT8_MAX; + + *out++ = valToCharFunct(val1); + *out++ = valToCharFunct(val2); + *out++ = valToCharFunct(val3); + *out++ = valToCharFunct(val4); + } + + return out - outStart; +} + +uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out) +{ + return Base64Encode(in, inLen, out, Base64ValToChar); +} + +uint16_t Base64URLEncode(const uint8_t * in, uint16_t inLen, char * out) +{ + return Base64Encode(in, inLen, out, Base64URLValToChar); +} + +uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out, Base64ValToCharFunct valToCharFunct) +{ + uint32_t outLen = 0; + + // Maximum number of input bytes to convert to base-64 in a single call to Base64Encode. + // Number is the largest multiple of 3 bytes where the resulting number of base-64 characters + // fits within a uint16_t. + enum + { + kMaxConvert = (UINT16_MAX / 4) * 3 + }; + + while (true) + { + uint16_t inChunkLen = (inLen > kMaxConvert) ? (uint16_t) kMaxConvert : (uint16_t) inLen; + + uint16_t outChunkLen = Base64Encode(in, inChunkLen, out, valToCharFunct); + + inLen -= inChunkLen; + outLen += outChunkLen; + + if (inLen == 0) + break; + + in += inChunkLen; + out += outChunkLen; + } + + return outLen; +} + +uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out) +{ + return Base64Encode32(in, inLen, out, Base64ValToChar); +} + +uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct) +{ + uint8_t * outStart = out; + + // isgraph() returns false for space and ctrl chars + while (inLen > 0 && isgraph(*in)) + { + if (inLen == 1) + goto fail; + + uint8_t a = charToValFunct(*in++); + uint8_t b = charToValFunct(*in++); + inLen -= 2; + + if (a == UINT8_MAX || b == UINT8_MAX) + goto fail; + + *out++ = (a << 2) | (b >> 4); + + if (inLen == 0 || *in == '=') + break; + + uint8_t c = charToValFunct(*in++); + inLen--; + + if (c == UINT8_MAX) + goto fail; + + *out++ = (b << 4) | (c >> 2); + + if (inLen == 0 || *in == '=') + break; + + uint8_t d = charToValFunct(*in++); + inLen--; + + if (d == UINT8_MAX) + goto fail; + + *out++ = (c << 6) | d; + } + + return out - outStart; + +fail: + return UINT16_MAX; +} + +uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out) +{ + return Base64Decode(in, inLen, out, Base64CharToVal); +} + +uint16_t Base64URLDecode(const char * in, uint16_t inLen, uint8_t * out) +{ + return Base64Decode(in, inLen, out, Base64URLCharToVal); +} + +uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct) +{ + uint32_t outLen = 0; + + // Maximum number of base-64 characters to convert in a single call to Base64Decode. + // Number is the largest multiple of 4 characters that fits in a uint16_t. + enum + { + kMaxConvert = (UINT16_MAX / 4) * 4 + }; + + while (true) + { + uint16_t inChunkLen = (inLen > kMaxConvert) ? (uint16_t) kMaxConvert : (uint16_t) inLen; + + uint16_t outChunkLen = Base64Decode(in, inChunkLen, out, charToValFunct); + if (outChunkLen == UINT16_MAX) + return UINT32_MAX; + + inLen -= inChunkLen; + outLen += outChunkLen; + + if (inLen == 0) + break; + + in += inChunkLen; + out += outChunkLen; + } + + return outLen; +} + +uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out) +{ + return Base64Decode32(in, inLen, out, Base64CharToVal); +} + +} // namespace chip + +#ifdef TEST + +#include +#include + +void TestBase64(const char * test, bool base64URL = false) +{ + uint8_t buf[256]; + char buf2[256]; + uint16_t len; + + strcpy((char *) buf, test); + + len = (base64URL) ? nl::Base64URLDecode((char *) buf, strlen((char *) buf), buf) + : nl::Base64Decode((char *) buf, strlen((char *) buf), buf); + printf("%s: ", test); + if (len != UINT16_MAX) + { + printf("(%d) ", len); + for (uint16_t i = 0; i < len; i++) + printf("%c", buf[i]); + + len = (base64URL) ? nl::Base64URLEncode(buf, len, buf2) : nl::Base64Encode(buf, len, buf2); + printf(" (%d) ", len); + for (uint16_t i = 0; i < len; i++) + printf("%c", buf2[i]); + } + else + printf("ERROR"); + printf("\n"); +} + +int main(int argc, char * argv[]) +{ + TestBase64(""); + TestBase64("Zg=="); + TestBase64("Zm8="); + TestBase64("Zm9v"); + TestBase64("Zm9vYg=="); + TestBase64("Zm9vYmE="); + TestBase64("Zm9vYmFy"); + TestBase64("QmFzZTY0D+8xMjM0D/8="); + + TestBase64("Zg"); + TestBase64("Zm8"); + TestBase64("Zm9vYg"); + TestBase64("Zm9vYmE"); + + TestBase64("QmFzZTY0D-8xMjM0D_8=", true); + + // Error cases + TestBase64("Z"); + TestBase64("Z\x019vYmFy"); + TestBase64("Zm9vY"); + TestBase64("Zm9vY;"); + TestBase64("Zm9 vYg"); +} + +#endif // TEST diff --git a/src/lib/support/Base64.h b/src/lib/support/Base64.h new file mode 100644 index 00000000000000..81928e7dcc22c1 --- /dev/null +++ b/src/lib/support/Base64.h @@ -0,0 +1,82 @@ +/* + * + * + * + * 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 + * Base-64 utility functions. + * + */ + +#ifndef BASE64_H_ +#define BASE64_H_ + +namespace chip { + +typedef char (*Base64ValToCharFunct)(uint8_t val); +typedef uint8_t (*Base64CharToValFunct)(uint8_t c); + +// Encode an array of bytes to a base64 string. +// +// Returns length of generated string. +// Output will contain padding characters ('=') as necessary to make length a multiple of 4 characters. +// Output DOES NOT include a null terminator. +// Output buffer must be at least (inLen + 2) / 3 * 4 bytes long. +// Input and output buffers CANNOT overlap. +// +extern uint16_t Base64Encode(const uint8_t *in, uint16_t inLen, char *out); +extern uint16_t Base64URLEncode(const uint8_t *in, uint16_t inLen, char *out); +extern uint16_t Base64Encode(const uint8_t *in, uint16_t inLen, char *out, Base64ValToCharFunct valToCharFunct); + +// Decode a base64 string to bytes. +// +// Returns length of decoded data, or UINT16_MAX if input could not be decoded. +// Input MAY contain padding characters ('=') but only at the end of the input string. +// Output buffer must be at least inLen * 3 / 4 bytes long, however the actual output +// may be shorter than this due to padding. +// Supports decode in place by setting out pointer equal to in. +// +extern uint16_t Base64Decode(const char *in, uint16_t inLen, uint8_t *out); +extern uint16_t Base64URLDecode(const char *in, uint16_t inLen, uint8_t *out); +extern uint16_t Base64Decode(const char *in, uint16_t inLen, uint8_t *out, Base64CharToValFunct charToValFunct); + +// Encode/decode functions that take/return 32-bit lengths. +// +// Similar to the above functions, except Base64Decode32() returns UINT32_MAX if the input cannot be decoded. +// +extern uint32_t Base64Encode32(const uint8_t *in, uint32_t inLen, char *out); +extern uint32_t Base64Encode32(const uint8_t *in, uint32_t inLen, char *out, Base64ValToCharFunct valToCharFunct); +extern uint32_t Base64Decode32(const char *in, uint32_t inLen, uint8_t *out); +extern uint32_t Base64Decode32(const char *in, uint32_t inLen, uint8_t *out, Base64CharToValFunct charToValFunct); + +/** Computes the base-64 encoded length for a given input length. + * + * The computed length includes room for padding characters. + * + * NOTE: The supplied argument must be an integer type. + */ +#define BASE64_ENCODED_LEN(LEN) ((((LEN) + 2) / 3) * 4) + +/** Computes the maximum possible decoded length for a given base-64 string input length. + * + * NOTE: The actual decoded length may be smaller than this due to padding. + */ +#define BASE64_MAX_DECODED_LEN(LEN) ((LEN) * 3 / 4) + + +} // namespace chip + +#endif /* BASE64_H_ */ diff --git a/src/lib/support/SupportLayer.am b/src/lib/support/SupportLayer.am index 9a339f1bda490c..156336495d0d92 100644 --- a/src/lib/support/SupportLayer.am +++ b/src/lib/support/SupportLayer.am @@ -34,4 +34,5 @@ CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES = \ support/ErrorStr.h \ support/FlagUtils.hpp \ support/logging/CHIPLogging.h \ + support/Base64.h \ $(NULL) diff --git a/src/system/SystemLayer.cpp b/src/system/SystemLayer.cpp index 3704d441b6dc20..17d393e7caceee 100644 --- a/src/system/SystemLayer.cpp +++ b/src/system/SystemLayer.cpp @@ -1161,7 +1161,7 @@ DLL_EXPORT Error StartTimer(Layer & aLayer, void * aContext, uint32_t aMilliseco { Error lReturn = CHIP_SYSTEM_NO_ERROR; - // At the moment there is no need to do anything for standalone chip + LWIP. + // At the moment there is no need to do anything for standalone CHIP + LWIP. // the Task will periodically call HandleTimer which will process any expired // timers. static_cast(aLayer);