100200300400500600
  
 
 

Tinfoil session 1: Enabling secure boot vector on linux

While being a strong proponent of security and privacy I for long neglected one particular security vector on my laptops - secureboot, relying on compensating controls I put in place. SecureBoot was pushed on me by Microsoft so I naturally reflexly rejected it. Now however is a time to reconsider the stance, especially in view of much higher pressure on privacy and as result much deeper penetration of foreing networked compute elements around us (this is the place we put our tinfoil hat on).

So the goal I have in mind in general is somewhat inspired by Librem laptops with libremkey and heads providing secure and tamper-proof boot vector. For this session the bar is set to merely make sure we can boot securily. Simple enough. Since I'm using Arch Linux the obvious starting point is Arch Wiki's article on SecureBoot. I'll need to create my own keys (stored securily), load them to EFI BIOS, sign the EFI image, enable secure boot and boot into linux. Easy peasy.

First we create crypto-vault to store our keys. They should not be stored on the same laptop of course, you should make a crypto vault from cheap usb thumbdrive. Cheap due to low volume, not poor quality. But for simplicity (and due to the fact the first laptop is mostly sitting on the table (I carry another one) - we'll do it on the local drive. Also I have a Nitrokey so the keyfile will be encryped by its pgp card.

First generate random 512bit key and put it on the ramdrive (so that we don't need to securely remove it later). Then use it as a keyfile to create luks partition:

dd if=/dev/random of=/tmp/luks bs=64 count=1
sudo cryptsetup luksFormat /dev/sda8 /tmp/luks

Now encrypt the key, store it lokally and recycle the raw key:

gpg -e -r me@ruff.mobi < /tmp/luks >./key
dd if=/dev/random of=/tmp/luks bs=64 count=1
rm /tmp/luks 
gpg -d -u me@ruff.mobi < ./key | sudo cryptsetup open /dev/sda8 Vault -d-

Format luks partition with fav fs (simple ext4 should be ok) and since we need root (sudo) to operate the vault we can move the key to root's home to avoid exposing it in open.

sudo mkfs.ext4 /dev/mapper/Vault
sudo mkdir /mnt/sec
sudo mv key /root/.key
sudo cryptsetup close Vault

Now let's create simple script to open the vault and run the created script to actually open it. If you plan using the script for a longer term it would be better using part UUID instead part number. Numbers are volatile.

vim sezamy
#!/bin/bash

LUKS=/dev/sda8
sudo cat /root/.key | gpg -d -u me@ruff.mobi | sudo cryptsetup open $LUKS Vault -d-
sudo mount /dev/mapper/Vault /mnt/sec

$ bash -x sezamy 
+ sudo cat /root/.key
+ gpg -d -u me@ruff.mobi
+ sudo cryptsetup open /dev/sda8 Vault -d-
gpg: verschl├╝sselt mit 512-Bit ECDH Schl├╝ssel, ID 7DAE27C8C4A956A3, erzeugt 2021-10-23
      "Ruslan Marchenko (ruff) <me@ruff.mobi>"
+ sudo mount /dev/mapper/Vault /mnt/sec

sudo mkdir /mnt/sec/ruff
sudo chown ruff:ruff /mnt/sec/ruff/
chmod 700 /mnt/sec/ruff/

Now let's create a home for EFI keys and generate the keys using ArchLinux wiki as a tutorial:

cd /mnt/sec/ruff/ && mkdir UEFI && cd UEFI && mkdir {db,dbx,KEK,PK}

openssl req -new -x509 -newkey rsa:4096 -nodes -sha256 -days 3650 \
 -keyout PK.key -subj '/CN=MyPK/' -out PK.crt
openssl req -new -x509 -newkey rsa:4096 -nodes -sha256 -days 3650 \
 -keyout KEK.key -subj '/CN=MyKEK/' -out KEK.crt
openssl req -new -x509 -newkey rsa:4096 -nodes -sha256 -days 3650 \
 -keyout SDB.key -subj '/CN=MySDB/' -out SDB.crt

sudo pacman -Syu sbsigntools efitools

uuidgen --random > UEFI.guid
cert-to-efi-sig-list -g `< UEFI.guid` PK.crt PK.esl
sign-efi-sig-list -g `< UEFI.guid` -k PK.key -c PK.crt PK PK.esl PK/PK.auth

cert-to-efi-sig-list -g `< UEFI.guid` KEK.crt KEK.esl
sign-efi-sig-list -g `< UEFI.guid` -k PK.key -c PK.crt KEK KEK.esl KEK/KEK.auth

cert-to-efi-sig-list -g `< UEFI.guid` SDB.crt SDB.esl
sign-efi-sig-list -g `< UEFI.guid` -k KEK.key -c KEK.crt db SDB.esl db/SDB.auth

sbkeysync --keystore . --verbose --dry-run --pk

At this point you should see your 3 new keys being detected by sbkeysync. If you're still using dual-boot you'd need to add Microsoft keys:

curl -L 'https://go.microsoft.com/fwlink/?LinkId=321192' > MSProdCA2011.cer
curl -L 'https://go.microsoft.com/fwlink/?linkid=321194' > MSUEFICA2011.cer

openssl x509 -in MSProdCA2011.cer -out MSProdCA2011.crt -inform DER
openssl x509 -in MSUEFICA2011.cer -out MSUEFICA2011.crt -inform DER 

Technically we can pipe it right from curl but we may need DER if we want to load them manually.

cert-to-efi-sig-list -g 77fa9abd-0359-4d32-bd60-28f4e78f784b MSProdCA2011.crt \
 MSProdCA2011.esl
cert-to-efi-sig-list -g 77fa9abd-0359-4d32-bd60-28f4e78f784b MSUEFICA2011.crt \
 MSUEFICA2011.esl
cat MSProdCA2011.esl MSUEFICA2011.esl > MS.esl

sign-efi-sig-list -a -g 77fa9abd-0359-4d32-bd60-28f4e78f784b -k KEK.key \
 -c KEK.crt db MS.esl db/MS.auth

The wiki is saying the efivars are locked by immutable flag, let's confirm this:

lsattr /sys/firmware/efi/efivars/{PK,KEK,db,dbx}*

They are indeed, so let's create a simple script which unsets immutable attr and syncs the keys:

echo 'sbkeysync --keystore . --verbose --pk' >> sync.sh
vim sync.sh 

and add chattr -i /sys/firmware/efi/efivars/{PK,KEK,db,dbx}* before and +i after

sudo bash -x sync.sh
...
Error writing key update: Permission denied
Error syncing keystore file ./db/SDB.auth

This operation is kind of finicky. On my two laptops, one refused entirely to load keys until I wiped all keys (which switched it into Setup Mode, hence is expected). Then it imported the keys with above command but I needed to re-run it multiple times, it seems sbkeysync is pushing keys in some arbitrary order, but EFI BIOS allows them to be loaded only in specific order (PK, KEK, db). My other laptop pushed db key (without PK/KEK) and then refused to accept either PK or KEK.

Ok, let's add via UEFI BIOS then. Copy auth files to ESP to be accessible from UEFI.

sudo cp {PK,KEK,db}/*.auth /esp/EFI/Linux/

Also sign the unified kernel image:

sudo mv /esp/EFI/Linux/arch.efi /esp/EFI/Linux/arch.uefi
sudo sbsign --key UEFI/SDB.key --cert UEFI/SDB.crt \
 --output /esp/EFI/Linux/arch.efi /esp/EFI/Linux/arch.uefi
warning: data remaining[17838080 vs 17848308]: gaps between PE/COFF sections?
warning: data remaining[17838080 vs 17848312]: gaps between PE/COFF sections?
Signing Unsigned original image

$ sbverify /esp/EFI/Linux/arch.efi
warning: data remaining[17840376 vs 17850608]: gaps between PE/COFF sections?
Signature verification failed
$ sbverify /esp/EFI/Boot/bootx64.efi 
Signature verification OK
$ sbverify /esp/EFI/Boot/bootx64.efi --list
signature 1
image signature issuers:
 - /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows
 Production PCA 2011
image signature certificates:
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
 Windows
   issuer:  /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
   Windows Production PCA 2011
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
 Windows Production PCA 2011
   issuer:  /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
   Root Certificate Authority 2010
$ sbverify /esp/EFI/Linux/arch.efi --cert UEFI/SDB.crt 
warning: data remaining[17840376 vs 17850608]: gaps between PE/COFF sections?
Signature verification OK

systemctl reboot --firmware-setup

Setup PK/KEK from auth file, enable secure boot, ensure boot entry points to signed efi file, ensure you have set admin password (otherwise what's a point). Save and reboot. Now - here we have different results on two different laptops. First my attemp to deal with old Asus trio (tx201la).

It failed. Even though db looks ok:

$ efi-readvar 
Variable PK, length 1324
PK: List 0, type X509
    Signature 0, size 1296, owner 54652821-eb08-4bd2-8108-9cbcebc308f4
        Subject:
            CN=MyPK
        Issuer:
            CN=MyPK
Variable KEK, length 1326
KEK: List 0, type X509
    Signature 0, size 1298, owner 54652821-eb08-4bd2-8108-9cbcebc308f4
        Subject:
            CN=MyKEK
        Issuer:
            CN=MyKEK
Variable db, length 4469
db: List 0, type X509
    Signature 0, size 1298, owner 54652821-eb08-4bd2-8108-9cbcebc308f4
        Subject:
            CN=MySDB
        Issuer:
            CN=MySDB
db: List 1, type X509
    Signature 0, size 1572, owner 26dc4851-195f-4ae1-9a19-fbf883bbb35e
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, \
 CN=Microsoft Corporation UEFI CA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, \
 CN=Microsoft Corporation Third Party Marketplace Root
db: List 2, type X509
    Signature 0, size 1515, owner 26dc4851-195f-4ae1-9a19-fbf883bbb35e
        Subject:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, \
 CN=Microsoft Windows Production PCA 2011
        Issuer:
            C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, \
 CN=Microsoft Root Certificate Authority 2010
Variable dbx has no entries
Variable MokList has no entries

It did boot into Windows though, hence MS keys are accepted in our new PK/KEK/DB chain. Let's check signature:

$ sbverify /esp/EFI/Linux/arch.efi 
warning: data remaining[17840376 vs 17850608]: gaps between PE/COFF sections?
Signature verification failed

Hm, ok, so despite having new platform keys UEFI is refusing to accept our signature. Let's compare with built-in shim:

$ sbverify /esp/EFI/Linux/arch.efi -v
warning: data remaining[17840376 vs 17850608]: gaps between PE/COFF sections?
signature 1
image signature issuers:
 - /CN=MySDB
image signature certificates:
 - subject: /CN=MySDB
   issuer:  /CN=MySDB
PKCS7 verification failed
139878892764160:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify
 error:crypto/pkcs7/pk7_smime.c:284:Verify error:self signed certificate
Signature verification failed
[ruff@trx ruff]$ sbverify /esp/EFI/Boot/bootx64.efi -v
signature 1
image signature issuers:
 - /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows
 Production PCA 2011
image signature certificates:
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
 Windows
   issuer:  /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
   Windows Production PCA 2011
 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
 Windows Production PCA 2011
   issuer:  /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft
   Root Certificate Authority 2010
PKCS7 verification passed
Signature verification OK

Ok, so at least verification utility refuses to accept self-signed SDB while being ok with chained MS Windows signature. Let's create our own cert-chain then and try to cargo-cult the existing shim:

openssl req  -new -newkey rsa:4096 -nodes -keyout Arch.key -sha256 \
 -subj '/CN=My Arch/' -out Arch.csr
openssl x509 -req -in Arch.csr -out Arch.crt -CA UEFI/SDB.crt \
 -CAkey UEFI/SDB.key -CAcreateserial -days 3650
sudo sbsign --key Arch.key --cert Arch.crt --addcert SDB.crt --output \
 /esp/EFI/Linux/arch.efi /esp/EFI/Linux/arch.uefi

systemctl reboot --firmware-setup

And nothing. Going forward - it is a red herring, sbverify utility still fails to verify because it verifies against system trust anchor (root CAs). Either way the Asus EFI BIOS refuses to accept image signed by custom key.

To prove that I've repeated the whole process on second laptop - Dell Inspirion. And there it worked flawlessly at the first attempt. Eg. as mentioned earlier the EFI BIOS refued to accept PK/KEK keys from sbkeysync command. However after copying them to ESP, rebooting into BIOS, setting them manually from files, enabling secure boot and rebooting - my signed EFI image got loaded without any hiccup. I just rebooted again to set admin password and hence seal the secureboot.

$ bootctl --esp-path /esp --no-variables 
systemd-boot not installed in ESP.
System:
     Firmware: UEFI 2.40 (American Megatrends 5.11)
  Secure Boot: enabled
   Setup Mode: user
 TPM2 Support: yes
 Boot into FW: supported

That's a result I expected, and a proof I did everything right the first time. However there are some gaps.

If you followed my thought train via prism of Arch Wiki, you might have not noticed the first gap - namely Unified Kernel Image (arch.uefi). That is - I didn't explain where did I get one, the process how to make one is explained in the referenced archwiki article.

The second gap is Microsoft key. Depending on how thick is a tinfoil on your hat you may want to skip this as it enables booting any image signed by Microsoft keys. While I do not trust microsoft products in terms of privacy, I still consider them having fair security in mind, merely due to the fact tons of huge businesses are relying on their security, and if that becomes (knowingly) compromised the backlash would destroy the company. But this is just for my peace of mind, I'm not at all trying to convince anyone. The choice is yours. And once I receive my gaming console I'll probably ditch it.

And as a spoiler for next part - do make sure you have your original boot method preserved (eg. unsecured efi grub). SecureBoot we just made is not yet ready for deployment, mere MVP.

Mon Nov 29 23:17:42 2021
 
 
© ruff 2011