diff --git a/config/nrfconnect/chip-module/Kconfig b/config/nrfconnect/chip-module/Kconfig index 3414bdd26c9687..c72080b7005409 100644 --- a/config/nrfconnect/chip-module/Kconfig +++ b/config/nrfconnect/chip-module/Kconfig @@ -206,6 +206,11 @@ choice CHIP_FACTORY_DATA_CERT_SOURCE endchoice +config CHIP_FACTORY_DATA_GENERATE_CD + bool "Generates Certification Declaration to the output build directory" + help + Generates the new Certification Declaration and stores it to the output build directory. + if CHIP_FACTORY_DATA_CERT_SOURCE_USER config CHIP_FACTORY_DATA_USER_CERTS_DAC_CERT @@ -230,6 +235,7 @@ endif # CHIP_FACTORY_DATA_CERT_SOURCE_USER # Configs for SPAKE2+ generation config CHIP_FACTORY_DATA_GENERATE_SPAKE2_VERIFIER bool "Generate SPAKE2+ verifier" + default y help Enables the generation of the SPAKE2+ verifier for the configured SPAKE2+ passcode, iteration count and salt. diff --git a/config/nrfconnect/chip-module/generate_factory_data.cmake b/config/nrfconnect/chip-module/generate_factory_data.cmake index dc1794562d89cf..3c286fc21dddee 100644 --- a/config/nrfconnect/chip-module/generate_factory_data.cmake +++ b/config/nrfconnect/chip-module/generate_factory_data.cmake @@ -48,14 +48,25 @@ 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") +if(CONFIG_CHIP_ROTATING_DEVICE_ID) + 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() else() - string(APPEND script_args "--rd_uid \"${CONFIG_CHIP_DEVICE_ROTATING_DEVICE_UID}\"\n") + string(APPEND script_args "--generate_rd_uid\n") endif() -else() - string(APPEND script_args "--generate_rd_uid\n") +endif() + +if(CONFIG_CHIP_FACTORY_DATA_CERT_SOURCE_GENERATED OR CONFIG_CHIP_FACTORY_DATA_GENERATE_CD) + find_program(chip_cert_exe NAMES chip-cert REQUIRED) + string(APPEND script_args "--chip_cert_path ${chip_cert_exe}\n") +endif() + +if(CONFIG_CHIP_FACTORY_DATA_GENERATE_CD) + string(APPEND script_args "--gen_cd\n") endif() # for development purpose user can use default certs instead of generating or providing them @@ -77,10 +88,8 @@ elseif(CONFIG_CHIP_FACTORY_DATA_CERT_SOURCE_USER) string(APPEND script_args "--dac_cert \"${CONFIG_CHIP_FACTORY_DATA_USER_CERTS_DAC_CERT}\"\n") string(APPEND script_args "--dac_key \"${CONFIG_CHIP_FACTORY_DATA_USER_CERTS_DAC_KEY}\"\n") string(APPEND script_args "--pai_cert \"${CONFIG_CHIP_FACTORY_DATA_USER_CERTS_PAI_CERT}\"\n") -else() - find_program(chip_cert_exe NAMES chip-cert REQUIRED) - string(APPEND script_args "--gen_cd\n") - string(APPEND script_args "--chip_cert_path ${chip_cert_exe}\n") +elseif(CONFIG_CHIP_FACTORY_DATA_CERT_SOURCE_GENERATED) + string(APPEND script_args "--gen_certs\n") endif() # add Password-Authenticated Key Exchange parameters @@ -90,8 +99,14 @@ string(APPEND script_args "--discriminator ${CONFIG_CHIP_DEVICE_DISCRIMINATOR}\n string(APPEND script_args "--passcode ${CONFIG_CHIP_DEVICE_SPAKE2_PASSCODE}\n") string(APPEND script_args "--include_passcode\n") string(APPEND script_args "--overwrite\n") -string(APPEND script_args "--product_finish ${CONFIG_CHIP_DEVICE_PRODUCT_FINISH}\n") +# check if spake2 verifier should be generated using script +if(NOT CONFIG_CHIP_FACTORY_DATA_GENERATE_SPAKE2_VERIFIER) + # Spake2 verifier should be provided using kConfig + string(APPEND script_args "--spake2_verifier \"${CONFIG_CHIP_DEVICE_SPAKE2_TEST_VERIFIER}\"\n") +endif() +# Product appearance +string(APPEND script_args "--product_finish ${CONFIG_CHIP_DEVICE_PRODUCT_FINISH}\n") if(CONFIG_CHIP_DEVICE_PRODUCT_COLOR) string(APPEND script_args "--product_color ${CONFIG_CHIP_DEVICE_PRODUCT_COLOR}\n") endif() @@ -100,12 +115,6 @@ if(CONFIG_CHIP_FACTORY_DATA_GENERATE_ONBOARDING_CODES) string(APPEND script_args "--generate_onboarding\n") endif() -# check if spake2 verifier should be generated using script -if(NOT CONFIG_CHIP_FACTORY_DATA_GENERATE_SPAKE2_VERIFIER) - # Spake2 verifier should be provided using kConfig - string(APPEND script_args "--spake2_verifier \"${CONFIG_CHIP_DEVICE_SPAKE2_TEST_VERIFIER}\"\n") -endif() - if(CONFIG_CHIP_DEVICE_ENABLE_KEY) # Add optional EnableKey that triggers user-specific action. string(APPEND script_args "--enable_key \"${CONFIG_CHIP_DEVICE_ENABLE_KEY}\"\n") diff --git a/docs/guides/nrfconnect_factory_data_configuration.md b/docs/guides/nrfconnect_factory_data_configuration.md index d30a12f1e82025..e4c3be4899df4e 100644 --- a/docs/guides/nrfconnect_factory_data_configuration.md +++ b/docs/guides/nrfconnect_factory_data_configuration.md @@ -51,6 +51,7 @@ data secure by applying hardware write protection. - [Building an example with factory data](#building-an-example-with-factory-data) - [Providing factory data parameters as a build argument list](#providing-factory-data-parameters-as-a-build-argument-list) - [Setting factory data parameters using interactive Kconfig interfaces](#setting-factory-data-parameters-using-interactive-kconfig-interfaces) + - [Default Kconfig values and developing aspects](#default-kconfig-values-and-developing-aspects) - [Programming factory data](#programming-factory-data) - [Using own factory data implementation](#using-own-factory-data-implementation) @@ -272,6 +273,7 @@ To use this script, complete the following steps: ``` --chip_cert_path + --gen_certs ``` > **Note:** To generate new certificates, you need the `chip-cert` @@ -293,7 +295,7 @@ To use this script, complete the following steps: --rd_uid ``` - - Generate a new ID and provide it (): + - (optional) Generate a new ID and provide it: ``` --generate_rd_uid @@ -328,6 +330,17 @@ To use this script, complete the following steps: --product_color ``` + j. (optional) Generate Certification Declaration for testing purposes + + ``` + --chip_cert_path + --gen_cd + ``` + + > **Note:** To generate new Certification Declaration, you need the + > `chip-cert` executable. See the note at the end of this section to learn + > how to get it. + 4. Run the script using the prepared list of arguments: ``` @@ -794,6 +807,55 @@ snippet: > interfaces, read the > [Kconfig documentation](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/build/kconfig/menuconfig.html). +### Default Kconfig values and developing aspects + +Each factory data parameter has its default value reflected in the Kconfig. The +list below shows some Kconfig settings that are configured in the nRF Connect +build system and have an impact on the application. You can modify them to +achieve the desired behavior of your application. + +- The device uses the test certificates located in the + `credentials/development/attestation/` directory, which are generated using + all default values. If you want to change the default `vendor_id`, + `product_id`, `vendor_name`, or `device_name` and generate new test + certificates, add the `CONFIG_CHIP_FACTORY_DATA_CERT_SOURCE_GENERATED=y` + Kconfig option. Remember to build the `chip-cert` application and add it to + the system PATH. + + For developing a production-ready product, you need to write the + certificates obtained during the certification process. To do this, add the + `CONFIG_CHIP_FACTORY_DATA_CERT_SOURCE_USER=y` Kconfig option and set the + appropriate paths for the following Kconfig options: + + - `CONFIG_CHIP_FACTORY_DATA_USER_CERTS_DAC_CERT` + - `CONFIG_CHIP_FACTORY_DATA_USER_CERTS_DAC_KEY` + - `CONFIG_CHIP_FACTORY_DATA_USER_CERTS_PAI_CERT` + +- By default, the SPAKE2+ verifier is generated during each example's build. + This means that this value will change automatically if you change any of + the following parameters: + + - `CONFIG_CHIP_DEVICE_SPAKE2_PASSCODE` + - `CONFIG_CHIP_DEVICE_SPAKE2_SALT` + - `CONFIG_CHIP_DEVICE_SPAKE2_IT` + + You can disable the generation of the SPAKE2+ verifier by setting the + `CONFIG_CHIP_FACTORY_DATA_GENERATE_SPAKE2_VERIFIER=n` Kconfig option. Then, + you will need to provide the externally-generated SPAKE2+ verifier using the + `CONFIG_CHIP_DEVICE_SPAKE2_TEST_VERIFIER` Kconfig value. + +- Generating the rotating device ID unique ID is disabled by default, but you + can enable it by setting the `CONFIG_CHIP_ROTATING_DEVICE_ID=y` and + `CONFIG_CHIP_DEVICE_GENERATE_ROTATING_DEVICE_UID=y` Kconfig values. + Moreover, if you set the `CONFIG_CHIP_ROTATING_DEVICE_ID` Kconfig option to + `y` and disable the `CONFIG_CHIP_DEVICE_GENERATE_ROTATING_DEVICE_UID` + Kconfig option, you will need to provide it manually using the + `CONFIG_CHIP_DEVICE_ROTATING_DEVICE_UID` Kconfig value. + +- You can generate the test Certification Declaration by using the + `CONFIG_CHIP_FACTORY_DATA_GENERATE_CD=y` Kconfig option. Remember to build + the `chip-cert` application and add it to the system PATH. +
## Programming factory data diff --git a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py index 6c2af9dc890617..7cddb766a90cca 100644 --- a/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py +++ b/scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py @@ -102,7 +102,8 @@ def gen_test_certs(chip_cert_exe: str, generate_cd: bool = False, cd_type: int = 1, paa_cert_path: str = None, - paa_key_path: str = None): + paa_key_path: str = None, + generate_all_certs: bool = False): """ Generate Matter certificates according to given Vendor ID and Product ID using the chip-cert executable. To use own Product Attestation Authority certificate provide paa_cert_path and paa_key_path arguments. @@ -120,6 +121,7 @@ def gen_test_certs(chip_cert_exe: str, /credentials/test/attestation directory. paa_key_path (str, optional): provide PAA key path. Defaults to None - a path will be set to /credentials/test/attestation directory. + generate_all_certs: Generate the new DAC and PAI certificates Returns: dictionary: ["PAI_CERT": (str), @@ -136,9 +138,10 @@ def gen_test_certs(chip_cert_exe: str, attestation_certs = namedtuple("attestation_certs", ["dac_cert", "dac_key", "pai_cert"]) - log.info("Generating new certificates using chip-cert...") - if generate_cd: + + log.info("Generating new Certification Declaration using chip-cert...") + # generate Certification Declaration cmd = [chip_cert_exe, "gen-cd", "--key", CD_KEY_PATH, @@ -162,47 +165,52 @@ def gen_test_certs(chip_cert_exe: str, "DAC_KEY": output + "/DAC_key" } - # generate PAI - cmd = [chip_cert_exe, "gen-att-cert", - "-t", "i", - "-c", device_name, - "-V", hex(vendor_id), - "-C", PAA_PATH, - "-K", PAA_KEY_PATH, - "-o", new_certificates["PAI_CERT"] + ".pem", - "-O", new_certificates["PAI_KEY"] + ".pem", - "-l", str(10000), - ] - subprocess.run(cmd) - - # generate DAC - cmd = [chip_cert_exe, "gen-att-cert", - "-t", "d", - "-c", device_name, - "-V", hex(vendor_id), - "-P", hex(product_id), - "-C", new_certificates["PAI_CERT"] + ".pem", - "-K", new_certificates["PAI_KEY"] + ".pem", - "-o", new_certificates["DAC_CERT"] + ".pem", - "-O", new_certificates["DAC_KEY"] + ".pem", - "-l", str(10000), - ] - subprocess.run(cmd) - - # convert to .der files - for cert_k, cert_v in new_certificates.items(): - action_type = "convert-cert" if cert_k.find("CERT") != -1 else "convert-key" - log.info(cert_v + ".der") - cmd = [chip_cert_exe, action_type, - cert_v + ".pem", - cert_v + ".der", - "--x509-der", + if generate_all_certs: + log.info("Generating new PAI and DAC certificates using chip-cert...") + + # generate PAI + cmd = [chip_cert_exe, "gen-att-cert", + "-t", "i", + "-c", device_name, + "-V", hex(vendor_id), + "-C", PAA_PATH, + "-K", PAA_KEY_PATH, + "-o", new_certificates["PAI_CERT"] + ".pem", + "-O", new_certificates["PAI_KEY"] + ".pem", + "-l", str(10000), + ] + subprocess.run(cmd) + + # generate DAC + cmd = [chip_cert_exe, "gen-att-cert", + "-t", "d", + "-c", device_name, + "-V", hex(vendor_id), + "-P", hex(product_id), + "-C", new_certificates["PAI_CERT"] + ".pem", + "-K", new_certificates["PAI_KEY"] + ".pem", + "-o", new_certificates["DAC_CERT"] + ".pem", + "-O", new_certificates["DAC_KEY"] + ".pem", + "-l", str(10000), ] subprocess.run(cmd) - return attestation_certs(new_certificates["DAC_CERT"] + ".der", - new_certificates["DAC_KEY"] + ".der", - new_certificates["PAI_CERT"] + ".der") + # convert to .der files + for cert_k, cert_v in new_certificates.items(): + action_type = "convert-cert" if cert_k.find("CERT") != -1 else "convert-key" + log.info(cert_v + ".der") + cmd = [chip_cert_exe, action_type, + cert_v + ".pem", + cert_v + ".der", + "--x509-der", + ] + subprocess.run(cmd) + + return attestation_certs(new_certificates["DAC_CERT"] + ".der", + new_certificates["DAC_KEY"] + ".der", + new_certificates["PAI_CERT"] + ".der") + + return attestation_certs(None, None, None) class FactoryDataGenerator: @@ -234,8 +242,8 @@ def _validate_args(self): raise AssertionError("Provided wrong user data, this is not a JSON format! {}".format(e)) assert self._args.spake2_verifier or self._args.passcode, \ "Cannot find Spake2+ verifier, to generate a new one please provide passcode (--passcode)" - assert (self._args.chip_cert_path or (self._args.dac_cert and self._args.pai_cert and self._args.dac_key)), \ - "Cannot 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.gen_certs and self._args.chip_cert_path) or (self._args.dac_cert and self._args.pai_cert and self._args.dac_key)), \ + "Cannot 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) and add --gen_certs argument" 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), \ @@ -273,23 +281,27 @@ def generate_json(self): # convert salt to bytestring to be coherent with Spake2+ verifier type spake_2_salt = self._args.spake2_salt - if self._args.chip_cert_path: - certs = gen_test_certs(self._args.chip_cert_path, - self._args.output[:self._args.output.rfind("/")], - self._args.vendor_id, - self._args.product_id, - self._args.vendor_name + "_" + self._args.product_name, - self._args.gen_cd, - self._args.cd_type, - self._args.paa_cert, - self._args.paa_key) - dac_cert = certs.dac_cert - pai_cert = certs.pai_cert - dac_key = certs.dac_key - else: + certs = gen_test_certs(self._args.chip_cert_path, + self._args.output[:self._args.output.rfind("/")], + self._args.vendor_id, + self._args.product_id, + self._args.vendor_name + "_" + self._args.product_name, + self._args.gen_cd, + self._args.cd_type, + self._args.paa_cert, + self._args.paa_key, + self._args.gen_certs) + + dac_cert = certs.dac_cert + pai_cert = certs.pai_cert + dac_key = certs.dac_key + + if not dac_cert: dac_cert = self._args.dac_cert - dac_key = self._args.dac_key + if not pai_cert: pai_cert = self._args.pai_cert + if not dac_key: + dac_key = self._args.dac_key # try to read DAC public and private keys dac_priv_key = get_raw_private_key_der(dac_key, self._args.dac_key_password) @@ -364,6 +376,7 @@ def _add_entry(self, name: str, value: any): def _generate_spake2_verifier(self): """ If verifier has not been provided in arguments list it should be generated via external script """ + log.info("Generating SPAKE2+ Verifier...") return generate_verifier(self._args.passcode, self._args.spake2_salt, self._args.spake2_it) def _generate_rotating_device_uid(self): @@ -479,6 +492,8 @@ def base64_str(s): return base64.b64decode(s) "This option requires a path to chip-cert executable." "By default you can find chip-cert in connectedhomeip/src/tools/chip-cert directory " "and build it there.")) + optional_arguments.add_argument("--gen_certs", action="store_true", + help="Generate a new DAC nad PAI certificates") 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,