diff options
author | Simon Glass | 2023-06-23 13:22:06 +0100 |
---|---|---|
committer | Heinrich Schuchardt | 2023-06-23 16:28:13 +0200 |
commit | ad29e08b79fd779a758b24106c8148a9c140f025 (patch) | |
tree | 0ae339446184561cad75e873a128593c3a376b3b /doc/usage | |
parent | 3c1e2c3261ce2b00455ad18a0e6ea10ed7af96eb (diff) |
doc: Bring in FIT signature files
Bring these files into the documentation.
Fix 'wtih' and 'it' typos and repeated 'could' while we are here.
Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'doc/usage')
-rw-r--r-- | doc/usage/cmd/source.rst | 2 | ||||
-rw-r--r-- | doc/usage/fit/beaglebone_vboot.rst | 612 | ||||
-rw-r--r-- | doc/usage/fit/index.rst | 3 | ||||
-rw-r--r-- | doc/usage/fit/signature.rst | 760 | ||||
-rw-r--r-- | doc/usage/fit/verified-boot.rst | 107 |
5 files changed, 1483 insertions, 1 deletions
diff --git a/doc/usage/cmd/source.rst b/doc/usage/cmd/source.rst index 61a45059096..6f5fa285134 100644 --- a/doc/usage/cmd/source.rst +++ b/doc/usage/cmd/source.rst @@ -22,7 +22,7 @@ Two formats for script files exist: * Flat Image Tree (FIT) The benefit of the FIT images is that they can be signed and verifed as -decribed in :download:`signature.txt <../../uImage.FIT/signature.txt>`. +described in :doc:`../fit/signature`. Both formats can be created with the mkimage tool. 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 diff --git a/doc/usage/fit/index.rst b/doc/usage/fit/index.rst index 0635d06b811..0576675ec00 100644 --- a/doc/usage/fit/index.rst +++ b/doc/usage/fit/index.rst @@ -12,3 +12,6 @@ doc/uImage.FIT source_file_format x86-fit-boot + signature + verified-boot + beaglebone_vboot diff --git a/doc/usage/fit/signature.rst b/doc/usage/fit/signature.rst new file mode 100644 index 00000000000..7d8292ece86 --- /dev/null +++ b/doc/usage/fit/signature.rst @@ -0,0 +1,760 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +U-Boot FIT Signature Verification +================================= + +Introduction +------------ + +FIT supports hashing of images so that these hashes can be checked on +loading. This protects against corruption of the image. However it does not +prevent the substitution of one image for another. + +The signature feature allows the hash to be signed with a private key such +that it can be verified using a public key later. Provided that the private +key is kept secret and the public key is stored in a non-volatile place, +any image can be verified in this way. + +See verified-boot.txt for more general information on verified boot. + + +Concepts +-------- + +Some familiarity with public key cryptography is assumed in this section. + +The procedure for signing is as follows: + + - hash an image in the FIT + - sign the hash with a private key to produce a signature + - store the resulting signature in the FIT + +The procedure for verification is: + + - read the FIT + - obtain the public key + - extract the signature from the FIT + - hash the image from the FIT + - verify (with the public key) that the extracted signature matches the + hash + +The signing is generally performed by mkimage, as part of making a firmware +image for the device. The verification is normally done in U-Boot on the +device. + + +Algorithms +---------- +In principle any suitable algorithm can be used to sign and verify a hash. +U-Boot supports a few hashing and verification algorithms. See below for +details. + +While it is acceptable to bring in large cryptographic libraries such as +openssl on the host side (e.g. mkimage), it is not desirable for U-Boot. +For the run-time verification side, it is important to keep code and data +size as small as possible. + +For this reason the RSA image verification uses pre-processed public keys +which can be used with a very small amount of code - just some extraction +of data from the FDT and exponentiation mod n. Code size impact is a little +under 5KB on Tegra Seaboard, for example. + +It is relatively straightforward to add new algorithms if required. If +another RSA variant is needed, then it can be added with the +U_BOOT_CRYPTO_ALGO() macro. If another algorithm is needed (such as DSA) then +it can be placed in a directory alongside lib/rsa/, and its functions added +using U_BOOT_CRYPTO_ALGO(). + + +Creating an RSA key pair and certificate +---------------------------------------- +To create a new public/private key pair, size 2048 bits:: + + $ openssl genpkey -algorithm RSA -out keys/dev.key \ + -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 + +To create a certificate for this containing the public key:: + + $ openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt + +If you like you can look at the public key also:: + + $ openssl rsa -in keys/dev.key -pubout + + +Device Tree Bindings +-------------------- +The following properties are required in the FIT's signature node(s) to +allow the signer to operate. These should be added to the .its file. +Signature nodes sit at the same level as hash nodes and are called +signature-1, signature-2, etc. + +algo + Algorithm name (e.g. "sha1,rsa2048") + +key-name-hint + Name of key to use for signing. The keys will normally be in + a single directory (parameter -k to mkimage). For a given key <name>, its + private key is stored in <name>.key and the certificate is stored in + <name>.crt. + +When the image is signed, the following properties are added (mandatory): + +value + The signature data (e.g. 256 bytes for 2048-bit RSA) + +When the image is signed, the following properties are optional: + +timestamp + Time when image was signed (standard Unix time_t format) + +signer-name + Name of the signer (e.g. "mkimage") + +signer-version + Version string of the signer (e.g. "2013.01") + +comment + Additional information about the signer or image + +padding + The padding algorithm, it may be pkcs-1.5 or pss, + if no value is provided we assume pkcs-1.5 + +For config bindings (see Signed Configurations below), the following +additional properties are optional: + +sign-images + A list of images to sign, each being a property of the conf + node that contains then. The default is "kernel,fdt" which means that these + two images will be looked up in the config and signed if present. + +For config bindings, these properties are added by the signer: + +hashed-nodes + A list of nodes which were hashed by the signer. Each is + a string - the full path to node. A typical value might be:: + + hashed-nodes = "/", "/configurations/conf-1", "/images/kernel", + "/images/kernel/hash-1", "/images/fdt-1", + "/images/fdt-1/hash-1"; + +hashed-strings + The start and size of the string region of the FIT that was hashed + +Example: See :doc:`sign-images` for an example image tree source file and +sign-configs.its for config signing. + + +Public Key Storage +------------------ +In order to verify an image that has been signed with a public key we need to +have a trusted public key. This cannot be stored in the signed image, since +it would be easy to alter. For this implementation we choose to store the +public key in U-Boot's control FDT (using CONFIG_OF_CONTROL). + +Public keys should be stored as sub-nodes in a /signature node. Required +properties are: + +algo + Algorithm name (e.g. "sha1,rsa2048" or "sha256,ecdsa256") + +Optional properties are: + +key-name-hint + Name of key used for signing. This is only a hint since it + is possible for the name to be changed. Verification can proceed by checking + all available signing keys until one matches. + +required + If present this indicates that the key must be verified for the + image / configuration to be considered valid. Only required keys are + normally verified by the FIT image booting algorithm. Valid values are + "image" to force verification of all images, and "conf" to force verification + of the selected configuration (which then relies on hashes in the images to + verify those). + +Each signing algorithm has its own additional properties. + +For RSA the following are mandatory: + +rsa,num-bits + Number of key bits (e.g. 2048) + +rsa,modulus + Modulus (N) as a big-endian multi-word integer + +rsa,exponent + Public exponent (E) as a 64 bit unsigned integer + +rsa,r-squared + (2^num-bits)^2 as a big-endian multi-word integer + +rsa,n0-inverse + -1 / modulus[0] mod 2^32 + +For ECDSA the following are mandatory: + +ecdsa,curve + Name of ECDSA curve (e.g. "prime256v1") + +ecdsa,x-point + Public key X coordinate as a big-endian multi-word integer + +ecdsa,y-point + Public key Y coordinate as a big-endian multi-word integer + +These parameters can be added to a binary device tree using parameter -K of the +mkimage command:: + + tools/mkimage -f fit.its -K control.dtb -k keys -r image.fit + +Here is an example of a generated device tree node:: + + signature { + key-dev { + required = "conf"; + algo = "sha256,rsa2048"; + rsa,r-squared = <0xb76d1acf 0xa1763ca5 0xeb2f126 + 0x742edc80 0xd3f42177 0x9741d9d9 + 0x35bb476e 0xff41c718 0xd3801430 + 0xf22537cb 0xa7e79960 0xae32a043 + 0x7da1427a 0x341d6492 0x3c2762f5 + 0xaac04726 0x5b262d96 0xf984e86d + 0xb99443c7 0x17080c33 0x940f6892 + 0xd57a95d1 0x6ea7b691 0xc5038fa8 + 0x6bb48a6e 0x73f1b1ea 0x37160841 + 0xe05715ce 0xa7c45bbd 0x690d82d5 + 0x99c2454c 0x6ff117b3 0xd830683b + 0x3f81c9cf 0x1ca38a91 0x0c3392e4 + 0xd817c625 0x7b8e9a24 0x175b89ea + 0xad79f3dc 0x4d50d7b4 0x9d4e90f8 + 0xad9e2939 0xc165d6a4 0x0ada7e1b + 0xfb1bf495 0xfc3131c2 0xb8c6e604 + 0xc2761124 0xf63de4a6 0x0e9565f9 + 0xc8e53761 0x7e7a37a5 0xe99dcdae + 0x9aff7e1e 0xbd44b13d 0x6b0e6aa4 + 0x038907e4 0x8e0d6850 0xef51bc20 + 0xf73c94af 0x88bea7b1 0xcbbb1b30 + 0xd024b7f3>; + rsa,modulus = <0xc0711d6cb 0x9e86db7f 0x45986dbe + 0x023f1e8c9 0xe1a4c4d0 0x8a0dfdc9 + 0x023ba0c48 0x06815f6a 0x5caa0654 + 0x07078c4b7 0x3d154853 0x40729023 + 0x0b007c8fe 0x5a3647e5 0x23b41e20 + 0x024720591 0x66915305 0x0e0b29b0 + 0x0de2ad30d 0x8589430f 0xb1590325 + 0x0fb9f5d5e 0x9eba752a 0xd88e6de9 + 0x056b3dcc6 0x9a6b8e61 0x6784f61f + 0x000f39c21 0x5eec6b33 0xd78e4f78 + 0x0921a305f 0xaa2cc27e 0x1ca917af + 0x06e1134f4 0xd48cac77 0x4e914d07 + 0x0f707aa5a 0x0d141f41 0x84677f1d + 0x0ad47a049 0x028aedb6 0xd5536fcf + 0x03fef1e4f 0x133a03d2 0xfd7a750a + 0x0f9159732 0xd207812e 0x6a807375 + 0x06434230d 0xc8e22dad 0x9f29b3d6 + 0x07c44ac2b 0xfa2aad88 0xe2429504 + 0x041febd41 0x85d0d142 0x7b194d65 + 0x06e5d55ea 0x41116961 0xf3181dde + 0x068bf5fbc 0x3dd82047 0x00ee647e + 0x0d7a44ab3>; + rsa,exponent = <0x00 0x10001>; + rsa,n0-inverse = <0xb3928b85>; + rsa,num-bits = <0x800>; + key-name-hint = "dev"; + }; + }; + + +Signed Configurations +--------------------- +While signing images is useful, it does not provide complete protection +against several types of attack. For example, it is possible to create a +FIT with the same signed images, but with the configuration changed such +that a different one is selected (mix and match attack). It is also possible +to substitute a signed image from an older FIT version into a newer FIT +(roll-back attack). + +As an example, consider this FIT:: + + / { + images { + kernel-1 { + data = <data for kernel1> + signature-1 { + algo = "sha1,rsa2048"; + value = <...kernel signature 1...> + }; + }; + kernel-2 { + data = <data for kernel2> + signature-1 { + algo = "sha1,rsa2048"; + value = <...kernel signature 2...> + }; + }; + fdt-1 { + data = <data for fdt1>; + signature-1 { + algo = "sha1,rsa2048"; + value = <...fdt signature 1...> + }; + }; + fdt-2 { + data = <data for fdt2>; + signature-1 { + algo = "sha1,rsa2048"; + value = <...fdt signature 2...> + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel-1"; + fdt = "fdt-1"; + }; + conf-2 { + kernel = "kernel-2"; + fdt = "fdt-2"; + }; + }; + }; + +Since both kernels are signed it is easy for an attacker to add a new +configuration 3 with kernel 1 and fdt 2:: + + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel-1"; + fdt = "fdt-1"; + }; + conf-2 { + kernel = "kernel-2"; + fdt = "fdt-2"; + }; + conf-3 { + kernel = "kernel-1"; + fdt = "fdt-2"; + }; + }; + +With signed images, nothing protects against this. Whether it gains an +advantage for the attacker is debatable, but it is not secure. + +To solve this problem, we support signed configurations. In this case it +is the configurations that are signed, not the image. Each image has its +own hash, and we include the hash in the configuration signature. + +So the above example is adjusted to look like this:: + + / { + images { + kernel-1 { + data = <data for kernel1> + hash-1 { + algo = "sha1"; + value = <...kernel hash 1...> + }; + }; + kernel-2 { + data = <data for kernel2> + hash-1 { + algo = "sha1"; + value = <...kernel hash 2...> + }; + }; + fdt-1 { + data = <data for fdt1>; + hash-1 { + algo = "sha1"; + value = <...fdt hash 1...> + }; + }; + fdt-2 { + data = <data for fdt2>; + hash-1 { + algo = "sha1"; + value = <...fdt hash 2...> + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel-1"; + fdt = "fdt-1"; + signature-1 { + algo = "sha1,rsa2048"; + value = <...conf 1 signature...>; + }; + }; + conf-2 { + kernel = "kernel-2"; + fdt = "fdt-2"; + signature-1 { + algo = "sha1,rsa2048"; + value = <...conf 1 signature...>; + }; + }; + }; + }; + + +You can see that we have added hashes for all images (since they are no +longer signed), and a signature to each configuration. In the above example, +mkimage will sign configurations/conf-1, the kernel and fdt that are +pointed to by the configuration (/images/kernel-1, /images/kernel-1/hash-1, +/images/fdt-1, /images/fdt-1/hash-1) and the root structure of the image +(so that it isn't possible to add or remove root nodes). The signature is +written into /configurations/conf-1/signature-1/value. It can easily be +verified later even if the FIT has been signed with other keys in the +meantime. + + +Details +------- +The signature node contains a property ('hashed-nodes') which lists all the +nodes that the signature was made over. The image is walked in order and each +tag processed as follows: + +DTB_BEGIN_NODE + The tag and the following name are included in the signature + if the node or its parent are present in 'hashed-nodes' + +DTB_END_NODE + The tag is included in the signature if the node or its parent + are present in 'hashed-nodes' + +DTB_PROPERTY + The tag, the length word, the offset in the string table, and + the data are all included if the current node is present in 'hashed-nodes' + and the property name is not 'data'. + +DTB_END + The tag is always included in the signature. + +DTB_NOP + The tag is included in the signature if the current node is present + in 'hashed-nodes' + +In addition, the signature contains a property 'hashed-strings' which contains +the offset and length in the string table of the strings that are to be +included in the signature (this is done last). + +IMPORTANT: To verify the signature outside u-boot, it is vital to not only +calculate the hash of the image and verify the signature with that, but also to +calculate the hashes of the kernel, fdt, and ramdisk images and check those +match the hash values in the corresponding 'hash*' subnodes. + + +Verification +------------ +FITs are verified when loaded. After the configuration is selected a list +of required images is produced. If there are 'required' public keys, then +each image must be verified against those keys. This means that every image +that might be used by the target needs to be signed with 'required' keys. + +This happens automatically as part of a bootm command when FITs are used. + +For Signed Configurations, the default verification behavior can be changed by +the following optional property in /signature node in U-Boot's control FDT. + +required-mode + Valid values are "any" to allow verified boot to succeed if + the selected configuration is signed by any of the 'required' keys, and "all" + to allow verified boot to succeed if the selected configuration is signed by + all of the 'required' keys. + +This property can be added to a binary device tree using fdtput as shown in +below examples:: + + fdtput -t s control.dtb /signature required-mode any + fdtput -t s control.dtb /signature required-mode all + + +Enabling FIT Verification +------------------------- +In addition to the options to enable FIT itself, the following CONFIGs must +be enabled: + +CONFIG_FIT_SIGNATURE + enable signing and verification in FITs + +CONFIG_RSA + enable RSA algorithm for signing + +CONFIG_ECDSA + enable ECDSA algorithm for signing + +WARNING: When relying on signed FIT images with required signature check +the legacy image format is default disabled by not defining +CONFIG_LEGACY_IMAGE_FORMAT + + +Testing +------- + +An easy way to test signing and verification is to use the test script +provided in test/vboot/vboot_test.sh. This uses sandbox (a special version +of U-Boot which runs under Linux) to show the operation of a 'bootm' +command loading and verifying images. + +A sample run is show below:: + + $ make O=sandbox sandbox_config + $ make O=sandbox + $ O=sandbox ./test/vboot/vboot_test.sh + + +Simple Verified Boot Test +------------------------- + +Please see :doc:`verified-boot` for more information:: + + /home/hs/ids/u-boot/sandbox/tools/mkimage -D -I dts -O dtb -p 2000 + Build keys + do sha1 test + Build FIT with signed images + Test Verified Boot Run: unsigned signatures:: OK + Sign images + Test Verified Boot Run: signed images: OK + Build FIT with signed configuration + Test Verified Boot Run: unsigned config: OK + Sign images + Test Verified Boot Run: signed config: OK + check signed config on the host + Signature check OK + OK + Test Verified Boot Run: signed config: OK + Test Verified Boot Run: signed config with bad hash: OK + do sha256 test + Build FIT with signed images + Test Verified Boot Run: unsigned signatures:: OK + Sign images + Test Verified Boot Run: signed images: OK + Build FIT with signed configuration + Test Verified Boot Run: unsigned config: OK + Sign images + Test Verified Boot Run: signed config: OK + check signed config on the host + Signature check OK + OK + Test Verified Boot Run: signed config: OK + Test Verified Boot Run: signed config with bad hash: OK + + Test passed + + +Software signing: keydir vs keyfile +----------------------------------- + +In the simplest case, signing is done by giving mkimage the 'keyfile'. This is +the path to a file containing the signing key. + +The alternative is to pass the 'keydir' argument. In this case the filename of +the key is derived from the 'keydir' and the "key-name-hint" property in the +FIT. In this case the "key-name-hint" property is mandatory, and the key must +exist in "<keydir>/<key-name-hint>.<ext>" Here the extension "ext" is +specific to the signing algorithm. + + +Hardware Signing with PKCS#11 or with HSM +----------------------------------------- + +Securely managing private signing keys can challenging, especially when the +keys are stored on the file system of a computer that is connected to the +Internet. If an attacker is able to steal the key, they can sign malicious FIT +images which will appear genuine to your devices. + +An alternative solution is to keep your signing key securely stored on hardware +device like a smartcard, USB token or Hardware Security Module (HSM) and have +them perform the signing. PKCS#11 is standard for interfacing with these crypto +device. + +Requirements: + - Smartcard/USB token/HSM which can work with some openssl engine + - openssl + +For pkcs11 engine usage: + - libp11 (provides pkcs11 engine) + - p11-kit (recommended to simplify setup) + - opensc (for smartcards and smartcard like USB devices) + - gnutls (recommended for key generation, p11tool) + +For generic HSMs respective openssl engine must be installed and locateable by +openssl. This may require setting up LD_LIBRARY_PATH if engine is not installed +to openssl's default search paths. + +PKCS11 engine support forms "key id" based on "keydir" and with +"key-name-hint". "key-name-hint" is used as "object" name (if not defined in +keydir). "keydir" (if defined) is used to define (prefix for) which PKCS11 source +is being used for lookup up for the key. + +PKCS11 engine key ids + "pkcs11:<keydir>;object=<key-name-hint>;type=<public|private>" + +or, if keydir contains "object=" + "pkcs11:<keydir>;type=<public|private>" + +or + "pkcs11:object=<key-name-hint>;type=<public|private>", + +Generic HSM engine support forms "key id" based on "keydir" and with +"key-name-hint". If "keydir" is specified for mkimage it is used as a prefix in +"key id" and is appended with "key-name-hint". + +Generic engine key ids: + "<keydir><key-name-hint>" + +or + "< key-name-hint>" + +In order to set the pin in the HSM, an environment variable "MKIMAGE_SIGN_PIN" +can be specified. + +The following examples use the Nitrokey Pro using pkcs11 engine. Instructions +for other devices may vary. + +Notes on pkcs11 engine setup: + +Make sure p11-kit, opensc are installed and that p11-kit is setup to use opensc. +/usr/share/p11-kit/modules/opensc.module should be present on your system. + + +Generating Keys On the Nitrokey:: + + $ gpg --card-edit + + Reader ...........: Nitrokey Nitrokey Pro (xxxxxxxx0000000000000000) 00 00 + Application ID ...: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Version ..........: 2.1 + Manufacturer .....: ZeitControl + Serial number ....: xxxxxxxx + Name of cardholder: [not set] + Language prefs ...: de + Sex ..............: unspecified + URL of public key : [not set] + Login data .......: [not set] + Signature PIN ....: forced + Key attributes ...: rsa2048 rsa2048 rsa2048 + Max. PIN lengths .: 32 32 32 + PIN retry counter : 3 0 3 + Signature counter : 0 + Signature key ....: [none] + Encryption key....: [none] + Authentication key: [none] + General key info..: [none] + + gpg/card> generate + Make off-card backup of encryption key? (Y/n) n + + Please note that the factory settings of the PINs are + PIN = '123456' Admin PIN = '12345678' + You should change them using the command --change-pin + + What keysize do you want for the Signature key? (2048) 4096 + The card will now be re-configured to generate a key of 4096 bits + Note: There is no guarantee that the card supports the requested size. + If the key generation does not succeed, please check the + documentation of your card to see what sizes are allowed. + What keysize do you want for the Encryption key? (2048) 4096 + The card will now be re-configured to generate a key of 4096 bits + What keysize do you want for the Authentication key? (2048) 4096 + The card will now be re-configured to generate a key of 4096 bits + Please specify how long the key should be valid. + 0 = key does not expire + <n> = key expires in n days + <n>w = key expires in n weeks + <n>m = key expires in n months + <n>y = key expires in n years + Key is valid for? (0) + Key does not expire at all + Is this correct? (y/N) y + + GnuPG needs to construct a user ID to identify your key. + + Real name: John Doe + Email address: john.doe@email.com + Comment: + You selected this USER-ID: + "John Doe <john.doe@email.com>" + + Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o + + +Using p11tool to get the token URL: + +Depending on system configuration, gpg-agent may need to be killed first:: + + $ p11tool --provider /usr/lib/opensc-pkcs11.so --list-tokens + Token 0: + URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29 + Label: OpenPGP card (User PIN (sig)) + Type: Hardware token + Manufacturer: ZeitControl + Model: PKCS#15 emulated + Serial: 000xxxxxxxxx + Module: (null) + + + Token 1: + URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%29 + Label: OpenPGP card (User PIN) + Type: Hardware token + Manufacturer: ZeitControl + Model: PKCS#15 emulated + Serial: 000xxxxxxxxx + Module: (null) + +Use the portion of the signature token URL after "pkcs11:" as the keydir argument (-k) to mkimage below. + + +Use the URL of the token to list the private keys:: + + $ p11tool --login --provider /usr/lib/opensc-pkcs11.so --list-privkeys \ + "pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29" + Token 'OpenPGP card (User PIN (sig))' with URL 'pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29' requires user PIN + Enter PIN: + Object 0: + URL: pkcs11:model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29;id=%01;object=Signature%20key;type=private + Type: Private key + Label: Signature key + Flags: CKA_PRIVATE; CKA_NEVER_EXTRACTABLE; CKA_SENSITIVE; + ID: 01 + +Use the label, in this case "Signature key" as the key-name-hint in your FIT. + +Create the fitImage:: + + $ ./tools/mkimage -f fit-image.its fitImage + + +Sign the fitImage with the hardware key:: + + $ ./tools/mkimage -F -k \ + "model=PKCS%2315%20emulated;manufacturer=ZeitControl;serial=000xxxxxxxxx;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29" \ + -K u-boot.dtb -N pkcs11 -r fitImage + + +Future Work +----------- + +- Roll-back protection using a TPM is done using the tpm command. This can + be scripted, but we might consider a default way of doing this, built into + bootm. + + +Possible Future Work +-------------------- + +- More sandbox tests for failure modes +- Passwords for keys/certificates +- Perhaps implement OAEP +- Enhance bootm to permit scripted signature verification (so that a script + can verify an image but not actually boot it) + + +.. sectionauthor:: Simon Glass <sjg@chromium.org>, 1-1-13 diff --git a/doc/usage/fit/verified-boot.rst b/doc/usage/fit/verified-boot.rst new file mode 100644 index 00000000000..301207711db --- /dev/null +++ b/doc/usage/fit/verified-boot.rst @@ -0,0 +1,107 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +U-Boot Verified Boot +==================== + +Introduction +------------ + +Verified boot here means the verification of all software loaded into a +machine during the boot process to ensure that it is authorised and correct +for that machine. + +Verified boot extends from the moment of system reset to as far as you wish +into the boot process. An example might be loading U-Boot from read-only +memory, then loading a signed kernel, then using the kernel's dm-verity +driver to mount a signed root filesystem. + +A key point is that it is possible to field-upgrade the software on machines +which use verified boot. Since the machine will only run software that has +been correctly signed, it is safe to read software from an updatable medium. +It is also possible to add a secondary signed firmware image, in read-write +memory, so that firmware can easily be upgraded in a secure manner. + + +Signing +------- + +Verified boot uses cryptographic algorithms to 'sign' software images. +Images are signed using a private key known only to the signer, but can +be verified using a public key. As its name suggests the public key can be +made available without risk to the verification process. The private and +public keys are mathematically related. For more information on how this +works look up "public key cryptography" and "RSA" (a particular algorithm). + +The signing and verification process looks something like this:: + + + Signing Verification + ======= ============ + + +--------------+ * + | RSA key pair | * +---------------+ + | .key .crt | * | Public key in | + +--------------+ +------> public key ----->| trusted place | + | | * +---------------+ + | | * | + v | * v + +---------+ | * +--------------+ + | |---------+ * | | + | signer | * | U-Boot | + | |---------+ * | signature |--> yes/no + +---------+ | * | verification | + ^ | * | | + | | * +--------------+ + | | * ^ + +----------+ | * | + | Software | +----> signed image -------------+ + | image | * + +----------+ * + + +The signature algorithm relies only on the public key to do its work. Using +this key it checks the signature that it finds in the image. If it verifies +then we know that the image is OK. + +The public key from the signer allows us to verify and therefore trust +software from updatable memory. + +It is critical that the public key be secure and cannot be tampered with. +It can be stored in read-only memory, or perhaps protected by other on-chip +crypto provided by some modern SOCs. If the public key can be changed, then +the verification is worthless. + + +Chaining Images +--------------- + +The above method works for a signer providing images to a run-time U-Boot. +It is also possible to extend this scheme to a second level, like this: + +#. Master private key is used by the signer to sign a first-stage image. +#. Master public key is placed in read-only memory. +#. Secondary private key is created and used to sign second-stage images. +#. Secondary public key is placed in first stage images +#. We use the master public key to verify the first-stage image. We then + use the secondary public key in the first-stage image to verify the second- + state image. +#. This chaining process can go on indefinitely. It is recommended to use a + different key at each stage, so that a compromise in one place will not + affect the whole change. + + +Flattened Image Tree (FIT) +-------------------------- + +The FIT format is already widely used in U-Boot. It is a flattened device +tree (FDT) in a particular format, with images contained within. FITs +include hashes to verify images, so it is relatively straightforward to +add signatures as well. + +The public key can be stored in U-Boot's CONFIG_OF_CONTROL device tree in +a standard place. Then when a FIT is loaded it can be verified using that +public key. Multiple keys and multiple signatures are supported. + +See :doc:`signature` for more information. + +.. sectionauthor:: Simon Glass <sjg@chromium.org> 1-1-13 |