blob: 0580ee10bdcc3c86fcdbb54c6bcefb16727cadf9 [file] [log] [blame]
Simon Glassfb8b1122023-06-23 13:22:06 +01001.. SPDX-License-Identifier: GPL-2.0+
2
3Verified Boot on the Beaglebone Black
4=====================================
5
6Introduction
7------------
8
9Before reading this, please read :doc:`verified-boot` and :doc:`signature`.
10These instructions are for mainline U-Boot from v2014.07 onwards.
11
12There is quite a bit of documentation in this directory describing how
13verified boot works in U-Boot. There is also a test which runs through the
14entire process of signing an image and running U-Boot (sandbox) to check it.
15However, it might be useful to also have an example on a real board.
16
17Beaglebone Black is a fairly common board so seems to be a reasonable choice
18for an example of how to enable verified boot using U-Boot.
19
20First a note that may to help avoid confusion. U-Boot and Linux both use
21device tree. They may use the same device tree source, but it is seldom useful
22for them to use the exact same binary from the same place. More typically,
23U-Boot has its device tree packaged with it, and the kernel's device tree is
24packaged with the kernel. In particular this is important with verified boot,
25since U-Boot's device tree must be immutable. If it can be changed then the
26public keys can be changed and verified boot is useless. An attacker can
27simply generate a new key and put his public key into U-Boot so that
28everything verifies. On the other hand the kernel's device tree typically
29changes when the kernel changes, so it is useful to package an updated device
30tree with the kernel binary. U-Boot supports the latter with its flexible FIT
31format (Flat Image Tree).
32
33
34Overview
35--------
36
37The steps are roughly as follows:
38
39#. Build U-Boot for the board, with the verified boot options enabled.
40
41#. Obtain a suitable Linux kernel
42
43#. Create a Image Tree Source file (ITS) file describing how you want the
44 kernel to be packaged, compressed and signed.
45
46#. Create a key pair
47
48#. Sign the kernel
49
50#. Put the public key into U-Boot's image
51
52#. Put U-Boot and the kernel onto the board
53
54#. Try it
55
56
57Step 1: Build U-Boot
58--------------------
59
60a. Set up the environment variable to point to your toolchain. You will need
61 this for U-Boot and also for the kernel if you build it. For example if you
62 installed a Linaro version manually it might be something like::
63
64 export CROSS_COMPILE=/opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux/bin/arm-linux-gnueabihf-
65
66 or if you just installed gcc-arm-linux-gnueabi then it might be::
67
68 export CROSS_COMPILE=arm-linux-gnueabi-
69
70b. Configure and build U-Boot with verified boot enabled::
71
72 export UBOOT=/path/to/u-boot
73 cd $UBOOT
74 # You can add -j10 if you have 10 CPUs to make it faster
75 make O=b/am335x_boneblack_vboot am335x_boneblack_vboot_config all
76 export UOUT=$UBOOT/b/am335x_boneblack_vboot
77
78c. You will now have a U-Boot image::
79
80 file b/am335x_boneblack_vboot/u-boot-dtb.img
81 b/am335x_boneblack_vboot/u-boot-dtb.img: u-boot legacy uImage,
82 U-Boot 2014.07-rc2-00065-g2f69f8, Firmware/ARM, Firmware Image
83 (Not compressed), 395375 bytes, Sat May 31 16:19:04 2014,
84 Load Address: 0x80800000, Entry Point: 0x00000000,
85 Header CRC: 0x0ABD6ACA, Data CRC: 0x36DEF7E4
86
87
88Step 2: Build Linux
89--------------------
90
91a. Find the kernel image ('Image') and device tree (.dtb) file you plan to
92 use. In our case it is am335x-boneblack.dtb and it is built with the kernel.
93 At the time of writing an SD Boot image can be obtained from here::
94
95 http://www.elinux.org/Beagleboard:Updating_The_Software#Image_For_Booting_From_microSD
96
97 You can write this to an SD card and then mount it to extract the kernel and
98 device tree files.
99
100 You can also build a kernel. Instructions for this are are here::
101
102 http://elinux.org/Building_BBB_Kernel
103
104 or you can use your favourite search engine. Following these instructions
105 produces a kernel Image and device tree files. For the record the steps
106 were::
107
108 export KERNEL=/path/to/kernel
109 cd $KERNEL
110 git clone git://github.com/beagleboard/kernel.git .
111 git checkout v3.14
112 ./patch.sh
113 cp configs/beaglebone kernel/arch/arm/configs/beaglebone_defconfig
114 cd kernel
115 make beaglebone_defconfig
116 make uImage dtbs # -j10 if you have 10 CPUs
117 export OKERNEL=$KERNEL/kernel/arch/arm/boot
118
119b. You now have the 'Image' and 'am335x-boneblack.dtb' files needed to boot.
120
121
122Step 3: Create the ITS
123----------------------
124
125Set up a directory for your work::
126
127 export WORK=/path/to/dir
128 cd $WORK
129
130Put this into a file in that directory called sign.its::
131
132 /dts-v1/;
133
134 / {
135 description = "Beaglebone black";
136 #address-cells = <1>;
137
138 images {
139 kernel {
140 data = /incbin/("Image.lzo");
141 type = "kernel";
142 arch = "arm";
143 os = "linux";
144 compression = "lzo";
145 load = <0x80008000>;
146 entry = <0x80008000>;
147 hash-1 {
148 algo = "sha1";
149 };
150 };
151 fdt-1 {
152 description = "beaglebone-black";
153 data = /incbin/("am335x-boneblack.dtb");
154 type = "flat_dt";
155 arch = "arm";
156 compression = "none";
157 hash-1 {
158 algo = "sha1";
159 };
160 };
161 };
162 configurations {
163 default = "conf-1";
164 conf-1 {
165 kernel = "kernel";
166 fdt = "fdt-1";
167 signature-1 {
168 algo = "sha1,rsa2048";
169 key-name-hint = "dev";
170 sign-images = "fdt", "kernel";
171 };
172 };
173 };
174 };
175
176
177The explanation for this is all in the documentation you have already read.
178But briefly it packages a kernel and device tree, and provides a single
179configuration to be signed with a key named 'dev'. The kernel is compressed
180with LZO to make it smaller.
181
182
183Step 4: Create a key pair
184-------------------------
185
186See :doc:`signature` for details on this step::
187
188 cd $WORK
189 mkdir keys
190 openssl genrsa -F4 -out keys/dev.key 2048
191 openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt
192
193Note: keys/dev.key contains your private key and is very secret. If anyone
194gets access to that file they can sign kernels with it. Keep it secure.
195
196
197Step 5: Sign the kernel
198-----------------------
199
200We need to use mkimage (which was built when you built U-Boot) to package the
201Linux kernel into a FIT (Flat Image Tree, a flexible file format that U-Boot
202can load) using the ITS file you just created.
203
204At the same time we must put the public key into U-Boot device tree, with the
205'required' property, which tells U-Boot that this key must be verified for the
206image to be valid. You will make this key available to U-Boot for booting in
207step 6::
208
209 ln -s $OKERNEL/dts/am335x-boneblack.dtb
210 ln -s $OKERNEL/Image
211 ln -s $UOUT/u-boot-dtb.img
212 cp $UOUT/arch/arm/dts/am335x-boneblack.dtb am335x-boneblack-pubkey.dtb
213 lzop Image
214 $UOUT/tools/mkimage -f sign.its -K am335x-boneblack-pubkey.dtb -k keys -r image.fit
215
216You should see something like this::
217
218 FIT description: Beaglebone black
219 Created: Sun Jun 1 12:50:30 2014
220 Image 0 (kernel)
221 Description: unavailable
222 Created: Sun Jun 1 12:50:30 2014
223 Type: Kernel Image
224 Compression: lzo compressed
225 Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB
226 Architecture: ARM
227 OS: Linux
228 Load Address: 0x80008000
229 Entry Point: 0x80008000
230 Hash algo: sha1
231 Hash value: c94364646427e10f423837e559898ef02c97b988
232 Image 1 (fdt-1)
233 Description: beaglebone-black
234 Created: Sun Jun 1 12:50:30 2014
235 Type: Flat Device Tree
236 Compression: uncompressed
237 Data Size: 31547 Bytes = 30.81 kB = 0.03 MB
238 Architecture: ARM
239 Hash algo: sha1
240 Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d
241 Default Configuration: 'conf-1'
242 Configuration 0 (conf-1)
243 Description: unavailable
244 Kernel: kernel
245 FDT: fdt-1
246
247
248Now am335x-boneblack-pubkey.dtb contains the public key and image.fit contains
249the signed kernel. Jump to step 6 if you like, or continue reading to increase
250your understanding.
251
252You can also run fit_check_sign to check it::
253
254 $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb
255
256which results in::
257
258 Verifying Hash Integrity ... sha1,rsa2048:dev+
259 ## Loading kernel from FIT Image at 7fc6ee469000 ...
260 Using 'conf-1' configuration
261 Verifying Hash Integrity ...
262 sha1,rsa2048:dev+
263 OK
264
265 Trying 'kernel' kernel subimage
266 Description: unavailable
267 Created: Sun Jun 1 12:50:30 2014
268 Type: Kernel Image
269 Compression: lzo compressed
270 Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB
271 Architecture: ARM
272 OS: Linux
273 Load Address: 0x80008000
274 Entry Point: 0x80008000
275 Hash algo: sha1
276 Hash value: c94364646427e10f423837e559898ef02c97b988
277 Verifying Hash Integrity ...
278 sha1+
279 OK
280
281 Unimplemented compression type 4
282 ## Loading fdt from FIT Image at 7fc6ee469000 ...
283 Using 'conf-1' configuration
284 Trying 'fdt-1' fdt subimage
285 Description: beaglebone-black
286 Created: Sun Jun 1 12:50:30 2014
287 Type: Flat Device Tree
288 Compression: uncompressed
289 Data Size: 31547 Bytes = 30.81 kB = 0.03 MB
290 Architecture: ARM
291 Hash algo: sha1
292 Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d
293 Verifying Hash Integrity ...
294 sha1+
295 OK
296
297 Loading Flat Device Tree ... OK
298
299 ## Loading ramdisk from FIT Image at 7fc6ee469000 ...
300 Using 'conf-1' configuration
301 Could not find subimage node
302
303 Signature check OK
304
305
306At the top, you see "sha1,rsa2048:dev+". This means that it checked an RSA key
307of size 2048 bits using SHA1 as the hash algorithm. The key name checked was
308'dev' and the '+' means that it verified. If it showed '-' that would be bad.
309
310Once the configuration is verified it is then possible to rely on the hashes
311in each image referenced by that configuration. So fit_check_sign goes on to
312load each of the images. We have a kernel and an FDT but no ramkdisk. In each
313case fit_check_sign checks the hash and prints sha1+ meaning that the SHA1
314hash verified. This means that none of the images has been tampered with.
315
316There is a test in test/vboot which uses U-Boot's sandbox build to verify that
317the above flow works.
318
319But it is fun to do this by hand, so you can load image.fit into a hex editor
320like ghex, and change a byte in the kernel::
321
322 $UOUT/tools/fit_info -f image.fit -n /images/kernel -p data
323 NAME: kernel
324 LEN: 7790938
325 OFF: 168
326
327This tells us that the kernel starts at byte offset 168 (decimal) in image.fit
328and extends for about 7MB. Try changing a byte at 0x2000 (say) and run
329fit_check_sign again. You should see something like::
330
331 Verifying Hash Integrity ... sha1,rsa2048:dev+
332 ## Loading kernel from FIT Image at 7f5a39571000 ...
333 Using 'conf-1' configuration
334 Verifying Hash Integrity ...
335 sha1,rsa2048:dev+
336 OK
337
338 Trying 'kernel' kernel subimage
339 Description: unavailable
340 Created: Sun Jun 1 13:09:21 2014
341 Type: Kernel Image
342 Compression: lzo compressed
343 Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB
344 Architecture: ARM
345 OS: Linux
346 Load Address: 0x80008000
347 Entry Point: 0x80008000
348 Hash algo: sha1
349 Hash value: c94364646427e10f423837e559898ef02c97b988
350 Verifying Hash Integrity ...
351 sha1 error
352 Bad hash value for 'hash-1' hash node in 'kernel' image node
353 Bad Data Hash
354
355 ## Loading fdt from FIT Image at 7f5a39571000 ...
356 Using 'conf-1' configuration
357 Trying 'fdt-1' fdt subimage
358 Description: beaglebone-black
359 Created: Sun Jun 1 13:09:21 2014
360 Type: Flat Device Tree
361 Compression: uncompressed
362 Data Size: 31547 Bytes = 30.81 kB = 0.03 MB
363 Architecture: ARM
364 Hash algo: sha1
365 Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d
366 Verifying Hash Integrity ...
367 sha1+
368 OK
369
370 Loading Flat Device Tree ... OK
371
372 ## Loading ramdisk from FIT Image at 7f5a39571000 ...
373 Using 'conf-1' configuration
374 Could not find subimage node
375
376 Signature check Bad (error 1)
377
378
379It has detected the change in the kernel.
380
381You can also be sneaky and try to switch images, using the libfdt utilities
382that come with dtc (package name is device-tree-compiler but you will need a
383recent version like 1.4::
384
385 dtc -v
386 Version: DTC 1.4.0
387
388First we can check which nodes are actually hashed by the configuration::
389
390 $ fdtget -l image.fit /
391 images
392 configurations
393
394 $ fdtget -l image.fit /configurations
395 conf-1
396 fdtget -l image.fit /configurations/conf-1
397 signature-1
398
399 $ fdtget -p image.fit /configurations/conf-1/signature-1
400 hashed-strings
401 hashed-nodes
402 timestamp
403 signer-version
404 signer-name
405 value
406 algo
407 key-name-hint
408 sign-images
409
410 $ fdtget image.fit /configurations/conf-1/signature-1 hashed-nodes
411 / /configurations/conf-1 /images/fdt-1 /images/fdt-1/hash /images/kernel /images/kernel/hash-1
412
413This gives us a bit of a look into the signature that mkimage added. Note you
414can also use fdtdump to list the entire device tree.
415
416Say we want to change the kernel that this configuration uses
417(/images/kernel). We could just put a new kernel in the image, but we will
418need to change the hash to match. Let's simulate that by changing a byte of
419the hash::
420
421 fdtget -tx image.fit /images/kernel/hash-1 value
422 c9436464 6427e10f 423837e5 59898ef0 2c97b988
423 fdtput -tx image.fit /images/kernel/hash-1 value c9436464 6427e10f 423837e5 59898ef0 2c97b981
424
425Now check it again::
426
427 $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb
428 Verifying Hash Integrity ... sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13
429 rsa_verify_with_keynode: RSA failed to verify: -13
430 -
431 Failed to verify required signature 'key-dev'
432 Signature check Bad (error 1)
433
434This time we don't even get as far as checking the images, since the
435configuration signature doesn't match. We can't change any hashes without the
436signature check noticing. The configuration is essentially locked. U-Boot has
437a public key for which it requires a match, and will not permit the use of any
438configuration that does not match that public key. The only way the
439configuration will match is if it was signed by the matching private key.
440
441It would also be possible to add a new signature node that does match your new
442configuration. But that won't work since you are not allowed to change the
443configuration in any way. Try it with a fresh (valid) image if you like by
444running the mkimage link again. Then::
445
446 fdtput -p image.fit /configurations/conf-1/signature-1 value fred
447 $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb
448 Verifying Hash Integrity ... -
449 sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13
450 rsa_verify_with_keynode: RSA failed to verify: -13
451 -
452 Failed to verify required signature 'key-dev'
453 Signature check Bad (error 1)
454
455
456Of course it would be possible to add an entirely new configuration and boot
457with that, but it still needs to be signed, so it won't help.
458
459
4606. Put the public key into U-Boot's image
461-----------------------------------------
462
463Having confirmed that the signature is doing its job, let's try it out in
464U-Boot on the board. U-Boot needs access to the public key corresponding to
465the private key that you signed with so that it can verify any kernels that
466you sign::
467
468 cd $UBOOT
469 make O=b/am335x_boneblack_vboot EXT_DTB=${WORK}/am335x-boneblack-pubkey.dtb
470
471Here we are overriding the normal device tree file with our one, which
472contains the public key.
473
474Now you have a special U-Boot image with the public key. It can verify can
475kernel that you sign with the private key as in step 5.
476
477If you like you can take a look at the public key information that mkimage
478added to U-Boot's device tree::
479
480 fdtget -p am335x-boneblack-pubkey.dtb /signature/key-dev
481 required
482 algo
483 rsa,r-squared
484 rsa,modulus
485 rsa,n0-inverse
486 rsa,num-bits
487 key-name-hint
488
489This has information about the key and some pre-processed values which U-Boot
490can use to verify against it. These values are obtained from the public key
491certificate by mkimage, but require quite a bit of code to generate. To save
492code space in U-Boot, the information is extracted and written in raw form for
493U-Boot to easily use. The same mechanism is used in Google's Chrome OS.
494
495Notice the 'required' property. This marks the key as required - U-Boot will
496not boot any image that does not verify against this key.
497
498
4997. Put U-Boot and the kernel onto the board
500-------------------------------------------
501
502The method here varies depending on how you are booting. For this example we
503are booting from an micro-SD card with two partitions, one for U-Boot and one
504for Linux. Put it into your machine and write U-Boot and the kernel to it.
505Here the card is /dev/sde::
506
507 cd $WORK
508 export UDEV=/dev/sde1 # Change thes two lines to the correct device
509 export KDEV=/dev/sde2
510 sudo mount $UDEV /mnt/tmp && sudo cp $UOUT/u-boot-dtb.img /mnt/tmp/u-boot.img && sleep 1 && sudo umount $UDEV
511 sudo mount $KDEV /mnt/tmp && sudo cp $WORK/image.fit /mnt/tmp/boot/image.fit && sleep 1 && sudo umount $KDEV
512
513
5148. Try it
515---------
516
517Boot the board using the commands below::
518
519 setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait
520 ext2load mmc 0:2 82000000 /boot/image.fit
521 bootm 82000000
522
523You should then see something like this::
524
525 U-Boot# setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait
526 U-Boot# ext2load mmc 0:2 82000000 /boot/image.fit
527 7824930 bytes read in 589 ms (12.7 MiB/s)
528 U-Boot# bootm 82000000
529 ## Loading kernel from FIT Image at 82000000 ...
530 Using 'conf-1' configuration
531 Verifying Hash Integrity ... sha1,rsa2048:dev+ OK
532 Trying 'kernel' kernel subimage
533 Description: unavailable
534 Created: 2014-06-01 19:32:54 UTC
535 Type: Kernel Image
536 Compression: lzo compressed
537 Data Start: 0x820000a8
538 Data Size: 7790938 Bytes = 7.4 MiB
539 Architecture: ARM
540 OS: Linux
541 Load Address: 0x80008000
542 Entry Point: 0x80008000
543 Hash algo: sha1
544 Hash value: c94364646427e10f423837e559898ef02c97b988
545 Verifying Hash Integrity ... sha1+ OK
546 ## Loading fdt from FIT Image at 82000000 ...
547 Using 'conf-1' configuration
548 Trying 'fdt-1' fdt subimage
549 Description: beaglebone-black
550 Created: 2014-06-01 19:32:54 UTC
551 Type: Flat Device Tree
552 Compression: uncompressed
553 Data Start: 0x8276e2ec
554 Data Size: 31547 Bytes = 30.8 KiB
555 Architecture: ARM
556 Hash algo: sha1
557 Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d
558 Verifying Hash Integrity ... sha1+ OK
559 Booting using the fdt blob at 0x8276e2ec
560 Uncompressing Kernel Image ... OK
561 Loading Device Tree to 8fff5000, end 8ffffb3a ... OK
562
563 Starting kernel ...
564
565 [ 0.582377] omap_init_mbox: hwmod doesn't have valid attrs
566 [ 2.589651] musb-hdrc musb-hdrc.0.auto: Failed to request rx1.
567 [ 2.595830] musb-hdrc musb-hdrc.0.auto: musb_init_controller failed with status -517
568 [ 2.606470] musb-hdrc musb-hdrc.1.auto: Failed to request rx1.
569 [ 2.612723] musb-hdrc musb-hdrc.1.auto: musb_init_controller failed with status -517
570 [ 2.940808] drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
571 [ 7.248889] libphy: PHY 4a101000.mdio:01 not found
572 [ 7.253995] net eth0: phy 4a101000.mdio:01 not found on slave 1
573 systemd-fsck[83]: Angstrom: clean, 50607/218160 files, 306348/872448 blocks
574
575 .---O---.
576 | | .-. o o
577 | | |-----.-----.-----.| | .----..-----.-----.
578 | | | __ | ---'| '--.| .-'| | |
579 | | | | | |--- || --'| | | ' | | | |
580 '---'---'--'--'--. |-----''----''--' '-----'-'-'-'
581 -' |
582 '---'
583
584 The Angstrom Distribution beaglebone ttyO0
585
586 Angstrom v2012.12 - Kernel 3.14.1+
587
588 beaglebone login:
589
590At this point your kernel has been verified and you can be sure that it is one
591that you signed. As an exercise, try changing image.fit as in step 5 and see
592what happens.
593
594
595Further Improvements
596--------------------
597
598Several of the steps here can be easily automated. In particular it would be
599capital if signing and packaging a kernel were easy, perhaps a simple make
600target in the kernel.
601
602Some mention of how to use multiple .dtb files in a FIT might be useful.
603
604U-Boot's verified boot mechanism has not had a robust and independent security
605review. Such a review should look at the implementation and its resistance to
606attacks.
607
608Perhaps the verified boot feature could be integrated into the Amstrom
609distribution.
610
611
612.. sectionauthor:: Simon Glass <sjg@chromium.org>, 2-June-14