# Hardening the USB Armory As a good crypto nerd, I usually use an entirely encrypted linux FS: `/` but also `/boot` using grub LUKS support. It's a good setup but it's not perfect, the BIOS and the bootloader are not protected. I recently got a USBArmory and I wanted to apply the same (or a better) setup. I found some useful links but no clear howto. So this is my setup. *This tutorial is for Archlinux ARM but you can use it as a base for other distributions, the principle is the same.* ## Requirements * USBArmory * USB to TTL cable (used to check the signed `/boot` and the signed bootloader) * Archlinux ARM ## Steps First of all, we'll use a USBArmory feature in this setup: the Secure Boot. It allow us to sign the bootloader. The bootloader is u-boot (grub doesn't support usbarmory). U-boot doesn't support LUKS decryption but it supports Verified boot which will check the kernel and some files (usually in `/boot`). And of course, the root system will be a LUKS partition. **I strongly suggest you backup your usbarmory before every step.** ## Encrypted root To decrypt the LUKS root FS, we need to setup an ssh server when the kernel boots. This part is Archlinux specific. For Debian, you can use this link: http://hacksr.blogspot.de/2012/05/ssh-unlock-with-fully-encrypted-ubuntu.html (I didn't test it). We work on the usbarmory, prepare everything and then edit the SD card on a desktop, create two partitions `/boot` and `/` and encrypt the `/` partition. 1. Install yaourt (see https://archlinux.fr/yaourt-en). 2. You should choose between the ssh server *dropbear* or the ssh server *tinyssh*. Dropbear doesn't support ed25519 and tinyssh doesn't support RSA/DH. Install requirements in user mode: ```shell alarm@usbarmory$ sudo yaourt -Sy mkinitcpio-netconf mkinitcpio-dropbear mkinitcpio-utils ``` or ```shell alarm@usbarmory$ sudo yaourt -Sy mkinitcpio-netconf mkinitcpio-tinyssh mkinitcpio-utils ``` For tinyssh, you should edit the `PKGBUILD` of tinyssh and ucspi-tcp and add `'armv7h'` in `arch`. 3. Copy your desktop public key in `/etc/dropbear/root_key` or `/etc/tinyssh/root_key` (as in `~/.ssh/authorized_keys`). 4. Edit `/etc/mkinitcpio.conf`, in the `MODULES` array, add `g_cdc usb_f_acm usb_f_ecm`. In HOOKS, before `filesystems` add `netconf dropbear encryptssh` or `netconf tinyssh encryptssh`. 5. Then, create the ramdisk: ```shell root@usbarmory# mkinitcpio -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img ``` 6. Edit kernel args to activate network and LUKS root. Edit `/boot/boot.txt`, replace: ``` setenv bootargs console=ttyGS0,115200 console=${console} root=PARTUUID=${uuid} rw rootwait ``` with: ``` setenv bootargs console=ttyGS0,115200 console=${console} ip=10.0.0.1::10.0.0.2:255.255.255.0::usb0:none cryptdevice=/dev/mmcblk0p2:root root=/dev/mapper/root rw rootwait ``` The `ip=` argument is for the default ArchlinuxARM setup: a static ip `10.0.0.1` with gateway `10.0.0.2`. For more information: https://wiki.archlinux.org/index.php/Mkinitcpio#Using_net. Be sure to have uboot-tools installed. Then: ```shell root@usbarmory# cd /boot root@usbarmory# ./mkscr ``` 7. Edit `/etc/fstab` and add: ``` /dev/mmcblk0p1 /boot none 0 2 ``` 8. Halt your usbarmory and put the microSD in your computer. Then backup your FS: ```shell desktop# mount /dev/mmcblk0p1 /mnt desktop# cd /mnt desktop# tar cf /tmp/armory_backup.tar * desktop# cd && umount /dev/mmcblk0p1 ``` Now, rewrite your partition table, the first partition must be `/boot` and the second must be `/`. ```shell desktop# fdisk /dev/mmcblk0 ``` For example: ``` Device Boot Start End Sectors Size Id Type /dev/mmcblk0p1 2048 526335 524288 256M 83 Linux /dev/mmcblk0p2 526336 27789311 27262976 13G 83 Linux ``` Now we recreate FS: ```shell desktop# mkfs.ext4 /dev/mmcblk0p1 desktop# cryptsetup luksFormat /dev/mmcblk0p2 --hash=sha256 desktop# cryptsetup luksOpen /dev/mmcblk0p2 usb desktop# mkfs.ext4 /dev/mapper/usb desktop# mount /dev/mapper/usb /mnt dekstop# mkdir /mnt/boot desktop# mount /dev/mmcblk0p1 /mnt/boot desktop# cd /mnt && tar xf /tmp/armory_backup.tar desktop# dd if=boot/u-boot.imx of=/dev/mmcblk0 bs=512 seek=seek2 conv=fsync dekstop# sync desktop# cd && umount /dev/mmcblk0p1 && umount /dev/mapper/usb dekstop# cryptsetup luksClose usb ``` 9. Now fingers crossed and boot your usbarmory. The LED should blink, the kernel should be up and dropbear/tinyssh waiting to unlock your root partition: ```shell dekstop$ ssh root@10.0.0.1 Enter passphrase for /dev/mmcblk0p2: Connection to 10.0.0.1 closed. ``` If you chose tinyssh, your first ssh will get a warning, add the public key in your ~/.ssh/known_hosts. This happens because tinyssh generates his key when you install it while dropbear copy existing ssh key (`/etc/ssh`). Now the kernel boot is done, you can ssh as usual. ## Signed /boot To use the Verified boot feature of u-boot, we need to compile a custom u-boot and create a FIT image which will contains the kernel, the initramfs and the signatures. The public key used to sign the FIT image will be stored in the u-boot binary using a control device tree. 1. Compile custom u-boot to enable FIT image support and FIT signature. Also modify the boot args and put the `/boot/boot.txt` contents directly into the u-boot binary. ```shell alarm@usbarmory ~$ git clone https://github.com/u-boot/u-boot.git alarm@usbarmory ~$ cd u-boot ``` Use this .patch for custom u-boot, you can put it into `/tmp/uboot.patch` for instance: ```diff diff --git a/configs/usbarmory_defconfig b/configs/usbarmory_defconfig index c25d103..2087577 100644 --- a/configs/usbarmory_defconfig +++ b/configs/usbarmory_defconfig @@ -1,5 +1,12 @@ CONFIG_ARM=y CONFIG_ARCH_MX5=y CONFIG_TARGET_USBARMORY=y +CONFIG_FIT=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_SIGNATURE=y +CONFIG_DM=y +CONFIG_RSA=y +CONFIG_OF_CONTROL=y +CONFIG_OF_SEPARATE=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_SETEXPR is not set diff --git a/include/configs/usbarmory.h b/include/configs/usbarmory.h index 714e3e2..43d76a8 100644 --- a/include/configs/usbarmory.h +++ b/include/configs/usbarmory.h @@ -81,10 +81,10 @@ #define CONFIG_HOSTNAME usbarmory #define CONFIG_BOOTCOMMAND \ "run distro_bootcmd; " \ - "setenv bootargs console=${console} ${bootargs_default}; " \ - "ext2load mmc 0:1 ${kernel_addr_r} /boot/uImage; " \ - "ext2load mmc 0:1 ${fdt_addr_r} /boot/${fdtfile}; " \ - "bootm ${kernel_addr_r} - ${fdt_addr_r}" + "setenv bootargs console=ttyGS0,115200 console=${console} ip=10.0.0.1::10.0.0.2:255.255.255.0::usb0:none cryptdevice=/dev/mmcblk0p2:root root=/dev/mapper/root rw rootwait;" \ + "if load ${devtype} ${devnum}:${bootpart} 0x80000000 /uImage; then" \ + " bootm 0x80000000;" \ + "fi" #define BOOT_TARGET_DEVICES(func) func(MMC, mmc, 0) ``` and apply it: ```shell alarm@usbarmory ~/u-boot$ git apply /tmp/uboot.patch ``` Now we compile u-boot tools: ```shell alarm@usbarmory ~/u-boot$ make distclean && make usbarmory_config && make ARCH=arm tools ``` 2. Prepare `/boot` and `/sboot`. I suggest you mount your first partition (actual `/dev/mmcblk0p1`) on `/sboot` and copy its contents into `/boot` (in the LUKS partition) so the clear partition `/dev/mmcblk0p1` will only contains the signed FIT image and an Archlinux update will not impact this partition. Edit `/etc/fstab` and replace `/boot` by `/sboot`. Then: ```shell root@usbarmory# umount /dev/mmcblk0p1 root@usbarmory# mount -a root@usbarmory# mv /sboot/* /boot/ ``` **Now /sboot is empty, don't reboot !** 3. Create working dir and gen 2048 bits RSA key and cert: ```shell alarm@usbarmory$ mkdir working && cd working alarm@usbarmory ~/working$ openssl genrsa -F4 -out ubootfit.key 2048 alarm@usbarmory ~/working$ openssl req -batch -new -x509 -key ubootfit.key -out uboot.crt ``` You can use another name for your key but you should replace `ubootfit` in the next files by your key name. 4. Create the DTS (device tree source) of the control device tree which will be stored in the u-boot binary (later in the MBR). Write the following in `uboot.dts`: ``` /dts-v1/; / { model = "Keys"; compatible = "inversepath,imx53-usbarmory", "fsl,imx53"; signature { key-ubootfit { required = "conf"; algo = "sha256,rsa2048"; key-name-hint = "ubootfit"; }; }; }; ``` Install `dtc`: ```shell usbarmory# pacman -S dtc ``` Now, generate the DTB (device tree blob) which will be the control device tree: ```shell alarm@usbarmory ~/working$ dtc -p 0x1000 uboot.dts -O dtb -o uboot.dtb ``` `uboot.dtb` is the control device tree, it'll be copied in the u-boot binary when we compile it. `uboot.dtb` doesn't contain the public key yet. 5. Copy the kernel, initramfs and usbarmory fdt into the working dir: ```shell alarm@usbarmory ~/working$ cp /boot/zImage ./ alarm@usbarmory ~/working$ cp /boot/initramfs-linux.img ./ alarm@usbarmory ~/working$ cp -r /boot/dtbs ./ ``` 6. Create the FIT image. Write the following in `image.fit`: ``` /dts-v1/; / { description = "USB Armory Kernel"; #address-cells = <1>; images { kernel@1 { description = "arch kernel"; data = /incbin/("./zImage"); type = "kernel"; arch = "arm"; os = "linux"; compression = "none"; load = <0x70800000>; entry = <0x70800000>; hash@1 { algo = "sha256"; }; }; ramdisk@1 { description = "initramfs"; data = /incbin/("./initramfs-linux.img"); type = "ramdisk"; arch = "arm"; os = "linux"; compression = "gzip"; load = <0x73000000>; entry = <0x73000000>; hash@1 { algo = "sha256"; }; }; fdt@1 { description = "imx53-usbarmory.dtb"; data = /incbin/("./dtbs/imx53-usbarmory.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; load = <0x71000000>; entry = <0x71000000>; hash@1 { algo = "sha256"; }; }; }; configurations { default = "config@1"; config@1 { description = "default"; kernel = "kernel@1"; ramdisk = "ramdisk@1"; fdt = "fdt@1"; signature@1 { algo = "sha256,rsa2048"; key-name-hint = "ubootfit"; sign-images = "kernel", "ramdisk", "fdt"; }; }; }; }; ``` Generate the FIT image: ```shell alarm@usbarmory ~/working$ ~/u-boot/tools/mkimage -D "-I dts -O dtb -p 2000" -f image.its uImage ``` The FIT image is not signed. This command will sign it and copy the public key into the control device tree: ```shell alarm@usbarmory ~/working$ ~/u-boot/tools/mkimage -F -k ./ -K ./uboot.dtb -r uImage ``` Now, FIT image is generated, signed and the public key is in the control device tree. Copy it in `/sboot`: ```shell alarm@usbarmory ~/working$ sudo cp uImage /sboot/ ``` 7. Compile uboot with the control device tree: ```shell alarm@usbarmory ~/u-boot$ make distclean && make usbarmory_config && make ARCH=arm EXT_DTB=~/working/uboot.dtb ``` Then copy it on the MBR: ```shell alarm@usbarmory ~/u-boot$ dd if=u-boot-dtb.imx of=/dev/mmcblk0 bs=512 seek=2 conv=fsync alarm@usbarmory ~/u-boot$ sync ``` Now remove `uboot-usbarmory` otherwise a uboot update will overwrite your custom u-boot: ```shell root@usbarmory# pacman -Rns uboot-usbarmory ``` 8. Reboot, using the USB to TTL cable, you should see: ``` CPU: Freescale i.MX53 rev2.1 at 800 MHz Reset cause: POR Model: Keys Board: Inverse Path USB armory MkI I2C: ready DRAM: 512 MiB MMC: FSL_SDHC: 0 *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Net: CPU Net Initialization Failed No ethernet found. Hit any key to stop autoboot: 0 switch to partitions #0, OK mmc0 is current device Scanning mmc 0:1... 9419922 bytes read in 573 ms (15.7 MiB/s) ## Loading kernel from FIT Image at 80000000 ... Using 'config@1' configuration Verifying Hash Integrity ... sha256,rsa2048:ubootfit+ OK Trying 'kernel@1' kernel subimage ... ``` The line `Verifying Hash Integrity ... sha256,rsa2048:ubootfit+ OK` shows the verification of the FIT image was successful. *Now u-boot will only boot on a signed FIT image.* 9. To update the FIT image, you don't need to recompile u-boot as long as the RSA key doesn't change. To generate a new FIT image and sign it (for example because a new kernel is available, or you need to update your ramdisk): ```shell alarm@usbarmory ~/working$ ~/u-boot/tools/mkimage -D "-I dts -O dtb -p 2000" -f image.its uImage alarm@usbarmory ~/working$ ~/u-boot/tools/mkimage -F -k ./ -r uImage ``` ## Signed bootloader This part is supposed to sign the u-boot binary using the usbarmory SoC and to be the first link in the chain of trust of this setup. **This step is the most sensitive as you can only write once into the usbarmory SoC and it could brick your device.** *I haven't tested this step because the tool provided by Freescale is no longer available. I'll update this step when a replacement is found.* This step is not described because this official link already does: https://github.com/inversepath/usbarmory/wiki/Secure-boot ## Links * https://github.com/ckuethe/usbarmory/wiki/Secure-Boot * https://github.com/inversepath/usbarmory/wiki/Secure-boot * https://wiki.archlinux.org/index.php/Dm-crypt/Specialties#Remote_unlocking_of_the_root_.28or_other.29_partition * http://www.denx-cs.de/doku/?q=m28verifiedboot * https://github.com/u-boot/u-boot/tree/master/doc/