This is a small collection of tools for allowing "remote attestation" between a computer and a phone via TOTP.
NOTE: This version works with Heads using mbedtls inplace of OpenSSL and needs cleanup before it can be merged back into the mjg59/tpmtotp branch. Additionally, the NVRAM support does not work.
Generates a TOTP token, seals it against the TPM using the state of PCRs 0-5 and 7, saves it to the file given as the first argument and prints an ANSI QR code
Takes the file given as the first argument, unseals it with the TPM and prints a TOTP value.
sealtotp and unsealtotp both use libtpm to talk to the TPM directly, and so will not run if a TPM service daemon such as Trousers is running. In addition, they need access to /dev/tpm0 and so will normally need to be run as root. To use, do the following:
./sealtotp totpblob
and enrol the QR code in an app like Google Authenticator. Copy totpblob and unsealtotp (and its dependencies) into your initrd and run
./unsealtotp totpblob
or
./plymouth-unsealtotp totpblob
in your boot process before requesting the disk decryption passphrase. Verify that the value matches the value on your phone before typing any passphrase. The plymouth variant talks to the Plymouth boot splash service and will display your token in the top left of the screen, updating every 30 seconds.
If you pass the -n argument to sealtotp, the sealed secret will be stored in nvram on the TPM. plymouth-unsealtotp and unsealtotp will automatically attempt to obtain a secret from there before falling back to attempting to read from files.
If you use /sys/firmware/efi/efivars/ as a prefix to the filename, tpmtotp will handle inserting and removing appropriate attributes and so permit the storage of the encrypted secret as a UEFI variable.
If you pass multiple filenames to the unseal commands, they will attempt to open each in turn and use the first that can be successfully opened. This allows you to attempt to open a UEFI variable and then fall back to an on-disk location.
You can choose which PCR values the secret is sealed to using the -p argument to sealtotp. A comma separated list of PCRs will default to using the current value of the PCR. If you are sealing a secret to calculated variables, you may pass them like so:
sealtotp -p 0=62 64 98 1C B8 2B D1 2F 45 C3 C2 06 18 6B C7 6E 23 EB 21 88, 1=3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75, 2=3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75
sealtotp requires libqrencode. unsealtotp requires liboath.
The TPM policy measurement does not currently include the initrd or kernel that you are booting, since grub lacks support for performing the initial measurement of these objects. This results in the following vulnerability:
-
Shim will be measured into PCR[4]
-
Shim will verify that the next stage loader is signed with a trusted key
-
The next stage loader will verify that the kernel is signed with a trusted key
-
The initrd will be loaded without any verification, and so will be able to unseal the TOTP value while still providing a malicious codebase
Avoiding this requires either signature validation of the initrd (problematic, as these are typically generated on the local system) or for the second stage loader (typically grub) to gain support for measuring its payloads into the TPM.
An attacker who is able to perform DMA-based attacks may be able to boot the system, attach a DMA-capable device and extract the valid TOTP secret from RAM. This will allow them to spoof legitimate boots later on. This can be avoided by ensuring that your kernel and hardware support and enable an IOMMU, or by adding support to the kernel to allow enumeration of DMA-capable external devices to be deferred until later.
Sufficiently malicious firmware may still be able to extract the TOTP secret from system RAM and exfiltrate it such that an attacker can later spoof legitimate boots on a compromised system. Of course, any such sufficiently malicious firmware is also capable of modifying your OS at runtime, so you've already lost.
Add commandline-switch to sealtotp for choosing trousers (tspi) vs direkt access (libtpm)
Add support for migration of sealed data to new PCR values in order to support bootloader updates.
Add TPM support to grub.
Get distributions to turn on iommus.
Modify the kernel to allow delayed enumeration of DMA-capable external devices.