From 128e8602019e817ab62fffc6c35a92e1550acda2 Mon Sep 17 00:00:00 2001 From: "S.J.R. van Schaik" Date: Thu, 2 Mar 2017 14:09:52 +0000 Subject: [PATCH] add initial documentation --- design.rst | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ flashrom.rst | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++ kexec.rst | 141 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 524 insertions(+) create mode 100644 design.rst create mode 100644 flashrom.rst create mode 100644 kexec.rst diff --git a/design.rst b/design.rst new file mode 100644 index 0000000..e1f77ef --- /dev/null +++ b/design.rst @@ -0,0 +1,207 @@ +Trusted Boot +============ + +Terminology +----------- + + * TBM: Trusted Boot Module (the entire system) or Trusted Boot Manager (the ARM Cortex M0 or AVR + microcontroller). + * ROTS: read-only trusted state. + +Prerequisites +------------- + + * SPI NOR Flash to store a minimalistic read-only Linux image without networking support. + * ARM Cortex M0 or AVR microcontroller. + * A programmable timer to set deadlines and to trigger an interrupt when these expire. + * Possibly SPI NOR Flash for the Trusted Boot Module to store information. + * Key storage and management. + * The option of image rollback? + +Trust Model +----------- + +There are different trust models that can be used depending on the use case. +These mostly depend on whether the concept of certificate authorities (CAs) is required or not. +Furthermore, the key storage also plays an important role in deciding which of the trust models to +use. + + #. "Home router" + CA: none + + * replace = reflash RO flash + + #. "Routers in company, sysadmin" + CA: none + + * replace = reflash or signed statement + + #. "Routers with CA" + CA: one. + + * replace CA = reflash ROTS + * replace key = by signed CA statement + + #. "Routers with multiple CAs" + CA: multiple. + + * replace CA = threshold + * replace key = by signed CA statement. + + #. "Routers with multiple CAs and initial CA in ROTS" + CA: multiple. + + * revoking keys, which is not the same as removing keys. + + #. CA: multiple. + + * replace CAs = reflash ROTS. + * replace key = by signed CA statement + +Key Storage +----------- + + * RO Flash + * TBM + * Box storage (e.g. HDD) + * Signed statements by embeddeding keys in images. + +Forced reboot +------------- + +Because the image is only trusted up to the extent that the user trusts the parties that have +signed the image, the image may contain vulnerabilities that the user will only be aware of at a +later point. +As these vulnerabilities can be used to compromise the system in such a way that it will prevent +from rebooting the system, it is important that the TBM can reboot the system. +The straightforward method of performing such a forced reboot would be to set a deadline that can +be postponed by the user up to *n* times, whereupon the TBM will simply force a reboot after these +attempts or when the user decides to reboot gracefully. + +Upgrading +--------- + +One possible attack vector is that when the image that is running has been compromised in such a +way that it prevents the system from running the update process, even after a reboot, then this +will force the system to always boot the version installed. +To mitigate this we could split up the image into two stages. +The read-only trusted stage will then first boot up a minimalistic environment within the image to +check for updates and to perform them, whereupon the full image will be booted. +Because it is impossible to update the read-only trusted stage as it is stored on a read-only +medium, we want to prevent the read-only stage from being able to perform updates, as it will +require a network stack as well as TLS support. +Both the network stack and TLS support are hard to implement correctly, thus they are both open to +many vulnerabilities that cannot be mitigated as the read-only trusted stage cannot be updated. +This is why the read-only trusted stage cannot be responsible for checking for updates and +performing updates. +Therefore, in combination with the concept of a forced reboot from the TBM, the system would always +be able to check for updates periodically. + +To keep track of the images, a table can be used to mark whether the update stage and the image +itself are known to work. +Based on that table the Trusted Boot Manager can then determine which image to rollback to in case +the updated image does not work. +Furthermore, one important decision is whether to only use images that are known to fully work or +to specifically select working stages separately. +Another important open question is how far back the system is allowed to rollback. + +Initial Keys and Configuration +------------------------------ + +The initial key(s) or certificates and the configuration for the TBM could be presented on a SD +card that is inserted before the system is booted for the very first time. +Alternatively, we could decide to have the user embed the keys within the ROTS image before +flashing it to the RO Flash. +Another option would be to offer the user the option of performing key management by means of +serial communication with the Trusted Boot Manager. + +Verification of images +---------------------- + +As the user added the keys from the signing parties to trust, the system should only allow images +that have been signed with these keys. +This verification can take place in the read-only trusted stage before booting the image to ensure +that it has not been comprimised. +Furthermore, as there is a notion of reproducible builds, there is the option of verifying the +packages within the image. +However, as the read-only trusted state should be kept simple to avoid any possible vulnerabilities +or compromises, it is better to delegate the responsibility of verifying these packages to the +parties that sign the image. +Similarly, while the idea of sending deltas between different versions of images does sound +beneficial in the sense that the amount of data that has to be downloaded is kept to a minimum, +this would also further complicate the read-only trusted stage to the extent that it could increase +the likelihood of possible vulnerabilities. + +Boot procedure +-------------- + + #. The image from SPI flash is read and booted. + #. The read-only trusted stage asks the Trusted Boot Manager what to boot and/or asks whether it + is allowed to boot a certain image. + By default the read-only trusted stage should boot a semi-trusted update stage. + #. The read-only trusted stage boots the semi-trusted update stage using ``kexec``. + #. The semi-trusted update stage checks for updates and downloads a new image if present. + #. Once downloaded the semi-trusted update stage finishes by telling the Trusted Boot Manager that + a new image has been downloaded or that no updates were available. + #. The Trusted Boot Manager reboots the system. + #. The image from SPI flash is read and booted. + #. The read-only trusted stage asks the Trusted Boot Manager what to boot and/or asks whether it + is allowed to boot a certain image. + After a reboot from the update process, the Trusted Boot Manager will tell the read-only + trusted stage to boot the latest image. + #. The read-only trusted stage uses ``kexec`` to boot the latest image. + +Image self-test and rollback +---------------------------- + +The image could be extended with a self-test, where the untrusted stage tells the Trusted Boot +Manager that it managed boot successfully. +In case the image fails to boot, the Trusted Boot Manager should set a deadline to expire before +the image is booted. +When this deadline expires, the Trusted Boot Manager can then assume that the image does not work +and should not be used in the future, allowing rollback to the last-known working image. +Furthermore, by solely working with the concept of a last-known working image and an updated image +the possibility of a downgrade is severely limited and no advanced implementation of an image +rollback system would be required. + +Trusted Boot Manager protocol +----------------------------- + +To have the board communicate with the TBM, we have to devise a protocol. +This communication is likely to place over serial communication through the UART ports. +Furthermore, the board can only send requests to the TBM to which the TBM responds, i.e. the TBM +should never be able to initiate communication. +In the read-only trusted stage the Trusted Boot Manager fully trusts the system and as such the +read-only trusted stage can operate using the full scope of the protocol. +At some point the TBM will be informed by the read-only trusted stage that it will boot into +untrusted state, which is an indication for the TBM to configure the deadline timer and that the +scope of the protocol should be limited as the untrusted stage is only allowed to inform the TBM +that it booted successfully. +The read-only trusted stage should be able to ask which images are preferred for booting and if +booting a certain image is allowed. +Furthermore, it would probably be interesting if a list of images that can be booted can be queried +from the TBM. +In the case that we decide to support initial communication from the read-only trusted stage, +either by flashing the RO Flash or by inserting a removable medium such a SD card, it might be +interesting to support this option of configuration in the protocol. + +Proposal(s) +----------- + +To reduce the amount of possible vulnerabilities, the codebase and complexity of the TBM should be +kept as minimalistic as possible. +From that point of view, it would be best that the read-only trusted stage only consists of the +tools necessary to verify signed images, to communicate with the TBM and to ``kexec`` those images. +Furthermore, since the read-only trusted stage cannot be modified once it has been written to the +RO Flash, it cannot be easily patched. +As such, it makes a lot of sense to not include a network and TLS stack, as it is very likely that +they contain many vulnerabilities that have yet to be found. +Separating the update stage from the actual image itself, allows the system to forcefully perform +updates, which would essentially prevent any attacker from extending the time window they have any +further. +However, similar to the read-only trusted stage, the update stage should only come with the tools +necessary to check for updates and to perform an update: a network stack, a TLS stack and tools to +communicate with the TBM. +Finally, from the point of view of maintaining minimalism, only allowing rollback to the last-known +working image of which both the update stage and the image itself is probably one of the most +elegant choices, as it easily prevents downgrade attacks and as it is easier to implement. diff --git a/flashrom.rst b/flashrom.rst new file mode 100644 index 0000000..2407836 --- /dev/null +++ b/flashrom.rst @@ -0,0 +1,176 @@ +.. + +Flashing ROMs +============= + +Prerequistes +------------ + + * BusPirate v3.6a + * SOIC clip + * Winbond 25Q128FV or any other SPI ROM + +Hooking up the ROM +------------------ + +Clip the Winbond 25Q128FV between the SOIC clip. Using the text on the ROM, we can orient the pins on the other side of the clip. The ones on the top from left to right are: + + * Chip Select (CS) + * Data Output (DO) + * Write Protect (WP) + * Ground (GND) + +The pins on the bottom from left to right are (i.e. the opposite side): + + * Power Supply (VCC) + * /HOLD or /RESET + * Clock (CLK) + * Data Input (DI) + +Using the colour codes of the wires, we should hook up the pins as follows: + + * CS <-> White <-> CS + * MISO <-> Black <-> DO + * GND <-> Brown <-> GND + * 5V <-> Orange <-> VCC + * CLK <-> Purple <-> CLK + * MOSI <-> Grey <-> DI + +.. code:: + + VCC H/R CLK DI + +-|---|---|---|-+ + | | + | Winbond | + | W25Q128.V | + |O | + +-|---|---|---|-+ + CS DO WP GND + +Google Flashrom +--------------- + +Unlike the mainline version of flashrom, Google's version has two flags to get the name and the +size of the Flash chip: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --flash-name + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + vendor="Macronix" name="MX25L6406E" + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --get-size + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + 8388608 + +With the ``layout.txt`` file, we can tag certain regions in the ROM with a custom name: + +.. code:: + + 000000:00ffff rw + 7e0000:7fffff ro + +Then we can create two random blobs to verify that the ROM works: + +.. code:: + dd if=/dev/urandom of=rw.dat count=64K bs=1 + dd if=/dev/urandom of=ro.dat count=64K bs=1 + +Finally, we can write these two blobs to the two ROM regions by specifying their names. +We also disable parsing the fmap and verifying the unmodified ROM regions to speed up the process. +To maintain an optimal stability an SPI speed of no more than 2 MHz is recommended when using a +BusPirate: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate -l layout.txt -i ro:ro.dat rw:rw.dat -w --ignore-fmap --fast-verify + +Now that the blobs have been written, we can look at the write-protect ranges supported by the chip: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --wp-list + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + Valid write protection ranges: + start: 0x000000, length: 0x000000 + start: 0x7e0000, length: 0x020000 + start: 0x7c0000, length: 0x040000 + start: 0x7a0000, length: 0x080000 + start: 0x700000, length: 0x100000 + start: 0x600000, length: 0x200000 + start: 0x400000, length: 0x400000 + start: 0x000000, length: 0x800000 + start: 0x000000, length: 0x800000 + start: 0x000000, length: 0x400000 + start: 0x000000, length: 0x600000 + start: 0x000000, length: 0x700000 + start: 0x000000, length: 0x780000 + start: 0x000000, length: 0x7c0000 + start: 0x000000, length: 0x7e0000 + start: 0x000000, length: 0x800000 + +For instance, we can set the write-protect range to be ``0x7e0000`` - ``0x810000``: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --wp-range 0x7e0000 0x020000 + +After setting the range, we are still able to modify the contents of the entire ROM. +To protect the range, we have to enable write protection as follows: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --wp-enable + +``WP#`` must be pulled down for the write protect to be effective, i.e. it must be connected to GND. +This prevents the user from disabling the write protection, changing the write-protected range and +from writing to the write-protected region. +For example, writing a different blob to the region tagged as ``rw`` does work: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --layout layout.txt -i rw:ro.dat --write --ignore-fmap --fast-verify + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + delay loop is unreliable, trying to continue Block protection could not be disabled! + Erasing and writing flash chip... Verifying flash... VERIFIED. + SUCCESS + +While writing a different blob to the region tagged as ``ro`` does not work, as it cannot be erased +due to write-protection: + +.. code:: + + ./flashrom --programmer=buspirate_spi:dev=/dev/buspirate --layout layout.txt -i ro:rw.dat --write --ignore-fmap --fast-verify + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + delay loop is unreliable, trying to continue Block protection could not be disabled! + Erasing and writing flash chip... ERASE FAILED at 0x007e0000! Expected=0xff, Read=0x15, failed byte count from 0x007e0000-0x007e0fff: 0xff1 + ERASE FAILED! + Reading current flash chip contents... + +Furthermore, changing the range is not possible either as long as ``WP#`` is pulled down: + +.. code:: + + ./flashrom --programmer=buspirate_spi:spispeed=2M,dev=/dev/buspirate --layout layout.txt --wp-range 0x000000 0x000000 + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + expected=0x80, but actual=0x9a. + FAILED + +Finally, disabling the write-protection feature is not possible either as long as ``WP#`` is pulled +down: + +.. code:: + + ./flashrom --programmer=buspirate_spi:spispeed=2M,dev=/dev/buspirate --layout layout.txt --wp-disable + flashrom v0.9.4 : bc6cab1 : Oct 30 2014 07:32:01 UTC on Linux 4.9.4-gentoo (x86_64), built with libpci 3.1.10, GCC 4.8.x-google 20140307 (prerelease), little endian + generic_disable_writeprotect(): error=-1. + FAILED + +References +---------- + + * http://dangerousprototypes.com/docs/SPI + * https://www.winbond.com/resource-files/w25q128fv_revhh1_100913_website1.pdf + * https://www.pjrc.com/teensy/W25Q128FV.pdf + * https://learn.sparkfun.com/tutorials/bus-pirate-v36a-hookup-guide + * https://www.chromium.org/chromium-os/packages/cros-flashrom + * http://www.tnhh.net/posts/unbricking-chromebook-with-beaglebone.html diff --git a/kexec.rst b/kexec.rst new file mode 100644 index 0000000..bb532b3 --- /dev/null +++ b/kexec.rst @@ -0,0 +1,141 @@ +.. + +Setting up kexec +================ + +Prerequisites +------------- + +To be able to use kexec and initramfs images, we have to make sure the following options have been enabled in the kernel config: + +.. code:: + :lang: text + + CONFIG_BLK_DEV_INITRD=y + CONFIG_RD_GZIP=y + CONFIG_RD_BZIP2=y + CONFIG_RD_LZMA=y + CONFIG_RD_XZ=y + CONFIG_RD_LZO=y + CONFIG_RD_LZ4=y + CONFIG_KEXEC=y + +Building an initramfs +--------------------- + +Let's first create the general structure of our initramfs: + +.. code:: + :lang: text + + mkdir -p /usr/src/initramfs/{bin,dev,etc,lib,lib64,mnt,proc,root,sbin,sys} + +Rather than compiling a lot of the common utilities that can be found in e.g. coreutils, we compile a static version of busybox to install within in our initramfs: + +.. code:: + :lang: text + + USE="static" emerge busybox + cp /usr/bin/busybox /usr/src/initramfs/bin/busybox + +In addition to busybox, we will also need kexec-tools to boot our kernel: + +.. code:: + :lang: text + USE="static" emerge kexec-tools + cp /usr/sbin/kexec /usr/src/initramfs/sbin/kexec + +If we run ldd on /usr/sbin/kexec, we can also figure out the dependencies that it will need to run: + +.. code:: + :lang: text + + ldd /usr/sbin/kexec + cp -r /lib/{libc.so.6,libz.so.1,ld-linux-armhf.so.3} /usr/src/initramfs/lib + +In /usr/src/initramfs/init, we set up the following init script: + +.. code:: + :lang: text + + #!/bin/busybox sh + + mount -t proc none /proc + mount -t sysfs none /sys + mount -t devtmpfs none + + busybox sh + + umount /dev + exec switch root /mnt/root /sbin/init + +The above script will drop us in a busybox shell session before resuming to switch to the mounted rootfs at /mnt/root. +Also, do make sure that is executable: + +.. code:: + :lang: text + + chmod +x /usr/src/initramfs/init + +Once everything has set up, we can pack it all into a initramfs as follows: + +.. code:: + :lang: text + + cd /usr/src/initramfs + find . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/initramfs.cpio.gz + +Booting a kernel +---------------- + +.. code:: + :lang: text + + cd /usr/src/linux + kexec --dtb=arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dtb --ramdisk=initramfs.cpio.gz --command-line="console=tty1 rootfs=/dev/mmcblk2p2 rootfstype=ext4 rootwait rw" arch/arm/boot/zImage + +Using kexec-tools to boot a uImage also works. + +Booting u-boot +-------------- + +First, we have to clone the u-boot repository and build u-boot: + +.. code:: + :lang: text + + git clone git://git.denx.de/u-boot.git --depth=1 -b v2017.01 + cd u-boot + make A20-OLinuXino-Lime2_defconfig + make + +Once we have built u-boot, we have to pack it up as a uImage, so that kexec can boot it. +However, before we can pack it up, we have to figure out the load address and the entry address for the board that we are trying to target. +For instance, for the Allwinner boards, we can find the load address as follows: + +.. code:: + :lang: text + + grep CONFIG_SYS_LOAD_ADDR include/configs/sunxi-common.h + grep CONFIG_SYS_TEXT_BASE include/configs/sunxi-common.h + +In this case, we'll find that the load address and text base are either configured to be ``0x22000000`` and ``0x2a000000`` or ``0x42000000`` and ``0x4a000000`` respectively. +Closer inspection shows that the Allwinner A20 (sun7i) uses the latter two. +After we have found the load address and text base, we can pack up u-boot: + +.. code:: + :lang: text + + mkimage -A arm -O linux -T kernel -C none -a 0x42000000 -e 0x4a000000 -d u-boot-dtb.bin u-boot.img + +TODO: this boots up to the point where u-boot display a bit of information and then just hangs. + +References +---------- + + * https://wiki.gentoo.org/wiki/Custom_Initramfs + * https://blogs.s-osg.org/use-mainline-u-boot-non-signed-kernels-exynos-chromebooks/ + * https://www.riscosopen.org/forum/forums/5/topics/350 + * https://github.com/c0d3z3r0/rockboot + * https://prabagaranvt.blogspot.nl/2012/09/kexec.html + * http://elinux.org/images/2/2f/ELC-2010-Damm-Kexec.pdf