22
33import argparse
44import base64
5+ import os
56import struct
7+ import subprocess
68import sys
9+ import tempfile
710
811# python3 -m pip install pycryptodomex
912from Cryptodome .Hash import HMAC , SHA1 , SHA256
@@ -105,6 +108,30 @@ class ImageFile:
105108 debug ("%08x %20s: [%6d] %s" % (self .pos (), 'RSA' , len (arr ), pem_file ))
106109 self .append (arr )
107110
111+ h = SHA256 .new ()
112+ h .update (key .n .to_bytes (256 , byteorder = 'little' ))
113+ h .update (key .e .to_bytes (8 , byteorder = 'little' ))
114+ d = h .hexdigest ()
115+ pub_str = ""
116+ for i in range (int (len (d )/ 8 )):
117+ pub_str += "0x%s%s%s%s, " % (d [i * 8 + 6 :i * 8 + 8 ], d [i * 8 + 4 :i * 8 + 6 ], d [i * 8 + 2 :i * 8 + 4 ], d [i * 8 + 0 :i * 8 + 2 ])
118+ debug ("Public key SHA256(N,e) = %s" % pub_str )
119+
120+ def append_rsa_signature_pkcs11 (self , hsm_wrapper ):
121+ temp = tempfile .NamedTemporaryFile (delete = False )
122+ temp .write (self ._bytes )
123+ temp .close () # close and flush before spawning PKCS#11 wrapper
124+
125+ res = subprocess .run ([hsm_wrapper , "-a" , "rsa2048-sha256" , temp .name ], capture_output = True )
126+ debug (res .stderr )
127+ if res .returncode != 0 :
128+ os .unlink (temp .name )
129+ raise Exception (f"HSM wrapper failed with exit code { res .returncode } : { res .stderr .decode ()} " )
130+ signature = res .stdout .decode ()
131+ os .unlink (temp .name )
132+ self .append (bytearray .fromhex (signature ))
133+ debug ("PKCS11 %08x %20s: [%6d] signature %s" % (self .pos (), 'RSA2048 - SHA256' , len (signature ), signature ))
134+
108135 def append_rsa_signature (self , digest_alg , private_pem ):
109136 """
110137 Append a RSA 2048 signature of the SHA256 of the data so far
@@ -132,19 +159,13 @@ class ImageFile:
132159 if len (hmac_key ) != expected_keylen :
133160 raise Exception ("Bad key length %d expected %d" % (len (hmac_key ), expected_keylen ))
134161
135- if digest_alg == 'hmac-sha256' :
136- digest = HMAC .new (base64 .b16decode (hmac_key , True ), self ._bytes , digestmod = SHA256 )
137- elif digest_alg == 'hmac-sha1' :
138- digest = HMAC .new (base64 .b16decode (hmac_key , True ), self ._bytes , digestmod = SHA1 )
139- elif digest_alg == 'sha256' :
140- digest = SHA256 .new (self ._bytes )
141- elif digest_alg == 'sha1' :
142- digest = SHA1 .new (self ._bytes )
162+ if digest_alg == 'hmac-sha1' :
163+ h = HMAC .new (base64 .b16decode (hmac_key , True ), self ._bytes , digestmod = SHA1 )
143164 else :
144165 raise Exception ("Digest not supported %s" % (digest_alg ))
145166
146- debug ("%08x %20s: [%6d] %s" % (self .pos (), digest_alg , len (digest .digest ()), digest .hexdigest ()))
147- self .append (digest .digest ())
167+ debug ("%08x %20s: [%6d] %s" % (self .pos (), digest_alg , len (h .digest ()), h .hexdigest ()))
168+ self .append (h .digest ())
148169
149170 def pos (self ):
150171 return len (self ._bytes )
@@ -161,30 +182,39 @@ class ImageFile:
161182 def close (self ):
162183 self ._of .close ()
163184
164- def create_2711_image (output , bootcode , private_key , private_keynum , hmac ):
185+ def create_2711_image (output , bootcode , private_key = None , private_keynum = 0 , hmac = None , hsm_wrapper = None ):
165186 """
166187 Create a 2711 C0 secure-boot compatible seconds stage signed binary.
167188 """
168189 image = ImageFile (output , MAX_BIN_SIZE )
169190 image .append_file (bootcode )
170191 image .append_length ()
171192 image .append_keynum (private_keynum )
172- image .append_rsa_signature ('sha1' , private_key )
193+ if hsm_wrapper :
194+ image .append_rsa_signature_pkcs11 (hsm_wrapper )
195+ else :
196+ image .append_rsa_signature ('sha1' , private_key )
173197 image .append_digest ('hmac-sha1' , hmac )
174198 image .write ()
175199 image .close ()
176200
177- def create_2712_image (output , bootcode , private_key , private_keynum , private_version ):
201+ def create_2712_image (output , bootcode , private_version = 0 , public_key = None , private_key = None , private_keynum = 0 , hsm_wrapper = None ):
178202 """
179- Create 2712 signed bootloader. The HMAC is removed and the full public key is appended.
203+ Create a prototype 2712 signed bootloader. The HMAC is removed and the
204+ full public key is appended.
180205 """
181206 image = ImageFile (output , MAX_BIN_SIZE )
182207 image .append_file (bootcode )
183208 image .append_length ()
184209 image .append_keynum (private_keynum )
185210 image .append_version (private_version )
186- image .append_rsa_signature ('sha256' , private_key )
187- image .append_public_key (private_key )
211+ if hsm_wrapper is not None :
212+ debug (f"Call HSM wrapper { hsm_wrapper } " )
213+ image .append_rsa_signature_pkcs11 (hsm_wrapper )
214+ image .append_public_key (public_key )
215+ else :
216+ image .append_rsa_signature ('sha256' , private_key )
217+ image .append_public_key (private_key )
188218 image .write ()
189219 image .close ()
190220
@@ -193,37 +223,43 @@ def main():
193223 Signs a second stage bootloader image.
194224
195225 Examples:
196- 2711 mode:
197- rpi-sign-bootcode --debug -c 2711 -i bootcode.bin.clr -o bootcode.bin -k 2711_rsa_priv_0.pem -n 0 -m bootcode-production.key
198-
199- 2712 C1 and D0 mode:
200- * HMAC not included on 2712
201- * RSA public key included - ROM just contains the hashes of the RPi public keys.
202-
203- Customer counter-signed signed:
226+
227+ Customer counter-signed:
204228 * Exactly the same as Raspberry Pi signing but the input is the Raspberry Pi signed bootcode.bin
205229 * The key number will probably always be 16 to indicate a customer signing
206230
207231 rpi-sign-bootcode --debug -c 2712 -i bootcode.bin.sign2 -o bootcode.bin -k customer.pem
232+
233+ PKCS#1 v1.5 - HSM wrapper:
234+ * hsm-wrapper takes a single argument which is a temporary filename containing the data to sign
235+ * hsm-wrapper outputs the PKCS#1 v1.5 signature in hex format
236+ * hsm-wrapper must return a non-zero exit code if signing fails
237+ * hsm-wrapper requires the -a rsa2048-sha256 parameter to specify the algorithm
238+ * There is no facility to pass the private key or custom HSM arguments - the caller should generate a dedicated wrapper script
239+ * The public key in PEM format MUST be specified with the -p option
240+
241+ rpi-sign-bootcode --debug -c 2712 -i bootcode.bin.sign2 -o bootcode.bin -p public.pem -H hsm-wrapper
208242 """
209243 parser = argparse .ArgumentParser (help_text )
210- parser .add_argument ('-o' , '--output' , required = False , help = 'Output filename . If not specified the signed images is written to stdout in base64 format' )
244+ parser .add_argument ('-o' , '--output' , required = False , help = 'Output filename. If not specified, the signed image is written to stdout in base64 format' )
211245 parser .add_argument ('-c' , '--chip' , required = True , type = int , help = 'Chip number' )
212- parser .add_argument ('-i' , '--input' , required = False , help = 'Path of the unsigned bootcode.bin file OR RPi signed bootcode file sign with the customer key. If NULLL the binary is read from stdin in base64 format' )
246+ parser .add_argument ('-i' , '--input' , required = False , help = 'Path of the unsigned bootcode.bin file OR RPi signed bootcode file to be signed with the customer key. If NULL, the binary is read from stdin in base64 format' )
213247 parser .add_argument ('-m' , '--hmac' , required = False , help = 'Path of the HMAC key file' )
214- parser .add_argument ('-k' , '--private-key' , dest = 'private_key' , required = True , help = 'Path of RSA private key (PEM format)' )
248+ parser .add_argument ('-k' , '--private-key' , dest = 'private_key' , required = False , default = None , help = 'Path of RSA private key (PEM format)' )
249+ parser .add_argument ('-p' , '--public-key' , dest = 'public_key' , required = False , default = None , help = 'Path of RSA public key (PEM format)' )
215250 parser .add_argument ('-n' , '--private-keynum' , dest = 'private_keynum' , required = False , default = 0 , type = int , help = 'ROM key index for RPi signing stage' )
251+ parser .add_argument ('-H' , '--hsm-wrapper' , default = None , required = False , help = 'Filename of HSM wrapper script which generates a PKCSv1.1 signature as hex' )
216252 parser .add_argument ('-d' , '--debug' , action = 'store_true' )
217- parser .add_argument ('-v' , '--private-version' , dest = 'private_version' , required = True , type = int , help = 'Version of firmware, stops firmware rollback, only valid 0-31' )
253+ parser .add_argument ('-v' , '--private-version' , dest = 'private_version' , required = False , default = 0 , type = int , help = 'Version of firmware, stops firmware rollback, only valid 0-31' )
218254
219255 args = parser .parse_args ()
220256 _CONFIG ['DEBUG' ] = args .debug
221257 if args .chip == 2711 :
222258 if args .hmac is None :
223259 raise Exception ("HMAC key requried for 2711" )
224- create_2711_image (args .output , args .input , args .private_key , args .private_keynum , args .hmac )
260+ create_2711_image (args .output , args .input , private_key = args .private_key , private_keynum = args .private_keynum , hmac = args .hmac , hsm_wrapper = args . hsm_wrapper )
225261 elif args .chip == 2712 :
226- create_2712_image (args .output , args .input , args .private_key , args .private_keynum , args .private_version )
262+ create_2712_image (args .output , args .input , private_version = args .private_version , public_key = args . public_key , private_key = args . private_key , private_keynum = args .private_keynum , hsm_wrapper = args .hsm_wrapper )
227263
228264if __name__ == '__main__' :
229265 main ()
0 commit comments