If you're reading this, you may have a interest in security and privacy. There are currently only a few actual solutions to purchase a system that has Heads installed, a NitroPad and a Purism Librem 14(and their other models). This guide will show you how to flash Heads onto a Lenovo Thinkpad X230 laptop. The reason I created this guide is to give to you(and myself), an easy to follow guide to build your own secure laptop. This is a living document and is open to Pull Requests.
To quickly summarize, Heads, likes it's brethren Coreboot, is a open source firmware that allows you to replace your BIOS with a secure alternative. Heads allows you to verify if your system has been tampered(aka EvilMaid), using a combination of the TPM, Heads Firmware, and NitroKey to verify integrity.
Explained elegantly(from the official Heads docs), "Heads is not just another Linux distribution - it combines physical hardening of specific hardware platforms and flash security features with custom coreboot firmware and a Linux boot loader in ROM. This moves the root of trust into the write-protected region of the SPI flash and prevents further software modifications to the bootup code (and on platforms that support it, Bootguard can protect against many hardware attacks as well). Controlling the first instruction the CPU executes allows Heads to measure every step of the boot firmware and configuration into the TPM, which makes it possible to attest to the user or a remote system that the machine has not been tampered with. While modern Intel CPUs require binary blobs to boot, these non-Free components are included in the measurements and are at least guaranteed to be unchanging. Once the system is in a known good state, the TPM is used as a hardware key storage to decrypt the drive.
Additionally, the hypervisor, kernel and initrd images are signed by keys controlled by the user. While all of these firmware and software changes don’t secure the system against every possible attack vector, they address several classes of attacks against the boot process and physical hardware that have been neglected in traditional installations, hopefully raising the difficulty beyond what most attackers are willing to spend."
More information can be found at https://osresearch.net/
- Lenovo Thinkpad X230(The regular model, not the swivel touch screen version). Check Amazon and Ebay
- Nitrokey Storage 2 or Nitrokey Pro 2
- CH341A Programmer kit
- Optional - Pomona SOIC8 Clip Recommended alternativa to the cheap clip that comes with the programmer
The Heads team recommends not downloading from the latest commit, so let's head to the commits section of their github repo at https://github.com/osresearch/heads/commits/master - It's fairly safe to aim for a commit that was two weeks prior to today's date. Make sure the commit was "Verified" and that the Github Checkrun's passed(look for the green check mark, not the red X)
If you want to compile the firmware yourself, save the Git commit shasum and proceed to the Building Heads Firmware section.
If you want to download pre-compiled firmware, go to the Download Pre-Compiled Firmware
Start a docker container:
docker run -it --entrypoint /bin/bash debian:11
Now you should be inside the docker container
apt update
apt install -y build-essential zlib1g-dev uuid-dev libdigest-sha-perl libelf-dev bc bzip2 bison flex git gnupg gawk iasl m4 nasm patch python python2 python3 wget gnat cpio ccache pkg-config cmake libusb-1.0-0-dev autoconf texinfo ncurses-dev doxygen graphviz udev libudev1 libudev-dev automake libtool rsync innoextract sudo libssl-dev device-tree-compiler u-boot-tools sharutils e2fsprogs parted curl unzip
git clone https://github.com/osresearch/heads
cd heads
# Replace the hash with the hash of the Git commit you want to use
git switch -c 96440b928acb06de5b925ea12014c9c280b23165
mkdir ./tmpDir
find ./Makefile ./patches/ ./modules/ -type f | sort -h |xargs sha256sum > ./tmpDir/all_modules_and_patches.sha256sums
find ./Makefile ./modules/coreboot ./modules/musl-cross* ./patches/coreboot* -type f | sort -h | xargs sha256sum > ./tmpDir/coreboot_musl-cross.sha256sums
find ./Makefile modules/musl-cross* -type f | sort -h | xargs sha256sum > ./tmpDir/musl-cross.sha256sums
./blobs/xx20/download_parse_me.sh
./blobs/xx30/download_clean_me.sh -m $(readlink -f ./blobs/xx30/me_cleaner.py)
# This final step builds out the ROMs. If you get errors, try rerunning the command a few times.
rm -rf build/x86/x230-hotp-maximized/* build/x86/log/* && make V=1 BOARD=x230-hotp-maximized || touch ./tmpDir/failed_build
Now that the firmware has compiled, we should see the firmware files and their hashes in the output.
Example:
6e433049eb1d6b71aa01aa58b89ab25d49fa69379463a6c356fe2b5337bcc2ad build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158.rom
2023-04-08 16:02:32+00:00 DD 8MB build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-bottom.rom
dd of=/heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-bottom.rom if=/heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158.rom bs=65536 count=128 skip=0 status=none
16b8194e01662d8658c621ed05631aa94814a4107e5dc5285350274a04d2e6f3 /heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-bottom.rom
2023-04-08 16:02:32+00:00 DD 4MB build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-top.rom
dd of=/heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-top.rom if=/heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158.rom bs=65536 count=64 skip=128 status=none
0c812ab2ce23c5ff7502d5ca2e6b7bc2f38819ec739d4bdd59188f563d150fe4 /heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-top.rom
6e433049eb1d6b71aa01aa58b89ab25d49fa69379463a6c356fe2b5337bcc2ad /heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158.rom
We need to copy the files that end in *-top.rom and *-bottom.rom to our host machine. While in the docker container, run the following commands:
- On your machine(the docker host), which keeping the docker container running, open a new terminal window.
cd
into the directory you want your roms saved to- Run
docker ps
and copy the Container ID for the container we launched - Then run:
# Note that the period at the end notes that we want the files copy to our current directory
docker cp <CONTAINER ID>:<ROM PATH> .
Example:
# docker cp faf35cbdc550:/heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-bottom.rom .
# docker cp faf35cbdc550:/heads/build/x86/x230-hotp-maximized/heads-x230-hotp-maximized-v0.2.0-1507-g1cf7158-top.rom .
You can now jump to Flashing via programmer
Original steps found here: https://osresearch.net/Downloading
Find the commit you want to use, then click on the green checkmark, and on the little pop up window, scroll and find "x230-hotp-maximized". Click on "Details". This will pull you into circleCI
Go to "Make Board" section and click the drop down. Look for the sha265 sums for heads-x230-hotp-maximized-vX.X.X-X-X-bottom.rom and heads-x230-hotp-maximized-vX.X.X-X-X-top.rom. Notate the sums.
Example: Top - 4aab0e269390eebab9c3cefadfc6fb60d388804f45c54a68c6a1c685df9693c1 Bottom - 83627cef6f6576167901b8b560c7f9d8e3c36392e96e401af382f0e8e01bdca6
Now scroll back to the top of the CircleCI page and click on the "ARTIFACTS" tab. Download the top and bottom rom files. You can right click the download links and click "Save Link As..." if you're on Firefox.
Verify the check sums using the sha256sum command.
$ echo "4aab0e269390eebab9c3cefadfc6fb60d388804f45c54a68c6a1c685df9693c1 ~/Documents/git/heads-guide/heads-x230-hotp-maximized-v0.2.0-1482-g96440b9-top.rom" | sha256sum --check
~/Documents/git/heads-guide/heads-x230-hotp-maximized-v0.2.0-1482-g96440b9-top.rom: OK
$ echo "83627cef6f6576167901b8b560c7f9d8e3c36392e96e401af382f0e8e01bdca6 ~/Documents/git/heads-guide/heads-x230-hotp-maximized-v0.2.0-1482-g96440b9-bottom.rom" | sha256sum --check
~/Documents/git/heads-guide/heads-x230-hotp-maximized-v0.2.0-1482-g96440b9-bottom.rom: OK
You're now ready to start flashing them
Connect clip to top chip and run sudo flashrom -p ch341a_spi
to discover what type of chip it is. Note: If you run into issues flashing, try reconnecting the connector or reconnecting the USB programmer to the USB port of the PC.
You should get a result that looks something like the following:
Found Macronix flash chip "MX25L3205(A)" (4096 kB, SPI) on ch341a_spi.
Found Macronix flash chip "MX25L3205D/MX25L3208D" (4096 kB, SPI) on ch341a_spi.
Found Macronix flash chip "MX25L3206E/MX25L3208E" (4096 kB, SPI) on ch341a_spi.
Found Macronix flash chip "MX25L3273E" (4096 kB, SPI) on ch341a_spi.
Copy each chip name(found inside the quotes) and search for them on https://osresearch.net/x230-maximized-flashing/ - The one that matches a result on that page is what you want. The chip on my board was MX25L3206E/MX25L3208E
.
Now run the following to make a backup of your existing rom(replace the chip name with the one that matches yours): sudo flashrom -r ~/Documents/git/heads-guide/top.bin --programmer ch341a_spi -c "MX25L3206E/MX25L3208E" && sudo flashrom -v ~/Documents/git/heads-guide/top.bin --programmer ch341a_spi -c "MX25L3206E/MX25L3208E"
If verified, let's flash the heads firmware onto the top chip via the following command(not writing automatically verifies the rom): sudo flashrom --programmer ch341a_spi -c MX25L3206E/MX25L3208E -w heads-x230-hotp-maximized-v0.2.0-1400-g3ac896b-top.rom
If you get a Verifying flash... VERIFIED.
message, you're good to proceed.
Now let's follow a similar pattern for the bottom chip
Connect clip to bottom chip and run sudo flashrom -p ch341a_spi
to discover what type of chip it is. This will most likely be different from the top chip, so compare what's on the website stated above.
Now run the following to make a backup of your existing rom(replace the chip name with the one that matches yours): sudo flashrom -r ~/Documents/git/heads-guide/bottom.bin --programmer ch341a_spi -c "MX25L6406E/MX25L6408E" && sudo flashrom -v ~/Documents/git/heads-guide/bottom.bin --programmer ch341a_spi -c "MX25L6406E/MX25L6408E"
If verified, let's flash the heads firmware onto the top chip via the following command(not writing automatically verifies the rom): sudo flashrom --programmer ch341a_spi -c MX25L6406E/MX25L6408E -w heads-x230-hotp-maximized-v0.2.0-1400-g3ac896b-bottom.rom
If you get a Verifying flash... VERIFIED.
message, you're good to proceed.
Now remove the clip and power on the machine. If it doesn't boot the first time, try again.
https://forum.archive.openwrt.org/viewtopic.php?id=69483 truncate -s +12288K openwrt.bin
--
download qubues iso format a card to ext4(fat32 wont work due to 4gb file limit)
copy iso and iso.asc to usb stick manually
copy qubes master gpg key to seperate usb key
plug them all in.
upload quubes master gpg key to tpm
to boot usb into qubes installer
heads will verify the gpp key using the qubes master gpg key and the iso.asc signing key
install qubes usual. set up disk encryption
Will wipe nitrokey(even when setting to the same admin/user pins)
make sure pings are no more than 12 characts.... could cause issues with nitro key
after installing os, if you get errors or anything after heads boots, just pick an option that will get you to the main menu
- Plug in your NitroKey and separate FAT formatted USB stick that your GPG public key could be saved to.
- On the first screen, it should say "ERROR: GPG keyring empty!" - Scroll down to the bottom and select "Exit to recovery shell"
- On the recovery shell, let's set the clock to the "exact time down to the second" in UTC via
date -s 'yyyy-mm-dd hh:mm:ss' && hwclock -w
- Then reboot
- When the system reboots, you'll be back on the same "ERROR: GPG keyring empty!" screen. Click on "OEM Factory Reset / Re-Ownership". Then hit "Continue"
- You'll get prompted with some questions:
- Type "N" for No for the first 3 questions
- Type "Y" for Yes for the 4th question.
- Type in a TPM Ownership Password
- Type in a GPG Admin Pin(Will become the new admin pin for your NitroKey)
- Type in a GPG User Pin(Will be the new user pin for your NitroKey)
- You'll then be asked if you want to set customer user information for the NitroKey's GPG key. Type "Y" for yes and hit enter
- Type in your desired name
- Type in your desired email address
- Type in your desired comment(recommended would be "Security Key")
- You'll then be asked if want to export the public key to a USB drive. Type "Y" for yes and hit enter.
- Then select the usb drive you want the public key to be saved to
- Now Heads will set up your NitroKey to work with Heads. This can take 5 - 10 minutes. Be patient and don't touch anything until it's completed
- You may get an error stating "Unable to create TPM counter". Just hit OK.
- You will probably then be brought to a page titled "ERROR: TOTP Generation Failed". Unplug your Nitrokey and reinsert it.
- Now click "Generate new HOTP/TOTP secret"
- You may get a warning that any old secrets will be erased. Click OK and hit enter.
- You'll then be presented with a QR code. Scan it with your phone and hit enter
- Type your admin gpg key passcode and hit enter
- Now you'll be back to the Heads Home Menu. Go to Options, then "Update checksums and sign all files in /boot", click on it. The next screen click Yes. On the next screen, type "Y" to confirm your have your GPG Card(aka NitroKey) inserted.
- If asked, type in your TPM Owner password.
- If asked, enter your GPG User Pin and hit enter
- If the signing fails, wait until you're back to the home screen, then pull out and reinsert your NitroKey and redo the steps for "Update checksums and sign all files in /boot".
- You should be back on the Heads home menu. Click "Default Boot"
- IF you get a "No Default Boot Option Configured" page, click yes.
- Now choose the OS you want to boot. If asked, to Confirm boot details, select "Make Default" and hit enter
- If asked if you would like to add a disk encryption to the TPM, select "N" for No. I haven't had luck figuring out this feature.
- When asked if your GPG card is inserted, type "Y" for yes and hit enter
- Type in your GPG user pin and hit enter
- Your system should now boot into the OS you chose.
Make sure the reboot the system one more time to ensure the Nitrokey works fine. If you get an error with the system Unable to unseal the TOTP or TOTP Generation Failed, rerun the "Generate new TOTP/HOTP secret"
When the system boots up, the nitrokey should blink green and the HOTP status should be "Success" on the Heads Boot Menu. If you don't have your NitroKey with you, you can verify boot integrity with your smart phone to compare the TOTP code. This relies on the time being accurate.
Will wipe nitrokey(even when setting to the same admin/user pins)
make sure pings are no more than 12 characts.... could cause issues with nitro key
after installing os, if you get errors or anything after heads boots, just pick an option that will get you to the main menu
drive with iso, should include iso, iso signing key, and qubes master signing key
- Plug in your NitroKey, drive with iso and separate FAT formatted USB stick that your GPG public key could be saved to.
- On the first screen, it should say "ERROR: GPG keyring empty!" - Scroll down to the bottom and select "Exit to recovery shell"
- On the recovery shell, let's set the clock to the "exact time down to the second" in UTC via
date -s 'yyyy-mm-dd hh:mm:ss' && hwclock -w
- Then reboot
- When the system reboots, you'll be back on the same "ERROR: GPG keyring empty!" screen. Click on "Add a GPG key to the running BIOS"
- Then click "Add a GPG key to the running BIOS and reflash"
- On the next screen where it states that a public key is required, click "Yes"
- Go to your drive that includes the iso and two keys. Then select the Qubes Master Key. You'll then be asked if you want to proceed with flashing your BIOS with the updated version, click "yes"
- You'll then be asked if you want to update checksums and sign all files in the /boot directory. Select "No" and hit enter.
- You'll then be taken to a page with the title "ERROR: TOTP Generation Failed!". Select "Ignore error and continue to main menu"
- Go to "Options" and then "Boot Options" and then "USB Boot"
- Click on your USB disk with the ISO, then select the ISO
- Using the GPG keys, Heads will verify the ISO. Then select the boot option for "Install Qubes OS"...
- Install Qubes like normal and reboot when prompted
- When the system reboots, you'll be back on the same "ERROR: TOTP Generation Failed" screen. Click on "Ignore error and continue to main menu". Then go to "Options -->" and then "OEM Factory Reset / Re-Ownership -->" and then click "Continue"
- If you get a "Measured Integrity Report" page, click Ok.
- You'll get prompted with some questions:
- Type "N" for No for the first 3 questions
- Type "Y" for Yes for the 4th question.
- Type in a TPM Ownership Password
- Type in a GPG Admin Pin(Will become the new admin pin for your NitroKey)
- Type in a GPG User Pin(Will be the new user pin for your NitroKey)
- You'll then be asked if you want to set customer user information for the NitroKey's GPG key. Type "Y" for yes and hit enter
- Type in your desired name
- Type in your desired email address
- Type in your desired comment(recommended would be "Security Key")
- You'll then be asked if want to export the public key to a USB drive. Type "Y" for yes and hit enter.
- Then select the usb drive you want the public key to be saved to
- Now Heads will set up your NitroKey to work with Heads. This can take 5 - 10 minutes. Be patient and don't touch anything until it's completed
- You may get an error stating "Unable to create TPM counter". Just hit OK.
- You "may" then be brought to a page titled "ERROR: TOTP Generation Failed". Regardless, unplug your Nitrokey and reinsert it. If you're on the Heads Boot Menu, got to "Options -->", then go to "TPM/TOTP/HOTP Options -->"
- Now click "Generate new HOTP/TOTP secret"
- You may get a warning that any old secrets will be erased. Click OK and hit enter.
- You'll then be presented with a QR code. Scan it with your phone and hit enter
- Type your admin gpg key passcode and hit enter
- Now you'll be back to the Heads Home Menu. Go to Options, then "Update checksums and sign all files in /boot", click on it. The next screen click Yes. On the next screen, type "Y" to confirm your have your GPG Card(aka NitroKey) inserted.
- Then type in your TPM Owner password.
- If asked, enter your GPG User Pin and hit enter
- If the signing fails, wait until you're back to the home screen, then pull out and reinsert your NitroKey and redo the steps for "Update checksums and sign all files in /boot".
- You should be back on the Heads home menu. Click "Default Boot"
- IF you get a "No Default Boot Option Configured" page, click yes.
- Now choose the OS you want to boot. If asked, to Confirm boot details, select "Make Default" and hit enter
- If asked if you would like to add a disk encryption to the TPM, select "N" for No. I haven't had luck figuring out this feature.
- When asked if your GPG card is inserted, type "Y" for yes and hit enter
- Type in your GPG user pin and hit enter
- Your system should now boot into the OS you chose.
Make sure the reboot the system one more time to ensure the Nitrokey works fine. If you get an error with the system Unable to unseal the TOTP or TOTP Generation Failed, rerun the "Generate new TOTP/HOTP secret"
When the system boots up, the nitrokey should blink green and the HOTP status should be "Success" on the Heads Boot Menu. If you don't have your NitroKey with you, you can verify boot integrity with your smart phone to compare the TOTP code. This relies on the time being accurate.
Connect to the top chip and run sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000
and see if it detects the chip. You may get a list of possible chips, pick the one from the chart found here: https://osresearch.net/x230-maximized-flashing/
We're going to want to read the top one and then verified what was read is correct via
sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -r top.bin -c MX25L3206E/MX25L3208E
and then
sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -v top.bin -c MX25L3206E/MX25L3208E
Now we're going to write the new rom to the top chip via sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -c MX25L3206E/MX25L3208E -w heads-x230-hotp-maximized-v0.2.0-1400-g3ac896b-top.rom
Now lets move onto the bottom chip
We're going to want to read the bottom one and then verified what was read is correct via
sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -r top.bin -c MX25L6406E/MX25L6408E
and then
sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -v top.bin -c MX25L6406E/MX25L6408E
Now we're going to write the new rom to the bottom chip via
sudo flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000 -c MX25L6406E/MX25L6408E -w heads-x230-hotp-maximized-v0.2.0-1400-g3ac896b-bottom.rom
Notes:
Set up GPG key on Nitrokey https://docs.nitrokey.com/storage/linux/gpa
copy public gpg key to seperate fat32 formatted usb stick Example filename: s1nack-nitrokey-032423.asc
https://www.rototron.info/recover-bricked-bios-using-flashrom-on-a-raspberry-pi/
https://libreboot.org/docs/install/spi.html#reading
Download the top and bottom rom files from this commit https://app.circleci.com/pipelines/github/osresearch/heads/524/workflows/d13d6a02-1ffa-40ac-b721-e31ff69483d4/jobs/7458
grab the sha-sums from the Make Board check and compare them with what was downloaded