summaryrefslogtreecommitdiffstats
path: root/scripts/clean-keydir
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/clean-keydir')
-rwxr-xr-xscripts/clean-keydir146
1 files changed, 146 insertions, 0 deletions
diff --git a/scripts/clean-keydir b/scripts/clean-keydir
new file mode 100755
index 0000000..bd61432
--- /dev/null
+++ b/scripts/clean-keydir
@@ -0,0 +1,146 @@
+#!/bin/bash
+
+# Copyright (c) 2012 Jonathan McDowell <noodles@earth.li>,
+# 2019 Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+# GNU GPL; v2 or later
+# Given a key directory, prune, clean, or minimize the keys
+
+# "prune" just does basic cleanup on the file, without getting rid of
+# any third-party signatures.
+
+set -e
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+ cat >&2 <<EOF
+Usage: $0 [prune|launder|clean|minimal] dir
+ prune: remove invalid parts
+ launder: invoke GnuPG's merge logic to trim the key
+ clean: prune and drop non-debian third-party certifications
+ minimal: prune and remove *all* third-party certifications
+EOF
+ exit 1
+fi
+
+declare -a GPGOPTIONS=(--batch
+ --no-tty
+ --quiet
+ --no-options
+ --homedir=/dev/null
+ --trust-model=always
+ --fixed-list-mode
+ --with-colons
+ --export-options=no-export-attributes
+ )
+
+
+if [ "$1" == prune ]; then
+ GPGOPTIONS+=(--no-keyring
+ --import-options=import-export
+ )
+elif [ "$1" == launder ]; then
+ # we are going to do something very ugly...
+ # see https://dev.gnupg.org/T4421
+ : pass
+elif [ "$1" == clean ]; then
+ # we need to include all the known keys so that we keep the
+ # interlocking signatures
+ make
+ GPGOPTIONS+=(--no-default-keyring
+ --import-options=import-export,import-clean
+ --export-options=export-clean
+ --keyring "$(readlink -f output/keyrings/debian-keyring.gpg)"
+ --keyring "$(readlink -f output/keyrings/debian-nonupload.gpg)"
+ --keyring "$(readlink -f output/keyrings/debian-maintainers.gpg)"
+ --keyring "$(readlink -f output/keyrings/debian-role-keys.gpg)"
+ --keyring "$(readlink -f output/keyrings/emeritus-keyring.gpg)"
+ )
+elif [ "$1" == minimal ]; then
+ GPGOPTIONS+=(--no-keyring
+ --import-options=import-export,import-minimal
+ --export-options=export-minimal
+ )
+else
+ echo "Must specify prune, launder, clean or minimal; not $1" >&2
+ exit 1
+fi
+
+if [ ! -d "$2" ]; then
+ printf '%s is not a directory' "$2" >&2
+ exit 1
+fi
+
+# takes name of transferable public key file as $1, emits the laundered key to file named $2
+launder_tpk() {
+ local interim="$(mktemp -d interim.XXXXXXX)"
+ local success=false
+ local key="$1"
+ local output="$2"
+ mkdir -p -m 0700 "$interim/gpg" "$interim/split"
+ cat > "$interim/gpg/gpg.conf" <<EOF
+batch
+no-tty
+quiet
+no-options
+trust-model always
+fixed-list-mode
+with-colons
+export-options no-export-attributes
+EOF
+ if gpg --homedir "$interim/gpg" --import-options=import-minimal --status-file "$interim/status" --import < "$key" &&
+ fpr="$(awk '{ if ($1 == "[GNUPG:]" && $2 == "IMPORT_OK" && $3 == "1") { print $4 } }' < "$interim/status")" &&
+ [ -n "$fpr" ] &&
+ gpg --homedir "$interim/gpg" --export | (cd "$interim/split" && gpgsplit) &&
+ gpg --homedir "$interim/gpg" --delete-key "$fpr"; then
+ local pub="$interim/split/000001-006.public_key"
+ local uid=$(ls "$interim/split/"*.user_id | head -n1)
+ local sig=$(printf '%s/split/%06d-002.sig' "$interim" $(( "$(echo "${uid##$interim/split/}" | sed -e 's_^0*__' -e 's_-.*$__')" + 1 )) )
+ if [ -r "$pub" ] && [ -r "$uid" ] && [ -r "$sig" ]; then
+ if cat "$pub" "$uid" "$sig" | gpg --homedir "$interim/gpg" --import &&
+ gpg --homedir "$interim/gpg" --import < "$key" &&
+ gpg --homedir "$interim/gpg" --output "$output" --export "$fpr"; then
+ success=true
+ else
+ printf 'Merging failed for %s (fpr: %s)\n' "$key" "$fpr" >&2
+ fi
+ else
+ printf 'Could not find minimal TPK for %s (fpr: %s)\n' "$key" "$fpr" >&2
+ fi
+ else
+ printf 'failed to do initial import of %s\n' "$key" >&2
+ fi
+ rm -rf "$interim"
+ [ $success = true ]
+}
+
+cd "$2"
+for key in 0x*; do
+ success=false
+ if [ "$1" == launder ]; then
+ if launder_tpk "$key" "$key.new"; then
+ success=true
+ fi
+ else
+ if gpg "${GPGOPTIONS[@]}" --output "$key.new" --import "$key"; then
+ success=true
+ fi
+ fi
+ if [ $success = true ] && [ -s $key.new ]; then
+ OLDSIZE=$(stat -c "%s" "$key")
+ NEWSIZE=$(stat -c "%s" "$key.new")
+ if [ $OLDSIZE -gt $NEWSIZE ]; then
+ echo "Cleaning $key [$OLDSIZE] -> [$NEWSIZE]"
+ mv "$key.new" "$key"
+ elif [ $OLDSIZE -eq $NEWSIZE ] && ! cmp --quiet "$key" "$key.new" ; then
+ printf "Packets were reordered in $key"
+ if [ "$1" == launder ]; then
+ echo " (but ignoring while doing launder: https://dev.gnupg.org/T4422)"
+ else
+ mv "$key.new" "$key"
+ echo
+ fi
+ fi
+ fi
+ [ -e "$key.new" ] && rm "$key.new"
+done
+
+exit 0