diff --git a/config/nrfconnect/chip-module/CMakeLists.txt b/config/nrfconnect/chip-module/CMakeLists.txt index f88a9a81d246da..ac4a8f7e561aa1 100644 --- a/config/nrfconnect/chip-module/CMakeLists.txt +++ b/config/nrfconnect/chip-module/CMakeLists.txt @@ -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 @@ -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 diff --git a/config/nrfconnect/chip-module/Kconfig b/config/nrfconnect/chip-module/Kconfig index 1f657aef3b9657..54dbe2cd8ef750 100644 --- a/config/nrfconnect/chip-module/Kconfig +++ b/config/nrfconnect/chip-module/Kconfig @@ -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. diff --git a/config/nrfconnect/chip-module/generate_factory_data.cmake b/config/nrfconnect/chip-module/generate_factory_data.cmake new file mode 100644 index 00000000000000..1928b3e4ff1441 --- /dev/null +++ b/config/nrfconnect/chip-module/generate_factory_data.cmake @@ -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: +# - .args a file containing arguments for nrfconnect_generate_partition.py script. +# - .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: +# - _cbor.args a file containing arguments for nrfconnect_generate_partition.py script. +# - .hex a file containing all factory data in CBOR format. +# - .bin a binary file containing all raw factory data in CBOR format. +# - .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 $\n") +string(APPEND cbor_script_args "--size $\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() diff --git a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py index 007ae9cb3d92a9..e0a3b690c1a5a3 100644 --- a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py +++ b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py @@ -15,11 +15,11 @@ # limitations under the License. # +from os.path import exists import sys import json import jsonschema import secrets -from random import randint import argparse import subprocess import logging as log @@ -71,10 +71,10 @@ def _validate_args(self): self._user_data = json.loads(self._args.user) except json.decoder.JSONDecodeError as e: raise AssertionError("Provided wrong user data, this is not a Json format! {}".format(e)) - assert (not self._args.spake2_verifier or (self._args.passcode and self._args.spake2p_path)), \ + assert (self._args.spake2_verifier or (self._args.passcode and self._args.spake2p_path)), \ "Can not find spake2 verifier, to generate a new one please provide passcode (--passcode) and path to spake2p script (--spake2p_path)" - if not self._args.rd_uid: - log.warning("Can not find rotating device UID in provided arguments list. A new one will be generated.") + assert (self._args.chip_cert_path or (self._args.dac_cert and self._args.pai_cert and self._args.dac_key)), \ + "Can not find paths to DAC or PAI certificates .der files. To generate a new ones please provide a path to chip-cert executable (--chip_cert_path)" assert self._args.output.endswith(".json"), \ "Output path doesn't contain .json file path. ({})".format(self._args.output) assert not (self._args.passcode in INVALID_PASSCODES), \ @@ -96,13 +96,17 @@ def generate_json(self): """ # generate missing data if needed if not self._args.rd_uid: - rd_uid = self._generate_rotating_device_uid() + if self._args.generate_rd_uid: + rd_uid = self._generate_rotating_device_uid() + else: + # rotating device ID unique ID was not provided, so do not store it in factory data. + rd_uid = None else: rd_uid = self._args.rd_uid if not self._args.spake2_verifier: spake_2_verifier = base64.b64decode(self._generate_spake2_verifier()).hex() else: - spake_2_verifier = self._args.spake2_verifier.hex() + spake_2_verifier = base64.b64decode(self._args.spake2_verifier).hex() try: json_file = open(self._args.output, "w+") @@ -110,26 +114,30 @@ def generate_json(self): print("Can not create JSON file in this location: {}".format(self._args.output)) sys.exit(-1) with json_file: - # serialize mandatory data + # serialize data self._add_entry("sn", self._args.sn) + self._add_entry("vendor_id", self._args.vendor_id) + self._add_entry("product_id", self._args.product_id) + self._add_entry("vendor_name", self._args.vendor_name) + self._add_entry("product_name", self._args.product_name) self._add_entry("date", self._args.date) self._add_entry("hw_ver", self._args.hw_ver) self._add_entry("hw_ver_str", self._args.hw_ver_str) - self._add_entry("rd_uid", HEX_PREFIX + rd_uid) self._add_entry("dac_cert", self._process_der(self._args.dac_cert)) self._add_entry("dac_key", self._process_der(self._args.dac_key)) self._add_entry("pai_cert", self._process_der(self._args.pai_cert)) - self._add_entry("cd", self._process_der(self._args.cd)) if self._args.include_passcode: self._add_entry("passcode", self._args.passcode) self._add_entry("spake2_it", self._args.spake2_it) self._add_entry("spake2_salt", self._args.spake2_salt) self._add_entry("spake2_verifier", HEX_PREFIX + spake_2_verifier) self._add_entry("discriminator", self._args.discriminator) + if rd_uid: + self._add_entry("rd_uid", HEX_PREFIX + rd_uid) + # add user-specific data + self._add_entry("user", self._args.user) factory_data_dict = dict(self._factory_data) - # add user-specific data - factory_data_dict["user"] = self._user_data json_object = json.dumps(factory_data_dict) is_json_valid = True @@ -146,7 +154,7 @@ def generate_json(self): def _add_entry(self, name: str, value: any): """ Add single entry to list of tuples ("key", "value") """ - if value: + if value or (isinstance(value, int) and value == 0): log.debug("Adding entry '{}' with size {} and type {}".format(name, sys.getsizeof(value), type(value))) self._factory_data.append((name, value)) @@ -158,6 +166,7 @@ def _generate_spake2_verifier(self): def _generate_rotating_device_uid(self): """ If rotating device unique ID has not been provided it should be generated """ + log.warning("Can not find rotating device UID in provided arguments list. A new one will be generated.") rdu = secrets.token_bytes(16) log.info("\n\nThe new rotate device UID: {}\n".format(rdu.hex())) return rdu.hex() @@ -208,48 +217,76 @@ def allow_any_int(i): return int(i, 0) help="Run this script with DEBUG logging level") parser.add_argument("--include_passcode", action="store_true", help="passcode is used only for generating Spake2 Verifier to include it in factory data add this argument") + parser.add_argument("--overwrite", action="store_true", + help="If output JSON file exist this argument allows to generate new factory data and overwrite it.") # Json known-keys values # mandatory keys mandatory_arguments.add_argument("--sn", type=str, required=True, - help="[ascii string] Provide serial number.") + help="[ascii string] Serial number of a device which can be used to identify \ + the serial number field in the Matter certificate structure. \ + Maximum length of serial number is 20 bytes. \ + Strings longer than 20 bytes will be declined in script") + mandatory_arguments.add_argument("--vendor_id", type=allow_any_int, + help="[int | hex int] Provide Vendor Identification Number") + mandatory_arguments.add_argument("--product_id", type=allow_any_int, + help="[int | hex int] Provide Product Identification Number") + mandatory_arguments.add_argument("--vendor_name", type=str, + help="[string] provide human-readable vendor name") + mandatory_arguments.add_argument("--product_name", type=str, + help="[string] provide human-readable product name") mandatory_arguments.add_argument("--date", type=str, required=True, - help="[ascii string] Provide manufacturing date in format MM.DD.YYYY_GG:MM .") + help="[ascii string] Provide manufacturing date \ + A manufacturing date specifies the date that the Node was manufactured. \ + Used format for providing a manufacturing date is ISO 8601 e.g. YYYY-MM-DD.") mandatory_arguments.add_argument("--hw_ver", type=allow_any_int, required=True, help="[int | hex int] Provide hardware version in int format.") mandatory_arguments.add_argument("--hw_ver_str", type=str, required=True, help="[ascii string] Provide hardware version in string format.") - mandatory_arguments.add_argument("--dac_cert", type=str, required=True, - help="[.der] Provide the path to .der file containing DAC certificate.") - mandatory_arguments.add_argument("--dac_key", type=str, required=True, - help="[.der] Provide the path to .der file containing DAC keys.") - mandatory_arguments.add_argument("--pai_cert", type=str, required=True, - help="[.der] Provide the path to .der file containing PAI certificate.") - mandatory_arguments.add_argument("--cd", type=str, required=True, - help="[.der] Provide the path to .der file containing Certificate Declaration.") - mandatory_arguments.add_argument("--spake2_it", type=allow_any_int, - help="[int | hex int] Provide Spake2 Iteraction Counter.") + mandatory_arguments.add_argument("--spake2_it", type=allow_any_int, required=True, + help="[int | hex int] Provide Spake2 Iteration Counter.") mandatory_arguments.add_argument("--spake2_salt", type=str, required=True, help="[ascii string] Provide Spake2 Salt.") - optional_arguments.add_argument("--discriminator", type=allow_any_int, required=True, - help="[int] Provide BLE pairing discriminator.") + mandatory_arguments.add_argument("--discriminator", type=allow_any_int, required=True, + help="[int] Provide BLE pairing discriminator. \ + A 12-bit value matching the field of the same name in \ + the setup code. Discriminator is used during a discovery process.") # optional keys + optional_arguments.add_argument("--chip_cert_path", type=str, + help="Generate DAC and PAI certificates instead giving a path to .der files. This option requires a path to chip-cert executable." + "By default You can find spake2p in connectedhomeip/src/tools/chip-cert directory and build it there.") + optional_arguments.add_argument("--dac_cert", type=str, + help="[.der] Provide the path to .der file containing DAC certificate.") + optional_arguments.add_argument("--dac_key", type=str, + help="[.der] Provide the path to .der file containing DAC keys.") + optional_arguments.add_argument("--pai_cert", type=str, + help="[.der] Provide the path to .der file containing PAI certificate.") + optional_arguments.add_argument("--generate_rd_uid", action="store_true", + help="Generate a new rotating device unique ID, print it out to console output and store it in factory data.") optional_arguments.add_argument("--rd_uid", type=str, - help="[hex string] Provide the rotating device unique ID. To generate the new rotate device unique ID use --rd_uid_gen.") + help="[hex string] Provide the rotating device unique ID. If this argument is not provided a new rotating device id unique id will be generated.") optional_arguments.add_argument("--passcode", type=allow_any_int, help="[int | hex] Default PASE session passcode. (This is mandatory to generate Spake2 Verifier).") optional_arguments.add_argument("--spake2p_path", type=str, help="[string] Provide a path to spake2p. By default You can find spake2p in connectedhomeip/src/tools/spake2p directory and build it there.") optional_arguments.add_argument("--spake2_verifier", type=str, - help="[hex string] Provide Spake2 Verifier without generating it.") + help="[ascii string] Provide Spake2 Verifier without generating it.") optional_arguments.add_argument("--user", type=str, help="[string] Provide additional user-specific keys in Json format: {'name_1': 'value_1', 'name_2': 'value_2', ... 'name_n', 'value_n'}.") args = parser.parse_args() + if(args.chip_cert_path): + print("Generating DAC and PAI certificates is not supported yet") + if args.verbose: log.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=log.DEBUG) else: log.basicConfig(format='[%(asctime)s] %(message)s', level=log.INFO) + # check if json file already exist + if(exists(args.output) and not args.overwrite): + log.error("Output file: {} already exist, to create a new one add argument '--overwrite'. By default overwriting is disabled".format(args.output)) + return + generator = FactoryDataGenerator(args) generator.generate_json() diff --git a/scripts/tools/nrfconnect/nrfconnect_factory_data.schema b/scripts/tools/nrfconnect/nrfconnect_factory_data.schema index 7ec7804d746b84..393c2b152b66b3 100644 --- a/scripts/tools/nrfconnect/nrfconnect_factory_data.schema +++ b/scripts/tools/nrfconnect/nrfconnect_factory_data.schema @@ -5,17 +5,20 @@ "type": "object", "required": [ "sn", + "vendor_id", + "product_id", + "vendor_name", + "product_name", "date", "hw_ver", "hw_ver_str", - "rd_uid", "dac_cert", "dac_key", "pai_cert", - "cd", "spake2_it", "spake2_salt", - "spake2_verifier" + "spake2_verifier", + "discriminator" ], "properties": { "sn": { @@ -23,12 +26,32 @@ "type": "string", "maxLength": 32 }, + "vendor_id": { + "description": "Vendor Identifier", + "type": "integer", + "minimum": 0, + "maximum": 65524 + }, + "product_id": { + "description": "Product Identifier", + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "vendor_name": { + "description": "human-readable vendor name", + "type": "string", + "maxLength": 32 + }, + "product_name": { + "description": "human-readable product name", + "type": "string", + "maxLength": 32 + }, "date": { - "description": "Manufacturing date according to ISO 8601 in notation YYYYMMDD", + "description": "Manufacturing date according to ISO 8601 in notation YYYY-MM-DD", "type": "string", - "minLength": 8, - "maxLength": 8, - "pattern": "[0-9]{4}[0-1]{1}[0-9]{1}[0-3]{1}[0-9]{1}" + "format": "date" }, "hw_ver": { "description": "Hardware version - integer", @@ -60,10 +83,6 @@ "description": "PAI certificate in hex-string format", "type": "string" }, - "cd": { - "description": "Certificate Declaration in hex-string format", - "type": "string" - }, "passcode": { "description": "A default PASE session passcode", "type": "integer", diff --git a/scripts/tools/nrfconnect/nrfconnect_generate_partition.py b/scripts/tools/nrfconnect/nrfconnect_generate_partition.py index e18805400bec08..bb267406427a6d 100644 --- a/scripts/tools/nrfconnect/nrfconnect_generate_partition.py +++ b/scripts/tools/nrfconnect/nrfconnect_generate_partition.py @@ -58,8 +58,6 @@ def generate_cbor(self): if self.__data_to_save: # prepare raw data from Json cbor_data = cbor.dumps(self.__data_to_save) - with open(self._output + "/output.cbor", "w+b") as cbor_output: - cbor.dump(cbor.loads(cbor_data), cbor_output) return cbor_data def create_hex(self, data: bytes): @@ -71,7 +69,7 @@ def create_hex(self, data: bytes): if len(data) > self._length: raise ValueError("generated CBOR file exceeds declared maximum partition size! {} > {}".format(len(data), self._length)) self._ih.putsz(self._offset, data) - self._ih.write_hex_file(self._output + "/output.hex", True) + self._ih.write_hex_file(self._output + ".hex", True) self._data_ready = True return True @@ -83,7 +81,7 @@ def create_bin(self): if not self._data_ready: log.error("Please create hex file first!") return False - self._ih.tobinfile(self._output + "/output.bin") + self._ih.tobinfile(self._output + ".bin") return True @staticmethod @@ -144,25 +142,32 @@ def allow_any_int(i): return int(i, 0) parser.add_argument("-i", "--input", type=str, required=True, help="Path to input .json file") parser.add_argument("-o", "--output", type=str, required=True, - help="Path to DIRECTORY, where .hex, .cbor and .bin files will be stored") + help="Prefix for output file paths, e.g. setting dir/output causes creation of the following files: dir/output.hex, and dir/output.bin") parser.add_argument("--offset", type=allow_any_int, required=True, - help="Partiton offset - a place in device's flash memory, where factory data will be stored") + help="Partition offset - an address in device's NVM memory, where factory data will be stored") parser.add_argument("--size", type=allow_any_int, required=True, help="The maximum partition size") parser.add_argument("-v", "--verbose", action="store_true", help="Run this script with DEBUG logging level") + parser.add_argument("-r", "--raw", action="store_true", + help="Do not print flashing help and other logs, only generate a .hex file. It can be useful when the script is used by other script.") args = parser.parse_args() if args.verbose: log.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=log.DEBUG) + elif args.raw: + log.basicConfig(format='%(message)s', level=log.ERROR) else: log.basicConfig(format='[%(asctime)s] %(message)s', level=log.INFO) partition_creator = PartitionCreator(args.offset, args.size, args.input, args.output) cbor_data = partition_creator.generate_cbor() try: + if not args.raw: + print("Generating .hex file: {}.hex with offset: {} and size: {}".format(args.output, hex(args.offset), hex(args.size))) if partition_creator.create_hex(cbor_data) and partition_creator.create_bin(): - print_flashing_help() + if not args.raw: + print_flashing_help() except ValueError as e: log.error(e) sys.exit(-1) diff --git a/src/platform/nrfconnect/CHIPDevicePlatformConfig.h b/src/platform/nrfconnect/CHIPDevicePlatformConfig.h index 69b58456ca4000..bcab84e5d9efc0 100644 --- a/src/platform/nrfconnect/CHIPDevicePlatformConfig.h +++ b/src/platform/nrfconnect/CHIPDevicePlatformConfig.h @@ -27,6 +27,42 @@ // ==================== Platform Adaptations ==================== +#ifndef CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER +#define CHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER CONFIG_CHIP_DEVICE_SERIAL_NUMBER +#endif + +#ifndef CHIP_DEVICE_CONFIG_TEST_MANUFACTURING_DATE +#define CHIP_DEVICE_CONFIG_TEST_MANUFACTURING_DATE CONFIG_CHIP_DEVICE_MANUFACTURING_DATE +#endif + +#ifndef CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION CONFIG_CHIP_DEVICE_HARDWARE_VERSION +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE CONFIG_CHIP_DEVICE_PAIRING_PASSCODE +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR CONFIG_CHIP_DEVICE_DISCRIMINATOR +#endif + +#ifndef CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION_STRING +#define CHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION_STRING CONFIG_CHIP_DEVICE_HARDWARE_VERSION_STRING +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_ITERATION_COUNT +#define CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_ITERATION_COUNT CONFIG_CHIP_DEVICE_SPAKE2_IT +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_SALT +#define CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_SALT CONFIG_CHIP_DEVICE_SPAKE2_SALT +#endif + +#ifndef CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_VERIFIER +#define CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_VERIFIER CONFIG_CHIP_DEVICE_SPAKE2_TEST_VERIFIER +#endif + #ifndef CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID #define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID CONFIG_CHIP_DEVICE_VENDOR_ID #endif @@ -35,6 +71,14 @@ #define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID CONFIG_CHIP_DEVICE_PRODUCT_ID #endif +#ifndef CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME +#define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME CONFIG_CHIP_DEVICE_VENDOR_NAME +#endif + +#ifndef CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME +#define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME CONFIG_CHIP_DEVICE_PRODUCT_NAME +#endif + #ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION #define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION CONFIG_CHIP_DEVICE_SOFTWARE_VERSION #endif