Skip to content

Commit 0d6a1a7

Browse files
committed
[nrf fromlist] 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. Upstream PR #: 90605 Signed-off-by: Grzegorz Chwierut <[email protected]>
1 parent 59275b9 commit 0d6a1a7

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
@@ -1212,10 +1212,13 @@ class SysbuildKconfigCheck(KconfigCheck):
12121212
UNDEF_KCONFIG_ALLOWLIST = {
12131213
# zephyr-keep-sorted-start re(^\s+")
12141214
"FOO",
1215+
"MCUBOOT_SIGNATURE_USING_KMU", # Used by west flash --ncs-provision
12151216
"MY_IMAGE", # Used in sysbuild documentation as example
12161217
"OTHER_APP_IMAGE_NAME", # Used in sysbuild documentation as example
12171218
"OTHER_APP_IMAGE_PATH", # Used in sysbuild documentation as example
12181219
"SECOND_SAMPLE", # Used in sysbuild documentation
1220+
"SECURE_BOOT_SIGNATURE_TYPE_ED25519", # Used by west flash --ncs-provision
1221+
"SECURE_BOOT_SIGNING_KEY_FILE", # Used by west flash --ncs-provision
12191222
"SUIT_ENVELOPE", # Used by nRF runners to program provisioning data
12201223
"SUIT_MPI_APP_AREA_PATH", # Used by nRF runners to program provisioning data
12211224
"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 = []
@@ -113,6 +118,75 @@ def do_get_boards(self):
113118
def do_require(self):
114119
self.require('nrfutil')
115120

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

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

0 commit comments

Comments
 (0)