cmd: Migrate from_env() from pxe.c to nvedit.c
[oweals/u-boot.git] / doc / uImage.FIT / beaglebone_vboot.txt
1 Verified Boot on the Beaglebone Black
2 =====================================
3
4 Introduction
5 ------------
6
7 Before reading this, please read verified-boot.txt and signature.txt. These
8 instructions are for mainline U-Boot from v2014.07 onwards.
9
10 There is quite a bit of documentation in this directory describing how
11 verified boot works in U-Boot. There is also a test which runs through the
12 entire process of signing an image and running U-Boot (sandbox) to check it.
13 However, it might be useful to also have an example on a real board.
14
15 Beaglebone Black is a fairly common board so seems to be a reasonable choice
16 for an example of how to enable verified boot using U-Boot.
17
18 First a note that may to help avoid confusion. U-Boot and Linux both use
19 device tree. They may use the same device tree source, but it is seldom useful
20 for them to use the exact same binary from the same place. More typically,
21 U-Boot has its device tree packaged wtih it, and the kernel's device tree is
22 packaged with the kernel. In particular this is important with verified boot,
23 since U-Boot's device tree must be immutable. If it can be changed then the
24 public keys can be changed and verified boot is useless. An attacker can
25 simply generate a new key and put his public key into U-Boot so that
26 everything verifies. On the other hand the kernel's device tree typically
27 changes when the kernel changes, so it is useful to package an updated device
28 tree with the kernel binary. U-Boot supports the latter with its flexible FIT
29 format (Flat Image Tree).
30
31
32 Overview
33 --------
34
35 The steps are roughly as follows:
36
37 1. Build U-Boot for the board, with the verified boot options enabled.
38
39 2. Obtain a suitable Linux kernel
40
41 3. Create a Image Tree Source file (ITS) file describing how you want the
42 kernel to be packaged, compressed and signed.
43
44 4. Create a key pair
45
46 5. Sign the kernel
47
48 6. Put the public key into U-Boot's image
49
50 7. Put U-Boot and the kernel onto the board
51
52 8. Try it
53
54
55 Step 1: Build U-Boot
56 --------------------
57
58 a. Set up the environment variable to point to your toolchain. You will need
59 this for U-Boot and also for the kernel if you build it. For example if you
60 installed 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
64 or if you just installed gcc-arm-linux-gnueabi then it might be
65
66    export CROSS_COMPILE=arm-linux-gnueabi-
67
68 b. 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
77 c. You will now have a U-Boot image:
78
79    file b/am335x_boneblack_vboot/u-boot-dtb.img
80 b/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
83 Step 2: Build Linux
84 --------------------
85
86 a. Find the kernel image ('Image') and device tree (.dtb) file you plan to
87 use. In our case it is am335x-boneblack.dtb and it is built with the kernel.
88 At 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
92 You can write this to an SD card and then mount it to extract the kernel and
93 device tree files.
94
95 You can also build a kernel. Instructions for this are are here:
96
97    http://elinux.org/Building_BBB_Kernel
98
99 or you can use your favourite search engine. Following these instructions
100 produces 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
113 c. You now have the 'Image' and 'am335x-boneblack.dtb' files needed to boot.
114
115
116 Step 3: Create the ITS
117 ----------------------
118
119 Set up a directory for your work.
120
121    export WORK=/path/to/dir
122    cd $WORK
123
124 Put 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
171 The explanation for this is all in the documentation you have already read.
172 But briefly it packages a kernel and device tree, and provides a single
173 configuration to be signed with a key named 'dev'. The kernel is compressed
174 with LZO to make it smaller.
175
176
177 Step 4: Create a key pair
178 -------------------------
179
180 See 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
187 Note: keys/dev.key contains your private key and is very secret. If anyone
188 gets access to that file they can sign kernels with it. Keep it secure.
189
190
191 Step 5: Sign the kernel
192 -----------------------
193
194 We need to use mkimage (which was built when you built U-Boot) to package the
195 Linux kernel into a FIT (Flat Image Tree, a flexible file format that U-Boot
196 can load) using the ITS file you just created.
197
198 At 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
200 image to be valid. You will make this key available to U-Boot for booting in
201 step 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
210 You should see something like this:
211
212 FIT description: Beaglebone black
213 Created:         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
242 Now am335x-boneblack-pubkey.dtb contains the public key and image.fit contains
243 the signed kernel. Jump to step 6 if you like, or continue reading to increase
244 your understanding.
245
246 You 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
250 which results in:
251
252 Verifying Hash Integrity ... sha1,rsa2048:dev+
253 ## Loading kernel from FIT Image at 7fc6ee469000 ...
254    Using 'conf-1' configuration
255    Verifying Hash Integrity ...
256 sha1,rsa2048:dev+
257 OK
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 ...
272 sha1+
273 OK
274
275 Unimplemented 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 ...
288 sha1+
289 OK
290
291    Loading Flat Device Tree ... OK
292
293 ## Loading ramdisk from FIT Image at 7fc6ee469000 ...
294    Using 'conf-1' configuration
295 Could not find subimage node
296
297 Signature check OK
298
299
300 At the top, you see "sha1,rsa2048:dev+". This means that it checked an RSA key
301 of 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
304 Once the configuration is verified it is then possible to rely on the hashes
305 in each image referenced by that configuration. So fit_check_sign goes on to
306 load each of the images. We have a kernel and an FDT but no ramkdisk. In each
307 case fit_check_sign checks the hash and prints sha1+ meaning that the SHA1
308 hash verified. This means that none of the images has been tampered with.
309
310 There is a test in test/vboot which uses U-Boot's sandbox build to verify that
311 the above flow works.
312
313 But it is fun to do this by hand, so you can load image.fit into a hex editor
314 like ghex, and change a byte in the kernel:
315
316    $UOUT/tools/fit_info -f image.fit -n /images/kernel -p data
317 NAME: kernel
318 LEN: 7790938
319 OFF: 168
320
321 This tells us that the kernel starts at byte offset 168 (decimal) in image.fit
322 and extends for about 7MB. Try changing a byte at 0x2000 (say) and run
323 fit_check_sign again. You should see something like:
324
325 Verifying Hash Integrity ... sha1,rsa2048:dev+
326 ## Loading kernel from FIT Image at 7f5a39571000 ...
327    Using 'conf-1' configuration
328    Verifying Hash Integrity ...
329 sha1,rsa2048:dev+
330 OK
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 ...
345 sha1 error
346 Bad hash value for 'hash-1' hash node in 'kernel' image node
347 Bad 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 ...
361 sha1+
362 OK
363
364    Loading Flat Device Tree ... OK
365
366 ## Loading ramdisk from FIT Image at 7f5a39571000 ...
367    Using 'conf-1' configuration
368 Could not find subimage node
369
370 Signature check Bad (error 1)
371
372
373 It has detected the change in the kernel.
374
375 You can also be sneaky and try to switch images, using the libfdt utilities
376 that come with dtc (package name is device-tree-compiler but you will need a
377 recent version like 1.4:
378
379    dtc -v
380 Version: DTC 1.4.0
381
382 First we can check which nodes are actually hashed by the configuration:
383
384    fdtget -l image.fit /
385 images
386 configurations
387
388    fdtget -l image.fit /configurations
389 conf-1
390 fdtget -l image.fit /configurations/conf-1
391 signature-1
392
393    fdtget -p image.fit /configurations/conf-1/signature-1
394 hashed-strings
395 hashed-nodes
396 timestamp
397 signer-version
398 signer-name
399 value
400 algo
401 key-name-hint
402 sign-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
407 This gives us a bit of a look into the signature that mkimage added. Note you
408 can also use fdtdump to list the entire device tree.
409
410 Say 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
412 need to change the hash to match. Let's simulate that by changing a byte of
413 the hash:
414
415     fdtget -tx image.fit /images/kernel/hash-1 value
416 c9436464 6427e10f 423837e5 59898ef0 2c97b988
417     fdtput -tx image.fit /images/kernel/hash-1 value c9436464 6427e10f 423837e5 59898ef0 2c97b981
418
419 Now check it again:
420
421    $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb
422 Verifying Hash Integrity ... sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13
423 rsa_verify_with_keynode: RSA failed to verify: -13
424 -
425 Failed to verify required signature 'key-dev'
426 Signature check Bad (error 1)
427
428 This time we don't even get as far as checking the images, since the
429 configuration signature doesn't match. We can't change any hashes without the
430 signature check noticing. The configuration is essentially locked. U-Boot has
431 a public key for which it requires a match, and will not permit the use of any
432 configuration that does not match that public key. The only way the
433 configuration will match is if it was signed by the matching private key.
434
435 It would also be possible to add a new signature node that does match your new
436 configuration. But that won't work since you are not allowed to change the
437 configuration in any way. Try it with a fresh (valid) image if you like by
438 running 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
442 Verifying Hash Integrity ... -
443 sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13
444 rsa_verify_with_keynode: RSA failed to verify: -13
445 -
446 Failed to verify required signature 'key-dev'
447 Signature check Bad (error 1)
448
449
450 Of course it would be possible to add an entirely new configuration and boot
451 with that, but it still needs to be signed, so it won't help.
452
453
454 6. Put the public key into U-Boot's image
455 -----------------------------------------
456
457 Having confirmed that the signature is doing its job, let's try it out in
458 U-Boot on the board. U-Boot needs access to the public key corresponding to
459 the private key that you signed with so that it can verify any kernels that
460 you sign.
461
462    cd $UBOOT
463    make O=b/am335x_boneblack_vboot EXT_DTB=${WORK}/am335x-boneblack-pubkey.dtb
464
465 Here we are overriding the normal device tree file with our one, which
466 contains the public key.
467
468 Now you have a special U-Boot image with the public key. It can verify can
469 kernel that you sign with the private key as in step 5.
470
471 If you like you can take a look at the public key information that mkimage
472 added to U-Boot's device tree:
473
474    fdtget -p am335x-boneblack-pubkey.dtb /signature/key-dev
475 required
476 algo
477 rsa,r-squared
478 rsa,modulus
479 rsa,n0-inverse
480 rsa,num-bits
481 key-name-hint
482
483 This has information about the key and some pre-processed values which U-Boot
484 can use to verify against it. These values are obtained from the public key
485 certificate by mkimage, but require quite a bit of code to generate. To save
486 code space in U-Boot, the information is extracted and written in raw form for
487 U-Boot to easily use. The same mechanism is used in Google's Chrome OS.
488
489 Notice the 'required' property. This marks the key as required - U-Boot will
490 not boot any image that does not verify against this key.
491
492
493 7. Put U-Boot and the kernel onto the board
494 -------------------------------------------
495
496 The method here varies depending on how you are booting. For this example we
497 are booting from an micro-SD card with two partitions, one for U-Boot and one
498 for Linux. Put it into your machine and write U-Boot and the kernel to it.
499 Here 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
508 8. Try it
509 ---------
510
511 Boot 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
517 You should then see something like this:
518
519 U-Boot# setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait
520 U-Boot# ext2load mmc 0:2 82000000 /boot/image.fit
521 7824930 bytes read in 589 ms (12.7 MiB/s)
522 U-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
557 Starting 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
567 systemd-fsck[83]: Angstrom: clean, 50607/218160 files, 306348/872448 blocks
568
569 .---O---.
570 |       |                  .-.           o o
571 |   |   |-----.-----.-----.| |   .----..-----.-----.
572 |       |     | __  |  ---'| '--.|  .-'|     |     |
573 |   |   |  |  |     |---  ||  --'|  |  |  '  | | | |
574 '---'---'--'--'--.  |-----''----''--'  '-----'-'-'-'
575                 -'  |
576                 '---'
577
578 The Angstrom Distribution beaglebone ttyO0
579
580 Angstrom v2012.12 - Kernel 3.14.1+
581
582 beaglebone login:
583
584 At this point your kernel has been verified and you can be sure that it is one
585 that you signed. As an exercise, try changing image.fit as in step 5 and see
586 what happens.
587
588
589 Further Improvements
590 --------------------
591
592 Several of the steps here can be easily automated. In particular it would be
593 capital if signing and packaging a kernel were easy, perhaps a simple make
594 target in the kernel.
595
596 Some mention of how to use multiple .dtb files in a FIT might be useful.
597
598 U-Boot's verified boot mechanism has not had a robust and independent security
599 review. Such a review should look at the implementation and its resistance to
600 attacks.
601
602 Perhaps the verified boot feature could could be integrated into the Amstrom
603 distribution.
604
605
606 Simon Glass
607 sjg@chromium.org
608 2-June-14