Skip to content

Commit

Permalink
[nrf noup] Move DAC priv key from Factory Data to PSA ITS
Browse files Browse the repository at this point in the history
We need a mechanism to move the DAC private key from the
factory data set to PSA ITS NVM storage during the first
boot of the device.
Then the DAC private key must be removed from the factory
data set and protected by overwriting.

In this commit:
- Added a method to FactoryDataProvider for moving and
removing DAC from the factory data set.
- Aligned the SignWithDeviceAttestationKey method to work with
stored DAC priv key in ITS NVM instead of raw data.
- Extended the FactoryDataParser module by Serialize method
that is responsible for creating CBOR-formated factory data
from the FactoryData struct.
- Added a Kconfig to define the PSA ITS NVM offset for
Matter keys.
x509 MBedTLS support seems to be not needed anymore -
we can disable it and save ~20kB of FLASH.
- Prevent the DAC private key from removal during the
factory reset - for now, disable
the CHIP_FACTORY_RESET_ERASE_NVS config by default.
  • Loading branch information
ArekBalysNordic committed Dec 29, 2023
1 parent 5f7a9f6 commit c46074b
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 22 deletions.
13 changes: 8 additions & 5 deletions config/nrfconnect/chip-module/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -284,4 +280,11 @@ 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_ITS_NVM_OFFSET
hex "Specify the offset for Matter crypto keys in the ITS NVM space"
default 0x30000
depends on CHIP_CRYPTO_PSA
help
Declared offset while using PSA key references. An example can override this definition based on implementation.

endif # CHIP
6 changes: 6 additions & 0 deletions src/platform/nrfconnect/CHIPPlatformConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
#endif // CHIP_CONFIG_SHA256_CONTEXT_ALIGN
#endif // CONFIG_CHIP_CRYPTO_PSA

#ifdef CONFIG_CHIP_CRYPTO_PSA
#ifndef CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE
#define CHIP_CONFIG_CRYPTO_PSA_KEY_ID_BASE CONFIG_CHIP_CRYPTO_PSA_ITS_NVM_OFFSET
#endif
#endif

// ==================== General Configuration Overrides ====================

#ifndef CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS
Expand Down
137 changes: 137 additions & 0 deletions src/platform/nrfconnect/FactoryDataParser.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,33 @@

#include "FactoryDataParser.h"

#include <zcbor_common.h>
#include <zcbor_decode.h>
#include <zcbor_encode.h>

#include <ctype.h>
#include <string.h>

#define zcbor_tstr_put_lit_cast(state, string) zcbor_tstr_encode_ptr(state, (char *) string, sizeof(string) - 1)

#define MAX_FACTORY_DATA_NESTING_LEVEL 4

static inline void updateSize(struct FactoryData * factoryData, const char * name, size_t len)
{
if (factoryData)
{
if (name)
{
// Add name string length + 1, actual length of the entry and 1 Byte of CBOR item header.
factoryData->actualSize += strlen(name) + 1 + len + 1;
}
else
{
factoryData->actualSize += len + 1;
}
}
}

static inline bool uint16_decode(zcbor_state_t * states, uint16_t * value)
{
uint32_t u32;
Expand Down Expand Up @@ -84,6 +104,87 @@ static bool DecodeEntry(zcbor_state_t * states, void * buffer, size_t bufferSize
return res;
}

bool SerializeFactoryData(uint8_t * factoryDataBuff, size_t factoryDataSize, const struct FactoryData * factoryData)
{
zcbor_state_t cbor_state[2];
zcbor_new_encode_state(cbor_state, MAX_FACTORY_DATA_NESTING_LEVEL, factoryDataBuff, factoryDataSize, 0);

bool res = zcbor_map_start_encode(cbor_state, 1);

res = res && zcbor_tstr_put_lit_cast(cbor_state, "version");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->version));
res = res && zcbor_tstr_put_lit_cast(cbor_state, "passcode");
res = res && zcbor_uint32_put(cbor_state, factoryData->passcode);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "sn");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->sn.data, factoryData->sn.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "date");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->date.data, factoryData->date.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "hw_ver_str");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->hw_ver_str.data, factoryData->hw_ver_str.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "rd_uid");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->rd_uid.data, factoryData->rd_uid.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "dac_cert");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->dac_cert.data, factoryData->dac_cert.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "pai_cert");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->pai_cert.data, factoryData->pai_cert.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "spake2_it");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->spake2_it));
res = res && zcbor_tstr_put_lit_cast(cbor_state, "spake2_salt");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->spake2_salt.data, factoryData->spake2_salt.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "spake2_verifier");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->spake2_verifier.data, factoryData->spake2_verifier.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "vendor_name");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->vendor_name.data, factoryData->vendor_name.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "product_name");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->product_name.data, factoryData->product_name.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "part_number");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->part_number.data, factoryData->part_number.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "product_url");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->product_url.data, factoryData->product_url.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "product_label");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->product_label.data, factoryData->product_label.len);
res = res && zcbor_tstr_put_lit_cast(cbor_state, "enable_key");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->enable_key.data, factoryData->enable_key.len);
if (factoryData->hwVerPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "hw_ver");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->hw_ver));
}
if (factoryData->vendorIdPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "vendor_id");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->vendor_id));
}
if (factoryData->productIdPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "product_id");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->product_id));
}
if (factoryData->discriminatorPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "discriminator");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->discriminator));
}
if (factoryData->productFinishPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "product_finish");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->product_finish));
}
if (factoryData->primaryColorPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "primary_color");
res = res && zcbor_uint32_put(cbor_state, (uint32_t) (factoryData->primary_color));
}
if (factoryData->userDataPresent)
{
res = res && zcbor_tstr_put_lit_cast(cbor_state, "user");
res = res && zcbor_bstr_encode_ptr(cbor_state, factoryData->user.data, factoryData->user.len);
}

res = res && zcbor_map_end_encode(cbor_state, 1);
return res;
}

bool FindUserDataEntry(struct FactoryData * factoryData, const char * entry, void * buffer, size_t bufferSize, size_t * outlen)
{
if ((!factoryData) || (!factoryData->user.data) || (!buffer) || (!outlen))
Expand Down Expand Up @@ -123,10 +224,16 @@ 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);

bool res = zcbor_map_start_decode(states);
updateSize(factoryData, NULL, 0);
struct zcbor_string currentString;

while (res)
Expand All @@ -142,44 +249,56 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData
if (strncmp("version", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint16_decode(states, &factoryData->version);
updateSize(factoryData, "version", sizeof(uint32_t));
}
else if (strncmp("hw_ver", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint16_decode(states, &factoryData->hw_ver);
factoryData->hwVerPresent = res;
updateSize(factoryData, "hw_ver", sizeof(uint32_t));
}
else if (strncmp("spake2_it", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_uint32_decode(states, &factoryData->spake2_it);
updateSize(factoryData, "spake2_it", sizeof(uint32_t));
}
else if (strncmp("vendor_id", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint16_decode(states, &factoryData->vendor_id);
factoryData->vendorIdPresent = res;
updateSize(factoryData, "vendor_id", sizeof(uint32_t));
}
else if (strncmp("product_id", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint16_decode(states, &factoryData->product_id);
factoryData->productIdPresent = res;
updateSize(factoryData, "product_id", sizeof(uint32_t));
}
else if (strncmp("discriminator", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint16_decode(states, &factoryData->discriminator);
factoryData->discriminatorPresent = res;
updateSize(factoryData, "discriminator", sizeof(uint32_t));
}
else if (strncmp("passcode", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_uint32_decode(states, &factoryData->passcode);

updateSize(factoryData, "passcode", sizeof(uint32_t));
}
else if (strncmp("sn", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->sn);
updateSize(factoryData, "sn", factoryData->sn.len);
}
else if (strncmp("date", (const char *) currentString.value, currentString.len) == 0)
{
// Date format is YYYY-MM-DD, so format needs to be validated and string parse to integer parts.
struct zcbor_string date;
res = res && zcbor_bstr_decode(states, &date);
updateSize(factoryData, "date", factoryData->date.len);
factoryData->date.data = (void *) date.value;
factoryData->date.len = date.len;
if (date.len == 10 && isdigit(date.value[0]) && isdigit(date.value[1]) && isdigit(date.value[2]) &&
isdigit(date.value[3]) && date.value[4] == '-' && isdigit(date.value[5]) && isdigit(date.value[6]) &&
date.value[7] == '-' && isdigit(date.value[8]) && isdigit(date.value[9]))
Expand All @@ -198,70 +317,88 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData
else if (strncmp("hw_ver_str", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->hw_ver_str);
updateSize(factoryData, "hw_ver_str", factoryData->hw_ver_str.len);
}
else if (strncmp("rd_uid", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->rd_uid);
updateSize(factoryData, "rd_uid", factoryData->rd_uid.len);
}
else if (strncmp("dac_cert", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_cert);
updateSize(factoryData, "dac_cert", factoryData->dac_cert.len);
}
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);
updateSize(factoryData, "dac_key", factoryData->dac_priv_key.len);
factoryData->dacPrivateKeyPresent = true;
}
else if (strncmp("pai_cert", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->pai_cert);
updateSize(factoryData, "pai_cert", factoryData->pai_cert.len);
}
else if (strncmp("spake2_salt", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->spake2_salt);
updateSize(factoryData, "spake2_salt", factoryData->spake2_salt.len);
}
else if (strncmp("spake2_verifier", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->spake2_verifier);
updateSize(factoryData, "spake2_verifier", factoryData->spake2_verifier.len);
}
else if (strncmp("vendor_name", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->vendor_name);
updateSize(factoryData, "vendor_name", factoryData->vendor_name.len);
}
else if (strncmp("product_name", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->product_name);
updateSize(factoryData, "product_name", factoryData->product_name.len);
}
else if (strncmp("part_number", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->part_number);
updateSize(factoryData, "part_number", factoryData->part_number.len);
}
else if (strncmp("product_url", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->product_url);
updateSize(factoryData, "product_url", factoryData->product_url.len);
}
else if (strncmp("product_label", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->product_label);
updateSize(factoryData, "product_label", factoryData->product_label.len);
}
else if (strncmp("enable_key", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->enable_key);
updateSize(factoryData, "enable_key", factoryData->enable_key.len);
}
else if (strncmp("product_finish", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint8_decode(states, &factoryData->product_finish);
factoryData->productFinishPresent = res;
updateSize(factoryData, "product_finish", sizeof(uint32_t));
}
else if (strncmp("primary_color", (const char *) currentString.value, currentString.len) == 0)
{
res = res && uint8_decode(states, &factoryData->primary_color);
factoryData->primaryColorPresent = res;
updateSize(factoryData, "primary_color", sizeof(uint32_t));
}
else if (strncmp("user", (const char *) currentString.value, currentString.len) == 0)
{
factoryData->user.data = (void *) states->payload;
res = res && zcbor_any_skip(states, NULL);
factoryData->user.len = (size_t) ((void *) states->payload - factoryData->user.data);
updateSize(factoryData, "user", factoryData->user.len);
factoryData->userDataPresent = true;
}
else
{
Expand Down
17 changes: 17 additions & 0 deletions src/platform/nrfconnect/FactoryDataParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct FactoryData
{
uint16_t version;
struct FactoryDataString sn;
struct FactoryDataString date;
uint16_t date_year;
uint8_t date_month;
uint8_t date_day;
Expand Down Expand Up @@ -66,8 +67,24 @@ struct FactoryData
bool discriminatorPresent;
bool productFinishPresent;
bool primaryColorPresent;
bool userDataPresent;
bool dacPrivateKeyPresent;
size_t actualSize;
};

/**
* @brief Serialize the new factory data set to binary CBOR format.
*
* Serialized data will not contain DAC private key
*
* @param factoryDataBuff buffer to store serialized factory data set
* @param factoryDataSize size of the output buffer
* @param factoryData reference pointer to the existing factory data set
* @return true if the factory data set was serialized successfully
* @return false if the error occurred, for example the output buffer size was to small.
*/
bool SerializeFactoryData(uint8_t * factoryDataBuff, size_t factoryDataSize, const struct FactoryData * factoryData);

/**
* @brief Parses raw factory data into the factory data structure.
*
Expand Down
Loading

0 comments on commit c46074b

Please sign in to comment.