Skip to content

Commit b9ddd52

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 b9ddd52

File tree

3 files changed

+230
-2
lines changed

3 files changed

+230
-2
lines changed

scripts/west_commands/sign.py

+158-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,142 @@ 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 environment or current directory
676+
zephyr_workspace_root = os.environ.get('ZEPHYR_WORKSPACE_ROOT')
677+
if zephyr_workspace_root:
678+
workspace_base = pathlib.Path(zephyr_workspace_root)
679+
if not workspace_base.is_dir():
680+
command.die(f"Invalid ZEPHYR_WORKSPACE_ROOT directory: {workspace_base}. Please check the environment variable.")
681+
else:
682+
workspace_base = pathlib.Path(os.getcwd())
683+
if not args.quiet:
684+
command.inf(f"ZEPHYR_WORKSPACE_ROOT not set, using current directory: {workspace_base}")
685+
686+
soc_dir = 'Give_Soc_Directory_path from the working directory' # Modify the Relative path to SoC dir
687+
soc_kconfig_base = workspace_base / soc_dir
688+
689+
# Get kernel binary name (default to 'zephyr')
690+
kernel_name = build_conf.get('CONFIG_KERNEL_BIN_NAME', 'zephyr')
691+
692+
# Input and output files (using BUILD_DIR)
693+
in_bin = b / 'zephyr' / f"{kernel_name}.bin.rps" # Raw binary input
694+
out_rps = args.sbin or (b / 'zephyr' / f"{kernel_name}.bin.rps") # Signed output
695+
696+
# Check if input binary exists
697+
if not in_bin.is_file():
698+
command.die(f"No unsigned .bin found at {in_bin}")
699+
700+
# Load configuration from Kconfig.soc
701+
soc_kconfig = soc_kconfig_base / 'Kconfig.soc' # Name of Kconfig file to be given here.Like Kconfig.soc or something.
702+
m4_ota_key = None
703+
m4_private_key = None
704+
commander_path = None
705+
706+
if soc_kconfig.is_file():
707+
if not args.quiet:
708+
command.inf(f"Parsing Kconfig.soc at {soc_kconfig}")
709+
# Read the Kconfig.soc file and extract relevant values
710+
with open(soc_kconfig) as f:
711+
current_config = None
712+
for line in f:
713+
line = line.strip()
714+
if line.startswith('config '):
715+
current_config = line.split('config ')[1].split()[0] # Extract config name
716+
elif line.startswith('default ') and current_config:
717+
value = line.split('default ')[1].strip().strip('"')
718+
if current_config == 'M4_OTA_KEY':
719+
m4_ota_key = value
720+
elif current_config == 'M4_PRIVATE_KEY':
721+
m4_private_key = value
722+
elif current_config == 'COMMANDER_PATH':
723+
commander_path = value
724+
else:
725+
if not args.quiet:
726+
command.inf(f"No Kconfig.soc found at {soc_kconfig}. Using default values.")
727+
728+
# Apply default values only if not set from Kconfig.soc
729+
m4_ota_key = m4_ota_key or "Give_default_OTA_KEY_VALUE" # Default OTA key
730+
m4_private_key = m4_private_key or "/default/path/to/m4_private_key.pem" # Default private key path
731+
commander_path = commander_path or "/default/path/to/SimplicityCommander-Linux/commander/commander" # Default Commander path
732+
733+
# Override with command-line arguments if provided
734+
m4_ota_key = getattr(args, 'm4-ota-key', m4_ota_key)
735+
m4_private_key = getattr(args, 'm4-private-key', m4_private_key)
736+
commander_path = getattr(args, 'commander-path', commander_path)
737+
738+
# Validate required settings
739+
if not m4_ota_key:
740+
command.die("M4_OTA_KEY not set in Kconfig.soc and no --m4-ota-key provided.")
741+
if not m4_private_key:
742+
command.die("M4_PRIVATE_KEY not set in Kconfig.soc and no --m4-private-key provided.")
743+
if not commander_path:
744+
command.die("COMMANDER_PATH not set in Kconfig.soc and no --commander-path provided.")
745+
746+
# Resolve paths
747+
tool_path = pathlib.Path(commander_path)
748+
if not tool_path.is_file():
749+
command.die(f"Commander not found at {tool_path}. Please ensure the path is correct.")
750+
751+
private_key_path = pathlib.Path(m4_private_key)
752+
if not private_key_path.is_file():
753+
command.die(f"Private key not found at {private_key_path}. Please ensure the path is correct.")
754+
755+
# Build the commander signing command
756+
sign_base = [
757+
str(tool_path),
758+
"rps",
759+
"convert",
760+
str(out_rps),
761+
"--app", str(in_bin),
762+
"--mic", m4_ota_key,
763+
"--encrypt", m4_ota_key,
764+
"--sign", str(private_key_path)
765+
]
766+
767+
sign_base.extend(args.tool_args)
768+
769+
if not args.quiet:
770+
command.inf(f"Signing with tool {tool_path}")
771+
command.inf(f"Unsigned binary: {in_bin}")
772+
command.inf(f"Signed output: {out_rps}")
773+
774+
try:
775+
subprocess.check_call(sign_base, stdout=subprocess.PIPE if args.quiet else None, stderr=subprocess.PIPE if args.quiet else None)
776+
except subprocess.CalledProcessError as e:
777+
command.die(f"Commander signing failed with exit code {e.returncode}")
778+
779+
if not args.quiet:
780+
command.inf(f"Successfully generated signed file: {out_rps}")
781+
782+
# Register the signer and add command-line arguments
783+
west_signers = {'silabs_rps': Silabs_rpsSigner}
784+
785+
def add_parser(subparsers):
786+
"""Add custom arguments for the silabs_rps signer."""
787+
parser = subparsers.add_parser('silabs_rps', help='Sign with Silicon Labs Commander')
788+
parser.add_argument('--m4-ota-key', help='OTA key for signing (overrides Kconfig.soc default)')
789+
parser.add_argument('--m4-private-key', help='Absolute path to private key file (overrides Kconfig.soc default)')
790+
parser.add_argument('--commander-path', help='Absolute path to Commander executable (overrides Kconfig.soc default)')
791+
return parser

soc/silabs/silabs_siwx91x/CMakeLists.txt

+45
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,48 @@ set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
1212
${KERNEL_BIN_NAME}
1313
${KERNEL_BIN_NAME}.rps
1414
)
15+
16+
# Custom signing with Silicon Labs Commander
17+
if(CONFIG_SIGNING_ENABLED)
18+
# Define input and output files
19+
set(ZEPHYR_BINARY ${CMAKE_BINARY_DIR}/zephyr/zephyr.bin)
20+
set(SIGNED_RPS ${CMAKE_BINARY_DIR}/zephyr/zephyr.bin.rps)
21+
22+
# Use $ENV{PWD} as working directory
23+
set(WORKSPACE_ROOT $ENV{PWD})
24+
25+
# Define the west sign command with explicit working directory and environment
26+
set(WEST_PATH $ENV{WEST_ROOT}/bin/west)
27+
if(NOT EXISTS ${WEST_PATH})
28+
set(WEST_PATH west) # Fallback to PATH search
29+
endif()
30+
set(west_sign_cmd
31+
${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} # Explicitly set working directory
32+
${CMAKE_COMMAND} -E env "PATH=$ENV{PATH}:$ENV{WEST_ROOT}/bin" "ZEPHYR_WORKSPACE_ROOT=${WORKSPACE_ROOT}"
33+
${WEST_PATH}
34+
sign
35+
-t silabs_rps
36+
-d ${CMAKE_BINARY_DIR}
37+
--sbin ${SIGNED_RPS}
38+
)
39+
40+
# Add custom command to invoke west sign during build
41+
add_custom_command(
42+
OUTPUT ${SIGNED_RPS}
43+
COMMAND ${CMAKE_COMMAND} -E echo "Forcing execution in ${CMAKE_BINARY_DIR}"
44+
COMMAND ${west_sign_cmd}
45+
DEPENDS zephyr_final
46+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR} # Ensure working directory is set
47+
COMMENT "Signing binary with 'west sign -t silabs_rps' to generate zephyr.bin.rps"
48+
VERBATIM
49+
)
50+
51+
# Add custom target to ensure signing is part of the build
52+
add_custom_target(silabs_rps_sign_target
53+
ALL
54+
DEPENDS ${SIGNED_RPS}
55+
COMMENT "Ensuring signing with Silicon Labs Commander is complete"
56+
)
57+
else()
58+
message(STATUS "CONFIG_SIGNIGING_ENABLED is not enabled")
59+
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 SL_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)