Skip to content

Instantly share code, notes, and snippets.

@KazWolfe
Last active October 24, 2017 06:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KazWolfe/2dca09c170b696609b47ab2c33c599e5 to your computer and use it in GitHub Desktop.
Save KazWolfe/2dca09c170b696609b47ab2c33c599e5 to your computer and use it in GitHub Desktop.
GPG Dynamic Smartcard Script
#!/bin/bash
# This script exists to solve a problem with GPG2's current smartcard system. Turns out there can only be
# one smart card (yubikey) attached to any public key at a time. This makes using your backup/spare keys
# very difficult. However, we can just automate the entire key registration procedure!
#
# Author: Kaz Wolfe (https://kazwolfe.io), 2017-10-23
# License: MIT
# INSTALL INSTRUCTIONS
# 1. Place this script in ~/.gnupg/sc-reset.sh
# 2. Edit the target key as described below.
# 3. Mark this file as executable.
# 4. Open /etc/udev/rules.d/z99-yubikey-smartcard.rules and paste in:
#
# ACTION=="add", SUBSYSTEM=="usb", \
# ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0010|0110|0111|0114|0116|0401|0403|0405|0407|0410", \
# ENV{DEVTYPE}=="usb_device" \
# RUN+="/usr/bin/sudo -iu your_username /home/your_username/.gnupg/sc-reset.sh"
#
# 5. Change your_username to, well, your username.
# 6. Run sudo udevadm control --reload-daemon
# Note that this script is NOT tested beyond a YubiKey 4 in a very controlled setting.
# This script may cause a lot of issues if something is unexpected. I might make it
# more resilient one day. I don't know.
# CHANGE ME! Use the output of `gpg2 --list-secret-keys --keyid-format LONG` and find the key you
# want to target
TARGET_KEY="rsa4096/1234567890ABCDEF"
export DISPLAY=:0.0
export XAUTHORITY=$HOME/.Xauthority
LOG="/tmp/udev-yubikey.log"
exec >> $LOG 2>&1
echo -e "\n-----$(date) -----\n[INFO] A new smartcard was deteted."
# Initialize GPG and grab some necessary state information.
CURRENT_GPG_STATE=$(gpg2 --no-tty --list-secret-keys --keyid-format=LONG --with-keygrip ${TARGET_KEY: -16})
if [[ $? != 0 ]]; then
echo "[ERROR] GPG failed to get required information on the key. Is the target key valid, and is GPG running?"
echo "Command output:"
echo "$CURRENT_GPG_STATE"
echo "--------- CUT ---------"
notify-send -u critical \
-i /usr/share/icons/hicolor/48x48/apps/gcr-gnupg.png \
"GPG Query Error" \
"GPG has failed to get necessary data for smartcard registration.\nPlease check $LOG for more information."
exit 1
fi
# Initialize smartcard, and grab its data.
GPG_SC_DATA=$(gpg2 --no-tty --card-status --keyid-format LONG)
if [[ $? != 0 ]]; then
echo "[ERROR] Smartcard was inserted, but not detected."
echo "Command output:"
echo "$GPG_SC_DATA"
echo "--------- CUT ---------"
notify-send -u critical \
-i /usr/share/icons/hicolor/48x48/apps/gcr-smart-card.png \
"PGP Smartcard Read Error" \
"There was an error reading the smart card. Please re-plug the smartcard to attempt import again.\nCheck $LOG for more information."
exit 2
fi
GPG_SC_DEVICE_ID=$(echo "$GPG_SC_DATA" | grep -i "application" | cut -d ':' -f 2 | sed -e 's/^[[:space:]]*//')
GPG_SERIAL_NUMBER=$(echo ${GPG_SC_DEVICE_ID:16:4} ${GPG_SC_DEVICE_ID:20:8})
# Check that the device actually has the target key we're looking for before touching anything.
if [[ "$GPG_SC_DATA" != *"$TARGET_KEY"* ]]; then
echo "[INFO] The smartcard did not have the target key. Ignoring."
exit 0
fi
# Check that the device is not currently bound. We shouldn't update when there's nothing to update.
if [[ $(echo "$CURRENT_GPG_STATE" | grep "$TARGET_KEY" -A 2) == *"$GPG_SERIAL_NUMBER"* ]]; then
echo "[INFO] GPG is already using smartcard SN $GPG_SERIAL_NUMBER. Ignoring."
exit 0
fi
# Delete all (known) key handles
echo "$CURRENT_GPG_STATE" | grep "Keygrip" | sed 's/[^0-9A-F]//g' | while read line; do
rm $HOME/.gnupg/private-keys-v1.d/$line.key
done
# Import the GPG private key stubs.
IMPORT_RESULT=$(gpg2 --card-status --no-tty)
if [[ $? != 0 ]]; then
echo "[ERROR] Could not import PGP Keystub! GPG keys are not available for this session."
echo "Command output:"
echo "$IMPORT_RESULT"
echo "--------- CUT ---------"
notify-send -u critical \
"PGP Smartcard Import Error" \
"There was an error importing the smartcard's data. PGP operations not work until the card is re-inserted.\nCheck $LOG for more information." \
-i /usr/share/icons/hicolor/48x48/apps/gcr-smart-card.png
exit 2
fi
# Tell the user an update just happened.
echo "[INFO] Key ${TARGET_KEY: -8:-4} ${TARGET_KEY: -4} was updated to use card SN "$GPG_SERIAL_NUMBER"."
notify-send -u normal \
"PGP Smartcard Updated" \
"The smartcard with ID $GPG_SERIAL_NUMBER has been assigned to key ${TARGET_KEY: -8:-4} ${TARGET_KEY: -4}." \
-i /usr/share/icons/hicolor/48x48/apps/gcr-smart-card.png
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment