-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Dan Fuhry
committed
Jun 27, 2016
0 parents
commit 8634128
Showing
6 changed files
with
262 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.pkg.tar.gz | ||
*.pkg.tar.xz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
pkgname=initramfs-scencrypt | ||
pkgdesc="initramfs hook that adds PGP smartcard support for LUKS FDE" | ||
pkgver=1.0 | ||
pkgrel=1 | ||
arch=(any) | ||
depends=(gnupg pcsclite libusb-compat) | ||
source=(scencrypt-hook | ||
scencrypt-install | ||
README.md) | ||
|
||
build() { | ||
return 0 | ||
} | ||
|
||
package() { | ||
mkdir -p "${pkgdir}/usr/lib/initcpio/hooks" | ||
mkdir -p "${pkgdir}/usr/lib/initcpio/install" | ||
|
||
cp "${srcdir}/scencrypt-hook" "${pkgdir}/usr/lib/initcpio/hooks/scencrypt" | ||
cp "${srcdir}/scencrypt-install" "${pkgdir}/usr/lib/initcpio/install/scencrypt" | ||
|
||
mkdir -p "${pkgdir}/usr/share/doc/${pkgname}" | ||
cp -a "${srcdir}/README.md" "${pkgdir}/usr/share/doc/${pkgname}/" | ||
} | ||
|
||
md5sums=('159bfe688d4e2784c9c9882bb33c6fac' | ||
'a497d9a7ae1918db780377c76004afa3' | ||
'6c68b216a5346c9e936a06cd4b839f7f') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# GnuPG hook for Arch Linux initcpio | ||
|
||
This initcpio hook allows you to use a PGP-compatible smart card during early boot to decrypt your full disk encrypted system. | ||
|
||
# Why? | ||
|
||
This is a great solution if you want the best possible encryption strength, but without having to type a huge passphrase in every time you boot. Using a hardware-backed RSA key on a PGP smart card, with the card performing decryption of the FDE key on-chip without releasing the private key to the OS, gives you extremely strong protection from attacks, even sophisticated/well-equipped adversaries. | ||
|
||
As is the case with all LUKS systems, anyone who gets root on your box after it's booted can run `dmsetup table --showkeys` to dump the master key, the changing of which requires a complete wipe of your disk, in contrast to LUKS user key changing which requires only wiping and replacing the key slot. So don't let an adversary get root - watch your setuid programs, sudo rights, running services, etc. carefully. | ||
|
||
PGP smart cards have varying options for PIN limits and reset or self-destruct functionality - choose one that fits your needs. | ||
|
||
This hook has only been tested with the Yubikey NEO. | ||
|
||
## Disclaimer | ||
|
||
Use this hook at your own risk. It's highly recommended to have a backup key somewhere, in case you lose or destroy your primary key. | ||
|
||
# Configuration process | ||
|
||
1. Install Arch onto a LUKS encrypted system and get it booting using the stock `encrypt` hook and passphrase. (Beyond the scope of this document) | ||
1. Configure your smartcard and get it working to the point that you can encrypt and decrypt things on your machine using the card. (Beyond the scope of this document) | ||
1. Generate a new random key and encrypt it: `dd if=/dev/random bs=64 count=1 | gpg --encrypt -r [email protected] > disk.bin.gpg` | ||
1. Decrypt the key into memory so you can add it to your LUKS volume: `gpg --decrypt -o /dev/shm/disk.bin disk.bin.gpg` | ||
1. `sudo cryptsetup luksAddKey /dev/your_luks_device /dev/shm/disk.bin` | ||
1. `shred -u -n1 /dev/shm/disk.bin` to delete the decrypted `disk.bin` file from memory. | ||
1. Edit `/etc/crypttab` to include your encrypted device. The line will look somewhat like: | ||
`arch_crypt /dev/your_luks_device /home/you/disk.bin.gpg discard` | ||
1. Edit `/etc/mkinitcpio.conf` and replace the `encrypt` hook with `scencrypt`. Do not leave both `encrypt` and `scencrypt` enabled. | ||
1. Run `mkinitcpio -p linux`. If there are no errors, reboot with your smart card plugged in to find out if it works. | ||
1. (Optional) `sudo cryptSetup luksRemoveKey /dev/your_luks_device` and type the passphrase you added when you were installing Arch. This will remove the old passphrase so that only your GPG-encrypted key file can unseal the disk. | ||
|
||
# Technical details | ||
|
||
The hook works by copying your encrypted key file to the initramfs, decrypting it in memory, passing it to LUKS to unseal the disk, and then using `shred` to overwrite it in memory. | ||
|
||
Behind the scenes, `gpg` starts `scdaemon`, which talks to `pcscd` and `pinentry-tty` to get your PIN and pass it to the card along with the payload for decryption. The private key itself is held securely on the smartcard - it cannot be released even with the PIN on hand. But the decryption is quick because the payload is small. Once the disk is mounted, the smartcard can safely be removed from the system - the result of the decryption is merely a "user key" that LUKS uses to decrypt the volume's master key. There is an excellent [white paper](http://clemens.endorphin.org/nmihde/nmihde-A4-ds.pdf) written by one of the original LUKS authors detailing LUKS's extensive anti-forensic hardening. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
post_install() { | ||
echo " >> initramfs-scencrypt - READ ME!" | ||
echo " >> Make sure to add your encrypted disks to /etc/crypttab. There is" | ||
echo " >> no support for cryptdevice=... on the kernel command line." | ||
echo "" | ||
echo " >> Key file path must end in .gpg and the private key must be" | ||
echo " >> accessible by root." | ||
echo "" | ||
echo " >> For more information, read /usr/share/doc/initramfs-scencrypt/README.md" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
#!/usr/bin/ash | ||
|
||
run_hook() { | ||
modprobe -a -q dm-crypt >/dev/null 2>&1 | ||
[ "${quiet}" = "y" ] && CSQUIET=">/dev/null" | ||
|
||
sed -re 's;#.*$;;g' -e '/^[ ]*$/ d' -i /etc/crypttab | ||
|
||
lineno=1 | ||
while true; do | ||
# fetch the next line from crypttab | ||
line="$(tail -n+$lineno /etc/crypttab | tail -1)" | ||
[ -z "$line" ] && break | ||
lineno=$(( $lineno + 1 )) | ||
|
||
# parse fields in the crypttab line | ||
read mapped_name device_path key_spec options <<EOF | ||
$line | ||
EOF | ||
|
||
IFS=: read key_file keyarg1 keyarg2 <<EOF | ||
$key_spec | ||
EOF | ||
# handle case of no key file | ||
if [ "$key_file" = "-" -o "$key_file" = "none" ]; then | ||
key_file= | ||
elif [ -c "${key_file}" ]; then | ||
# key file is a character device | ||
length=${keyarg1:-32} | ||
dd if=$key_file of=/keyfile.bin bs=1 count=$length >/dev/null 2>&1 | ||
key_file=/keyfile.bin | ||
elif [ -b "${key_file}" ]; then | ||
echo "ERROR: Key files on block devices are not supported yet." | ||
key_file= | ||
elif [ -r "${key_file}" -a "${key_file%.gpg}" != "${key_file}" ]; then | ||
# if the key file ends in ".gpg" then handle as encrypted with GPG | ||
# pcscd is needed for smartcard communication | ||
/usr/bin/pcscd --auto-exit | ||
|
||
# /.gnupg is where the scdaemon socket lives | ||
test -d /.gnupg || mkdir -p /.gnupg | ||
chmod -R go-rwx /.gnupg /etc/initcpio/gpg | ||
|
||
# test communication with card - this is also needed for decryption | ||
# to work at all | ||
/usr/bin/gpg --homedir "/etc/initcpio/gpg" --card-status >/dev/null 2>&1 | ||
|
||
# now attempt to decrypt | ||
if /usr/bin/gpg --homedir "/etc/initcpio/gpg" --decrypt -o "/keyfile.bin" "${key_file}"; then | ||
# we got it! | ||
key_file="/keyfile.bin" | ||
else | ||
# if decryption fails, still prompt for a passphrase | ||
echo "Failed to decrypt key file with GPG." | ||
echo "Falling back to passphrase." | ||
key_file= | ||
fi | ||
elif [ -r "${key_file}" ]; then | ||
cp "${key_file}" /keyfile.bin | ||
key_file=/keyfile.bin | ||
fi | ||
|
||
## end key retrieval | ||
## start device setup | ||
|
||
# parse options | ||
luksoptions="" | ||
for option in ${options//,/ }; do | ||
case "$option" in | ||
discard) | ||
luksoptions="$luksoptions --allow-discards" | ||
;; | ||
*) | ||
echo "Warning: ignoring unknown crypttab option: $option" | ||
esac | ||
done | ||
|
||
# resolve block device | ||
if resolved=$(resolve_device "${device_path}" "${rootdelay}"); then | ||
if cryptsetup isLuks "${device_path}"; then | ||
# LUKS devices | ||
|
||
# open device | ||
if [ -n "$key_file" ]; then | ||
if ! eval cryptsetup luksOpen --key-file="${key_file}" $luksoptions "${resolved}" "${mapped_name}"; then | ||
echo "WARNING: Failed to luksOpen crypto device ${device_path}" | ||
fi | ||
rm -f "$key_file" | ||
else | ||
if ! eval cryptsetup luksOpen $luksoptions "${resolved}" "${mapped_name}"; then | ||
echo "WARNING: Failed to luksOpen crypto device ${device_path}" | ||
fi | ||
fi | ||
else | ||
# non-LUKS | ||
echo "ERROR: ${device_path} is not a LUKS volume." | ||
echo "Plain dm-crypt is not supported by the scencrypt hook." | ||
fi | ||
else | ||
echo "WARNING: Failed to resolve crypto device ${device_path}" | ||
fi | ||
done | ||
|
||
rm -rf /etc/initcpio/gpg | ||
killall gpg-agent scdaemon pcscd | ||
} | ||
|
||
# vim: set ft=sh ts=4 sw=4 et: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#!/bin/bash | ||
|
||
build() { | ||
local mod | ||
|
||
add_module dm-crypt | ||
if [[ $CRYPTO_MODULES ]]; then | ||
for mod in $CRYPTO_MODULES; do | ||
add_module "$mod" | ||
done | ||
else | ||
add_all_modules '/crypto/' | ||
fi | ||
|
||
if [ -d /etc/initcpio/gpg ]; then | ||
rm -rf /etc/initcpio/gpg | ||
fi | ||
|
||
mkdir -p /etc/initcpio/gpg | ||
chmod 0700 /etc/initcpio/gpg | ||
echo "pinentry-program /usr/bin/pinentry-tty" > /etc/initcpio/gpg/gpg-agent.conf | ||
|
||
add_binary "cryptsetup" | ||
add_binary "dmsetup" | ||
add_binary "gpg" | ||
add_binary "gpg-agent" | ||
add_binary "pinentry-tty" | ||
add_binary "pcscd" | ||
add_binary "/usr/lib/gnupg/scdaemon" | ||
add_file "/etc/initcpio/gpg/gpg-agent.conf" | ||
add_file "/usr/lib/udev/rules.d/10-dm.rules" | ||
add_file "/usr/lib/udev/rules.d/13-dm-disk.rules" | ||
add_file "/usr/lib/udev/rules.d/95-dm-notify.rules" | ||
add_file "/usr/lib/initcpio/udev/11-dm-initramfs.rules" "/usr/lib/udev/rules.d/11-dm-initramfs.rules" | ||
|
||
add_file "/etc/crypttab" | ||
|
||
sed -re 's;#.*$;;g' -e '/^[ \t]*$/ d' /etc/crypttab | awk '{print $3;}' | \ | ||
while read f; do | ||
if [ "${f:0:1}" = "/" -a -f "$f" ]; then | ||
add_file "$f" | ||
keyid=($(file $f | egrep -o '[A-F0-9]{8}' | sed -re 's;([0-9A-F]{2});\1 ;g')) | ||
keyid=${keyid[3]}${keyid[2]}${keyid[1]}${keyid[0]}${keyid[7]}${keyid[6]}${keyid[5]}${keyid[4]} | ||
gpg --homedir /root/.gnupg --export-secret-keys 0x${keyid} | gpg --homedir /etc/initcpio/gpg --import | ||
fi | ||
done | ||
|
||
add_file "/etc/initcpio/gpg/pubring.kbx" | ||
#for f in /root/.gnupg/private-keys-v1.d/*.key; do | ||
# add_file "$f" "/etc/initcpio/gpg/${f#/root/.gnupg/}" | ||
#done | ||
|
||
add_runscript | ||
} | ||
|
||
help() { | ||
cat <<HELPEOF | ||
This hook allows for an encrypted root device. Users should specify the device | ||
to be unlocked using 'cryptdevice=device:dmname' on the kernel command line, | ||
where 'device' is the path to the raw device, and 'dmname' is the name given to | ||
the device after unlocking, and will be available as /dev/mapper/dmname. | ||
For unlocking via keyfile, 'cryptkey=device:fstype:path' should be specified on | ||
the kernel cmdline, where 'device' represents the raw block device where the key | ||
exists, 'fstype' is the filesystem type of 'device' (or auto), and 'path' is | ||
the absolute path of the keyfile within the device. | ||
Without specifying a keyfile, you will be prompted for the password at runtime. | ||
This means you must have a keyboard available to input it, and you may need | ||
the keymap hook as well to ensure that the keyboard is using the layout you | ||
expect. | ||
HELPEOF | ||
} | ||
|
||
# vim: set ft=sh ts=4 sw=4 et: |