uboot/doc/uImage.FIT/beaglebone_vboot.txt
<<
>>
Prefs
   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