Skip to content

Commit

Permalink
[nrfconnect] Created a CMAKE script to generate factory data (#19153)
Browse files Browse the repository at this point in the history
- A newly created CMAKE script allows generating factory data JSON and
.hex files during the building.
- Created .hex file containing factory data partition can be merged with the firmware.
- The whole process is controlled by kconfigs created for nrfconnect platform.
- The CHIPDevicePlatformConfig definitions connected with factory data were updated
according to created kconfigs.
- Using proper kconfigs it is possible to generate the SPAKE2 passcode verifier and
Rotating Device ID Unique ID during building.
- Removed Certificate Declaration from factory data.
- Changed manufacturing date fromat to ISO 8601
- There is also the possibility to use the default values for development purposes.
  • Loading branch information
ArekBalysNordic authored Jun 9, 2022
1 parent 0adc4f3 commit 44a6f1b
Show file tree
Hide file tree
Showing 7 changed files with 518 additions and 46 deletions.
9 changes: 9 additions & 0 deletions config/nrfconnect/chip-module/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ if (CONFIG_CHIP)
include(ExternalProject)
include(../../zephyr/ota-image.cmake)
include(../../zephyr/zephyr-util.cmake)
include(generate_factory_data.cmake)

# ==============================================================================
# Declare configuration variables and define constants
Expand Down Expand Up @@ -350,4 +351,12 @@ if (CONFIG_CHIP_OTA_IMAGE_BUILD)
add_dependencies(chip-ota-image dfu_multi_image_pkg)
endif()

# ==============================================================================
# Define 'factory_data' target for generating a factory data partition
# ==============================================================================

if(CONFIG_CHIP_FACTORY_DATA_BUILD)
nrfconnect_generate_factory_data()
endif()

endif() # CONFIG_CHIP
152 changes: 152 additions & 0 deletions config/nrfconnect/chip-module/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,155 @@ config CHIP_DEBUG_SYMBOLS
default y
help
Build the application with debug symbols.

config CHIP_FACTORY_DATA_BUILD
bool "Enable Factory Data build"
default n
help
Enables generation of factory data during the building.
It requires factory_data partition to exist in the partition manager
configuration file pm_static.yml.
As a result a new output file factory_data.hex will be created.

if CHIP_FACTORY_DATA_BUILD

# Factory data definitions
config CHIP_MERGE_FACTORY_DATA_WITH_FIRMWARE
bool "Enable merging generated factory data with the build target .hex file"
default y
help
Enables merging generated factory data with the build target merged.hex file.
As a result, a new output file merged.hex will consist of all partitions including
factory data.

# Use default certificates without generating or providing them
config CHIP_FACTORY_DATA_USE_DEFAULTS_CERTS
bool "Use default certificates located in Matter repository"
default y
help
Pre-generated certificates can be used for development purpose.
This config includes default pre-generated certificates
which are located in credentials/development/attestation/ directory
instead of generating new ones.
If this config is set to `n` new certificates will be generated.

# Configs for SPAKE2 generation
config CHIP_FACTORY_DATA_GENERATE_SPAKE2_VERIFIER
bool "Enable spake2 verifier generation"
help
Enables generation of spake2 verifier according to
given iteration counter, salt and passcode.
To generate Spake2 verifier a spake2p executable must be available
from system variables environment.

config CHIP_DEVICE_GENERATE_ROTATING_DEVICE_UID
bool "Enable generation of a new Rotating device id unique id"
default y
help
Enables generation of a new Rotating device id unique id.

endif #CHIP_FACTORY_DATA_BUILD

# Factory data parameters
config CHIP_DEVICE_SERIAL_NUMBER
string "Serial number of device"
default "11223344556677889900"
help
A serial number parameter defines an unique number of manufactured device.
Maximum length of serial number is 32 characters.

config CHIP_DEVICE_VENDOR_NAME
string "Human-readable vendor name"
default "Nordic Semiconductor ASA"
help
A human-readable vendor name which provides a simple string
containing identification of device's vendor for the Content APP.
This information should be included in the Matter Basic Cluster.

config CHIP_DEVICE_PRODUCT_NAME
string "Human-readable product name"
default "not-specified"
help
A human-readable product name which provides a simple string
containing identification of the product for the Content APP.

config CHIP_DEVICE_MANUFACTURING_DATE
string "Manufacturing date in ISO 8601"
default "2022-01-01"
help
A manufacturing date specifies the date that the device was manufactured.
The format used for providing a manufacturing date is ISO 8601 e.g. YYYY-MM-DD.

config CHIP_DEVICE_HARDWARE_VERSION
int "Integer representation of hardware version"
default 0
help
A hardware version number specifies the version number
of the hardware of the device. The meaning of its value,
and the versioning scheme, are vendor defined.

config CHIP_DEVICE_HARDWARE_VERSION_STRING
string "user-friendly string representation of hardware version"
default "prerelease"
help
A hardware version string parameter specifies the version
of the hardware of the device as a more user-friendly value
than that represented by the hardware version integer value.
The meaning of its value, and the versioning scheme, are
vendor defined.

config CHIP_DEVICE_DISCRIMINATOR
hex "Device pairing discriminator"
default 0xF00
help
A 12-bit value matching the field of the same name in
the setup code. Discriminator is used during
a discovery process.

config CHIP_DEVICE_SPAKE2_PASSCODE
int "Spake2+ passcode"
default 20202021
range 1 99999998
help
A pairing passcode is a 27-bit unsigned integer which serves
as a proof of possession during commissioning.
Its value shall be restricted to the values 0x0000001 to 0x5F5E0FE
(00000001 to 99999998 in decimal), excluding the invalid Passcode values:
- 00000000, 11111111, 22222222, 33333333, 44444444, 55555555,
66666666, 77777777, 88888888, 99999999, 12345678, 87654321.

config CHIP_DEVICE_SPAKE2_IT
int "Spake2+ iteration count"
default 1000
help
The Spake2 iteration count is associated with the ephemeral
PAKE passcode verifier to be used for the commissioning.
The iteration count is used as a crypto parameter to process
spake2 verifier.

config CHIP_DEVICE_SPAKE2_SALT
string "Spake2+ salt in string format"
default "U1BBS0UyUCBLZXkgU2FsdA=="
help
The spake2 salt is random data that is used as an additional input
to a one-way function that “hashes” data.
A new salt should be randomly generated for each password.
The minimum length of spake2 salt is 16 Bytes.
The maximum length of spake2 salt is 32 Bytes.

config CHIP_DEVICE_SPAKE2_TEST_VERIFIER
string "Testing spake2+ verifier"
default "uWFwqugDNGiEck/po7KHwwMwwqZgN10XuyBajPGuyzUEV/iree4lOrao5GuwnlQ65CJzbeUB49s31EH+NEkg0JVI5MGCQGMMT/SRPFNRODm3wH/MBiehuFc6FJ/NH6Rmzw=="
help
The spake 2 verifier generated using default SPAKE2 salt,
iteration count and passcode. This value can be used for development
or testing purposes.
Generated with:
spake2p gen-verifier -o - -i 1000 -s "U1BBS0UyUCBLZXkgU2FsdA==" -p 20202021

config CHIP_DEVICE_ROTATING_DEVICE_UID
string "A rotating device id unique id"
default "91a9c12a7c80700a31ddcfa7fce63e44"
help
A device rotating id unique id which will be generated if
this config is not set in prj.conf file.
206 changes: 206 additions & 0 deletions config/nrfconnect/chip-module/generate_factory_data.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#
# Copyright (c) 2022 Project CHIP Authors
#
# 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.
#


# Create a JSON file based on factory data given via kConfigs.
#
# This function creates a list of arguments for external script and then run it to write a JSON file.
# Created JSON file can be checked using JSON SCHEMA file if it is provided.
#
# This script can be manipulated using following kConfigs:
# - To merge generated factory data with final zephyr.hex file set kConfig CONFIG_CHIP_MERGE_FACTORY_DATA_WITH_FIRMWARE=y
# - To use default certification paths set CONFIG_CHIP_FACTORY_DATA_USE_DEFAULTS_CERTS_PATH=y
#
# During generation process a some file will be created in zephyr's build directory:
# - <factory_data_target>.args a file containing arguments for nrfconnect_generate_partition.py script.
# - <factory_data_target>.json a file containing all factory data written in JSON format.
#
# [Args]:
# factory_data_target - a name for target to generate factory_data.
# script_path - a path to script that makes a JSON factory data file from given arguments.
# schema_path - a path to JSON schema file which can be used to verify generated factory data JSON file.
# This argument is optional, if you don't want to verify the JSON file put it empty "".
# output_path - a path to output directory, where created JSON file will be stored.
function(nrfconnect_create_factory_data_json factory_data_target script_path schema_path output_path)

# set script args for future purpose
set(script_args)
## generate all script arguments
string(APPEND script_args "--sn \"${CONFIG_CHIP_DEVICE_SERIAL_NUMBER}\"\n")
string(APPEND script_args "--date \"${CONFIG_CHIP_DEVICE_MANUFACTURING_DATE}\"\n")
string(APPEND script_args "--vendor_id ${CONFIG_CHIP_DEVICE_VENDOR_ID}\n")
string(APPEND script_args "--product_id ${CONFIG_CHIP_DEVICE_PRODUCT_ID}\n")
string(APPEND script_args "--vendor_name \"${CONFIG_CHIP_DEVICE_VENDOR_NAME}\"\n")
string(APPEND script_args "--product_name \"${CONFIG_CHIP_DEVICE_PRODUCT_NAME}\"\n")
string(APPEND script_args "--hw_ver ${CONFIG_CHIP_DEVICE_HARDWARE_VERSION}\n")
string(APPEND script_args "--hw_ver_str \"${CONFIG_CHIP_DEVICE_HARDWARE_VERSION_STRING}\"\n")

# check if Rotating Device Id Unique Id should be generated
if(NOT CONFIG_CHIP_DEVICE_GENERATE_ROTATING_DEVICE_UID)
if(NOT DEFINED CONFIG_CHIP_DEVICE_ROTATING_DEVICE_UID)
message(FATAL_ERROR "CHIP_DEVICE_ROTATING_DEVICE_UID was not provided. To generate it use CONFIG_CHIP_DEVICE_GENERATE_ROTATING_DEVICE_UID=y")
else()
string(APPEND script_args "--rd_uid \"${CONFIG_CHIP_DEVICE_ROTATING_DEVICE_UID}\"\n")
endif()
endif()

# for development purpose user can use default certs instead of generating or providing them
if(CONFIG_CHIP_FACTORY_DATA_USE_DEFAULTS_CERTS)
# convert decimal PID to its hexadecimal representation to find out certification files in repository
math(EXPR LOCAL_PID "${CONFIG_CHIP_DEVICE_PRODUCT_ID}" OUTPUT_FORMAT HEXADECIMAL)
string(SUBSTRING ${LOCAL_PID} 2 -1 raw_pid)
# all certs are located in ${CHIP_ROOT}/credentials/development/attestation
# it can be used during development without need to generate new certifications
string(APPEND script_args "--dac_cert \"${CHIP_ROOT}/credentials/development/attestation/Matter-Development-DAC-${raw_pid}-Cert.der\"\n")
string(APPEND script_args "--dac_key \"${CHIP_ROOT}/credentials/development/attestation/Matter-Development-DAC-${raw_pid}-Key.der\"\n")
string(APPEND script_args "--pai_cert \"${CHIP_ROOT}/credentials/development/attestation/Matter-Development-PAI-noPID-Cert.der\"\n")
else()
# try to generate a new DAC and PAI certs and DAC key
# request script to generate a new certificates
# by adding an argument to script_args
find_program(chip-cert NAMES chip-cert)
if(NOT chip-cert)
message(FATAL_ERROR "Could not find chip_cert_path executable in PATH")
endif()
string(APPEND script_args "--chip_cert_path ${chip-cert}\n")
endif()

# add Password-Authenticated Key Exchange parameters
string(APPEND script_args "--spake2_it \"${CONFIG_CHIP_DEVICE_SPAKE2_IT}\"\n")
string(APPEND script_args "--spake2_salt \"${CONFIG_CHIP_DEVICE_SPAKE2_SALT}\"\n")
string(APPEND script_args "--discriminator ${CONFIG_CHIP_DEVICE_DISCRIMINATOR}\n")
string(APPEND script_args "--passcode ${CONFIG_CHIP_DEVICE_SPAKE2_PASSCODE}\n")

# check if spake2 verifier should be generated using script
if(CONFIG_CHIP_FACTORY_DATA_GENERATE_SPAKE2_VERIFIER)
# request script to generate a new spake2_verifier
# by adding an argument to script_args
find_program(spake_exe NAMES spake2p)
if(NOT spake_exe)
message(FATAL_ERROR "Could not find spake2p executable in PATH")
endif()
string(APPEND script_args "--spake2p_path ${spake_exe}\n")
else()
# Spake2 verifier should be provided using kConfig
string(APPEND script_args "--spake2_verifier \"${CONFIG_CHIP_DEVICE_SPAKE2_TEST_VERIFIER}\"\n")
endif()

# Set output JSON file and path to SCHEMA file to validate generated factory data
string(APPEND script_args "-o \"${output_path}/${factory_data_target}.json\"\n")
string(APPEND script_args "-s \"${schema_path}\"\n")

# execute first script to create a JSON file
separate_arguments(separated_script_args NATIVE_COMMAND ${script_args})
add_custom_target(${factory_data_target} ALL
COMMAND ${Python3_EXECUTABLE} ${FACTORY_DATA_SCRIPT_PATH} ${separated_script_args}
COMMENT "Generating new Factory Data..."
)

endfunction()


# Create a .hex file with factory data in CBOR format.
#
# This function creates a .hex and .cbor files from given JSON factory data file.
#
#
# During generation process some files will be created in zephyr's build directory:
# - <factory_data_target>_cbor.args a file containing arguments for nrfconnect_generate_partition.py script.
# - <factory_data_target>.hex a file containing all factory data in CBOR format.
# - <factory_data_target>.bin a binary file containing all raw factory data in CBOR format.
# - <factory_data_target>.cbor a file containing all factory data in CBOR format.
#
# [Args]:
# factory_data_target - a name for target to generate factory_data.
# script_path - a path to script that makes a factory data .hex file from given arguments.
# output_path - a path to output directory, where created JSON file will be stored.
# output_hex - an output variable to store a .hex file. This variable can be used to merge with firmware .hex file.
function(nrfconnect_create_factory_data_hex_file factory_data_target script_path output_path output_hex)

# Pass the argument list via file
set(cbor_script_args "-i ${output_path}/${factory_data_target}.json\n")
string(APPEND cbor_script_args "-o ${output_path}/${factory_data_target}\n")
# get partition address and offset from partition manager during compilation
string(APPEND cbor_script_args "--offset $<TARGET_PROPERTY:partition_manager,PM_FACTORY_DATA_ADDRESS>\n")
string(APPEND cbor_script_args "--size $<TARGET_PROPERTY:partition_manager,PM_FACTORY_DATA_OFFSET>\n")
string(APPEND cbor_script_args "-r\n")

# execute second script to create a hex file containing factory data in cbor format
separate_arguments(separated_cbor_script_args NATIVE_COMMAND ${cbor_script_args})
set(factory_data_hex ${output_path}/${factory_data_target}.hex)

# return output hex to parent scope
set(${output_hex} ${factory_data_hex} PARENT_SCOPE)
add_custom_command(OUTPUT ${factory_data_hex}
COMMAND ${Python3_EXECUTABLE} ${script_path} ${separated_cbor_script_args}
COMMENT "Generating factory data HEX file..."
DEPENDS ${factory_data_target}
)

endfunction()

# Generate factory data partition using given args
#
#
# During generation process a some file will be created in zephyr's build directory:
# - merged.hex a file containing firmware and factory data merged to single file
# - factory_data.hex a file containing only a factory data partition including proper offset
#
function(nrfconnect_generate_factory_data)

find_package(Python REQUIRED)

# CHIP_ROOT must be provided as a reference set all localization of scripts
if(NOT CHIP_ROOT)
message(FATAL_ERROR "CHIP_ROOT variable is not set, please add it to CMakeLists.txt file")
endif()

# Localize all scripts needed to generate factory data partition
set(FACTORY_DATA_SCRIPT_PATH ${CHIP_ROOT}/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py)
set(GENERATE_CBOR_SCRIPT_PATH ${CHIP_ROOT}/scripts/tools/nrfconnect/nrfconnect_generate_partition.py)
SET(MERGE_HEX_SCRIPT_PATH ${CHIP_ROOT}/config/nrfconnect/chip-module/merge_factory_data.py)
set(FACTORY_DATA_SCHEMA_PATH ${CHIP_ROOT}/scripts/tools/nrfconnect/nrfconnect_factory_data.schema)
set(OUTPUT_FILE_PATH ${APPLICATION_BINARY_DIR}/zephyr)

# create a JSON file with all factory data
nrfconnect_create_factory_data_json(factory_data
${FACTORY_DATA_SCRIPT_PATH}
${FACTORY_DATA_SCHEMA_PATH}
${OUTPUT_FILE_PATH})

# create a .hex file with factory data in CBOR format based on the JSON file created previously
nrfconnect_create_factory_data_hex_file(factory_data
${GENERATE_CBOR_SCRIPT_PATH}
${OUTPUT_FILE_PATH}
factory_data_hex)

if(CONFIG_CHIP_MERGE_FACTORY_DATA_WITH_FIRMWARE)
# set custom target for merging factory_data hex file
add_custom_target(factory_data_merge
DEPENDS ${factory_data_hex}
)
set_property(GLOBAL PROPERTY
factory_data_PM_HEX_FILE
${factory_data_hex}
)
set_property(GLOBAL PROPERTY
${parent_slot}_PM_TARGET
${target_name}_merge
)
endif()


endfunction()
Loading

0 comments on commit 44a6f1b

Please sign in to comment.