|
|
@@ -0,0 +1,446 @@ |
|
|
# 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 [email protected] |
|
|
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/ |