Skip to content

Commit eb548ec

Browse files
committed
west: runners: Add ncs-provision to west flash command
Added automatic KMU key provisioning for both NSIB and MCUboot. A new '--ncs-provision' command line option is added to nrfutil runner. This enables automated key provisioning during the flashing process to enable testing nRF54L aplications using Twister. Only applicable on NCS, when KMU keys are configured. Signed-off-by: Grzegorz Chwierut <[email protected]>
1 parent 21b20de commit eb548ec

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

scripts/ci/check_compliance.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,10 +1202,13 @@ class SysbuildKconfigCheck(KconfigCheck):
12021202
UNDEF_KCONFIG_ALLOWLIST = {
12031203
# zephyr-keep-sorted-start re(^\s+")
12041204
"FOO",
1205+
"MCUBOOT_SIGNATURE_USING_KMU", # Used by west flash --ncs-provision
12051206
"MY_IMAGE", # Used in sysbuild documentation as example
12061207
"OTHER_APP_IMAGE_NAME", # Used in sysbuild documentation as example
12071208
"OTHER_APP_IMAGE_PATH", # Used in sysbuild documentation as example
12081209
"SECOND_SAMPLE", # Used in sysbuild documentation
1210+
"SECURE_BOOT_SIGNATURE_TYPE_ED25519", # Used by west flash --ncs-provision
1211+
"SECURE_BOOT_SIGNING_KEY_FILE", # Used by west flash --ncs-provision
12091212
"SUIT_ENVELOPE", # Used by nRF runners to program provisioning data
12101213
"SUIT_MPI_APP_AREA_PATH", # Used by nRF runners to program provisioning data
12111214
"SUIT_MPI_GENERATE", # Used by nRF runners to program provisioning data

scripts/west_commands/runners/nrf_common.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False,
9797

9898
# Only applicable for nrfutil
9999
self.suit_starter = False
100+
self.ncs_provision = False
100101

101102
self.tool_opt = []
102103
if tool_opt is not None:
@@ -542,6 +543,9 @@ def do_exec_op(self, op, force=False):
542543
Throws subprocess.CalledProcessError with the appropriate
543544
returncode if a failure arises.'''
544545

546+
def do_ncs_provision(self):
547+
''' Provision default keys. Only applicable for nrfutil '''
548+
545549
def flush_ops(self, force=True):
546550
''' Execute any remaining ops in the self.ops array.
547551
Throws subprocess.CalledProcessError with the appropriate
@@ -589,6 +593,8 @@ def do_run(self, command, **kwargs):
589593
if self.recover:
590594
self.recover_target()
591595
self.program_hex()
596+
if self.ncs_provision:
597+
self.do_ncs_provision()
592598
if self.reset:
593599
self.reset_target()
594600
# All done, now flush any outstanding ops

scripts/west_commands/runners/nrfutil.py

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ class NrfUtilBinaryRunner(NrfBinaryRunner):
1919
def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False,
2020
erase_mode=None, ext_erase_mode=None, reset=True, tool_opt=None,
2121
force=False, recover=False, suit_starter=False,
22-
ext_mem_config_file=None):
22+
ext_mem_config_file=None, ncs_provision=False):
2323

2424
super().__init__(cfg, family, softreset, pinreset, dev_id, erase,
2525
erase_mode, ext_erase_mode, reset, tool_opt, force,
2626
recover)
2727

2828
self.suit_starter = suit_starter
2929
self.ext_mem_config_file = ext_mem_config_file
30+
self.ncs_provision = ncs_provision
3031

3132
self._ops = []
3233
self._op_id = 1
@@ -57,7 +58,8 @@ def do_create(cls, cfg, args):
5758
reset=args.reset, tool_opt=args.tool_opt,
5859
force=args.force, recover=args.recover,
5960
suit_starter=args.suit_manifest_starter,
60-
ext_mem_config_file=args.ext_mem_config_file)
61+
ext_mem_config_file=args.ext_mem_config_file,
62+
ncs_provision=args.ncs_provision)
6163

6264
@classmethod
6365
def do_add_parser(cls, parser):
@@ -68,6 +70,9 @@ def do_add_parser(cls, parser):
6870
parser.add_argument('--ext-mem-config-file', required=False,
6971
dest='ext_mem_config_file',
7072
help='path to an JSON file with external memory configuration')
73+
parser.add_argument('--ncs-provision',
74+
action='store_true',
75+
help='run ncs-provision with default keys')
7176

7277
def _exec(self, args):
7378
jout_all = []
@@ -115,6 +120,75 @@ def do_get_boards(self):
115120
def do_require(self):
116121
self.require('nrfutil')
117122

123+
def _generate_ncs_provision_key_file(
124+
self,
125+
keys: list[str] | str,
126+
keyname: str, # UROT_PUBKEY, BL_PUBKEY, APP_PUBKEY
127+
output_file: Path
128+
):
129+
"""Generate a key file for ncs-provision
130+
131+
This function uses the 'west ncs-provision' command to generate a JSON key file.
132+
Note: This functionality requires the sdk-nrf repository to be present, as it's
133+
not part of the Zephyr tree.
134+
"""
135+
build_dir = Path(self.cfg.build_dir).parent
136+
ncs_keyfile = build_dir / 'keyfile.json'
137+
if ncs_keyfile.exists():
138+
ncs_keyfile.unlink()
139+
command = [
140+
'west', 'ncs-provision', 'upload',
141+
'--keyname', keyname,
142+
'--build-dir', str(build_dir),
143+
'--dry-run'
144+
]
145+
for key in keys:
146+
command += ["--key", key]
147+
self.check_call(command)
148+
149+
# move the generated ncs keyfile to the output_file
150+
if output_file.exists():
151+
output_file.unlink()
152+
ncs_keyfile.rename(output_file)
153+
154+
def _ncs_provision_for_nsib(self):
155+
if not self.sysbuild_conf.getboolean('SB_CONFIG_SECURE_BOOT_SIGNATURE_TYPE_ED25519'):
156+
return
157+
build_dir = Path(self.cfg.build_dir).parent
158+
key_file = self.sysbuild_conf.get('SB_CONFIG_SECURE_BOOT_SIGNING_KEY_FILE') or str(
159+
build_dir / 'GENERATED_NON_SECURE_SIGN_KEY_PRIVATE.pem')
160+
if not Path(key_file).exists():
161+
raise RuntimeError(f'Key file {key_file} does not exist')
162+
163+
ncs_keyfile = build_dir / 'keyfile_for_nsib.json'
164+
self._generate_ncs_provision_key_file(
165+
keys=[key_file],
166+
keyname='BL_PUBKEY',
167+
output_file=ncs_keyfile
168+
)
169+
self.exec_op('x-provision-keys', keyfile=str(ncs_keyfile))
170+
171+
def _ncs_provision_for_mcuboot(self):
172+
if not self.sysbuild_conf.getboolean('SB_CONFIG_MCUBOOT_SIGNATURE_USING_KMU'):
173+
return
174+
key_file = self.sysbuild_conf.get('SB_CONFIG_BOOT_SIGNATURE_KEY_FILE')
175+
if not Path(key_file).exists():
176+
raise RuntimeError(f'Key figrch_rework_testsle {key_file} does not exist')
177+
178+
ncs_keyfile = Path(self.cfg.build_dir).parent / 'keyfile_for_mcuboot.json'
179+
self._generate_ncs_provision_key_file(
180+
keys=[key_file],
181+
keyname='UROT_PUBKEY',
182+
output_file=ncs_keyfile
183+
)
184+
self.exec_op('x-provision-keys', keyfile=str(ncs_keyfile))
185+
186+
def do_ncs_provision(self):
187+
if not self.ncs_provision:
188+
return
189+
self._ncs_provision_for_nsib()
190+
self._ncs_provision_for_mcuboot()
191+
118192
def _insert_op(self, op):
119193
op['operationId'] = f'{self._op_id}'
120194
self._op_id += 1
@@ -147,6 +221,8 @@ def _append_batch(self, op, json_file):
147221
cmd += ['--reset-kind', _op['kind']]
148222
elif op_type == 'erase':
149223
cmd.append(f'--{_op["kind"]}')
224+
elif op_type == 'x-provision-keys':
225+
cmd += ['--key-file', _op['keyfile']]
150226

151227
cmd += ['--core', op['core']] if op.get('core') else []
152228
cmd += ['--x-family', f'{self.family}']

0 commit comments

Comments
 (0)