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