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