diff --git a/config/nrfconnect/chip-module/Kconfig b/config/nrfconnect/chip-module/Kconfig index 6afd11cca2..b664bfa867 100644 --- a/config/nrfconnect/chip-module/Kconfig +++ b/config/nrfconnect/chip-module/Kconfig @@ -107,10 +107,6 @@ config CHIP_FACTORY_DATA bool "Factory data provider" select ZCBOR imply FPROTECT - imply MBEDTLS_X509_LIBRARY if CHIP_CRYPTO_PSA - imply MBEDTLS_X509_CRT_PARSE_C if CHIP_CRYPTO_PSA - imply MBEDTLS_PK_PARSE_C if CHIP_CRYPTO_PSA - imply MBEDTLS_TLS_LIBRARY if CHIP_CRYPTO_PSA help Enables the default nRF Connect factory data provider implementation that supports reading the factory data encoded in the CBOR format from the @@ -250,6 +246,7 @@ endif # CHIP_FACTORY_DATA_BUILD config CHIP_FACTORY_RESET_ERASE_NVS bool default y + depends on !CHIP_CRYPTO_PSA_MIGRATE_DAC_PRIV_KEY config CHIP_LOG_SIZE_OPTIMIZATION bool "Disable some detailed logs to decrease flash usage" @@ -284,4 +281,16 @@ config CHIP_ENABLE_READ_CLIENT This config can be disabled for device types that do not require Read Client functionality. Disabling this config can save flash and RAM space. +config CHIP_CRYPTO_PSA_MIGRATE_DAC_PRIV_KEY + bool "Migrate DAC private key from factory data to PSA ITS" + depends on CHIP_CRYPTO_PSA + depends on CHIP_FACTORY_DATA + select EXPERIMENTAL + help + Move DAC private key from the factory data set to the PSA ITS secure storage + and remove it. After the first boot of the device the DAC private key will be moved + to the PSA ITS secure storage and will not be available in the factory data anymore. + It will be overwritten in the factory data set by zeros. + + endif # CHIP diff --git a/src/crypto/CHIPCryptoPALPSA.h b/src/crypto/CHIPCryptoPALPSA.h index 1a64c1f879..0f71d5e835 100644 --- a/src/crypto/CHIPCryptoPALPSA.h +++ b/src/crypto/CHIPCryptoPALPSA.h @@ -62,7 +62,8 @@ enum class KeyIdBase : psa_key_id_t { Minimum = CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE, Operational = Minimum, ///< Base of the PSA key ID range for Node Operational Certificate private keys - Maximum = Operational + kMaxValidFabricIndex, + DACPrivKey = Operational + kMaxValidFabricIndex + 1, + Maximum = DACPrivKey, }; static_assert(to_underlying(KeyIdBase::Minimum) >= PSA_KEY_ID_USER_MIN && to_underlying(KeyIdBase::Maximum) <= PSA_KEY_ID_USER_MAX, diff --git a/src/platform/nrfconnect/CHIPPlatformConfig.h b/src/platform/nrfconnect/CHIPPlatformConfig.h index c8d67115f9..c1d8a3e665 100644 --- a/src/platform/nrfconnect/CHIPPlatformConfig.h +++ b/src/platform/nrfconnect/CHIPPlatformConfig.h @@ -54,6 +54,10 @@ #endif // CHIP_CONFIG_SHA256_CONTEXT_ALIGN #endif // CONFIG_CHIP_CRYPTO_PSA +#ifndef CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE +#define CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE 0x30000 +#endif // CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE + // ==================== General Configuration Overrides ==================== #ifndef CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS diff --git a/src/platform/nrfconnect/FactoryDataParser.c b/src/platform/nrfconnect/FactoryDataParser.c index 610c78ab3e..29b8be9a95 100644 --- a/src/platform/nrfconnect/FactoryDataParser.c +++ b/src/platform/nrfconnect/FactoryDataParser.c @@ -17,12 +17,13 @@ #include "FactoryDataParser.h" +#include #include #include #include -#define MAX_FACTORY_DATA_NESTING_LEVEL 4 +#define MAX_FACTORY_DATA_NESTING_LEVEL 2 static inline bool uint16_decode(zcbor_state_t * states, uint16_t * value) { @@ -123,6 +124,11 @@ bool FindUserDataEntry(struct FactoryData * factoryData, const char * entry, voi bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData * factoryData) { + if (!buffer || !factoryData || bufferSize == 0) + { + return false; + } + memset(factoryData, 0, sizeof(*factoryData)); ZCBOR_STATE_D(states, MAX_FACTORY_DATA_NESTING_LEVEL, buffer, bufferSize, 1); @@ -209,7 +215,8 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData } else if (strncmp("dac_key", (const char *) currentString.value, currentString.len) == 0) { - res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_priv_key); + res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_priv_key); + factoryData->dacPrivateKeyOffset = (size_t) ((uint8_t *) factoryData->dac_priv_key.data - buffer); } else if (strncmp("pai_cert", (const char *) currentString.value, currentString.len) == 0) { diff --git a/src/platform/nrfconnect/FactoryDataParser.h b/src/platform/nrfconnect/FactoryDataParser.h index 54be7b80b8..4fe5f2aa4a 100644 --- a/src/platform/nrfconnect/FactoryDataParser.h +++ b/src/platform/nrfconnect/FactoryDataParser.h @@ -66,6 +66,7 @@ struct FactoryData bool discriminatorPresent; bool productFinishPresent; bool primaryColorPresent; + size_t dacPrivateKeyOffset; }; /** diff --git a/src/platform/nrfconnect/FactoryDataProvider.cpp b/src/platform/nrfconnect/FactoryDataProvider.cpp index caa1ad434f..11bb33fc1a 100644 --- a/src/platform/nrfconnect/FactoryDataProvider.cpp +++ b/src/platform/nrfconnect/FactoryDataProvider.cpp @@ -24,7 +24,15 @@ #include #endif -#include +#include + +#ifdef CONFIG_CHIP_CRYPTO_PSA +#include +#include +#include + +static const struct device * const kFlashDev = DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_flash_controller)); +#endif namespace chip { namespace { @@ -59,7 +67,33 @@ CHIP_ERROR FactoryDataProvider::Init() uint8_t * factoryData = nullptr; size_t factoryDataSize; - CHIP_ERROR error = mFlashFactoryData.ProtectFactoryDataPartitionAgainstWrite(); + CHIP_ERROR error = mFlashFactoryData.GetFactoryDataPartition(factoryData, factoryDataSize); + + if (error != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to read factory data partition"); + return error; + } + + if (!ParseFactoryData(factoryData, static_cast(factoryDataSize), &mFactoryData)) + { + ChipLogError(DeviceLayer, "Failed to parse factory data"); + return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + } + + // Check if factory data version is correct + if (mFactoryData.version != CONFIG_CHIP_FACTORY_DATA_VERSION) + { + ChipLogError(DeviceLayer, "Factory data version mismatch. Flash version: %d vs code version: %d", mFactoryData.version, + CONFIG_CHIP_FACTORY_DATA_VERSION); + return CHIP_ERROR_VERSION_MISMATCH; + } + +#ifdef CONFIG_CHIP_CRYPTO_PSA + VerifyOrDie(MoveDACPrivateKeyToSecureStorage(factoryData, factoryDataSize) == CHIP_NO_ERROR); +#endif + + error = mFlashFactoryData.ProtectFactoryDataPartitionAgainstWrite(); // Protection against write for external storage is not supported. if (error == CHIP_ERROR_NOT_IMPLEMENTED) @@ -73,30 +107,104 @@ CHIP_ERROR FactoryDataProvider::Init() return error; } - error = mFlashFactoryData.GetFactoryDataPartition(factoryData, factoryDataSize); + return error; +} - if (error != CHIP_NO_ERROR) +#ifdef CONFIG_CHIP_CRYPTO_PSA +template +CHIP_ERROR FactoryDataProvider::MoveDACPrivateKeyToSecureStorage(uint8_t * factoryDataPartition, + size_t factoryDataSize) +{ + + if (!factoryDataPartition || factoryDataSize == 0) { - ChipLogError(DeviceLayer, "Failed to read factory data partition"); - return error; + return CHIP_ERROR_INVALID_ARGUMENT; } - if (!ParseFactoryData(factoryData, static_cast(factoryDataSize), &mFactoryData)) + if (mFactoryData.dac_priv_key.len != kDACPrivateKeyLength) { - ChipLogError(DeviceLayer, "Failed to parse factory data"); - return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + return CHIP_ERROR_INVALID_LIST_LENGTH; } - // Check if factory data version is correct - if (mFactoryData.version != CONFIG_CHIP_FACTORY_DATA_VERSION) + uint8_t clearedDACPrivKey[kDACPrivateKeyLength]; + memset(clearedDACPrivKey, 0x00, sizeof(clearedDACPrivKey)); + + // Check if factory data contains DAC private key + if (memcmp(mFactoryData.dac_priv_key.data, clearedDACPrivKey, kDACPrivateKeyLength) != 0) { - ChipLogError(DeviceLayer, "Factory data version mismatch. Flash version: %d vs code version: %d", mFactoryData.version, - CONFIG_CHIP_FACTORY_DATA_VERSION); - return CHIP_ERROR_VERSION_MISMATCH; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + // If there is the new DAC private key present in the factory data set and also there is + // the existing one in ITS NVM storage, then skip saving it again. + if (psa_get_key_attributes(mDACPrivKeyId, &attributes) != PSA_SUCCESS) + { + ChipLogProgress(DeviceLayer, "Found DAC Private Key in factory data set. Copying to secure storage..."); + + // Remove the key if any exists and can be corrupted. + psa_destroy_key(mDACPrivKeyId); + + psa_reset_key_attributes(&attributes); + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attributes, kDACPrivateKeyLength * 8); + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); +#ifdef CONFIG_CHIP_CRYPTO_PSA_MIGRATE_DAC_PRIV_KEY + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attributes, mDACPrivKeyId); +#else + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); +#endif + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); + + VerifyOrReturnError(psa_import_key(&attributes, reinterpret_cast(mFactoryData.dac_priv_key.data), + kDACPrivateKeyLength, &mDACPrivKeyId) == PSA_SUCCESS, + CHIP_ERROR_INTERNAL); + } + +#ifdef CONFIG_CHIP_CRYPTO_PSA_MIGRATE_DAC_PRIV_KEY + // Check once again if the saved key has attributes set before removing it from the factory data set. + VerifyOrReturnError(psa_get_key_attributes(mDACPrivKeyId, &attributes) == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + // Get the actual block size. + const flash_parameters * flashParameters = flash_get_parameters(kFlashDev); + VerifyOrReturnError(flashParameters, CHIP_ERROR_INTERNAL); + + // To write zeros directly to the Flash memory without erasing whole page the start address must be aligned to the + // write_block_size value (alignedDacPrivKeyOffset), then we need align the required buffer size to the write_block_size as + // well (requiredFlashSpaceSize) to meet Flash write requirements, and we need to calculate how many bytes of the factory + // data set must be copied to the additional buffer space created after the alignments (bytesToLeftBefore, and + // bytesToLeftAfter) + size_t alignedDacPrivKeyOffset = ROUND_DOWN(mFactoryData.dacPrivateKeyOffset, flashParameters->write_block_size); + size_t bytesToLeftBefore = mFactoryData.dacPrivateKeyOffset % flashParameters->write_block_size; + size_t requiredFlashSpaceSize = ROUND_UP(kDACPrivateKeyLength + bytesToLeftBefore, flashParameters->write_block_size); + size_t bytesToLeftAfter = requiredFlashSpaceSize - bytesToLeftBefore - kDACPrivateKeyLength; + + // Allocate the memory buffer for removing DAC private key. + chip::Platform::ScopedMemoryBuffer removedPrivKeyBuffer; + VerifyOrReturnError(removedPrivKeyBuffer.Calloc(requiredFlashSpaceSize), CHIP_ERROR_NO_MEMORY); + + // Copy the existing parts of the aligned memory space to before and after the DAC private key space. + memcpy(removedPrivKeyBuffer.Get(), factoryDataPartition + alignedDacPrivKeyOffset, bytesToLeftBefore); + memcpy(removedPrivKeyBuffer.Get() + bytesToLeftBefore + kDACPrivateKeyLength, + factoryDataPartition + mFactoryData.dacPrivateKeyOffset + kDACPrivateKeyLength, bytesToLeftAfter); + + // Write aligned buffer directly to the Flash without erasing. + VerifyOrReturnError(0 == + flash_write(kFlashDev, kFactoryDataPartitionAddress + alignedDacPrivKeyOffset, + removedPrivKeyBuffer.Get(), requiredFlashSpaceSize), + CHIP_ERROR_INTERNAL); + + // Parse the factory data again and verify if the procedure finished successfully + VerifyOrReturnError(ParseFactoryData(factoryDataPartition, static_cast(factoryDataSize), &mFactoryData), + CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); + + // Verify if the factory data does not contain the DAC private key anymore. + VerifyOrReturnError(memcmp(mFactoryData.dac_priv_key.data, clearedDACPrivKey, kDACPrivateKeyLength) == 0, + CHIP_ERROR_INTERNAL); +#endif } return CHIP_NO_ERROR; } +#endif template CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBuffer) @@ -160,8 +268,18 @@ CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(c VerifyOrReturnError(outSignBuffer.size() >= signature.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL); ReturnErrorCodeIf(!mFactoryData.dac_cert.data, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); - ReturnErrorCodeIf(!mFactoryData.dac_priv_key.data, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); +#ifdef CONFIG_CHIP_CRYPTO_PSA + size_t outputLen = 0; + + psa_status_t err = psa_sign_message(mDACPrivKeyId, PSA_ALG_ECDSA(PSA_ALG_SHA_256), messageToSign.data(), messageToSign.size(), + signature.Bytes(), signature.Capacity(), &outputLen); + + VerifyOrReturnError(!err, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(outputLen == chip::Crypto::kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INTERNAL); + ReturnErrorOnFailure(signature.SetLength(outputLen)); +#else + ReturnErrorCodeIf(!mFactoryData.dac_priv_key.data, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); // Extract public key from DAC cert. ByteSpan dacCertSpan{ reinterpret_cast(mFactoryData.dac_cert.data), mFactoryData.dac_cert.len }; chip::Crypto::P256PublicKey dacPublicKey; @@ -171,6 +289,7 @@ CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(c LoadKeypairFromRaw(ByteSpan(reinterpret_cast(mFactoryData.dac_priv_key.data), mFactoryData.dac_priv_key.len), ByteSpan(dacPublicKey.Bytes(), dacPublicKey.Length()), keypair)); ReturnErrorOnFailure(keypair.ECDSA_sign_msg(messageToSign.data(), messageToSign.size(), signature)); +#endif return CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); } diff --git a/src/platform/nrfconnect/FactoryDataProvider.h b/src/platform/nrfconnect/FactoryDataProvider.h index 15dae3ab7e..576a49b177 100644 --- a/src/platform/nrfconnect/FactoryDataProvider.h +++ b/src/platform/nrfconnect/FactoryDataProvider.h @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_CHIP_CRYPTO_PSA +#include +#endif + #include #include #include @@ -109,6 +113,9 @@ class FactoryDataProvider : public chip::Credentials::DeviceAttestationCredentia { public: CHIP_ERROR Init(); +#ifdef CONFIG_CHIP_CRYPTO_PSA + CHIP_ERROR MoveDACPrivateKeyToSecureStorage(uint8_t * factoryDataPartition, size_t factoryDataSize); +#endif // ===== Members functions that implement the DeviceAttestationCredentialsProvider CHIP_ERROR GetCertificationDeclaration(MutableByteSpan & outBuffer) override; @@ -175,6 +182,9 @@ class FactoryDataProvider : public chip::Credentials::DeviceAttestationCredentia struct FactoryData mFactoryData; FlashFactoryData mFlashFactoryData; +#ifdef CONFIG_CHIP_CRYPTO_PSA + psa_key_id_t mDACPrivKeyId = to_underlying(chip::Crypto::KeyIdBase::DACPrivKey); +#endif }; } // namespace DeviceLayer