Skip to content

Commit 6015f25

Browse files
committed
Scripts: Security enablement during west build
Added changes to automate the signing of zephyr security using customtool during the west build process. Signed-off-by: Aasim Shaik <[email protected]>
1 parent e7d5905 commit 6015f25

File tree

3 files changed

+235
-12
lines changed

3 files changed

+235
-12
lines changed

scripts/west_commands/sign.py

+160-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import sys
1414

1515
from elftools.elf.elffile import ELFFile
16-
16+
# custom tool signer
17+
from west.configuration import config
1718
from west import manifest
1819
from west.commands import Verbosity
1920
from west.util import quote_sh_list
@@ -79,6 +80,20 @@
7980
[rimage] sections in your west config file(s); this is especially useful
8081
when invoking west sign _indirectly_ through CMake/ninja. See how at
8182
https://docs.zephyrproject.org/latest/develop/west/sign.html
83+
84+
silabs_rps
85+
-----
86+
87+
To create a signed binary with the silabs_rps tool, run this from your build directory:
88+
89+
west sign -t silabs_rps -- --mic YOUR_OTA_KEY --encrypt YOUR_OTA_KEY --sign YOUR_PRIVATE_KEY.pem
90+
91+
For this to work, either silabs_rps commander must be installed in your PATH.
92+
93+
The input binary (zephyr.bin.rps) is signed using the specified OTA key and private key,
94+
producing zephyr.bin.rps by default. If CONFIG_M4_OTA_KEY and CONFIG_M4_PRIVATE_KEY are set
95+
in your build configuration, they will be used unless overridden by command-line arguments.
96+
Additional arguments after '--' are passed to silabs_rps directly.
8297
'''
8398

8499
class ToggleAction(argparse.Action):
@@ -112,7 +127,7 @@ def do_add_parser(self, parser_adder):
112127

113128
# general options
114129
group = parser.add_argument_group('tool control options')
115-
group.add_argument('-t', '--tool', choices=['imgtool', 'rimage'],
130+
group.add_argument('-t', '--tool', choices=['imgtool', 'rimage', 'silabs_rps'],
116131
help='''image signing tool name; imgtool and rimage
117132
are currently supported (imgtool is deprecated)''')
118133
group.add_argument('-p', '--tool-path', default=None,
@@ -195,6 +210,8 @@ def do_run(self, args, ignored):
195210
signer = ImgtoolSigner()
196211
elif args.tool == 'rimage':
197212
signer = RimageSigner()
213+
elif args.tool == 'silabs_rps':
214+
signer = Silabs_rpsSigner()
198215
# (Add support for other signers here in elif blocks)
199216
else:
200217
if args.tool is None:
@@ -633,3 +650,144 @@ def sign(self, command, build_dir, build_conf, formats):
633650

634651
os.remove(out_bin)
635652
os.rename(out_tmp, out_bin)
653+
654+
class Silabs_rpsSigner(Signer):
655+
"""Signer class for Silicon Labs Commander tool via west sign -t silabs_rps."""
656+
657+
def sign(self, command, build_dir, build_conf, formats):
658+
"""Sign the Zephyr binary using Silicon Labs Commander.
659+
660+
:param command: The Sign instance (provides args and utility methods)
661+
:param build_dir: The build directory path
662+
:param build_conf: BuildConfiguration object for the build directory
663+
:param formats: List of formats to generate (e.g., ['bin', 'rps'])
664+
"""
665+
self.command = command
666+
args = command.args
667+
b = pathlib.Path(build_dir) # BUILD_DIR from -d option
668+
669+
# Check if signing is needed
670+
if not formats:
671+
if not args.quiet:
672+
command.inf("No output formats specified, skipping signing.")
673+
return
674+
675+
# Determine workspace base based on context (automation vs. manual)
676+
zephyr_workspace_root = os.environ.get('ZEPHYR_WORKSPACE_ROOT')
677+
if zephyr_workspace_root:
678+
# Automation context: Use ZEPHYR_WORKSPACE_ROOT as the workspace root
679+
workspace_base = pathlib.Path(zephyr_workspace_root)
680+
if not workspace_base.is_dir():
681+
command.die(f"Invalid ZEPHYR_WORKSPACE_ROOT directory: {workspace_base}. Please check the environment variable.")
682+
soc_dir = 'GIVE PATH FOR KCONFIG.SOC AFTER WORKSPACE' # Relative path from workspace root
683+
else:
684+
# Manual context: Assume current directory is workspace root
685+
workspace_base = pathlib.Path(os.getcwd())
686+
if not args.quiet:
687+
command.inf(f"ZEPHYR_WORKSPACE_ROOT not set, using current directory: {workspace_base}")
688+
soc_dir = 'GIVE PATH FOR KCONFIG.SOC AFTER WORKSPACE' # Same relative path
689+
690+
soc_kconfig_base = workspace_base / soc_dir
691+
692+
# Get kernel binary name (default to 'zephyr')
693+
kernel_name = build_conf.get('CONFIG_KERNEL_BIN_NAME', 'zephyr')
694+
695+
# Input and output files (using BUILD_DIR)
696+
in_bin = b / 'zephyr' / f"{kernel_name}.bin.rps" # Raw binary input (corrected)
697+
out_rps = args.sbin or (b / 'zephyr' / f"{kernel_name}.bin.rps") # Signed output
698+
699+
# Check if input binary exists
700+
if not in_bin.is_file():
701+
command.die(f"No unsigned .bin found at {in_bin}")
702+
703+
# Load configuration from Kconfig.soc
704+
soc_kconfig = soc_kconfig_base / 'Kconfig.soc'
705+
m4_ota_key = None
706+
m4_private_key = None
707+
commander_path = None
708+
709+
if soc_kconfig.is_file():
710+
if not args.quiet:
711+
command.inf(f"Parsing Kconfig.soc at {soc_kconfig}")
712+
with open(soc_kconfig) as f:
713+
current_config = None
714+
for line in f:
715+
line = line.strip()
716+
if line.startswith('config '):
717+
current_config = line.split('config ')[1].split()[0] # Extract config name
718+
elif line.startswith('default ') and current_config:
719+
value = line.split('default ')[1].strip().strip('"')
720+
if current_config == 'M4_OTA_KEY':
721+
m4_ota_key = value
722+
elif current_config == 'M4_PRIVATE_KEY':
723+
m4_private_key = value
724+
elif current_config == 'COMMANDER_PATH':
725+
commander_path = value
726+
else:
727+
if not args.quiet:
728+
command.inf(f"No Kconfig.soc found at {soc_kconfig}. Using default values.")
729+
730+
# Apply default values only if not set from Kconfig.soc
731+
m4_ota_key = m4_ota_key or "OTA_KEY_IN_HEX" # Default OTA key
732+
m4_private_key = m4_private_key or "M4_PRIVATE_KEY_PATH" # Updated default private key path
733+
commander_path = commander_path or "COMMANDER_PATH_HERE" # Updated default Commander path
734+
735+
# Override with command-line arguments if provided
736+
m4_ota_key = getattr(args, 'm4-ota-key', m4_ota_key)
737+
m4_private_key = getattr(args, 'm4-private-key', m4_private_key)
738+
commander_path = getattr(args, 'commander-path', commander_path)
739+
740+
# Validate required settings
741+
if not m4_ota_key:
742+
command.die("M4_OTA_KEY not set in Kconfig.soc and no --m4-ota-key provided.")
743+
if not m4_private_key:
744+
command.die("M4_PRIVATE_KEY not set in Kconfig.soc and no --m4-private-key provided.")
745+
if not commander_path:
746+
command.die("COMMANDER_PATH not set in Kconfig.soc and no --commander-path provided.")
747+
748+
# Resolve paths
749+
tool_path = pathlib.Path(commander_path)
750+
if not tool_path.is_file():
751+
command.die(f"Commander not found at {tool_path}. Please ensure the path is correct.")
752+
753+
private_key_path = pathlib.Path(m4_private_key)
754+
if not private_key_path.is_file():
755+
command.die(f"Private key not found at {private_key_path}. Please ensure the path is correct.")
756+
757+
# Build the commander signing command
758+
sign_base = [
759+
str(tool_path),
760+
"rps",
761+
"convert",
762+
str(out_rps),
763+
"--app", str(in_bin),
764+
"--mic", m4_ota_key,
765+
"--encrypt", m4_ota_key,
766+
"--sign", str(private_key_path)
767+
]
768+
769+
sign_base.extend(args.tool_args)
770+
771+
if not args.quiet:
772+
command.inf(f"Signing with tool {tool_path}")
773+
command.inf(f"Unsigned binary: {in_bin}")
774+
command.inf(f"Signed output: {out_rps}")
775+
776+
try:
777+
subprocess.check_call(sign_base, stdout=subprocess.PIPE if args.quiet else None, stderr=subprocess.PIPE if args.quiet else None)
778+
except subprocess.CalledProcessError as e:
779+
command.die(f"Commander signing failed with exit code {e.returncode}")
780+
781+
if not args.quiet:
782+
command.inf(f"Successfully generated signed file: {out_rps}")
783+
784+
# Register the signer and add command-line arguments
785+
west_signers = {'silabs_rps': Silabs_rpsSigner}
786+
787+
def add_parser(subparsers):
788+
"""Add custom arguments for the silabs_rps signer."""
789+
parser = subparsers.add_parser('silabs_rps', help='Sign with Silicon Labs Commander')
790+
parser.add_argument('--m4-ota-key', help='OTA key for signing (overrides Kconfig.soc default)')
791+
parser.add_argument('--m4-private-key', help='Absolute path to private key file (overrides Kconfig.soc default)')
792+
parser.add_argument('--commander-path', help='Absolute path to Commander executable (overrides Kconfig.soc default)')
793+
return parser
+48-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,52 @@
1-
# Copyright (c) 2024 Silicon Laboratories Inc.
1+
# Copyright (c) 2023 Antmicro
22
# SPDX-License-Identifier: Apache-2.0
33

4-
add_subdirectory(siwg917)
4+
include(west) # Add this at the top or before using ${WEST}
55

6-
# Necessary to not overwrite NWP Firmware
7-
math(EXPR FLASH_LOAD_ADDRESS "(${CONFIG_FLASH_BASE_ADDRESS}) + (${CONFIG_FLASH_LOAD_OFFSET})")
6+
zephyr_include_directories(.)
87

9-
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
10-
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/siwx91x_isp_prepare.py
11-
--load-addr ${FLASH_LOAD_ADDRESS}
12-
${KERNEL_BIN_NAME}
13-
${KERNEL_BIN_NAME}.rps
14-
)
8+
zephyr_sources_ifdef(CONFIG_SOC_SERIES_SIWG917 soc.c)
9+
zephyr_sources_ifdef(CONFIG_WISECONNECT_NETWORK_STACK nwp_init.c)
10+
11+
set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld CACHE INTERNAL "")
12+
13+
# Custom signing with Silicon Labs Commander
14+
if(CONFIG_SIGNING_ENABLED)
15+
16+
# Define input and output files
17+
set(ZEPHYR_BINARY ${CMAKE_BINARY_DIR}/zephyr/zephyr.bin)
18+
set(SIGNED_RPS ${CMAKE_BINARY_DIR}/zephyr/zephyr.bin.rps)
19+
20+
# Use $ENV{PWD} as working directory
21+
set(WORKSPACE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
22+
23+
# Define the west sign command with explicit working directory and environment
24+
set(west_sign_cmd
25+
${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} # Explicitly set working directory
26+
${CMAKE_COMMAND} -E env "PATH=$ENV{PATH}:$ENV{WEST_ROOT}/bin" "ZEPHYR_WORKSPACE_ROOT=${WORKSPACE_ROOT}"
27+
${WEST} sign
28+
-t silabs_rps
29+
-d ${CMAKE_BINARY_DIR}
30+
--sbin ${SIGNED_RPS}
31+
)
32+
33+
# Add custom command to invoke west sign during build
34+
add_custom_command(
35+
OUTPUT ${SIGNED_RPS}
36+
COMMAND ${CMAKE_COMMAND} -E echo "Forcing execution in ${CMAKE_BINARY_DIR}"
37+
COMMAND ${west_sign_cmd}
38+
DEPENDS zephyr_final
39+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR} # Ensure working directory is set
40+
COMMENT "Signing binary with 'west sign -t silabs_rps' to generate zephyr.bin.rps"
41+
VERBATIM
42+
)
43+
44+
# Add custom target to ensure signing is part of the build
45+
add_custom_target(silabs_rps_sign_target
46+
ALL
47+
DEPENDS ${SIGNED_RPS}
48+
COMMENT "Ensuring signing with Silicon Labs Commander is complete"
49+
)
50+
else()
51+
message(STATUS "CONFIG_SIGN_WITH_COMMANDER is not enabled")
52+
endif()

soc/silabs/silabs_siwx91x/Kconfig.soc

+27
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,30 @@ config SOC_FAMILY_SILABS_SIWX91X
55
bool
66

77
rsource "*/Kconfig.soc"
8+
9+
config SIGNING_ENABLED
10+
bool "Sign binary with Silicon Labs Commander"
11+
default n
12+
help
13+
Enable signing the Zephyr binary with Silicon Labs Commander during build.
14+
15+
config M4_OTA_KEY
16+
string "M4 OTA Key for Commander"
17+
default "GIVE_YOUR_OTA_KEY_IN_HEX"
18+
depends on SIGNING_ENABLED
19+
help
20+
The OTA key (hex string) used for signing and encryption with Silicon Labs Commander.
21+
22+
config M4_PRIVATE_KEY
23+
string "M4 Private Key Path for Commander"
24+
default "Give_full_path_to_m4_private_key.pem"
25+
depends on SIGNING_ENABLED
26+
help
27+
Path to the private key file used for signing with Silicon Labs Commander, relative to the board directory.
28+
29+
config COMMANDER_PATH
30+
string "Path to Silicon Labs Commander executable"
31+
default "Give_full_path_to_SimplicityCommander-Linux/commander/commander"
32+
depends on SIGNING_ENABLED
33+
help
34+
Path to the Silicon Labs Commander executable, relative to the current working directory (e.g., $ENV{PWD}), used for signing the firmware binary.

0 commit comments

Comments
 (0)