diff options
author | Corey Hinshaw | 2018-10-03 22:33:32 -0400 |
---|---|---|
committer | Corey Hinshaw | 2018-10-03 22:33:32 -0400 |
commit | 34646d61c9a0ea7335429f48bc42f18db0025849 (patch) | |
tree | 727067ef2ccf3d7e0a888e33074e302f3b1cae32 | |
download | aur-34646d61c9a0ea7335429f48bc42f18db0025849.tar.gz |
Initial commit
-rw-r--r-- | .SRCINFO | 18 | ||||
-rw-r--r-- | .editorconfig | 13 | ||||
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | PKGBUILD | 22 | ||||
-rw-r--r-- | README.md | 64 | ||||
-rw-r--r-- | hook_tpm2 | 145 | ||||
-rw-r--r-- | install_tpm2 | 47 |
7 files changed, 313 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 000000000000..8da3d73e1558 --- /dev/null +++ b/.SRCINFO @@ -0,0 +1,18 @@ +pkgbase = mkinitcpio-tpm2-encrypt + pkgdesc = mkinitcpio hook that decrypts a TPM2-sealed LUKS keyfile + pkgver = 1.0 + pkgrel = 1 + url = https://aur.archlinux.org/packages/mkinitcpio-tpm2-encrypt/ + arch = any + license = GPL3 + depends = mkinitcpio + depends = tpm2-tools + source = install_tpm2 + source = hook_tpm2 + source = README.md + sha256sums = 43139f076c03ca147d8bf368b98bdd6b237ab927e53194feb944f58e38914901 + sha256sums = f1cd4ec3197ec4843265e0d61518f641a0dbd650c42cf0399cace0076163fc7c + sha256sums = 11585844eb33ce997e55087dbec0610693bd79e3b1ffb02c9818214ce401a9fb + +pkgname = mkinitcpio-tpm2-encrypt + diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..26ae5ad881ee --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +indent_size = 2 +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..19681f384f40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.tar.gz +*.tar.xz +/pkg +/src diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000000..d535ab895fd4 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,22 @@ +# Maintainer: Corey Hinshaw <coreyhinshaw(at)gmail(dot)com> + +pkgname=mkinitcpio-tpm2-encrypt +pkgver=1.0 +pkgrel=1 +pkgdesc="mkinitcpio hook that decrypts a TPM2-sealed LUKS keyfile" +url="https://aur.archlinux.org/packages/mkinitcpio-tpm2-encrypt/" +arch=(any) +license=('GPL3') +depends=('mkinitcpio' 'tpm2-tools') +source=('install_tpm2' + 'hook_tpm2' + 'README.md') +sha256sums=('43139f076c03ca147d8bf368b98bdd6b237ab927e53194feb944f58e38914901' + 'f1cd4ec3197ec4843265e0d61518f641a0dbd650c42cf0399cace0076163fc7c' + '11585844eb33ce997e55087dbec0610693bd79e3b1ffb02c9818214ce401a9fb') + +package() { + install -Dm644 install_tpm2 "${pkgdir}/usr/lib/initcpio/install/tpm2" + install -Dm644 hook_tpm2 "${pkgdir}/usr/lib/initcpio/hooks/tpm2" + install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README" +} diff --git a/README.md b/README.md new file mode 100644 index 000000000000..5ad0e35d54e4 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +mkinitcpio TPM2 hook +==================== + +This mkinitcpio hook allows for an encrypted root device to use a key sealed by +a TPM 2.0. It should be placed immediately before the `encrypt` hook in +`/etc/mkinitcpio.conf`. + + HOOKS="base udev ... block tpm2 encrypt filesystems + +The `tpm2` hook attempts to "unseal" a LUKS keyfile previously sealed by the +TPM. The sealed files must reside on an unencrypted filesystem available to the +kernel at boot or may be stored in TPM non-volatile memory (NVRAM). For example, +assuming your unencrypted keyfile is at `/root/mykey` and a primary TPM key has +been persisted to `0x81000001`: + + # tpm2_createpolicy -P -L sha1:0,2,4,7 -f pcr.pol -T device:/dev/tpmrm0 + # tpm2_create -H 0x81000001 -g sha256 -G keyedhash -A 0x492 -I /root/mykey \ + -L pcr.pol -r /boot/mykey.priv -u /boot/mykey.pub -T device:/dev/tpmrm0 + +After generating a TPM-sealed key, both `tpmkey` and `tpmpcr` should be specified +on the kernel command line. + +The `tpmkey` parameter has several formats: + + tpmkey=[device]:[path]:[handle] + tpmkey=[device]:[publicpath]:[privatepath]:[handle] + tpmkey=nvram:[index] + tpmkey=nvram:[index]:[offset]:[size] + +Where `[device]` represents the raw block device on which the key exists, +`[path]` is the absolute base path of the sealed files within the device, and +`[handle]` is the TPM handle of the key's parent object. If only `[path]` is +specified, '.pub' and '.priv' will be appended to the path to locate the public +and private files, respectively. The absolute `[publicpath]` and `[privatepath]` +can be specified separately if needed. For example, if `/dev/sda1` is an EFI +partition mounted at `/boot`: + + tpmkey=/dev/sda1:/mykey:0x81000001 + +Setting `[device]` to 'nvram' indicates that the key is stored in TPM NVRAM. In +this case `[index]` is the NVRAM area index, `[offset]` is the offset of the key +in bytes and `[size]` is the size of the key in bytes. + +The `tpmpcr` parameter should hold the TPM2 PCR bank specification that will +unlock the sealed key. Multiple specs can be separated by a '|' and key +decryption will be attempted with each set of banks. + +You may also need to add the `vfat` file system driver to the `MODULES` array: + + MODULES=(vfat) + +Finally, rebuild the initramfs: + + # mkinitcpio -p linux + +During boot, the hook will initialize the TPM and attempt to unseal the key. If +the key is successfully unsealed, it will be passed to the `encrypt` hook to +perform the actual decryption of the root file system. + +Depending on the PCR banks to which the sealed key is bound, system changes such +as kernel updates or firmware adjustments may prevent the key from being +unsealed. If this happens, the disk must be manually unlocked with a passphrase +and a new sealed key file needs to be generated. For this reason, it is CRUCIAL +to add a separate "recovery" passphrase to the LUKS keys. diff --git a/hook_tpm2 b/hook_tpm2 new file mode 100644 index 000000000000..a4c6b8f1d3a1 --- /dev/null +++ b/hook_tpm2 @@ -0,0 +1,145 @@ +#!/usr/bin/ash + +run_hook() { + local ckeyfile tpmkeypub tpmkeypriv tpmkeyparent tpmkeyindex tpmkeyoffset tpmkeysize + local tkdev tkarg1 tkarg2 tkarg3 resolved + local tpmload pcrbank unseal unsealout tpmok + + # This file will be loaded by the encrypt hook + ckeyfile="/crypto_keyfile.bin" + + # Rootfs location for sealed key files + tpmkeypub="/tpm_keyfile.pub" + tpmkeypriv="/tpm_keyfile.priv" + + # Default TPM device + [ -z $tpmdev ] && tpmdev="/dev/tpmrm0" + + # Parse tpmkey command line argument + if [ -n "$tpmkey" ]; then + IFS=: read tkdev tkarg1 tkarg2 tkarg3 <<EOF +$tpmkey +EOF + unset IFS + + case "$tkdev" in + rootfs) + # Key is in initcpio root filesystem. Use files in place + if [ -z "$tkarg3" ]; then + tpmkeypub="${tkarg1}.pub" + tpmkeypriv="${tkarg1}.priv" + tpmkeyparent="$tkarg2" + else + tpmkeypub="$tkarg1" + tpmkeypriv="$tkarg2" + tpmkeyparent="$tkarg3" + fi + ;; + nvram) + # Key is in NVRAM. Populate NVRAM variables + tpmkeyindex="$tkarg1" + tpmkeyoffset="$tkarg2" + tpmkeysize="$tkarg3" + ;; + *) + # Key is on block device + # Locate, mount, and copy the key files + if resolved=$(resolve_device "${tkdev}" ${rootdelay}); then + mkdir /tpmkey + mount -r -t auto "$resolved" /tpmkey + + if [ -z "$tkarg3" ]; then + dd if="/tpmkey/${tkarg1}.pub" of="$tpmkeypub" >/dev/null 2>&1 + dd if="/tpmkey/${tkarg1}.priv" of="$tpmkeypriv" >/dev/null 2>&1 + tpmkeyparent="$tkarg2" + else + dd if="/tpmkey/${tkarg1}" of="$tpmkeypub" >/dev/null 2>&1 + dd if="/tpmkey/${tkarg2}" of="$tpmkeypriv" >/dev/null 2>&1 + tpmkeyparent="$tkarg3" + fi + + umount /tpmkey + rmdir /tpmkey + fi + ;; + esac + + # If there is no NVRAM index and no sealed files, print an error + if [ -z "$tpmkeyindex" ] && [ ! -f "$tpmkeypub" -o ! -f "$tpmkeypriv" ]; then + err "TPM keyfiles could not be opened" + fi + fi + + # We must have a PCR list to retrieve a key + [ -n "$tpmkey" ] && [ -z "$tpmpcr" ] && err "TPM PCR bank not specified" + + + # If we have a key and PCR list, decrypt it + if [ -n "$tpmpcr" -a -n "$tpmkeyindex" ] || [ -n "$tpmpcr" -a -f "$tpmkeypub" -a -f "$tpmkeypriv" ]; then + # Load key object if stored on disk + tpmload=0 + if [ -z "$tpmkeyindex" ]; then + tpm2_load -Q -H "$tpmkeyparent" -r "$tpmkeypriv" -u "$tpmkeypub" -C /tpmobject.ctx -T "device:${tpmdev}" 2>&1 >/dev/null + tpmload=$? + fi + + # Format nvram arguments + [ -n "$tpmkeyoffset" ] && tpmkeyoffset="-o ${tpmkeyoffset}" + [ -n "$tpmkeysize" ] && tpmkeysize="-s ${tpmkeysize}" + + # Attempt to decrypt key with each PCR bank specified + unseal=1 + if [ $tpmload -eq 0 ]; then + IFS="|" + for pcrbank in $tpmpcr; do + if [ -n "$tpmkeyindex" ]; then + unsealout=$(tpm2_nvread -Q -x "$tpmkeyindex" -a "$tpmkeyindex" $tpmkeyoffset $tpmkeysize -L "$pcrbank" -f $ckeyfile -T "device:${tpmdev}" 2>&1) + unseal=$? + else + unsealout=$(tpm2_unseal -Q -c /tpmobject.ctx -L "$pcrbank" -o "$ckeyfile" -T "device:${tpmdev}" 2>&1) + unseal=$? + fi + if [ $unseal -eq 0 ]; then break; fi + done + unset IFS + fi + + # Check decryption resuts and report + tpmok=0 + if [ $unseal -eq 0 ]; then + tpmok=1 + elif echo "$unsealout" | grep -sqiE 'Could not load tcti'; then + err "TPM communication error" + elif echo "$unsealout" | grep -sqiE 'Error.*0x99d'; then + echo + echo "!!! TPM WARNING: PCR VALUES HAVE CHANGED !!!" + echo "This is an indication that the boot configuration has been altered since" + echo "the TPM key was generated. This is normal after kernel updates or firmware" + echo "changes, however this could also indicate a malicious change to your system." + echo + elif [ -n "$tpmkeyindex" ]; then + err "Could not read key from TPM NVRAM" + elif [ $tpmload -ne 0 ]; then + err "Could not load TPM keyfile" + else + err "Could not unseal TPM keyfile" + fi + + if [ $tpmok -gt 0 ]; then + msg ":: LUKS key successfully decrypted by TPM" + else + rm -f "$ckeyfile" + msg ":: TPM Could not decrypt LUKS key" + fi + fi + + # Cleanup + rm -f /tpmobject.ctx "$tpmkeypub" "$tpmkeypriv" +} + +run_cleanuphook() { + # Remove key if still present + rm -f "$ckeyfile" +} + +# vim: set ft=sh ts=4 sw=4 et: diff --git a/install_tpm2 b/install_tpm2 new file mode 100644 index 000000000000..5a153515d252 --- /dev/null +++ b/install_tpm2 @@ -0,0 +1,47 @@ +#!/bin/bash + +build() { + add_module "tpm_tis" + add_module "tpm_crb" + + add_binary "/usr/bin/tpm2_unseal" "/usr/bin/tpm2_unseal" + add_binary "/usr/bin/tpm2_load" "/usr/bin/tpm2_load" + add_binary "/usr/bin/tpm2_nvread" "/usr/bin/tpm2_nvread" + add_binary "/usr/lib/libtss2-tcti-device.so.0" + + add_runscript +} + + +help() { + cat <<HELPEOF +This hook allows for an encrypted root device to use a key sealed by a +TPM 2.0. It should be placed immediately before the 'encrypt' hook. After +generating a TPM-sealed key, both 'tpmkey' and 'tpmpcr' should be +specified on the kernel command line. + +'tpmkey' has several formats: + + tpmkey=[device]:[path]:[handle] + tpmkey=[device]:[publicpath]:[privatepath]:[handle] + tpmkey=nvram:[index] + tpmkey=nvram:[index]:[offset]:[size] + +Where [device] represents the raw block device on which the key exists, +[path] is the absolute base path of the keyfiles within the device, and +[handle] is the TPM handle of the key's parent object. If only [path] is +specified, '.pub' and '.priv' will be appended to the path to locate the +public and private files, respectively. The absolute [publicpath] and +[privatepath] can be specified separately if needed. + +Setting [device] to 'nvram' indicates that the key is stored in TPM NVRAM. +In this case [index] is the NVRAM area index, [offset] is the offset of +the key in bytes and [size] is the size of the key in bytes. + +'tpmpcr' should hold the TPM2 PCR bank specification that will unlock the +sealed key. Multiple specs can be separated by a '|' and key decryption +will be attempted with each set of banks. +HELPEOF +} + +# vim: set ft=sh ts=4 sw=4 et: |