Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESP32] Added cmake script to generate the factory and esp_secure_cert partition during build time and flash in a single flash command. #30592

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/esp32/components/chip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ if(NOT CHIP_ROOT)
endif()

include(${CMAKE_CURRENT_LIST_DIR}/ota-image.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/factory.cmake)

set(CHIP_REQUIRE_COMPONENTS esp_eth freertos lwip bt mbedtls fatfs app_update console openthread nvs_flash spi_flash)

Expand Down Expand Up @@ -547,6 +548,11 @@ if(CONFIG_ENABLE_PW_RPC)
endforeach()
endif()

if(CONFIG_ENABLE_BUILD_TIME_PARTITION_SCRIPT)
generate_build_time_partition(fctry esp_secure_cert ${CHIP_ROOT} FLASH_IN_PROJECT)
add_dependencies(build_time_partition app)
endif()

# Build Matter OTA image
if (CONFIG_CHIP_OTA_IMAGE_BUILD)
chip_ota_image(chip-ota-image
Expand Down
13 changes: 10 additions & 3 deletions config/esp32/components/chip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ menu "CHIP Device Layer"
NVS namespace. If this option is enabled, the application can use an API to set a CD,
the configured CD will be used for subsequent CD reads.

config ENABLE_ESP_INSIGHTS_TRACE
config ENABLE_ESP_INSIGHTS_TRACE
bool "Enable Matter ESP Insights"
depends on ESP_INSIGHTS_ENABLED
default y
Expand All @@ -960,21 +960,28 @@ menu "CHIP Device Layer"
Enabling the above option will enable the esp32 specific tracing functionality and report the
diagnostic information to the insights cloud.

config ENABLE_ESP_INSIGHTS_SYSTEM_STATS
config ENABLE_ESP_INSIGHTS_SYSTEM_STATS
bool "Enable System Stats for insights"
depends on ESP_INSIGHTS_ENABLED
default n
help
This option enables the system statistics to be sent to the insights cloud.

config MAX_PERMIT_LIST_SIZE
config MAX_PERMIT_LIST_SIZE
int "Set permit list size for Insights traces"
range 5 30
depends on ESP_INSIGHTS_ENABLED
default 20
help
Maximum number of group entries that can be included in the permit list for reporting
the traces to insights.
config ENABLE_BUILD_TIME_PARTITION_SCRIPT
bool "Enable script to build fctry and esp_secure_cert partition in during build time."
default n
help
Enables the cmake script to generate and map the factory parition bin and esp-secure-cert
partition bin to their corresponding addresses specified in partitions.csv and flash the partitons
in a single idf.py flash command.

endmenu

Expand Down
183 changes: 183 additions & 0 deletions config/esp32/components/chip/factory.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#
# Copyright (c) 2023 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.
#

function(set_default_value VAR DEFAULT_VALUE)
get_property(VAR_CACHE_TYPE CACHE ${VAR} PROPERTY TYPE)
message(status "var cache type : ${VAR} ${VAR_CACHE_TYPE}")
if (VAR_CACHE_TYPE STREQUAL "UNINITIALIZED")
set(${VAR}_EXPLICITLY_SET TRUE CACHE BOOL "${VAR} is not explicitly set")
set(${VAR} ${DEFAULT_VALUE} CACHE STRING ${VAR})
else()
set(${VAR}_EXPLICITLY_SET FALSE CACHE BOOL "${VAR} is explicitly set.")
set(${VAR} ${DEFAULT_VALUE} CACHE STRING ${VAR})
endif()
endfunction()

function(set_values)
# Set variables with default values if not defined
set_default_value(DEVICE_NAME "My bulb")
set_default_value(VENDOR_NAME "Test-vendor")
set_default_value(DISCRIMINATOR 3841)
set_default_value(PASSCODE 20202020)
set_default_value(VENDOR_ID 0xFFF2)
set_default_value(PRODUCT_ID 0x8001)
set_default_value(HARDWARE_VERSION 1)
set_default_value(DISCOVERY_MODE 2)
set_default_value(HARDWARE_VERSION_STR "Devkit")
set_default_value(DAC_CERT "${CHIP_ROOT}/credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Cert.der")
set_default_value(DAC_KEY "${CHIP_ROOT}/credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Key.der")
set_default_value(PAI_CERT "${CHIP_ROOT}/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Cert.der")
set_default_value(CERT_DCLRN "${CHIP_ROOT}/credentials/test/certification-declaration/Chip-Test-CD-FFF2-8001.der")
endfunction()

function(generate_build_time_partition fctry_partition esp_secure_cert_partition chip_root)
set(options FLASH_IN_PROJECT)
set(multi DEPENDS)
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
get_filename_component(chip_root_abs_path ${chip_root} ABSOLUTE)

set(generate_esp32_chip_factory_bin.py ${PYTHON} ${chip_root}/scripts/tools/generate_esp32_chip_factory_bin.py)
set(gen_att_certs.py ${PYTHON} ${chip_root}/scripts/tools/gen_att_certs.py)

partition_table_get_partition_info(fctry_partition_size "--partition-name ${fctry_partition}" "size")
partition_table_get_partition_info(fctry_partition_offset "--partition-name ${fctry_partition}" "offset")

partition_table_get_partition_info(secure_cert_partition_size "--partition-name ${esp_secure_cert_partition}" "size")
partition_table_get_partition_info(secure_cert_partition_offset "--partition-name ${esp_secure_cert_partition}" "offset")

message(STATUS "fctry_partition_size : ${fctry_partition_size}")
message(STATUS "fctry_partition_offset : ${fctry_partition_offset}")
message(STATUS "secure_cert_partition_size : ${secure_cert_partition_size}")
message(STATUS "secure_cert_partition_offset : ${secure_cert_partition_offset}")

if("${fctry_partition_size}" AND "${fctry_partition_offset}")
set_values()
message(STATUS "Vendor id set: ${VENDOR_ID_EXPLICITLY_SET}")
message(STATUS "Product id set: ${PRODUCT_ID_EXPLICITLY_SET}")

if ("${VENDOR_ID_EXPLICITLY_SET}" AND "${PRODUCT_ID_EXPLICITLY_SET}")
string(RANDOM LENGTH 8 ALPHABET 0123456789 OUTPUT_VARIABLE RANDOM_PASSCODE)
set(PASSCODE ${RANDOM_PASSCODE})
message(STATUS "random passcode : ${RANDOM_PASSCODE}")

math(EXPR PASSCODE_MOD "${RANDOM_PASSCODE} % 999999998")
message(STATUS "Random passcode Mod: ${PASSCODE_MOD}")
set(PASSCODE ${PASSCODE_MOD})

string(RANDOM LENGTH 4 ALPHABET 0123456789 OUTPUT_VARIABLE RANDOM_DISCRIMINATOR)
set(DISCRIMINATOR ${RANDOM_DISCRIMINATOR})
message(STATUS "random discriminator : ${RANDOM_DISCRIMINATOR}")

math(EXPR DISCRIMINATOR_MOD "${RANDOM_DISCRIMINATOR} % 4096")
message(STATUS "Random discriminator Mod: ${DISCRIMINATOR_MOD}")
set(DISCRIMINATOR ${DISCRIMINATOR_MOD})

math(EXPR VENDOR_DEC ${VENDOR_ID} OUTPUT_FORMAT DECIMAL)
math(EXPR PRODUCT_DEC ${PRODUCT_ID} OUTPUT_FORMAT DECIMAL)

message(STATUS "Vendor Decimal: ${VENDOR_DEC}")
message(STATUS "Product Decimal: ${PRODUCT_DEC}")

set(OUTDIR attestation_${VENDOR_DEC}_${PRODUCT_DEC})
message(STATUS "Outdir: ${OUTDIR}")

execute_process(COMMAND ${gen_att_certs.py} --vendor-id ${VENDOR_ID} --product-id ${PRODUCT_ID}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

set(DAC_CERT ${CMAKE_BINARY_DIR}/certs/${OUTDIR}/DAC_cert.der)
set(DAC_KEY ${CMAKE_BINARY_DIR}/certs/${OUTDIR}/DAC_key.der)
set(PAI_CERT ${CMAKE_BINARY_DIR}/certs/${OUTDIR}/PAI_cert.der)
set(CERT_DCLRN ${CMAKE_BINARY_DIR}/certs/${OUTDIR}/CD.der)
endif()

set(PREVIOUS_VALUES_FILE "${CMAKE_BINARY_DIR}/previous_values.txt")

if (NOT EXISTS ${PREVIOUS_VALUES_FILE})
file(WRITE ${PREVIOUS_VALUES_FILE} "")
endif()

file(READ ${PREVIOUS_VALUES_FILE} PREVIOUS_VALUES)

set(CURRENT_VALUES_STRING
"${DEVICE_NAME}${VENDOR_NAME}${DISCRIMINATOR}${PASSCODE}${VENDOR_ID}${PRODUCT_ID}${HARDWARE_VERSION}${HARDWARE_VERSION_STR}${DAC_CERT}${DAC_KEY}${PAI_CERT}${CERT_DCLRN}")

message(STATUS "Vendor id set: ${VENDOR_ID_EXPLICITLY_SET}")
message(STATUS "Bulb Name: ${DEVICE_NAME}")
message(STATUS "Vendor Name: ${VENDOR_NAME}")
message(STATUS "Hardware Version: ${HARDWARE_VERSION}")
message(STATUS "Hardware Version String: ${HARDWARE_VERSION_STR}")
message(STATUS "Vendor ID: ${VENDOR_ID}")
message(STATUS "Product ID: ${PRODUCT_ID}")
message(STATUS "Discovery Mode : ${DISCOVERY_MODE}")
message(STATUS "DAC Cert: ${DAC_CERT}")
message(STATUS "DAC Key: ${DAC_KEY}")
message(STATUS "PAI Cert: ${PAI_CERT}")
message(STATUS "Certification Declaration: ${CERT_DCLRN}")
message(STATUS "Passcode: ${PASSCODE}")
message(STATUS "Discriminator: ${DISCRIMINATOR}")


if (NOT "${CURRENT_VALUES_STRING}" STREQUAL "${PREVIOUS_VALUES}")
message(STATUS "Values have changed. Triggering add_custom_target.")
add_custom_target(build_time_partition ALL
COMMAND ${generate_esp32_chip_factory_bin.py} -d ${DISCRIMINATOR}
-p ${PASSCODE}
--product-name "${DEVICE_NAME}"
--vendor-name "${VENDOR_NAME}"
--vendor-id ${VENDOR_ID}
--product-id ${PRODUCT_ID}
--hw-ver ${HARDWARE_VERSION}
--hw-ver-str "${HARDWARE_VERSION_STR}"
--dac-cert ${DAC_CERT}
--dac-key ${DAC_KEY}
--pai-cert ${PAI_CERT}
--cd ${CERT_DCLRN}
--discovery-mode ${DISCOVERY_MODE}
--dac-in-secure-cert
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
file(WRITE ${PREVIOUS_VALUES_FILE} ${CURRENT_VALUES_STRING})
else()
add_custom_target(build_time_partition)
message(STATUS "Values have not changed. Skipping add_custom_target.")
endif()

set(factory_partition_bin ${CMAKE_BINARY_DIR}/bin/factory_partition.bin)
set(esp_secure_cert_partition_bin ${CMAKE_BINARY_DIR}/bin/esp_secure_cert_partititon.bin)
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)

esptool_py_flash_target(${fctry_partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
esptool_py_flash_to_partition(${fctry_partition}-flash "${fctry_partition}" "${factory_partition_bin}")

esptool_py_flash_target(${esp_secure_cert_partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
esptool_py_flash_to_partition(${esp_secure_cert_partition}-flash "${esp_secure_cert_partition}" "${esp_secure_cert_partition_bin}")

add_dependencies(${fctry_partition}-flash build_time_partition)
add_dependencies(${esp_secure_cert_partition}-flash build_time_partition)

if(arg_FLASH_IN_PROJECT)
esptool_py_flash_to_partition(flash "${fctry_partition}" "${factory_partition_bin}")
esptool_py_flash_to_partition(flash "${esp_secure_cert_partition}" "${esp_secure_cert_partition_bin}")
add_dependencies(flash build_time_partition)
endif()
else()
set(message "Failed to create Factory partition image for partition '${partition}'. "
"Check project configuration if using the correct partition table file.")
fail_at_build_time(factory_${partition}_bin "${message}")

endif()
endfunction()
2 changes: 2 additions & 0 deletions scripts/setup/requirements.esp32.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ bitarray==2.6.0
bitstring>=3.1.6,<4
ecdsa>=0.16.0
construct==2.10.54
pypng==0.0.21
PyQRCode==1.2.1
python-socketio<5
itsdangerous<2.1 ; python_version < "3.11"
esp_idf_monitor==1.1.1
Expand Down
138 changes: 138 additions & 0 deletions scripts/tools/gen_att_certs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
#
# Copyright (c) 2022 Project CHIP Authors
# All rights reserved.
#
# 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.
#

import argparse
import base64
import json
import logging as log
import os
import subprocess
import sys
from collections import namedtuple
from os.path import exists

CHIP_ROOT = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('scripts', 'tools'))]
chip_cert_exe = os.path.join(CHIP_ROOT, 'out', 'host', 'chip-cert')

if not os.path.exists(chip_cert_exe):
print("Error: chip-cert executable not found.Please build chip-cert in connectedhomeip by ninja -C out/host")


def gen_test_certs(vendor_id: int,
product_id: int,
output: str):

CD_PATH = CHIP_ROOT + "/credentials/test/certification-declaration/Chip-Test-CD-Signing-Cert.pem"
CD_KEY_PATH = CHIP_ROOT + "/credentials/test/certification-declaration/Chip-Test-CD-Signing-Key.pem"
PAA_PATH = CHIP_ROOT + "/credentials/test/attestation/Chip-Test-PAA-NoVID-Cert.pem"
PAA_KEY_PATH = CHIP_ROOT + "/credentials/test/attestation/Chip-Test-PAA-NoVID-Key.pem"

attestation_certs = namedtuple("attestation_certs", ["dac_cert", "dac_key", "pai_cert"])

log.info("Generating new Certification Declaration using chip-cert...")

# generate Certification Declaration
cmd = [chip_cert_exe, "gen-cd",
"--key", CD_KEY_PATH,
"--cert", CD_PATH,
"--out", output + "_" + str(vendor_id) + "_" + str(product_id) + "/CD.der",
"--format-version", "1",
"--vendor-id", hex(vendor_id),
"--product-id", hex(product_id),
"--device-type-id", "0",
"--certificate-id", "FFFFFFFFFFFFFFFFFFF",
"--security-level", "0",
"--security-info", "0",
"--certification-type", "0",
"--version-number", "0xFFFF",
]
subprocess.run(cmd)

new_certificates = {"PAI_CERT": output + "_" + str(vendor_id) + "_" + str(product_id) + "/PAI_cert",
"PAI_KEY": output + "_" + str(vendor_id) + "_" + str(product_id)+"/PAI_key",
"DAC_CERT": output + "_" + str(vendor_id) + "_" + str(product_id) + "/DAC_cert",
"DAC_KEY": output + "_" + str(vendor_id) + "_" + str(product_id) + "/DAC_key"
}

log.info("Generating new PAI and DAC certificates using chip-cert...")

# generate PAI
cmd = [chip_cert_exe, "gen-att-cert",
"-t", "i",
"-c", "device",
"-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",
"-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",
]
subprocess.run(cmd)

return attestation_certs(new_certificates["DAC_CERT"] + ".der",
new_certificates["DAC_KEY"] + ".der",
new_certificates["PAI_CERT"] + ".der")


def get_args():
def any_base_int(s): return int(s, 0)
parser = argparse.ArgumentParser(description="ESP32 Attestation generation tool")
parser.add_argument("-o", "--output", type=str, required=False,
help="Output path to store attestation certificates", default="certs/attestation")
parser.add_argument('--vendor-id', type=any_base_int, help="Vendor id")
parser.add_argument('--product-id', type=any_base_int, help="Product id")
return parser.parse_args()


def set_up_out_dirs(args):
os.makedirs(args.output + "_" + str(args.vendor_id) + "_" + str(args.product_id), exist_ok=True)


def main():
args = get_args()
set_up_out_dirs(args)
certs = gen_test_certs(args.vendor_id, args.product_id, args.output)


if __name__ == "__main__":
main()
Loading