diff options
Diffstat (limited to 'doc/usage/fit/beaglebone_vboot.rst')
-rw-r--r-- | doc/usage/fit/beaglebone_vboot.rst | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/doc/usage/fit/beaglebone_vboot.rst b/doc/usage/fit/beaglebone_vboot.rst new file mode 100644 index 00000000000..0580ee10bdc --- /dev/null +++ b/doc/usage/fit/beaglebone_vboot.rst @@ -0,0 +1,612 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Verified Boot on the Beaglebone Black +===================================== + +Introduction +------------ + +Before reading this, please read :doc:`verified-boot` and :doc:`signature`. +These instructions are for mainline U-Boot from v2014.07 onwards. + +There is quite a bit of documentation in this directory describing how +verified boot works in U-Boot. There is also a test which runs through the +entire process of signing an image and running U-Boot (sandbox) to check it. +However, it might be useful to also have an example on a real board. + +Beaglebone Black is a fairly common board so seems to be a reasonable choice +for an example of how to enable verified boot using U-Boot. + +First a note that may to help avoid confusion. U-Boot and Linux both use +device tree. They may use the same device tree source, but it is seldom useful +for them to use the exact same binary from the same place. More typically, +U-Boot has its device tree packaged with it, and the kernel's device tree is +packaged with the kernel. In particular this is important with verified boot, +since U-Boot's device tree must be immutable. If it can be changed then the +public keys can be changed and verified boot is useless. An attacker can +simply generate a new key and put his public key into U-Boot so that +everything verifies. On the other hand the kernel's device tree typically +changes when the kernel changes, so it is useful to package an updated device +tree with the kernel binary. U-Boot supports the latter with its flexible FIT +format (Flat Image Tree). + + +Overview +-------- + +The steps are roughly as follows: + +#. Build U-Boot for the board, with the verified boot options enabled. + +#. Obtain a suitable Linux kernel + +#. Create a Image Tree Source file (ITS) file describing how you want the + kernel to be packaged, compressed and signed. + +#. Create a key pair + +#. Sign the kernel + +#. Put the public key into U-Boot's image + +#. Put U-Boot and the kernel onto the board + +#. Try it + + +Step 1: Build U-Boot +-------------------- + +a. Set up the environment variable to point to your toolchain. You will need + this for U-Boot and also for the kernel if you build it. For example if you + installed a Linaro version manually it might be something like:: + + export CROSS_COMPILE=/opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux/bin/arm-linux-gnueabihf- + + or if you just installed gcc-arm-linux-gnueabi then it might be:: + + export CROSS_COMPILE=arm-linux-gnueabi- + +b. Configure and build U-Boot with verified boot enabled:: + + export UBOOT=/path/to/u-boot + cd $UBOOT + # You can add -j10 if you have 10 CPUs to make it faster + make O=b/am335x_boneblack_vboot am335x_boneblack_vboot_config all + export UOUT=$UBOOT/b/am335x_boneblack_vboot + +c. You will now have a U-Boot image:: + + file b/am335x_boneblack_vboot/u-boot-dtb.img + 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 + + +Step 2: Build Linux +-------------------- + +a. Find the kernel image ('Image') and device tree (.dtb) file you plan to + use. In our case it is am335x-boneblack.dtb and it is built with the kernel. + At the time of writing an SD Boot image can be obtained from here:: + + http://www.elinux.org/Beagleboard:Updating_The_Software#Image_For_Booting_From_microSD + + You can write this to an SD card and then mount it to extract the kernel and + device tree files. + + You can also build a kernel. Instructions for this are are here:: + + http://elinux.org/Building_BBB_Kernel + + or you can use your favourite search engine. Following these instructions + produces a kernel Image and device tree files. For the record the steps + were:: + + export KERNEL=/path/to/kernel + cd $KERNEL + git clone git://github.com/beagleboard/kernel.git . + git checkout v3.14 + ./patch.sh + cp configs/beaglebone kernel/arch/arm/configs/beaglebone_defconfig + cd kernel + make beaglebone_defconfig + make uImage dtbs # -j10 if you have 10 CPUs + export OKERNEL=$KERNEL/kernel/arch/arm/boot + +b. You now have the 'Image' and 'am335x-boneblack.dtb' files needed to boot. + + +Step 3: Create the ITS +---------------------- + +Set up a directory for your work:: + + export WORK=/path/to/dir + cd $WORK + +Put this into a file in that directory called sign.its:: + + /dts-v1/; + + / { + description = "Beaglebone black"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("Image.lzo"); + type = "kernel"; + arch = "arm"; + os = "linux"; + compression = "lzo"; + load = <0x80008000>; + entry = <0x80008000>; + hash-1 { + algo = "sha1"; + }; + }; + fdt-1 { + description = "beaglebone-black"; + data = /incbin/("am335x-boneblack.dtb"); + type = "flat_dt"; + arch = "arm"; + compression = "none"; + hash-1 { + algo = "sha1"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + signature-1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; + }; + + +The explanation for this is all in the documentation you have already read. +But briefly it packages a kernel and device tree, and provides a single +configuration to be signed with a key named 'dev'. The kernel is compressed +with LZO to make it smaller. + + +Step 4: Create a key pair +------------------------- + +See :doc:`signature` for details on this step:: + + cd $WORK + mkdir keys + openssl genrsa -F4 -out keys/dev.key 2048 + openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt + +Note: keys/dev.key contains your private key and is very secret. If anyone +gets access to that file they can sign kernels with it. Keep it secure. + + +Step 5: Sign the kernel +----------------------- + +We need to use mkimage (which was built when you built U-Boot) to package the +Linux kernel into a FIT (Flat Image Tree, a flexible file format that U-Boot +can load) using the ITS file you just created. + +At the same time we must put the public key into U-Boot device tree, with the +'required' property, which tells U-Boot that this key must be verified for the +image to be valid. You will make this key available to U-Boot for booting in +step 6:: + + ln -s $OKERNEL/dts/am335x-boneblack.dtb + ln -s $OKERNEL/Image + ln -s $UOUT/u-boot-dtb.img + cp $UOUT/arch/arm/dts/am335x-boneblack.dtb am335x-boneblack-pubkey.dtb + lzop Image + $UOUT/tools/mkimage -f sign.its -K am335x-boneblack-pubkey.dtb -k keys -r image.fit + +You should see something like this:: + + FIT description: Beaglebone black + Created: Sun Jun 1 12:50:30 2014 + Image 0 (kernel) + Description: unavailable + Created: Sun Jun 1 12:50:30 2014 + Type: Kernel Image + Compression: lzo compressed + Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB + Architecture: ARM + OS: Linux + Load Address: 0x80008000 + Entry Point: 0x80008000 + Hash algo: sha1 + Hash value: c94364646427e10f423837e559898ef02c97b988 + Image 1 (fdt-1) + Description: beaglebone-black + Created: Sun Jun 1 12:50:30 2014 + Type: Flat Device Tree + Compression: uncompressed + Data Size: 31547 Bytes = 30.81 kB = 0.03 MB + Architecture: ARM + Hash algo: sha1 + Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d + Default Configuration: 'conf-1' + Configuration 0 (conf-1) + Description: unavailable + Kernel: kernel + FDT: fdt-1 + + +Now am335x-boneblack-pubkey.dtb contains the public key and image.fit contains +the signed kernel. Jump to step 6 if you like, or continue reading to increase +your understanding. + +You can also run fit_check_sign to check it:: + + $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb + +which results in:: + + Verifying Hash Integrity ... sha1,rsa2048:dev+ + ## Loading kernel from FIT Image at 7fc6ee469000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... + sha1,rsa2048:dev+ + OK + + Trying 'kernel' kernel subimage + Description: unavailable + Created: Sun Jun 1 12:50:30 2014 + Type: Kernel Image + Compression: lzo compressed + Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB + Architecture: ARM + OS: Linux + Load Address: 0x80008000 + Entry Point: 0x80008000 + Hash algo: sha1 + Hash value: c94364646427e10f423837e559898ef02c97b988 + Verifying Hash Integrity ... + sha1+ + OK + + Unimplemented compression type 4 + ## Loading fdt from FIT Image at 7fc6ee469000 ... + Using 'conf-1' configuration + Trying 'fdt-1' fdt subimage + Description: beaglebone-black + Created: Sun Jun 1 12:50:30 2014 + Type: Flat Device Tree + Compression: uncompressed + Data Size: 31547 Bytes = 30.81 kB = 0.03 MB + Architecture: ARM + Hash algo: sha1 + Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d + Verifying Hash Integrity ... + sha1+ + OK + + Loading Flat Device Tree ... OK + + ## Loading ramdisk from FIT Image at 7fc6ee469000 ... + Using 'conf-1' configuration + Could not find subimage node + + Signature check OK + + +At the top, you see "sha1,rsa2048:dev+". This means that it checked an RSA key +of size 2048 bits using SHA1 as the hash algorithm. The key name checked was +'dev' and the '+' means that it verified. If it showed '-' that would be bad. + +Once the configuration is verified it is then possible to rely on the hashes +in each image referenced by that configuration. So fit_check_sign goes on to +load each of the images. We have a kernel and an FDT but no ramkdisk. In each +case fit_check_sign checks the hash and prints sha1+ meaning that the SHA1 +hash verified. This means that none of the images has been tampered with. + +There is a test in test/vboot which uses U-Boot's sandbox build to verify that +the above flow works. + +But it is fun to do this by hand, so you can load image.fit into a hex editor +like ghex, and change a byte in the kernel:: + + $UOUT/tools/fit_info -f image.fit -n /images/kernel -p data + NAME: kernel + LEN: 7790938 + OFF: 168 + +This tells us that the kernel starts at byte offset 168 (decimal) in image.fit +and extends for about 7MB. Try changing a byte at 0x2000 (say) and run +fit_check_sign again. You should see something like:: + + Verifying Hash Integrity ... sha1,rsa2048:dev+ + ## Loading kernel from FIT Image at 7f5a39571000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... + sha1,rsa2048:dev+ + OK + + Trying 'kernel' kernel subimage + Description: unavailable + Created: Sun Jun 1 13:09:21 2014 + Type: Kernel Image + Compression: lzo compressed + Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB + Architecture: ARM + OS: Linux + Load Address: 0x80008000 + Entry Point: 0x80008000 + Hash algo: sha1 + Hash value: c94364646427e10f423837e559898ef02c97b988 + Verifying Hash Integrity ... + sha1 error + Bad hash value for 'hash-1' hash node in 'kernel' image node + Bad Data Hash + + ## Loading fdt from FIT Image at 7f5a39571000 ... + Using 'conf-1' configuration + Trying 'fdt-1' fdt subimage + Description: beaglebone-black + Created: Sun Jun 1 13:09:21 2014 + Type: Flat Device Tree + Compression: uncompressed + Data Size: 31547 Bytes = 30.81 kB = 0.03 MB + Architecture: ARM + Hash algo: sha1 + Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d + Verifying Hash Integrity ... + sha1+ + OK + + Loading Flat Device Tree ... OK + + ## Loading ramdisk from FIT Image at 7f5a39571000 ... + Using 'conf-1' configuration + Could not find subimage node + + Signature check Bad (error 1) + + +It has detected the change in the kernel. + +You can also be sneaky and try to switch images, using the libfdt utilities +that come with dtc (package name is device-tree-compiler but you will need a +recent version like 1.4:: + + dtc -v + Version: DTC 1.4.0 + +First we can check which nodes are actually hashed by the configuration:: + + $ fdtget -l image.fit / + images + configurations + + $ fdtget -l image.fit /configurations + conf-1 + fdtget -l image.fit /configurations/conf-1 + signature-1 + + $ fdtget -p image.fit /configurations/conf-1/signature-1 + hashed-strings + hashed-nodes + timestamp + signer-version + signer-name + value + algo + key-name-hint + sign-images + + $ fdtget image.fit /configurations/conf-1/signature-1 hashed-nodes + / /configurations/conf-1 /images/fdt-1 /images/fdt-1/hash /images/kernel /images/kernel/hash-1 + +This gives us a bit of a look into the signature that mkimage added. Note you +can also use fdtdump to list the entire device tree. + +Say we want to change the kernel that this configuration uses +(/images/kernel). We could just put a new kernel in the image, but we will +need to change the hash to match. Let's simulate that by changing a byte of +the hash:: + + fdtget -tx image.fit /images/kernel/hash-1 value + c9436464 6427e10f 423837e5 59898ef0 2c97b988 + fdtput -tx image.fit /images/kernel/hash-1 value c9436464 6427e10f 423837e5 59898ef0 2c97b981 + +Now check it again:: + + $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb + Verifying Hash Integrity ... sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13 + rsa_verify_with_keynode: RSA failed to verify: -13 + - + Failed to verify required signature 'key-dev' + Signature check Bad (error 1) + +This time we don't even get as far as checking the images, since the +configuration signature doesn't match. We can't change any hashes without the +signature check noticing. The configuration is essentially locked. U-Boot has +a public key for which it requires a match, and will not permit the use of any +configuration that does not match that public key. The only way the +configuration will match is if it was signed by the matching private key. + +It would also be possible to add a new signature node that does match your new +configuration. But that won't work since you are not allowed to change the +configuration in any way. Try it with a fresh (valid) image if you like by +running the mkimage link again. Then:: + + fdtput -p image.fit /configurations/conf-1/signature-1 value fred + $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb + Verifying Hash Integrity ... - + sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13 + rsa_verify_with_keynode: RSA failed to verify: -13 + - + Failed to verify required signature 'key-dev' + Signature check Bad (error 1) + + +Of course it would be possible to add an entirely new configuration and boot +with that, but it still needs to be signed, so it won't help. + + +6. Put the public key into U-Boot's image +----------------------------------------- + +Having confirmed that the signature is doing its job, let's try it out in +U-Boot on the board. U-Boot needs access to the public key corresponding to +the private key that you signed with so that it can verify any kernels that +you sign:: + + cd $UBOOT + make O=b/am335x_boneblack_vboot EXT_DTB=${WORK}/am335x-boneblack-pubkey.dtb + +Here we are overriding the normal device tree file with our one, which +contains the public key. + +Now you have a special U-Boot image with the public key. It can verify can +kernel that you sign with the private key as in step 5. + +If you like you can take a look at the public key information that mkimage +added to U-Boot's device tree:: + + fdtget -p am335x-boneblack-pubkey.dtb /signature/key-dev + required + algo + rsa,r-squared + rsa,modulus + rsa,n0-inverse + rsa,num-bits + key-name-hint + +This has information about the key and some pre-processed values which U-Boot +can use to verify against it. These values are obtained from the public key +certificate by mkimage, but require quite a bit of code to generate. To save +code space in U-Boot, the information is extracted and written in raw form for +U-Boot to easily use. The same mechanism is used in Google's Chrome OS. + +Notice the 'required' property. This marks the key as required - U-Boot will +not boot any image that does not verify against this key. + + +7. Put U-Boot and the kernel onto the board +------------------------------------------- + +The method here varies depending on how you are booting. For this example we +are booting from an micro-SD card with two partitions, one for U-Boot and one +for Linux. Put it into your machine and write U-Boot and the kernel to it. +Here the card is /dev/sde:: + + cd $WORK + export UDEV=/dev/sde1 # Change thes two lines to the correct device + export KDEV=/dev/sde2 + sudo mount $UDEV /mnt/tmp && sudo cp $UOUT/u-boot-dtb.img /mnt/tmp/u-boot.img && sleep 1 && sudo umount $UDEV + sudo mount $KDEV /mnt/tmp && sudo cp $WORK/image.fit /mnt/tmp/boot/image.fit && sleep 1 && sudo umount $KDEV + + +8. Try it +--------- + +Boot the board using the commands below:: + + setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait + ext2load mmc 0:2 82000000 /boot/image.fit + bootm 82000000 + +You should then see something like this:: + + U-Boot# setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait + U-Boot# ext2load mmc 0:2 82000000 /boot/image.fit + 7824930 bytes read in 589 ms (12.7 MiB/s) + U-Boot# bootm 82000000 + ## Loading kernel from FIT Image at 82000000 ... + Using 'conf-1' configuration + Verifying Hash Integrity ... sha1,rsa2048:dev+ OK + Trying 'kernel' kernel subimage + Description: unavailable + Created: 2014-06-01 19:32:54 UTC + Type: Kernel Image + Compression: lzo compressed + Data Start: 0x820000a8 + Data Size: 7790938 Bytes = 7.4 MiB + Architecture: ARM + OS: Linux + Load Address: 0x80008000 + Entry Point: 0x80008000 + Hash algo: sha1 + Hash value: c94364646427e10f423837e559898ef02c97b988 + Verifying Hash Integrity ... sha1+ OK + ## Loading fdt from FIT Image at 82000000 ... + Using 'conf-1' configuration + Trying 'fdt-1' fdt subimage + Description: beaglebone-black + Created: 2014-06-01 19:32:54 UTC + Type: Flat Device Tree + Compression: uncompressed + Data Start: 0x8276e2ec + Data Size: 31547 Bytes = 30.8 KiB + Architecture: ARM + Hash algo: sha1 + Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d + Verifying Hash Integrity ... sha1+ OK + Booting using the fdt blob at 0x8276e2ec + Uncompressing Kernel Image ... OK + Loading Device Tree to 8fff5000, end 8ffffb3a ... OK + + Starting kernel ... + + [ 0.582377] omap_init_mbox: hwmod doesn't have valid attrs + [ 2.589651] musb-hdrc musb-hdrc.0.auto: Failed to request rx1. + [ 2.595830] musb-hdrc musb-hdrc.0.auto: musb_init_controller failed with status -517 + [ 2.606470] musb-hdrc musb-hdrc.1.auto: Failed to request rx1. + [ 2.612723] musb-hdrc musb-hdrc.1.auto: musb_init_controller failed with status -517 + [ 2.940808] drivers/rtc/hctosys.c: unable to open rtc device (rtc0) + [ 7.248889] libphy: PHY 4a101000.mdio:01 not found + [ 7.253995] net eth0: phy 4a101000.mdio:01 not found on slave 1 + systemd-fsck[83]: Angstrom: clean, 50607/218160 files, 306348/872448 blocks + + .---O---. + | | .-. o o + | | |-----.-----.-----.| | .----..-----.-----. + | | | __ | ---'| '--.| .-'| | | + | | | | | |--- || --'| | | ' | | | | + '---'---'--'--'--. |-----''----''--' '-----'-'-'-' + -' | + '---' + + The Angstrom Distribution beaglebone ttyO0 + + Angstrom v2012.12 - Kernel 3.14.1+ + + beaglebone login: + +At this point your kernel has been verified and you can be sure that it is one +that you signed. As an exercise, try changing image.fit as in step 5 and see +what happens. + + +Further Improvements +-------------------- + +Several of the steps here can be easily automated. In particular it would be +capital if signing and packaging a kernel were easy, perhaps a simple make +target in the kernel. + +Some mention of how to use multiple .dtb files in a FIT might be useful. + +U-Boot's verified boot mechanism has not had a robust and independent security +review. Such a review should look at the implementation and its resistance to +attacks. + +Perhaps the verified boot feature could be integrated into the Amstrom +distribution. + + +.. sectionauthor:: Simon Glass <sjg@chromium.org>, 2-June-14 |