Commit | Line | Data |
---|---|---|
3e569a6b SG |
1 | U-Boot FIT Signature Verification |
2 | ================================= | |
3 | ||
4 | Introduction | |
5 | ------------ | |
6 | FIT supports hashing of images so that these hashes can be checked on | |
7 | loading. This protects against corruption of the image. However it does not | |
8 | prevent the substitution of one image for another. | |
9 | ||
10 | The signature feature allows the hash to be signed with a private key such | |
11 | that it can be verified using a public key later. Provided that the private | |
12 | key is kept secret and the public key is stored in a non-volatile place, | |
13 | any image can be verified in this way. | |
14 | ||
15 | See verified-boot.txt for more general information on verified boot. | |
16 | ||
17 | ||
18 | Concepts | |
19 | -------- | |
20 | Some familiarity with public key cryptography is assumed in this section. | |
21 | ||
22 | The procedure for signing is as follows: | |
23 | ||
24 | - hash an image in the FIT | |
25 | - sign the hash with a private key to produce a signature | |
26 | - store the resulting signature in the FIT | |
27 | ||
28 | The procedure for verification is: | |
29 | ||
30 | - read the FIT | |
31 | - obtain the public key | |
32 | - extract the signature from the FIT | |
33 | - hash the image from the FIT | |
34 | - verify (with the public key) that the extracted signature matches the | |
35 | hash | |
36 | ||
37 | The signing is generally performed by mkimage, as part of making a firmware | |
38 | image for the device. The verification is normally done in U-Boot on the | |
39 | device. | |
40 | ||
41 | ||
42 | Algorithms | |
43 | ---------- | |
44 | In principle any suitable algorithm can be used to sign and verify a hash. | |
45 | At present only one class of algorithms is supported: SHA1 hashing with RSA. | |
46 | This works by hashing the image to produce a 20-byte hash. | |
47 | ||
48 | While it is acceptable to bring in large cryptographic libraries such as | |
49 | openssl on the host side (e.g. mkimage), it is not desirable for U-Boot. | |
50 | For the run-time verification side, it is important to keep code and data | |
51 | size as small as possible. | |
52 | ||
53 | For this reason the RSA image verification uses pre-processed public keys | |
54 | which can be used with a very small amount of code - just some extraction | |
55 | of data from the FDT and exponentiation mod n. Code size impact is a little | |
56 | under 5KB on Tegra Seaboard, for example. | |
57 | ||
58 | It is relatively straightforward to add new algorithms if required. If | |
59 | another RSA variant is needed, then it can be added to the table in | |
60 | image-sig.c. If another algorithm is needed (such as DSA) then it can be | |
61 | placed alongside rsa.c, and its functions added to the table in image-sig.c | |
62 | also. | |
63 | ||
64 | ||
4c1d5c29 AD |
65 | Creating an RSA key pair and certificate |
66 | ---------------------------------------- | |
67 | To create a new public/private key pair, size 2048 bits: | |
3e569a6b | 68 | |
e0f2f155 MW |
69 | $ openssl genpkey -algorithm RSA -out keys/dev.key \ |
70 | -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 | |
3e569a6b | 71 | |
4c1d5c29 | 72 | To create a certificate for this containing the public key: |
3e569a6b SG |
73 | |
74 | $ openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt | |
75 | ||
76 | If you like you can look at the public key also: | |
77 | ||
78 | $ openssl rsa -in keys/dev.key -pubout | |
79 | ||
80 | ||
81 | Device Tree Bindings | |
82 | -------------------- | |
83 | The following properties are required in the FIT's signature node(s) to | |
e43f74ac | 84 | allow the signer to operate. These should be added to the .its file. |
3e569a6b SG |
85 | Signature nodes sit at the same level as hash nodes and are called |
86 | signature@1, signature@2, etc. | |
87 | ||
6af5520f | 88 | - algo: Algorithm name (e.g. "sha1,rsa2048") |
3e569a6b SG |
89 | |
90 | - key-name-hint: Name of key to use for signing. The keys will normally be in | |
91 | a single directory (parameter -k to mkimage). For a given key <name>, its | |
92 | private key is stored in <name>.key and the certificate is stored in | |
93 | <name>.crt. | |
94 | ||
95 | When the image is signed, the following properties are added (mandatory): | |
96 | ||
97 | - value: The signature data (e.g. 256 bytes for 2048-bit RSA) | |
98 | ||
99 | When the image is signed, the following properties are optional: | |
100 | ||
101 | - timestamp: Time when image was signed (standard Unix time_t format) | |
102 | ||
103 | - signer-name: Name of the signer (e.g. "mkimage") | |
104 | ||
105 | - signer-version: Version string of the signer (e.g. "2013.01") | |
106 | ||
107 | - comment: Additional information about the signer or image | |
108 | ||
4d098529 SG |
109 | For config bindings (see Signed Configurations below), the following |
110 | additional properties are optional: | |
3e569a6b | 111 | |
4d098529 SG |
112 | - sign-images: A list of images to sign, each being a property of the conf |
113 | node that contains then. The default is "kernel,fdt" which means that these | |
114 | two images will be looked up in the config and signed if present. | |
115 | ||
116 | For config bindings, these properties are added by the signer: | |
117 | ||
118 | - hashed-nodes: A list of nodes which were hashed by the signer. Each is | |
119 | a string - the full path to node. A typical value might be: | |
120 | ||
121 | hashed-nodes = "/", "/configurations/conf@1", "/images/kernel@1", | |
122 | "/images/kernel@1/hash@1", "/images/fdt@1", | |
123 | "/images/fdt@1/hash@1"; | |
124 | ||
125 | - hashed-strings: The start and size of the string region of the FIT that | |
126 | was hashed | |
127 | ||
128 | Example: See sign-images.its for an example image tree source file and | |
129 | sign-configs.its for config signing. | |
3e569a6b SG |
130 | |
131 | ||
132 | Public Key Storage | |
133 | ------------------ | |
134 | In order to verify an image that has been signed with a public key we need to | |
135 | have a trusted public key. This cannot be stored in the signed image, since | |
136 | it would be easy to alter. For this implementation we choose to store the | |
137 | public key in U-Boot's control FDT (using CONFIG_OF_CONTROL). | |
138 | ||
139 | Public keys should be stored as sub-nodes in a /signature node. Required | |
140 | properties are: | |
141 | ||
6af5520f | 142 | - algo: Algorithm name (e.g. "sha1,rsa2048") |
3e569a6b SG |
143 | |
144 | Optional properties are: | |
145 | ||
146 | - key-name-hint: Name of key used for signing. This is only a hint since it | |
147 | is possible for the name to be changed. Verification can proceed by checking | |
148 | all available signing keys until one matches. | |
149 | ||
150 | - required: If present this indicates that the key must be verified for the | |
151 | image / configuration to be considered valid. Only required keys are | |
152 | normally verified by the FIT image booting algorithm. Valid values are | |
e43f74ac | 153 | "image" to force verification of all images, and "conf" to force verification |
3e569a6b SG |
154 | of the selected configuration (which then relies on hashes in the images to |
155 | verify those). | |
156 | ||
157 | Each signing algorithm has its own additional properties. | |
158 | ||
159 | For RSA the following are mandatory: | |
160 | ||
161 | - rsa,num-bits: Number of key bits (e.g. 2048) | |
162 | - rsa,modulus: Modulus (N) as a big-endian multi-word integer | |
e0f2f155 | 163 | - rsa,exponent: Public exponent (E) as a 64 bit unsigned integer |
3e569a6b SG |
164 | - rsa,r-squared: (2^num-bits)^2 as a big-endian multi-word integer |
165 | - rsa,n0-inverse: -1 / modulus[0] mod 2^32 | |
166 | ||
167 | ||
4d098529 SG |
168 | Signed Configurations |
169 | --------------------- | |
170 | While signing images is useful, it does not provide complete protection | |
171 | against several types of attack. For example, it it possible to create a | |
172 | FIT with the same signed images, but with the configuration changed such | |
173 | that a different one is selected (mix and match attack). It is also possible | |
174 | to substitute a signed image from an older FIT version into a newer FIT | |
175 | (roll-back attack). | |
176 | ||
177 | As an example, consider this FIT: | |
178 | ||
179 | / { | |
180 | images { | |
181 | kernel@1 { | |
182 | data = <data for kernel1> | |
183 | signature@1 { | |
184 | algo = "sha1,rsa2048"; | |
185 | value = <...kernel signature 1...> | |
186 | }; | |
187 | }; | |
188 | kernel@2 { | |
189 | data = <data for kernel2> | |
190 | signature@1 { | |
191 | algo = "sha1,rsa2048"; | |
192 | value = <...kernel signature 2...> | |
193 | }; | |
194 | }; | |
195 | fdt@1 { | |
196 | data = <data for fdt1>; | |
197 | signature@1 { | |
198 | algo = "sha1,rsa2048"; | |
199 | vaue = <...fdt signature 1...> | |
200 | }; | |
201 | }; | |
202 | fdt@2 { | |
203 | data = <data for fdt2>; | |
204 | signature@1 { | |
205 | algo = "sha1,rsa2048"; | |
206 | vaue = <...fdt signature 2...> | |
207 | }; | |
208 | }; | |
209 | }; | |
210 | configurations { | |
211 | default = "conf@1"; | |
212 | conf@1 { | |
213 | kernel = "kernel@1"; | |
214 | fdt = "fdt@1"; | |
215 | }; | |
216 | conf@1 { | |
217 | kernel = "kernel@2"; | |
218 | fdt = "fdt@2"; | |
219 | }; | |
220 | }; | |
221 | }; | |
222 | ||
223 | Since both kernels are signed it is easy for an attacker to add a new | |
224 | configuration 3 with kernel 1 and fdt 2: | |
225 | ||
226 | configurations { | |
227 | default = "conf@1"; | |
228 | conf@1 { | |
229 | kernel = "kernel@1"; | |
230 | fdt = "fdt@1"; | |
231 | }; | |
232 | conf@1 { | |
233 | kernel = "kernel@2"; | |
234 | fdt = "fdt@2"; | |
235 | }; | |
236 | conf@3 { | |
237 | kernel = "kernel@1"; | |
238 | fdt = "fdt@2"; | |
239 | }; | |
240 | }; | |
241 | ||
242 | With signed images, nothing protects against this. Whether it gains an | |
243 | advantage for the attacker is debatable, but it is not secure. | |
244 | ||
e43f74ac | 245 | To solve this problem, we support signed configurations. In this case it |
4d098529 SG |
246 | is the configurations that are signed, not the image. Each image has its |
247 | own hash, and we include the hash in the configuration signature. | |
248 | ||
249 | So the above example is adjusted to look like this: | |
250 | ||
251 | / { | |
252 | images { | |
253 | kernel@1 { | |
254 | data = <data for kernel1> | |
255 | hash@1 { | |
256 | algo = "sha1"; | |
257 | value = <...kernel hash 1...> | |
258 | }; | |
259 | }; | |
260 | kernel@2 { | |
261 | data = <data for kernel2> | |
262 | hash@1 { | |
263 | algo = "sha1"; | |
264 | value = <...kernel hash 2...> | |
265 | }; | |
266 | }; | |
267 | fdt@1 { | |
268 | data = <data for fdt1>; | |
269 | hash@1 { | |
270 | algo = "sha1"; | |
271 | value = <...fdt hash 1...> | |
272 | }; | |
273 | }; | |
274 | fdt@2 { | |
275 | data = <data for fdt2>; | |
276 | hash@1 { | |
277 | algo = "sha1"; | |
278 | value = <...fdt hash 2...> | |
279 | }; | |
280 | }; | |
281 | }; | |
282 | configurations { | |
283 | default = "conf@1"; | |
284 | conf@1 { | |
285 | kernel = "kernel@1"; | |
286 | fdt = "fdt@1"; | |
287 | signature@1 { | |
288 | algo = "sha1,rsa2048"; | |
289 | value = <...conf 1 signature...>; | |
290 | }; | |
291 | }; | |
292 | conf@2 { | |
293 | kernel = "kernel@2"; | |
294 | fdt = "fdt@2"; | |
295 | signature@1 { | |
296 | algo = "sha1,rsa2048"; | |
297 | value = <...conf 1 signature...>; | |
298 | }; | |
299 | }; | |
300 | }; | |
301 | }; | |
302 | ||
303 | ||
304 | You can see that we have added hashes for all images (since they are no | |
305 | longer signed), and a signature to each configuration. In the above example, | |
306 | mkimage will sign configurations/conf@1, the kernel and fdt that are | |
307 | pointed to by the configuration (/images/kernel@1, /images/kernel@1/hash@1, | |
308 | /images/fdt@1, /images/fdt@1/hash@1) and the root structure of the image | |
309 | (so that it isn't possible to add or remove root nodes). The signature is | |
310 | written into /configurations/conf@1/signature@1/value. It can easily be | |
311 | verified later even if the FIT has been signed with other keys in the | |
312 | meantime. | |
313 | ||
314 | ||
3e569a6b SG |
315 | Verification |
316 | ------------ | |
317 | FITs are verified when loaded. After the configuration is selected a list | |
318 | of required images is produced. If there are 'required' public keys, then | |
319 | each image must be verified against those keys. This means that every image | |
320 | that might be used by the target needs to be signed with 'required' keys. | |
321 | ||
322 | This happens automatically as part of a bootm command when FITs are used. | |
323 | ||
324 | ||
325 | Enabling FIT Verification | |
326 | ------------------------- | |
327 | In addition to the options to enable FIT itself, the following CONFIGs must | |
328 | be enabled: | |
329 | ||
e43f74ac | 330 | CONFIG_FIT_SIGNATURE - enable signing and verification in FITs |
3e569a6b SG |
331 | CONFIG_RSA - enable RSA algorithm for signing |
332 | ||
21d29f7f HS |
333 | WARNING: When relying on signed FIT images with required signature check |
334 | the legacy image format is default disabled by not defining | |
335 | CONFIG_IMAGE_FORMAT_LEGACY | |
3e569a6b SG |
336 | |
337 | Testing | |
338 | ------- | |
e43f74ac | 339 | An easy way to test signing and verification is to use the test script |
3e569a6b SG |
340 | provided in test/vboot/vboot_test.sh. This uses sandbox (a special version |
341 | of U-Boot which runs under Linux) to show the operation of a 'bootm' | |
342 | command loading and verifying images. | |
343 | ||
344 | A sample run is show below: | |
345 | ||
346 | $ make O=sandbox sandbox_config | |
347 | $ make O=sandbox | |
348 | $ O=sandbox ./test/vboot/vboot_test.sh | |
349 | Simple Verified Boot Test | |
350 | ========================= | |
351 | ||
352 | Please see doc/uImage.FIT/verified-boot.txt for more information | |
353 | ||
646257d1 | 354 | /home/hs/ids/u-boot/sandbox/tools/mkimage -D -I dts -O dtb -p 2000 |
3e569a6b | 355 | Build keys |
646257d1 | 356 | do sha1 test |
3e569a6b SG |
357 | Build FIT with signed images |
358 | Test Verified Boot Run: unsigned signatures:: OK | |
359 | Sign images | |
360 | Test Verified Boot Run: signed images: OK | |
361 | Build FIT with signed configuration | |
362 | Test Verified Boot Run: unsigned config: OK | |
363 | Sign images | |
364 | Test Verified Boot Run: signed config: OK | |
29a23f9d | 365 | check signed config on the host |
ce1400f6 | 366 | Signature check OK |
29a23f9d HS |
367 | OK |
368 | Test Verified Boot Run: signed config: OK | |
646257d1 HS |
369 | Test Verified Boot Run: signed config with bad hash: OK |
370 | do sha256 test | |
371 | Build FIT with signed images | |
372 | Test Verified Boot Run: unsigned signatures:: OK | |
373 | Sign images | |
374 | Test Verified Boot Run: signed images: OK | |
375 | Build FIT with signed configuration | |
376 | Test Verified Boot Run: unsigned config: OK | |
377 | Sign images | |
378 | Test Verified Boot Run: signed config: OK | |
29a23f9d | 379 | check signed config on the host |
ce1400f6 | 380 | Signature check OK |
29a23f9d HS |
381 | OK |
382 | Test Verified Boot Run: signed config: OK | |
646257d1 | 383 | Test Verified Boot Run: signed config with bad hash: OK |
3e569a6b SG |
384 | |
385 | Test passed | |
386 | ||
ce1400f6 | 387 | |
f1ca1fde GM |
388 | Hardware Signing with PKCS#11 |
389 | ----------------------------- | |
390 | ||
391 | Securely managing private signing keys can challenging, especially when the | |
392 | keys are stored on the file system of a computer that is connected to the | |
393 | Internet. If an attacker is able to steal the key, they can sign malicious FIT | |
394 | images which will appear genuine to your devices. | |
395 | ||
396 | An alternative solution is to keep your signing key securely stored on hardware | |
397 | device like a smartcard, USB token or Hardware Security Module (HSM) and have | |
398 | them perform the signing. PKCS#11 is standard for interfacing with these crypto | |
399 | device. | |
400 | ||
401 | Requirements: | |
402 | Smartcard/USB token/HSM which can work with the pkcs11 engine | |
403 | openssl | |
404 | libp11 (provides pkcs11 engine) | |
405 | p11-kit (recommended to simplify setup) | |
406 | opensc (for smartcards and smartcard like USB devices) | |
407 | gnutls (recommended for key generation, p11tool) | |
408 | ||
409 | The following examples use the Nitrokey Pro. Instructions for other devices may vary. | |
410 | ||
411 | Notes on pkcs11 engine setup: | |
412 | ||
413 | Make sure p11-kit, opensc are installed and that p11-kit is setup to use opensc. | |
414 | /usr/share/p11-kit/modules/opensc.module should be present on your system. | |
415 | ||
416 | ||
417 | Generating Keys On the Nitrokey: | |
418 | ||
419 | $ gpg --card-edit | |
420 | ||
421 | Reader ...........: Nitrokey Nitrokey Pro (xxxxxxxx0000000000000000) 00 00 | |
422 | Application ID ...: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | |
423 | Version ..........: 2.1 | |
424 | Manufacturer .....: ZeitControl | |
425 | Serial number ....: xxxxxxxx | |
426 | Name of cardholder: [not set] | |
427 | Language prefs ...: de | |
428 | Sex ..............: unspecified | |
429 | URL of public key : [not set] | |
430 | Login data .......: [not set] | |
431 | Signature PIN ....: forced | |
432 | Key attributes ...: rsa2048 rsa2048 rsa2048 | |
433 | Max. PIN lengths .: 32 32 32 | |
434 | PIN retry counter : 3 0 3 | |
435 | Signature counter : 0 | |
436 | Signature key ....: [none] | |
437 | Encryption key....: [none] | |
438 | Authentication key: [none] | |
439 | General key info..: [none] | |
440 | ||
441 | gpg/card> generate | |
442 | Make off-card backup of encryption key? (Y/n) n | |
443 | ||
444 | Please note that the factory settings of the PINs are | |
445 | PIN = '123456' Admin PIN = '12345678' | |
446 | You should change them using the command --change-pin | |
447 | ||
448 | What keysize do you want for the Signature key? (2048) 4096 | |
449 | The card will now be re-configured to generate a key of 4096 bits | |
450 | Note: There is no guarantee that the card supports the requested size. | |
451 | If the key generation does not succeed, please check the | |
452 | documentation of your card to see what sizes are allowed. | |
453 | What keysize do you want for the Encryption key? (2048) 4096 | |
454 | The card will now be re-configured to generate a key of 4096 bits | |
455 | What keysize do you want for the Authentication key? (2048) 4096 | |
456 | The card will now be re-configured to generate a key of 4096 bits | |
457 | Please specify how long the key should be valid. | |
458 | 0 = key does not expire | |
459 | <n> = key expires in n days | |
460 | <n>w = key expires in n weeks | |
461 | <n>m = key expires in n months | |
462 | <n>y = key expires in n years | |
463 | Key is valid for? (0) | |
464 | Key does not expire at all | |
465 | Is this correct? (y/N) y | |
466 | ||
467 | GnuPG needs to construct a user ID to identify your key. | |
468 | ||
469 | Real name: John Doe | |
470 | Email address: john.doe@email.com | |
471 | Comment: | |
472 | You selected this USER-ID: | |
473 | "John Doe <john.doe@email.com>" | |
474 | ||
475 | Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o | |
476 | ||
477 | ||
478 | Using p11tool to get the token URL: | |
479 | ||
480 | Depending on system configuration, gpg-agent may need to be killed first. | |
481 | ||
482 | $ p11tool --provider /usr/lib/opensc-pkcs11.so --list-tokens | |
483 | Token 0: | |
484 | URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29 | |
485 | Label: OpenPGP card (User PIN (sig)) | |
486 | Type: Hardware token | |
487 | Manufacturer: ZeitControl | |
488 | Model: PKCS#15 emulated | |
489 | Serial: 000xxxxxxxxx | |
490 | Module: (null) | |
491 | ||
492 | ||
493 | Token 1: | |
494 | URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%29 | |
495 | Label: OpenPGP card (User PIN) | |
496 | Type: Hardware token | |
497 | Manufacturer: ZeitControl | |
498 | Model: PKCS#15 emulated | |
499 | Serial: 000xxxxxxxxx | |
500 | Module: (null) | |
501 | ||
502 | Use the portion of the signature token URL after "pkcs11:" as the keydir argument (-k) to mkimage below. | |
503 | ||
504 | ||
505 | Use the URL of the token to list the private keys: | |
506 | ||
507 | $ p11tool --login --provider /usr/lib/opensc-pkcs11.so --list-privkeys \ | |
508 | "pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29" | |
509 | Token 'OpenPGP card (User PIN (sig))' with URL 'pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29' requires user PIN | |
510 | Enter PIN: | |
511 | Object 0: | |
512 | URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29;id=%01;object=Signature%20key;type=private | |
513 | Type: Private key | |
514 | Label: Signature key | |
515 | Flags: CKA_PRIVATE; CKA_NEVER_EXTRACTABLE; CKA_SENSITIVE; | |
516 | ID: 01 | |
517 | ||
518 | Use the label, in this case "Signature key" as the key-name-hint in your FIT. | |
519 | ||
520 | Create the fitImage: | |
521 | $ ./tools/mkimage -f fit-image.its fitImage | |
522 | ||
523 | ||
524 | Sign the fitImage with the hardware key: | |
525 | ||
526 | $ ./tools/mkimage -F -k \ | |
527 | "model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29" \ | |
528 | -K u-boot.dtb -N pkcs11 -r fitImage | |
529 | ||
530 | ||
3e569a6b SG |
531 | Future Work |
532 | ----------- | |
533 | - Roll-back protection using a TPM is done using the tpm command. This can | |
534 | be scripted, but we might consider a default way of doing this, built into | |
535 | bootm. | |
536 | ||
537 | ||
538 | Possible Future Work | |
539 | -------------------- | |
540 | - Add support for other RSA/SHA variants, such as rsa4096,sha512. | |
541 | - Other algorithms besides RSA | |
542 | - More sandbox tests for failure modes | |
543 | - Passwords for keys/certificates | |
544 | - Perhaps implement OAEP | |
545 | - Enhance bootm to permit scripted signature verification (so that a script | |
546 | can verify an image but not actually boot it) | |
547 | ||
548 | ||
549 | Simon Glass | |
550 | sjg@chromium.org | |
551 | 1-1-13 |