mirror of
https://github.com/apache/nuttx.git
synced 2026-05-09 23:12:17 +08:00
arch/espressif: add flash encryption support on CMake
Add support for burning flash encryption E-Fuses on target. Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
This commit is contained in:
committed by
archer
parent
e59604bbe6
commit
b5b7e8f72e
@@ -65,12 +65,6 @@ if(CONFIG_ESPRESSIF_HR_TIMER)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESPRESSIF_EFUSE)
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_ENABLED)
|
||||
message(
|
||||
WARNING
|
||||
"Flash Encryption is not supported on CMake. Use Make to build the image instead."
|
||||
)
|
||||
endif()
|
||||
list(APPEND SRCS esp_efuse.c)
|
||||
endif()
|
||||
|
||||
@@ -424,6 +418,15 @@ add_custom_command(
|
||||
COMMENT "Generating ESP-compatible binary"
|
||||
VERBATIM)
|
||||
|
||||
# Flash encryption: burn key to eFuses (similar to tools/espressif/Config.mk
|
||||
# BURN_EFUSES). Build: ESPTOOL_PORT=/dev/ttyUSB0 NOCHECK=1 cmake --build
|
||||
# <builddir> -t burn_enc_key (NOCHECK is required by burn_flash_enc_key.py; same
|
||||
# idea as make NOCHECK.)
|
||||
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_ENABLED)
|
||||
include(${NUTTX_DIR}/tools/espressif/espressif_burn_enc_key.cmake)
|
||||
endif()
|
||||
|
||||
# ##############################################################################
|
||||
# Flash operation Usage: ESPTOOL_PORT=/dev/ttyUSBx cmake --build <build_dir>
|
||||
# --target flash
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
# tools/espressif/burn_flash_enc_key.py
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Burn the flash encryption key into hardware eFuses (same role as
|
||||
# tools/espressif/Config.mk BURN_EFUSES + espefuse.py burn_key).
|
||||
#
|
||||
# Invocation
|
||||
# ----------
|
||||
# Normally you do not run this script by hand. The CMake target ``burn_enc_key``
|
||||
# (arch/risc-v/src/common/espressif/CMakeLists.txt) runs:
|
||||
#
|
||||
# cmake -E env NUTTX_ESPEFUSE=<path> NUTTX_KEY=<path> -- python3 "$0"
|
||||
#
|
||||
# The serial port is not passed by CMake: set ESPTOOL_PORT in the environment
|
||||
# when you run the build, e.g.:
|
||||
#
|
||||
# ESPTOOL_PORT=/dev/ttyUSB0 NOCHECK=1 cmake --build <builddir> -t burn_enc_key
|
||||
#
|
||||
# Environment variables
|
||||
# ---------------------
|
||||
# Required (set by CMake via -E env):
|
||||
# NUTTX_ESPEFUSE Absolute path to espefuse.py (from find_program at configure time).
|
||||
# NUTTX_KEY Absolute path to the XTS_AES_128 flash encryption key (.bin).
|
||||
#
|
||||
# Required (your shell / build environment):
|
||||
# ESPTOOL_PORT Serial device for espefuse.py -p (e.g. /dev/ttyUSB0).
|
||||
#
|
||||
# Required (safety gate; same idea as ``make NOCHECK`` in Config.mk):
|
||||
# NOCHECK Must be set (any value) so non-interactive burn is explicit.
|
||||
# If unset, the script exits with an error before calling espefuse.
|
||||
#
|
||||
# Behaviour
|
||||
# ---------
|
||||
# 1. Runs ``espefuse.py --port $ESPTOOL_PORT summary``; fails if that command fails.
|
||||
# 2. If the summary shows ``?? ??`` in the BLOCK1 region, the key is treated as
|
||||
# already programmed and the script exits 0 without burning again.
|
||||
# 3. Otherwise runs ``burn_key BLOCK_KEY0 <key> XTS_AES_128_KEY``, always with
|
||||
# ``--do-not-confirm`` when NOCHECK is set (as required above).
|
||||
#
|
||||
# Exit status: 0 on skip (already burned) or successful burn; non-zero on error.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def _efuse_summary_lines(summary_out: str) -> str:
|
||||
"""Text from espefuse summary corresponding to ``grep -A1 BLOCK1``."""
|
||||
|
||||
chunks: list[str] = []
|
||||
lines = summary_out.splitlines()
|
||||
for i, line in enumerate(lines):
|
||||
if "BLOCK1" in line:
|
||||
chunks.append(line)
|
||||
if i + 1 < len(lines):
|
||||
chunks.append(lines[i + 1])
|
||||
return "\n".join(chunks)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
for var in ("NUTTX_ESPEFUSE", "ESPTOOL_PORT", "NUTTX_KEY"):
|
||||
if var not in os.environ or not os.environ[var]:
|
||||
print(
|
||||
f"Error: {var} is not set or empty.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
if "NOCHECK" not in os.environ:
|
||||
print(
|
||||
"Error: NOCHECK is not set; refusing to run without explicit confirmation bypass.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(
|
||||
"Set NOCHECK=1 (or NOCHECK with any value) to proceed non-interactively.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
espefuse = os.environ["NUTTX_ESPEFUSE"]
|
||||
port = os.environ["ESPTOOL_PORT"]
|
||||
key_path = os.environ["NUTTX_KEY"]
|
||||
|
||||
proc = subprocess.run(
|
||||
[espefuse, "--port", port, "summary"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
)
|
||||
summary_out = proc.stdout or ""
|
||||
if proc.returncode != 0:
|
||||
print(
|
||||
f"espefuse.py summary failed for {port}: {summary_out}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return proc.returncode
|
||||
|
||||
efuse_summary = _efuse_summary_lines(summary_out)
|
||||
if "?? ??" in efuse_summary:
|
||||
print("Encryption key already burned. Skipping...")
|
||||
return 0
|
||||
|
||||
print("Burning flash encryption key...")
|
||||
burn = subprocess.run(
|
||||
[
|
||||
espefuse,
|
||||
"--do-not-confirm",
|
||||
"--port",
|
||||
port,
|
||||
"burn_key",
|
||||
"BLOCK_KEY0",
|
||||
key_path,
|
||||
"XTS_AES_128_KEY",
|
||||
],
|
||||
)
|
||||
return burn.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,95 @@
|
||||
# ##############################################################################
|
||||
# tools/espressif/espressif_burn_enc_key.cmake
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you 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.
|
||||
#
|
||||
# ##############################################################################
|
||||
#
|
||||
# Include() from the main NuttX CMake configure when
|
||||
# CONFIG_ESPRESSIF_SECURE_FLASH_ENC_ENABLED. Defines the ``burn_enc_key`` target
|
||||
# (no cmake -P). Expects NUTTX_DIR, CMAKE_BINARY_DIR, CMAKE_SOURCE_DIR.
|
||||
#
|
||||
# Serial port: ESPTOOL_PORT in the environment at build time (e.g.
|
||||
# ESPTOOL_PORT=/dev/ttyUSB0). NOCHECK must also be set so the burn is explicit
|
||||
# (see burn_flash_enc_key.py).
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
find_program(ESPEFUSE espefuse espefuse.py)
|
||||
if(NOT ESPEFUSE)
|
||||
message(FATAL_ERROR "espefuse.py not found (required for burn_enc_key)")
|
||||
endif()
|
||||
|
||||
find_program(PYTHON3 python3)
|
||||
if(NOT PYTHON3)
|
||||
message(FATAL_ERROR "python3 not found (required for burn_enc_key)")
|
||||
endif()
|
||||
|
||||
set(BINARY_DIR "${CMAKE_BINARY_DIR}")
|
||||
set(SOURCE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
include(${NUTTX_DIR}/tools/espressif/espressif_esptool_common.cmake)
|
||||
|
||||
if(NOT EXISTS "${FLASH_ENC_KEY_PATH}")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"burn_enc_key: flash encryption key file missing. "
|
||||
"Generate the encryption key using: espsecure.py generate_flash_encryption_key <key_name.bin>"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_FLASH_DEVICE_ENCRYPTED)
|
||||
add_custom_target(
|
||||
burn_enc_key
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E echo
|
||||
"burn_enc_key: device already encrypted (Kconfig); skipping E-Fuse burn."
|
||||
VERBATIM)
|
||||
elseif(CONFIG_ESPRESSIF_EFUSE_VIRTUAL)
|
||||
add_custom_target(
|
||||
burn_enc_key
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E echo
|
||||
"burn_enc_key: virtual E-Fuses enabled (Kconfig); skipping E-Fuse burn."
|
||||
VERBATIM)
|
||||
else()
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_USE_HOST_KEY)
|
||||
set(_nuttx_burn_key_msg
|
||||
"Using host key: ${CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME}")
|
||||
else()
|
||||
set(_nuttx_burn_key_msg
|
||||
"Using randomly generated key flow (see Kconfig / documentation).")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
burn_enc_key
|
||||
COMMAND
|
||||
bash -c
|
||||
"if [ -z \"$$ESPTOOL_PORT\" ]; then echo 'burn_enc_key: ESPTOOL_PORT is not set. Example: ESPTOOL_PORT=/dev/ttyUSB0 cmake --build . -t burn_enc_key'; exit 1; fi"
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "${_nuttx_burn_key_msg}"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E echo
|
||||
"This operation is NOT REVERSIBLE. See flash encryption documentation."
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env "NUTTX_ESPEFUSE=${ESPEFUSE}"
|
||||
"NUTTX_KEY=${FLASH_ENC_KEY_PATH}" -- ${PYTHON3}
|
||||
"${NUTTX_DIR}/tools/espressif/burn_flash_enc_key.py"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Burn flash encryption key to eFuses (espefuse.py)"
|
||||
VERBATIM)
|
||||
unset(_nuttx_burn_key_msg)
|
||||
endif()
|
||||
@@ -25,7 +25,8 @@
|
||||
# Shared Espressif + esptool layout and flash parameters for CMake scripts.
|
||||
#
|
||||
# Prerequisites (callers must do this first): - include(nuttx_kconfig.cmake) -
|
||||
# nuttx_export_kconfig(${BINARY_DIR}/.config)
|
||||
# nuttx_export_kconfig(${DOTCONFIG}) - BINARY_DIR and SOURCE_DIR set (for
|
||||
# FLASH_ENC_KEY_PATH)
|
||||
#
|
||||
# Defines set by including this file:
|
||||
# CHIP_SERIES - Chip string for esptool -c (from CONFIG_ESPRESSIF_CHIP_SERIES)
|
||||
@@ -36,6 +37,8 @@
|
||||
# FLASH_SIZE - Flash size (e.g. 2MB, 4MB), for merge_bin/elf2image -fs
|
||||
# FLASH_MODE - Flash mode (dio, dout, qio, qout), for elf2image -fm only
|
||||
# FLASH_FREQ - Flash speed (e.g. 40m), for elf2image -ff/write_flash -ff
|
||||
# FLASH_ENC_KEY_PATH - Resolved path to CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME
|
||||
# (empty if unset; relative paths checked vs BINARY_DIR then SOURCE_DIR)
|
||||
#
|
||||
# ##############################################################################
|
||||
# cmake-format: on
|
||||
@@ -66,13 +69,15 @@ else()
|
||||
endif()
|
||||
set(EFUSE_OFFSET ${EFUSE_FLASH_OFFSET})
|
||||
|
||||
# MCUboot application slot (Config.mk APP_OFFSET)
|
||||
if(CONFIG_ESPRESSIF_ESPTOOL_TARGET_PRIMARY)
|
||||
set(MCUBOOT_APP_OFFSET ${CONFIG_ESPRESSIF_OTA_PRIMARY_SLOT_OFFSET})
|
||||
elseif(CONFIG_ESPRESSIF_ESPTOOL_TARGET_SECONDARY)
|
||||
set(MCUBOOT_APP_OFFSET ${CONFIG_ESPRESSIF_OTA_SECONDARY_SLOT_OFFSET})
|
||||
else()
|
||||
message(FATAL_ERROR "Missing MCUBoot slot target: PRIMARY or SECONDARY")
|
||||
# MCUboot application slot (Config.mk APP_OFFSET); default when not MCUboot boot
|
||||
if(CONFIG_ESPRESSIF_BOOTLOADER_MCUBOOT)
|
||||
if(CONFIG_ESPRESSIF_ESPTOOL_TARGET_PRIMARY)
|
||||
set(MCUBOOT_APP_OFFSET ${CONFIG_ESPRESSIF_OTA_PRIMARY_SLOT_OFFSET})
|
||||
elseif(CONFIG_ESPRESSIF_ESPTOOL_TARGET_SECONDARY)
|
||||
set(MCUBOOT_APP_OFFSET ${CONFIG_ESPRESSIF_OTA_SECONDARY_SLOT_OFFSET})
|
||||
else()
|
||||
message(FATAL_ERROR "Missing MCUBoot slot target: PRIMARY or SECONDARY")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Flash capacity (merge_bin --fill-flash-size, elf2image -fs)
|
||||
@@ -109,3 +114,23 @@ if(DEFINED CONFIG_ESPRESSIF_FLASH_FREQ)
|
||||
else()
|
||||
set(FLASH_FREQ "40m")
|
||||
endif()
|
||||
|
||||
# Host flash encryption key file (Config.mk / espressif_mkimage FLASH_ENC)
|
||||
if((NOT DEFINED BINARY_DIR) OR (NOT DEFINED SOURCE_DIR))
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"espressif_esptool_common.cmake: BINARY_DIR and SOURCE_DIR must be set before include()"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(FLASH_ENC_KEY_PATH "")
|
||||
if(DEFINED CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME)
|
||||
set(FLASH_ENC_KEY_PATH "${CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME}")
|
||||
if(NOT IS_ABSOLUTE "${FLASH_ENC_KEY_PATH}")
|
||||
if(EXISTS "${BINARY_DIR}/${FLASH_ENC_KEY_PATH}")
|
||||
set(FLASH_ENC_KEY_PATH "${BINARY_DIR}/${FLASH_ENC_KEY_PATH}")
|
||||
elseif(EXISTS "${SOURCE_DIR}/${FLASH_ENC_KEY_PATH}")
|
||||
set(FLASH_ENC_KEY_PATH "${SOURCE_DIR}/${FLASH_ENC_KEY_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -187,6 +187,90 @@ endif()
|
||||
|
||||
file(APPEND "${BINARY_DIR}/nuttx.manifest" "nuttx.bin\n")
|
||||
|
||||
# ##############################################################################
|
||||
# Flash encryption (matches tools/espressif/Config.mk FLASH_ENC + ENC_APP)
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_ENABLED)
|
||||
message(STATUS "Flash Encryption is enabled!")
|
||||
find_program(ESPSECURE espsecure espsecure.py)
|
||||
|
||||
if(CONFIG_ESPRESSIF_EFUSE_VIRTUAL)
|
||||
message(WARNING "Virtual E-Fuses are enabled! E-Fuses will not be burned.")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_USE_HOST_KEY)
|
||||
if(NOT EXISTS "${FLASH_ENC_KEY_PATH}")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"FLASH ENCRYPTION error: Key file '${CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME}' not found.\n"
|
||||
"Generate the encryption key using: espsecure.py generate_flash_encryption_key <key_name.bin>\n"
|
||||
"Refer to the documentation on flash encryption before proceeding.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESPRESSIF_SPIFLASH)
|
||||
message(STATUS "Applying encryption to user MTD partition on flash.")
|
||||
if(NOT EXISTS "${FLASH_ENC_KEY_PATH}")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Flash encryption key is required for user MTD partition encryption. Key file: '${CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME}'\n"
|
||||
"Make sure CONFIG_ESPRESSIF_SECURE_FLASH_ENC_HOST_KEY_NAME is set or disable SPI Flash."
|
||||
)
|
||||
endif()
|
||||
if(NOT ESPSECURE)
|
||||
message(
|
||||
FATAL_ERROR "espsecure.py not found - cannot encrypt MTD partition")
|
||||
endif()
|
||||
if(NOT DEFINED CONFIG_ESPRESSIF_STORAGE_MTD_SIZE)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"CONFIG_ESPRESSIF_STORAGE_MTD_SIZE is required for SPI Flash + flash encryption"
|
||||
)
|
||||
endif()
|
||||
if(NOT DEFINED CONFIG_ESPRESSIF_STORAGE_MTD_OFFSET)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"CONFIG_ESPRESSIF_STORAGE_MTD_OFFSET is required for SPI Flash + flash encryption"
|
||||
)
|
||||
endif()
|
||||
|
||||
math(EXPR MTD_SIZE_INT "${CONFIG_ESPRESSIF_STORAGE_MTD_SIZE}")
|
||||
message(
|
||||
STATUS
|
||||
"Encrypting user MTD partition offset: ${CONFIG_ESPRESSIF_STORAGE_MTD_OFFSET}, size: ${CONFIG_ESPRESSIF_STORAGE_MTD_SIZE} (${MTD_SIZE_INT})"
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env LC_ALL=C sh -c
|
||||
"dd if=/dev/zero ibs=1 count=${MTD_SIZE_INT} 2>/dev/null | tr '\\000' '\\377' > blank_mtd.bin"
|
||||
RESULT_VARIABLE BLANK_MTD_RESULT
|
||||
WORKING_DIRECTORY ${BINARY_DIR})
|
||||
|
||||
if(NOT BLANK_MTD_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to create blank_mtd.bin for MTD encryption")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${ESPSECURE} encrypt_flash_data --aes_xts --keyfile
|
||||
${FLASH_ENC_KEY_PATH} --address 0 --output ${BINARY_DIR}/enc_mtd.bin
|
||||
${BINARY_DIR}/blank_mtd.bin
|
||||
RESULT_VARIABLE ENC_MTD_RESULT
|
||||
WORKING_DIRECTORY ${BINARY_DIR})
|
||||
|
||||
if(NOT ENC_MTD_RESULT EQUAL 0)
|
||||
file(REMOVE "${BINARY_DIR}/blank_mtd.bin")
|
||||
message(FATAL_ERROR "espsecure.py encrypt_flash_data failed")
|
||||
endif()
|
||||
|
||||
file(REMOVE "${BINARY_DIR}/blank_mtd.bin")
|
||||
|
||||
message(STATUS "Generated: enc_mtd.bin (encrypted user MTD placeholder)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ##############################################################################
|
||||
# Merge binaries (optional)
|
||||
# ##############################################################################
|
||||
@@ -226,6 +310,15 @@ if(CONFIG_ESPRESSIF_MERGE_BINS)
|
||||
message(
|
||||
STATUS "Merge bin: ${MCUBOOT_APP_OFFSET} -> ${BINARY_DIR}/nuttx.bin")
|
||||
|
||||
if(CONFIG_ESPRESSIF_SECURE_FLASH_ENC_ENABLED AND CONFIG_ESPRESSIF_SPIFLASH)
|
||||
list(APPEND ESPTOOL_BINS ${CONFIG_ESPRESSIF_STORAGE_MTD_OFFSET}
|
||||
"${BINARY_DIR}/enc_mtd.bin")
|
||||
message(
|
||||
STATUS
|
||||
"Merge bin: ${CONFIG_ESPRESSIF_STORAGE_MTD_OFFSET} -> ${BINARY_DIR}/enc_mtd.bin"
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(CONFIG_ESPRESSIF_SIMPLE_BOOT)
|
||||
# Simple boot: same base offset as BL_OFFSET (0x2000 on ESP32-P4, else 0x0)
|
||||
list(APPEND ESPTOOL_BINS ${BL_OFFSET} "${BINARY_DIR}/nuttx.bin")
|
||||
|
||||
Reference in New Issue
Block a user