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