Adventures with the USB Armory, pt1

A while ago, I saw the USB Armory on Crowd Supply, and decided to get one. It led me down many rabbit holes, which I found extremely pleasing.

One of the (many) interesting things about the armory is the ability to use it as a USB combined gadget (network, hid, and mass storage) – along with the crypto / secure boot features of the i.MX53 SoC that the Armory uses as its processor (32-bit ARM Cortex A8, 800mhz). With secure boot enabled, you get access to the “Security Controller”, which is essentially a somewhat magic way to do AES CBC with a secret key that cannot be accessed by an operating system running on the armory.

I was particularly interested in that as a way to store disk encryption secrets since it would require a combination of 1) the specific Armory device storing the passwords and 2) a vault password. I figure I can plug this thing in, unlock it, and have it “type” a disk password for me using the HID support. Fancy, right?

Before all that though, we’ll need to get secure boot to be a thing. Turning on secure boot isn’t difficult, per se – but there are many steps. You should also refer to the Inverse Path wiki page, but this post attempts to be a lot more detailed in several parts.

We’ll need a few things:

Super Root Key (SRK) CAs

The i.MX53 HAB (High Availability Boot) works by storing a hash of up to 4 “Super Root Keys” in a “fusebox”, which is an area on the device that can be programmed – caveat being, this process can be done exactly once as you might expect, and any errors pretty much render it useless from a secure boot perspective. If you do something rash like put the Armory in closed mode before verifying your work, congratulations, now you have a brick. Unlike TPM setup on PCs, this is a one time, irreversible operation. No pressure, right?

IMG/CSF Keys

We will generate two certificates signed by one of our SRK CA’s – an IMG certificate, used to sign, uh, well…images (that is, the bootloader – in our case, uboot) and a CSF key. The CSF key is used to sign a “Command Sequence File” that essentially tells the SoC what we’re doing from a cryptographic standpoint (e.g. “Use this certificate which should verify against SRK #1 and then ensure the bootloader verifies using that certificate”). I made two separate certificates for this purpose.

USB to TTL adapter

You’ll need this in order to get into the uboot command line, which is how you actually program the fuses. I used an Adafruit USB to TTL adapter, and in the absence of solderless headers, I used some simple clips I found at a local electronics store to get to the serial port. Remember if you have a v1 the pins are different versus the beta – attach ground to pin 1 (it’s the one that has a square around it), attach RX (white) to pin 5, and TX (green) to pin 6. We can then use screen to attach to the serial console.

Kernel Signing Certificate

As part of the secure boot process, we not only want to ensure that our bootloader is signed, but it would also be nice to verify that the kernel we will then boot is signed as well. We’ll use this key to sign a Linux kernel, and this public key will be embedded into the uboot image we generate. This ensures that the kernel and the bootloader are cryptographically verified (a future step I’d like to take is extending this to kernel modules as well).

Let’s begin by getting everything from the certificate side prepared for secure booting. The first step is setting up all the PKI. I have an existing PKI structure, and I wanted to incorporate the SRK CA’s into that. Before we start, one extremely important caveat (and one that is not particularly well-documented at all) is that the i.MX53 can only support 2048 bit CA keys, because the i.mx53 is lacking CAAM – its crypto accelerator is known as SAHARA (i.mx6 and higher have CAAM and therefore support up to 4096 bit keys).

I have a fairly simple hierarchy and decided to make the SRK CAs subordinates, e.g. Global CA -> SRK CA 1,2,3,4.

First, make four new CAs using easyrsa:

for x in 1 2 3 4; do 
  mkdir SRKCA_${x} && tar xf EasyRSA-3.0.1.tgz -C SRKCA_${x} --strip-components=1; 
done

Make sure to edit the vars files for the CAs and set up everything as needed. I usually set EASYRSA_DN to “org” because that’s what I’m about; your strategy may be different. Most importantly, remember that all certificates must be no larger than 2048-bit.

Next, we need to make four CA certificates:

for x in 1 2 3 4; do 
  cd SRKCA_${x} && ./easyrsa init-pki && ./easyrsa build-ca subca && cd ..; 
done

I used subca here because mine are subordinate to a root CA; if yours aren’t, just omit it. I recommend setting the expiry to 10 years, although it seems, according to the NXP documentation, validity in terms of cert dates is not considered.

But still.

If you’re some kind of goddamn animal, feel free to add nopass here as well to omit passwords on the SRK CAs. But we aren’t animals – we’re human beings, right?

We need to generate a CSF and IMG key. X509 attributes, etc, don’t matter at all, but I added a new csf_img type to the x509-types and added code signing as an x509 extension, because, again, reasons. I’ll omit this here, and assume you just want to sign the certificate like a normal person.

Generate and sign the CSF and IMG keys:

openssl req -new -keyout SRKCA_1_CSF_key.pem -newkey rsa:2048 -out SRKCA_1_CSF.req
openssl req -new -keyout SRKCA_1_IMG_key.pem -newkey rsa:2048 -out SRKCA_1_IMG.req

Have the first SRK CA sign the CSF and IMG keys:

cp SRKCA_1_CSF.req SRKCA_1_IMG.req SRKCA_1/pki/reqs
cd SRKCA_1
./easyrsa sign-req SRKCA_1_CSF
./easyrsa sign-req SRKCA_1_IMG

I generated the SRK CA certificates on an offline machine and ensured the CSF and IMG keys have strong passwords. You might do things differently, that’s fine by me.

Next, you’ll need a uboot certificate. This doesn’t really have to be signed by the SRK CA, but having it be signed by the same CA made logical sense (to me).

openssl req -new -newkey rsa:2048 -out usbarmory.req -keyout usbarmory.key
cp usbarmory.req SRKCA_1/pki/reqs
./easyrsa sign-req usbarmory

This is the cert that will sign the kernel itself, the public key will be embedded into the uboot image we will build.

You should make a directory and copy the CA certificates there, along with the CSF, IMG, and kernel signing certificates and private keys. It’ll save you time later. Anywhere you see ${KEYS_PATH} I’m referring to this directory.

mkdir secureboot_keys
for x in 1 2 3 4; do 
  cp SRKCA_${x}/pki/ca.crt ./secureboot_keys/SRK_1_crt.pem; 
done
export KEYS_PATH=/path/to/secureboot_keys

Now that the PKI is set up, we need to generate two important files: a table file, and a fuses file. The table file is used by the CSF file as a reference as to which of the SRKs is being selected for verification. The fuse file is the pattern we will later fuse into the SoC fusebox so we can enable the closed security mode (e.g. secure boot enabled and locked).

Inverse Path provides a tool to do this in the usbarmory repository, usbarmory_srktool. They recommend that you check the output using the NXP tools, which, given that we can effectively destroy the hardware with any mistakes, is a good recommendation. In order to get those tools, you’ll have to register on the NXP site and then you can download the i.MX code signing tools.

usbarmory_srktool  \
  --key1  ${KEYS_PATH}/SRK_1_crt.pem \
  --key2  ${KEYS_PATH}/SRK_2_crt.pem \
  --key3  ${KEYS_PATH}/SRK_3_crt.pem \
  --key4  ${KEYS_PATH}/SRK_4_crt.pem \
  --hash  ${KEYS_PATH}/SRK_1_2_3_4_fuse.bin \
  --table ${KEYS_PATH}/SRK_1_2_3_4_table.bin

This will generate two files, SRK_1_2_3_4_fuse.bin, and SRK_1_2_3_4_table.bin. We can use the NXP tools to verify. I was using CST-2.3.2 as of this writing.

./linux64/srktool -h 4 -c SRK_1_crt.pem,SRK_2_crt.pem,SRK_3_crt.pem,SRK_4_crt.pem 
--table SRK_1_2_3_4_table_nxp.bin --efuses SRK_1_2_3_4_fuse_nxp.bin --digest sha256

(Note that spaces aren’t allowed between the commas, otherwise a certificate will be left off without any notice and you may be hosed)

Verify the output is the same from both tools:

baughj@ubiquity:/srv/armory/keys# hexdump -C SRK_1_2_3_4_fuse.bin 
00000000  d3 4d b3 3f ca fe ba be  d0 0d 1c eb 00 da b1 05  |.M.?............|
00000010  f0 0d 13 37 d0 0d d1 5e  a5 e0 13 37 13 37 13 37  |...7...^...7.7.7|
00000020
baughj@ubiquity:/srv/armory/keys# hexdump -C SRK_1_2_3_4_fuse_nxp.bin 
00000000  d3 4d b3 3f ca fe ba be  d0 0d 1c eb 00 da b1 05  |.M.?............|
00000010  f0 0d 13 37 d0 0d d1 5e  a5 e0 13 37 13 37 13 37  |...7...^...7.7.7|
00000020

It should go without saying that your output will (and must be) different!

Next, we need to set up our build environment. I used a debian jessie VM for this (ignoring the fact that it is contaminated with systemd…like many things these days).

We need a few things first:

apt-get install build-essential device-tree-compiler

We’ll also need to be able to actually compile for armhf. The easiest way to do this is to add the cross-toolchains repository, import the signing key, and install the crossbuild package for armhf:

echo 'deb http://emdebian.org/tools/debian/ jessie main' > /etc/apt/sources.list.d/crosstools.list
curl https://emdebian.org/tools/debian/emdebian-toolchain-archive.key | sudo apt-key add -
sudo dpkg --add-architecture armhf
sudo apt-get update
sudo apt-get install crossbuild-essential-armhf

We’ll also need a few Ruby gems, so make sure you have ruby and gem installed:

apt-get install ruby2.1 gem2.1
gem install bit-struct digest

After this, note that you’ll have arm-linux-gnueabihf-gcc-4.9 and friends, which is what the Inverse Path wiki refers to when it references your “ARM toolchain prefix” (e.g. arm-linux-gnueabihf-.

Now we’re ready to compile the kernel and uboot. You’ll need kernel sources. I installed the Linux 4.10.1 source referenced in the preparing a bootable MicroSD image documentation. You’ll need a zImage at first, too.

I just followed the directions from the aforementioned wiki on that:

export ARCH=arm
export CROSS_COMPILER=arm-linux-gnueabihf-

wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.10.1.tar.xz
tar xvf linux-4.10.1.tar.xz && cd linux-4.10.1
wget https://raw.githubusercontent.com/inversepath/usbarmory/master/software/kernel_conf/usbarmory_linux-4.10.config -O .config
wget https://raw.githubusercontent.com/inversepath/usbarmory/master/software/kernel_conf/imx53-usbarmory-host.dts -O arch/arm/boot/dts/imx53-usbarmory-host.dts
wget https://raw.githubusercontent.com/inversepath/usbarmory/master/software/kernel_conf/imx53-usbarmory-gpio.dts -O arch/arm/boot/dts/imx53-usbarmory-gpio.dts
wget https://raw.githubusercontent.com/inversepath/usbarmory/master/software/kernel_conf/imx53-usbarmory-spi.dts -O arch/arm/boot/dts/imx53-usbarmory-spi.dts
wget https://raw.githubusercontent.com/inversepath/usbarmory/master/software/kernel_conf/imx53-usbarmory-i2c.dts -O arch/arm/boot/dts/imx53-usbarmory-i2c.dts
wget https://raw.githubusercontent.com/inversepath/usbarmory/master/software/kernel_conf/imx53-usbarmory-scc2.dts -O arch/arm/boot/dts/imx53-usbarmory-scc2.dts
make zImage modules imx53-usbarmory.dtb imx53-usbarmory-host.dtb imx53-usbarmory-gpio.dtb imx53-usbarmory-spi.dtb imx53-usbarmory-i2c.dtb imx53-usbarmory-scc2.dtb

Remember that you’ll need to define ARCH and CROSS_COMPILER to do all of these steps. If GCC ends up whining in a seemingly cryptic fashion…

  CHK     include/config/kernel.release
Cannot use CONFIG_CC_STACKPROTECTOR_REGULAR: -fstack-protector not supported by compiler
Makefile:1066: recipe for target 'prepare-compiler-check' failed
make: *** [prepare-compiler-check] Error 1

…this is why.

Make sure to copy the modules over to your SD card – otherwise you won’t have things like usb ethernet gadget and heartbeat LEDs and…well, half the stuff that makes the Armory interesting.

sudo make INSTALL_MOD_PATH=$TARGET_MNT ARCH=arm modules_install

Don’t worry about copying the DTBs to /boot like the wiki suggests – they will become irrelevant, as will the zImage – but we still need to generate them.

With the kernel built, now we can proceed to build our bootloader.

Download and uncompress uboot:

wget ftp://ftp.denx.de/pub/u-boot/u-boot-2016.05.tar.bz2
tar xvf u-boot-2016.05.tar.bz2 && cd u-boot-2016.05

Inverse Path has provided a number of patches we want, but we need to apply them in steps – one of the patches disables the uboot command line entirely, which isn’t super useful for doing the fuse programming.

We’ll apply the HAB and verified boot support patches first:

patch -p1 < ../0001-Add-HAB-support.patch
patch -p1 < ../0002-Add-verified-boot-support.patch

Part of this patch will also kill the delay at boot, which also isn’t useful for doing the fuse programming – so let’s fix that while we’re at it (in includes/configs/usbarmory.h):

/* Disable autoboot */
#undef CONFIG_BOOTDELAY
#define CONFIG_BOOTDELAY 5

Now we can build our bootloader. Let’s set a bunch of variables now to make our lives easier:

export KERNEL_SRC=/path/to/the/4.10.1/source/we/compiled/earlier
export CROSS_COMPILE=arm-linux-gnueabihf- 
export KEYS_PATH=/path/to/your/SRKCA/certificates/and/IMG/CSF/uboot/signing/certificates
export USBARMORY_GIT=/path/to/a/checkout/of/inversepath/usbarmory/repository
export UBOOT_LOCATION=/some/path/to/my/uboot
cd ${UBOOT_LOCATION}
make distclean
make usbarmory_config
make tools

Now, we need to generate a compiled device tree file. We pad it by 2048 bits (0x1000) to leave room to insert our public key:

dtc -p 0x1000 ${USBARMORY_GIT}/software/secure_boot/pubkey.dts -O dtb -o pubkey.dtb

The DTC file format is pretty straightforward – another caveat that if you don’t name your key usbarmory – as in literally, the filename – you’ll have to update it in the file itself:

/dts-v1/;

/ {
        model = "Leet Haxs Inc USB Armory";
        compatible = "inversepath,imx53-usbarmory", "fsl,imx53";
        signature {
                sig@0 {
                        required = "conf";
                        algo = "sha256,rsa2048";
                        key-name-hint = "my_super_cool_signing_key";
                };
        };
};

Note that whatever you put for model here will show up in the bootloader, so feel free to put something ridiculous so you can feel cool later while looking at the output.

Generate the image tree blob (ITB) file using the template in the Inverse Path repository:

tools/mkimage -D "-I dts -O dtb -p 2000 -i $KERNEL_SRC" 
-f ${USBARMORY_GIT}/software/secure_boot/usbarmory.its usbarmory.itb

And sign it:

tools/mkimage -D "-I dts -O dtb -p 2000" -F -k ${KEYS_PATH} 
-K pubkey.dtb -r usbarmory.itb

N.B. If you plan on using the security controller, you need to edit the DTS file referenced above (usbarmory.its) and use a devicetree blob that includes the SCC2. For instance:

fdt@1 {
  description = "USB armory devicetree blob - with scc2";
  data = /incbin/("arch/arm/boot/dts/imx53-usbarmory-scc2.dtb");
  type = "flat_dt";
  compression = "none";
  arch = "arm";
  fdt-version = ;
  hash@1 {
  algo = "sha256";
  };
};

Now we can build the actual bootloader, referencing the public key blob we created above:

make ARCH=arm V=1 EXT_DTB=pubkey.dtb

I put V=1 because I don’t think anything I do on a computer is truly important until it involves scrolling text.

A nice next step before we go pulling the cart ahead of the horse is to ensure that the kernel signature actually verifies before we proceed, which you can do using the uboot tools:

tools/fit_check_sign -f usbarmory.itb -k pubkey.dtb

Now we can generate our CSF file using the Inverse Path tools:

${USBARMORY_GIT}/software/secure_boot/usbarmory_csftool \
--csf_key ${KEYS_PATH}/CSF_1_key.pem \
--csf_crt ${KEYS_PATH}/CSF_1_crt.pem \
--img_key ${KEYS_PATH}/IMG_1_key.pem \
--img_crt ${KEYS_PATH}/IMG_1_crt.pem \
--table ${KEYS_PATH}/SRK_1_2_3_4_table.bin \
--index 1 \
--image u-boot-dtb.imx \
--output csf-armory.bin

Next, we concatenate our bootloader and our CSF file into our signed image:

cat u-boot-dtb.imx csf-armory.bin > u-boot-signed.imx

You’ll also need to copy usbarmory.itb to /boot on the microSD card. Note that this replaces zImage, and there’s now no need for the DTB files either.

cp usbarmory.itb ${TARGET_MNT}/boot

Lastly, write your signed bootloader onto the SD card (make sure you do /dev/sdb or whatever here, and not a partition!)

dd if=u-boot-signed.imx of=${TARGET_DEV} bs=512 seek=2 conv=fsync

Now, we can reboot. Assuming your kernel boots and everything works normally – proceed to fusing the hash! Woo! I sure hope you use your values here!

Go into the bootloader (via the aforementioned USB to TTL cable – on Linux, you can do something like screen /dev/ttyUSB0 115200 after plugging in the Adafruit cable to get a console. => from here on in references a uboot prompt.

I will say again, the fuse values here should be the ones in your fuse file. The values here are an example!

For instance, if our values were:

00000000  d3 4d b3 3f ca fe ba be  d0 0d 1c eb 00 da b1 05  |.M.?............|
00000010  f0 0d 13 37 d0 0d d1 5e  a5 e0 13 37 13 37 13 37  |...7...^...7.7.7|
00000020

We need to turn on the fuse power first in order to write values:

=> i2c mw 0x34 0x33 0xf9
=> i2c mw 0x34 0x10 0x40

Now we can fuse our SRK hashes:

# Fuse. Note that the banks and offsets change for each line!
=> fuse prog -y 1 0x1 0xd3                                                   # first value in the list
=> fuse prog -y 3 0x1 0x4d 0xb3 0x3f 0xca 0xfe 0xba 0xbe 0xd0 0x0d 0x1c 0xeb # next 11 hex bytes
=> fuse prog -y 3 0xc 0x00 0xda 0xb1 0x05 0xf0 0x0d 0x13 0x37 0xd0 0x0d 0xd1 # next 11 hex bytes
=> fuse prog -y 3 0x17 0x5e 0xa5 0xe0 0x13 0x37 0x13 0x37 0x13 0x37          # last 9 hex bytes - 32 bytes in total!

Lastly, we need to lock the values we just fused:

=> fuse prog -y 1 0x0 0x4
=> fuse prog -y 3 0x0 0x3

We can reboot and check our status with hab_status:

=> hab_status

Secure boot enabled

HAB Configuration: 0xf0, HAB State: 0x66
No HAB Events Found!

IF AND ONLY IF you get no events above, now you can put the SoC into “closed” security mode:

=> i2c mw 0x34 0x33 0xf9
=> i2c mw 0x34 0x10 0x40
=> fuse prog -y 0 0x4 0x2
=> fuse prog -y 0 0x5 0x1

Secure Boot is now enabled! Woo! Now you can access the SCC…but that’s the subject for another post.

As a last step, you should apply the last patch (0003-Disable-CLI.patch) which will turn off the uboot command line (more secure) and rebuild / reinstall your bootloader.

Last but not least, there is a bug with SAHARA support in kernel 4.10.1, don’t use it (add it to the blacklist). You’ll find in particular that cryptsetup won’t decrypt volumes correctly under 4.10.1 if the sahara module is loaded.

Leave a Reply

Your email address will not be published. Required fields are marked *