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