diff --git a/config/nrfconnect/chip-module/Kconfig b/config/nrfconnect/chip-module/Kconfig index 6afd11cca2..89e5c92664 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 @@ -249,7 +245,7 @@ endif # CHIP_FACTORY_DATA_BUILD # See config/zephyr/Kconfig for full definition config CHIP_FACTORY_RESET_ERASE_NVS bool - default y + default y if !CHIP_CRYPTO_PSA # For now erasing whole NVS sector is not supported for PSA Crypto config CHIP_LOG_SIZE_OPTIMIZATION bool "Disable some detailed logs to decrease flash usage" 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..f193337e95 100644 --- a/src/platform/nrfconnect/FactoryDataParser.c +++ b/src/platform/nrfconnect/FactoryDataParser.c @@ -17,12 +17,14 @@ #include "FactoryDataParser.h" +#include #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 +125,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 +216,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->dac_priv_key_offset = (size_t) ((uint8_t *) factoryData->dac_priv_key.data - buffer); } else if (strncmp("pai_cert", (const char *) currentString.value, currentString.len) == 0) { @@ -269,5 +277,8 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData } } - return res && zcbor_list_map_end_force_decode(states); + res = res && zcbor_list_map_end_force_decode(states); + factoryData->actualSize = (size_t) (states->payload - buffer); + + return res; } diff --git a/src/platform/nrfconnect/FactoryDataParser.h b/src/platform/nrfconnect/FactoryDataParser.h index 54be7b80b8..1a1431c960 100644 --- a/src/platform/nrfconnect/FactoryDataParser.h +++ b/src/platform/nrfconnect/FactoryDataParser.h @@ -66,6 +66,8 @@ struct FactoryData bool discriminatorPresent; bool productFinishPresent; bool primaryColorPresent; + size_t actualSize; + size_t dac_priv_key_offset; }; /** diff --git a/src/platform/nrfconnect/FactoryDataProvider.cpp b/src/platform/nrfconnect/FactoryDataProvider.cpp index caa1ad434f..4441553c53 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,83 @@ 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_INCORRECT_STATE; } - // Check if factory data version is correct - if (mFactoryData.version != CONFIG_CHIP_FACTORY_DATA_VERSION) + uint8_t ClearedDACPrivKey[kDACPrivateKeyLength]; + memset(ClearedDACPrivKey, 0xFF, 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 it is a security violation + // TODO: Consider other reactions to this violation: blocking the application, factory reset, etc. + if (psa_get_key_attributes(mDACPrivKeyId, &attributes) != PSA_SUCCESS) + { + ChipLogProgress(DeviceLayer, "Found DAC Private Key in factory data set. Copying to ITS..."); + + 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)); + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attributes, mDACPrivKeyId); + 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); + } + + // Allocate needed memory space to perform removal and moving DAC private key + const flash_parameters * flashParameters = flash_get_parameters(kFlashDev); + VerifyOrReturnError(flashParameters, CHIP_ERROR_INTERNAL); + size_t alignedSize = ROUND_UP(mFactoryData.actualSize, flashParameters->write_block_size); + chip::Platform::ScopedMemoryBuffer factoryDataBuff; + VerifyOrReturnError(factoryDataBuff.Calloc(alignedSize), CHIP_ERROR_NO_MEMORY); + + // Copy existing factoryDataSet + memcpy(factoryDataBuff.Get(), factoryDataPartition, alignedSize); + + // Overwrite the DAC private key + memcpy(factoryDataBuff.Get() + mFactoryData.dac_priv_key_offset, ClearedDACPrivKey, kDACPrivateKeyLength); + + // Replace the old factory data set with the new one. + VerifyOrReturnError(0 == flash_erase(kFlashDev, kFactoryDataPartitionAddress, kFactoryDataPartitionSize), + CHIP_ERROR_INTERNAL); + VerifyOrReturnError(0 == flash_write(kFlashDev, kFactoryDataPartitionAddress, factoryDataBuff.Get(), alignedSize), + 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); } return CHIP_NO_ERROR; } +#endif template CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBuffer) @@ -160,8 +247,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 +268,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