33#
44# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
55
6- from pathlib import Path
6+ from __future__ import annotations
7+
78import re
8- import sys
99import subprocess
10+ import sys
11+ from pathlib import Path
12+
1013from cryptography .hazmat .primitives .serialization import load_pem_private_key
1114from west .commands import WestCommand
1215
13- nrf54l15_key_slots = [226 , 228 , 230 ]
14- nrf54l15_key_policies = {"revokable" : "REVOKED" ,
15- "lock" : "LOCKED" }
16+ KEY_SLOTS : dict [str , list [int ]] = {
17+ "UROT_PUBKEY" : [226 , 228 , 230 ],
18+ "BL_PUBKEY" : [242 , 244 , 246 ],
19+ "APP_PUBKEY" : [202 , 204 , 206 ],
20+ }
21+ KEY_SLOT_METADATA : str = "0x10ba0030"
22+ KMU_KEY_SLOT_DEST_ADDR : str = "0x20000000"
23+ ALGORITHM : str = "ED25519"
24+ NRF54L15_KEY_POLICIES : dict [str , str ] = {"revokable" : "REVOKED" , "lock" : "LOCKED" }
1625
1726
1827class NcsProvision (WestCommand ):
28+
1929 def __init__ (self ):
2030 super ().__init__ (
2131 "ncs-provision" ,
@@ -25,74 +35,99 @@ def __init__(self):
2535
2636 def do_add_parser (self , parser_adder ):
2737 parser = parser_adder .add_parser (
28- self .name , help = self .help , description = self .description
29- )
38+ self .name , help = self .help , description = self .description )
3039
31- subparsers = parser .add_subparsers (
32- dest = "command"
33- )
40+ subparsers = parser .add_subparsers (dest = "command" )
3441 upload_parser = subparsers .add_parser ("upload" , help = "Send to KMU" )
3542 upload_parser .add_argument (
36- "-k" , "--key" , type = Path , action = 'append' , dest = "keys" ,
37- help = "Input .pem file with ED25519 private key"
43+ "-k" ,
44+ "--key" ,
45+ type = Path ,
46+ action = "append" ,
47+ dest = "keys" ,
48+ help = "Input .pem file with ED25519 private key" ,
49+ )
50+ upload_parser .add_argument (
51+ "--keyname" ,
52+ choices = KEY_SLOTS .keys (),
53+ # default value for backward compatibility
54+ default = "UROT_PUBKEY" ,
55+ help = "Key name to upload" ,
56+ )
57+ upload_parser .add_argument (
58+ "-p" ,
59+ "--policy" ,
60+ type = str ,
61+ choices = ["revokable" , "lock" , "lock-last" ],
62+ default = "lock-last" ,
63+ help = "Policy applied to the given set of keys. "
64+ "revokable: keys can be revoked each by one. "
65+ "lock: all keys stay as they are. "
66+ "lock-last: last key is uploaded as locked, "
67+ "others as revokable" ,
68+ )
69+ upload_parser .add_argument (
70+ "-s" , "--soc" , type = str , help = "SoC" ,
71+ choices = ["nrf54l05" , "nrf54l10" , "nrf54l15" ], required = True
3872 )
39- upload_parser .add_argument ("-p" , "--policy" , type = str ,
40- choices = ["revokable" , "lock" , "lock-last" ], default = "lock-last" ,
41- help = "Policy applied to the given set of keys."
42- " revokable: keys can be revoked each by one."
43- " lock: all keys stay as they are."
44- " lock-last: last key is uploaded as locked,"
45- " others as revokable" )
46- upload_parser .add_argument ("-s" , "--soc" , type = str , help = "SoC" ,
47- choices = ["nrf54l05" , "nrf54l10" , "nrf54l15" ], required = True )
4873 upload_parser .add_argument ("--dev-id" , help = "Device serial number" )
4974
5075 return parser
5176
5277 def do_run (self , args , unknown_args ):
5378 if args .command == "upload" :
5479 if args .soc in ["nrf54l05" , "nrf54l10" , "nrf54l15" ]:
55- if len (args .keys ) > len (nrf54l15_key_slots ):
80+ keyname = args .keyname
81+ if len (args .keys ) > len (KEY_SLOTS [keyname ]):
5682 sys .exit (
5783 "Error: requested upload of more keys than there are designated slots." )
58- slot = 0
59- for keyfile in args . keys :
60- with open ( keyfile , 'rb' ) as f :
61- priv_key = load_pem_private_key ( f .read (), password = None )
84+ for slot_idx , keyfile in enumerate ( args . keys ):
85+ with open ( keyfile , "rb" ) as f :
86+ priv_key = load_pem_private_key (
87+ f .read (), password = None )
6288 pub_key = priv_key .public_key ()
6389 if args .policy == "lock-last" :
64- if slot == (len (args .keys ) - 1 ):
65- key_policy = nrf54l15_key_policies ["lock" ]
90+ if slot_idx == (len (args .keys ) - 1 ):
91+ key_policy = NRF54L15_KEY_POLICIES ["lock" ]
6692 else :
67- key_policy = nrf54l15_key_policies ["revokable" ]
93+ key_policy = NRF54L15_KEY_POLICIES ["revokable" ]
6894 else :
69- key_policy = nrf54l15_key_policies [args .policy ]
70- command = [
71- "nrfprovision" ,
72- "provision" ,
73- "-r" ,
74- key_policy ,
75- "-v" ,
76- pub_key .public_bytes_raw ().hex (),
77- "-m" ,
78- "0x10ba0030" ,
79- "-i" ,
80- str (nrf54l15_key_slots [slot ]),
81- "-a" ,
82- "ED25519" ,
83- "-d" ,
84- "0x20000000" ,
85- "--verify"
86- ]
87- if args .dev_id :
88- command .extend (["--snr" , args .dev_id ])
95+ key_policy = NRF54L15_KEY_POLICIES [args .policy ]
96+ dev_id = args .dev_id
97+ pub_key_hex = pub_key .public_bytes_raw ().hex ()
98+ slot_id = str (KEY_SLOTS [keyname ][slot_idx ])
99+ command = self ._build_command (
100+ dev_id = dev_id , key_policy = key_policy , pub_key = pub_key_hex , slot_id = slot_id
101+ )
89102 nrfprovision = subprocess .run (
90- command ,
91- stderr = subprocess .PIPE ,
92- text = True
103+ command , stderr = subprocess .PIPE , text = True
93104 )
94105 stderr = nrfprovision .stderr
95106 print (stderr , file = sys .stderr )
96- if re .search (' fail' , stderr ) or nrfprovision .returncode :
107+ if re .search (" fail" , stderr ) or nrfprovision .returncode :
97108 sys .exit ("Uploading failed!" )
98- slot += 1
109+
110+ @staticmethod
111+ def _build_command (
112+ key_policy : str , pub_key : str , slot_id : str , dev_id : str | None
113+ ) -> list [str ]:
114+ command = [
115+ "nrfprovision" ,
116+ "provision" ,
117+ "--rpolicy" ,
118+ key_policy ,
119+ "--value" ,
120+ pub_key ,
121+ "--metadata" ,
122+ KEY_SLOT_METADATA ,
123+ "--id" ,
124+ slot_id ,
125+ "--algorithm" ,
126+ ALGORITHM ,
127+ "--dest" ,
128+ KMU_KEY_SLOT_DEST_ADDR ,
129+ "--verify" ,
130+ ]
131+ if dev_id :
132+ command .extend (["--snr" , dev_id ])
133+ return command
0 commit comments