Skip to content

Instantly share code, notes, and snippets.

@mafredri
Last active December 17, 2018 10:14
Show Gist options
  • Save mafredri/96af39be4fb2977081dfce7989a7c2c7 to your computer and use it in GitHub Desktop.
Save mafredri/96af39be4fb2977081dfce7989a7c2c7 to your computer and use it in GitHub Desktop.
gpg-yubikey-sign: Sign Git commits with any of your valid GPG subkeys
#!/usr/bin/env zsh
# gpg-yubikey-sign
#
# This script lets you use any of your unexpired subkeys connected to
# your public key (0xDEADBEEFCAFEBAAD) to sign git commits. It will also
# display "Waiting for YubiKey touch..." to remind you that an action is
# required.
#
# Add this script to your PATH and configure git to use it for gpg
# signing.
#
# git config --global user.signingkey 0xDEADBEEFCAFEBAAD
# git config --global gpg.program gpg-yubikey-sign
emulate -R zsh
setopt err_exit
typeset -a ARGS=("$@")
# Only use this script for signing.
if [[ $* != *" -bsau"* ]]; then
exec gpg $ARGS
fi
KEYID=$ARGS[-1]
EPOCH=$(date +%s)
# This creates a string of all your available subkeys
# and their expiry dates.
typeset -a SUBKEYS
typeset -A SUBKEYS_WITH_EXPIRY
SUBKEYS_WITH_EXPIRY=($(
gpg --with-colons --list-keys $KEYID \
| grep -E "^sub:u:4096:1:[^:]+:[^:]+:[^:]+:"::::s::::::23: \
| cut -d: -f5,7 \
| tr ':' ' '
))
for key expiry in ${(kv)SUBKEYS_WITH_EXPIRY}; do
# Only include subkeys that have not expired.
if (( expiry > EPOCH )); then
SUBKEYS+=($key)
fi
done
if (( $#SUBKEYS == 0 )); then
echo "error: no unexpired subkeys available for $KEYID" >>$TTY
exit 1
fi
SUBKEYS_JOINED="${(j:|:)SUBKEYS}"
CARD=$(
gpg --with-colons --card-status \
| grep -E "^fpr:[^:]+(${SUBKEYS_JOINED}):" \
| cut -d: -f2
)
if [[ $CARD = "" ]]; then
echo "error: no card matching subkeys connected for $KEYID" >>$TTY
exit 1
fi
# Replace key in arguments and force subkey via !.
ARGS[-1]="$CARD!"
if [[ $GIT_REFLOG_ACTION = rebase* ]]; then
# Write a newline to prevent overwriting text (Rebasing (X/Y)).
echo -n $'\nWaiting for YubiKey touch...' > $TTY
gpg $ARGS
RET=$?
# Move cursor to position 1, erase line (Waiting...) and move cursor one
# line up so that Git can continue where it left of.
echo -n $'\e[1G\e[K\e[1A' > $TTY
exit $RET
fi
echo -n $'Waiting for YubiKey touch...' > $TTY
gpg $ARGS
RET=$?
# Move cursor to position 1 and erase the rest of the line.
echo -n $'\e[1G\e[K' > $TTY
exit $RET
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment