Last active
October 24, 2017 06:01
-
-
Save KazWolfe/2dca09c170b696609b47ab2c33c599e5 to your computer and use it in GitHub Desktop.
GPG Dynamic Smartcard Script
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
#!/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