Skip to content

Instantly share code, notes, and snippets.

@thsutton
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thsutton/00b3a92e7fafbb7f2c64 to your computer and use it in GitHub Desktop.
Save thsutton/00b3a92e7fafbb7f2c64 to your computer and use it in GitHub Desktop.
Edit a GPG encrypted file, maintaining (and extending) multiple recipients
#!/bin/sh
#
# Access a shared secret file.
set -eu
error() {
echo $1
exit 1
}
[ 0 -lt $# ] || {
cat << EOF
Usage: $0 FILE [KEYID] [KEYID] [...]
Decrypt and edit FILE, encrypting for the existing recipients and also any new
recipients mentioned on the command line.
EOF
exit 1 ;
}
SECRET="$1"
SHRED=$(which shred)
GPG=$(which gpg)
MKTEMP=$(which mktemp)
VI=$(which vi)
[ -z "$EDITOR" ] && EDITOR=$VI
# Check the programs we need are available.
[ -n "$MKTEMP" ] || error "The mktemp(1) program is missing."
[ -n "$GPG" ] || error "The gpg(1) program is missing."
[ -n "$SHRED" ] || error "The shred(1) program is missing."
[ -n "$EDITOR" ] || error "There is no editor configured."
# Check that the secret file exists and is encrypted in the format we expect.
[ -f "$SECRET" ] || error "The secret file '$SECRET' does not exist."
grep -q -- "^-----BEGIN PGP MESSAGE-----" "$SECRET" || \
error "The secret file is not encrypted."
# Extract the header and footer so we can put them back later.
HEADER=$(cat "$SECRET" | sed -e '/^-----BEGIN PGP MESSAGE-----$/,$d')
FOOTER=$(cat "$SECRET" | sed -e '1,/^-----END PGP MESSAGE-----$/d')
# List the recipients
RECIPIENTS=$(cat "$SECRET" \
| $GPG --batch --armor --decrypt --list-only --status-fd 1 2> /dev/null \
| awk '/^\[GNUPG:\] ENC_TO / { print "-r",$3 }' \
)
# Check there actually are some recipients.
[ -n "$RECIPIENTS" ] || \
error "There are no recipients."
# Add any new recipients from the command line.
shift
for addition in $*; do
RECIPIENTS="$RECIPIENTS -r $addition"
done
# TODO: Check that public keys for all recipients are available in the
# keychain.
# TODO: Check that public keys for all recipients are signed by a trusted key.
# Do something really shitty.
PLAIN=$($MKTEMP /tmp/secret.XXXXXXXXXX)
trap 'echo "Removing decrypted $PLAIN file." && rm -f "$PLAIN"' INT TERM EXIT
$GPG --armor --decrypt "$SECRET" > "$PLAIN"
# Let the user edit the file.
echo "Editing $PLAIN; in case of error shred(1) and rm(1) this yourself!" > /dev/stderr
$EDITOR "$PLAIN"
# Now re-encrypt for the same recipients it already had.
cp "${SECRET}" "${SECRET}.bak"
echo "$HEADER" > "${SECRET}"
$GPG --armor --encrypt $RECIPIENTS < "$PLAIN" >> "${SECRET}"
echo "$FOOTER" >> "${SECRET}"
# Shred and delete the temporary file.
$SHRED "$PLAIN"
rm -f "$PLAIN"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment