summaryrefslogtreecommitdiffstats
path: root/modules.d/91crypt-gpg
diff options
context:
space:
mode:
Diffstat (limited to 'modules.d/91crypt-gpg')
-rw-r--r--modules.d/91crypt-gpg/README50
-rwxr-xr-xmodules.d/91crypt-gpg/crypt-gpg-lib.sh68
-rwxr-xr-xmodules.d/91crypt-gpg/module-setup.sh73
3 files changed, 191 insertions, 0 deletions
diff --git a/modules.d/91crypt-gpg/README b/modules.d/91crypt-gpg/README
new file mode 100644
index 0000000..be6df55
--- /dev/null
+++ b/modules.d/91crypt-gpg/README
@@ -0,0 +1,50 @@
+# Directions for changing a system from password-based gpg keyfile
+# to smartcard-based gpg keyfile
+
+# Be sure that you meet the following requirements:
+# 1. GnuPG >= 2.1 installed with
+# * Smartcard support enabled (scdaemon must be built)
+# * Direct CCID access built into scdaemon
+# 2. A password-based gpg keyfile ${KEYFILE} (e.g. "keyfile.gpg"):
+# That is, a file containing the slot key for LUKS, which
+# has been encrypted symmetrically with GnuPG using
+# a password.
+# 3. Your public OpenPGP identity ${RECIPIENT} (e.g. "3A696356")
+# 4. An OpenPGP smartcard holding the decryption key associated
+# with your public identity
+# 5. A CCID smartcard reader
+
+# Notes: Requirement 4. and 5. can of course be one device, e.g.
+# a USB token with an integrated OpenPGP smartcard
+
+# Make a backup of your keyfile (assuming it lies on the boot partition)
+$ cp /boot/${KEYFILE} /safe/place/keyfile.bak.gpg
+
+# Change your keyfile from purely password-based to both
+# password-based and key-based (you can then decrypt the keyfile
+# with either method). As an example aes256 is chosen, the cipher
+# is not important to this guide, but do note that your kernel
+# must support it at boot time (be it built into the kernel image
+# or loaded as a module from the initramfs).
+$ cat /safe/place/keyfile.bak.gpg | gpg -d | gpg --encrypt --recipient ${RECIPIENT} --cipher-algo aes256 --armor -c > /safe/place/keyfile_sc.gpg
+
+# Verify that you can decrypt your new keyfile both with the password
+# and your smartcard.
+# (with smartcard inserted, you should be prompted for your PIN, unless
+# you already did so and have not yet timed out)
+$ gpg -d /safe/place/keyfile_sc.gpg
+# (with smartcard disconnected, you should be prompted for your password)
+$ gpg -d /safe/place/keyfile_sc.gpg
+
+# After verification, replace your old keyfile with your new one
+$ su -c 'cp /safe/place/keyfile_sc.gpg /boot/${KEYFILE}'
+
+# Export your public key to where crypt-gpg can find it
+$ gpg --armor --export-options export-minimal --export ${RECIPIENT} > /safe/place/crypt-public-key.gpg
+$ su -c 'cp /safe/place/crypt-public-key.gpg /etc/dracut.conf.d/crypt-public-key.gpg'
+
+# Rebuild your initramfs as usual
+# When booting with any of the requirements not met, crypt-gpg will default to password-based keyfile unlocking.
+# If all requirements are met and smartcard support is not disabled by setting the kernel option "rd.luks.smartcard=0"
+# crypt-gpg will try find and use a connected OpenPGP smartcard by prompting you for the PIN and then
+# unlocking the gpg keyfile with the smartcard.
diff --git a/modules.d/91crypt-gpg/crypt-gpg-lib.sh b/modules.d/91crypt-gpg/crypt-gpg-lib.sh
new file mode 100755
index 0000000..538419f
--- /dev/null
+++ b/modules.d/91crypt-gpg/crypt-gpg-lib.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+command -v ask_for_password > /dev/null || . /lib/dracut-crypt-lib.sh
+
+# gpg_decrypt mnt_point keypath keydev device
+#
+# Decrypts symmetrically encrypted (password or OpenPGP smartcard) key to standard output.
+#
+# mnt_point - mount point where <keydev> is already mounted
+# keypath - GPG encrypted key path relative to <mnt_point>
+# keydev - device on which key resides; only to display in prompt
+# device - device to be opened by cryptsetup; only to display in prompt
+gpg_decrypt() {
+ local mntp="$1"
+ local keypath="$2"
+ local keydev="$3"
+ local device="$4"
+
+ local gpghome=/tmp/gnupg
+ local opts="--homedir $gpghome --no-mdc-warning --skip-verify --quiet"
+ opts="$opts --logger-file /dev/null --batch --no-tty --passphrase-fd 0"
+
+ mkdir -m 0700 -p "$gpghome"
+
+ # Setup GnuPG home and gpg-agent for usage of OpenPGP smartcard.
+ # This requires GnuPG >= 2.1, as it uses the new ,,pinentry-mode´´
+ # feature, which - when set to ,,loopback´´ - allows us to pipe
+ # the smartcard's pin to GnuPG (instead of using a normal pinentry
+ # program needed with GnuPG < 2.1), making for uncomplicated
+ # integration with the existing codebase.
+ local useSmartcard="0"
+ local gpgMajorVersion
+ local gpgMinorVersion
+ local cmd
+ gpgMajorVersion="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* ([0-9]*).*|\1|p')"
+ gpgMinorVersion="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* [0-9]*\.([0-9]*).*|\1|p')"
+
+ if [ "${gpgMajorVersion}" -ge 2 ] && [ "${gpgMinorVersion}" -ge 1 ] \
+ && [ -f /root/crypt-public-key.gpg ] && getargbool 1 rd.luks.smartcard; then
+ useSmartcard="1"
+ echo "allow-loopback-pinentry" >> "$gpghome/gpg-agent.conf"
+ GNUPGHOME="$gpghome" gpg-agent --quiet --daemon
+ GNUPGHOME="$gpghome" gpg --quiet --no-tty --import < /root/crypt-public-key.gpg
+ local smartcardSerialNumber
+ smartcardSerialNumber="$(GNUPGHOME=$gpghome gpg --no-tty --card-status \
+ | sed -n -r -e 's|Serial number.*: ([0-9]*)|\1|p' | tr -d '\n')"
+ if [ -n "${smartcardSerialNumber}" ]; then
+ inputPrompt="PIN (OpenPGP card ${smartcardSerialNumber})"
+ fi
+ GNUPGHOME="$gpghome" gpg-connect-agent 1> /dev/null learn /bye
+ opts="$opts --pinentry-mode=loopback"
+ cmd="GNUPGHOME=$gpghome gpg --card-status --no-tty > /dev/null 2>&1; gpg $opts --decrypt $mntp/$keypath"
+ else
+ cmd="gpg $opts --decrypt $mntp/$keypath"
+ fi
+
+ ask_for_password \
+ --cmd "$cmd" \
+ --prompt "${inputPrompt:-Password ($keypath on $keydev for $device)}" \
+ --tries 3 --tty-echo-off
+
+ # Clean up the smartcard gpg-agent
+ if [ "${useSmartcard}" = "1" ]; then
+ GNUPGHOME="$gpghome" gpg-connect-agent 1> /dev/null killagent /bye
+ fi
+
+ rm -rf -- "$gpghome"
+}
diff --git a/modules.d/91crypt-gpg/module-setup.sh b/modules.d/91crypt-gpg/module-setup.sh
new file mode 100755
index 0000000..501869a
--- /dev/null
+++ b/modules.d/91crypt-gpg/module-setup.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+# GPG support is optional
+# called by dracut
+check() {
+ require_binaries gpg tr || return 1
+
+ if sc_requested; then
+ if ! sc_supported; then
+ dwarning "crypt-gpg: GnuPG >= 2.1 with scdaemon and libusb required for ccid smartcard support"
+ return 1
+ fi
+ return 0
+ fi
+
+ return 255
+}
+
+# called by dracut
+depends() {
+ echo crypt
+}
+
+# called by dracut
+install() {
+ inst_multiple gpg tr
+ inst "$moddir/crypt-gpg-lib.sh" "/lib/dracut-crypt-gpg-lib.sh"
+
+ if sc_requested; then
+ inst_multiple gpg-agent
+ inst_multiple gpg-connect-agent
+ inst_multiple -o /usr/libexec/scdaemon /usr/lib/gnupg/scdaemon
+ cp "$dracutsysrootdir$(sc_public_key)" "${initdir}/root/"
+ fi
+}
+
+sc_public_key() {
+ echo -n "/etc/dracut.conf.d/crypt-public-key.gpg"
+}
+
+# CCID Smartcard support requires GnuPG >= 2.1 with scdaemon and libusb
+sc_supported() {
+ local gpgMajor
+ local gpgMinor
+ local scdaemon
+ gpgMajor="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* ([0-9]*).*|\1|p')"
+ gpgMinor="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* [0-9]*\.([0-9]*).*|\1|p')"
+
+ if [[ -x "$dracutsysrootdir"/usr/libexec/scdaemon ]]; then
+ scdaemon=/usr/libexec/scdaemon
+ elif [[ -x "$dracutsysrootdir"/usr/lib/gnupg/scdaemon ]]; then
+ scdaemon=/usr/lib/gnupg/scdaemon
+ else
+ return 1
+ fi
+
+ if [[ ${gpgMajor} -gt 2 || ${gpgMajor} -eq 2 && ${gpgMinor} -ge 1 ]] \
+ && require_binaries gpg-agent \
+ && require_binaries gpg-connect-agent \
+ && ($DRACUT_LDD "${dracutsysrootdir}${scdaemon}" | grep libusb > /dev/null); then
+ return 0
+ else
+ return 1
+ fi
+}
+
+sc_requested() {
+ if [ -f "$dracutsysrootdir$(sc_public_key)" ]; then
+ return 0
+ else
+ return 1
+ fi
+}