blob: 76411fd4cb30371ef7a3e7db9654b81334a8b1aa [file] [log] [blame]
#!/bin/bash
# git-verify-to-tip
# -----------------
#
# Verify PGP signatures on all commits from the last signed tag
# or any arbitrary object in the repository history.
#
# This script can be installed as hooks/pre-push.
#
# Configurable parameters
# -----------------------
# We always ensure the signing key is both GOOD and VALID, which means
# that the keys you are checking against should be imported into your
# gnupghome and signed by a trusted key (e.g. your own). If you want to
# use a different GNUPG directory other than the one in your home, you
# can "export GNUPGHOME=some/path" before running this script. In addition,
# you may explicitly specify the keys to trust in the ONLYKEYS parameter below,
# e.g. if you want to make sure the signatures came from a very small subset
# of developers. Pipe-separate multiple keys, e.g.:
# ONLYKEYS="ABAF11C65A2970B130ABE3C479BE3E4300411886|647F28654894E3BD457199BE38DBBDC86092693E"
ONLYKEYS=
#
# By default, we check signatures on every commit, but if you set this to
# --merges, we will only check signatures on merges. You can also add any
# other flags accepted by git-rev-list.
REVFLAGS=
#
# When set to "" we start from the latest annotated tag we find.
# You can also list an arbitrary commit object here.
# When running as hooks/pre-push, we ignore this entirely and use the
# commit information provided by git on stdin.
STARTFROM=
#
# We can also get these parameters from the git config. E.g.:
# [verify-to-tip]
# onlykeys = ABAF11C65A2970B130ABE3C479BE3E4300411886|647F28654894E3BD457199BE38DBBDC86092693E
# revflags = --merges
# startfrom = abcdef123456
#
if [[ -z ${ONLYKEYS} ]]; then
ONLYKEYS=$(git config --get verify-to-tip.onlykeys)
fi
if [[ -z ${REVFLAGS} ]]; then
REVFLAGS=$(git config --get verify-to-tip.revflags)
fi
if [[ -z ${STARTFROM} ]]; then
STARTFROM=$(git config --get verify-to-tip.startfrom)
fi
# End configuration
function verify_raw_gpg {
# We are looking for [GNUPG:] GOODSIG and [GNUPG:] VALIDSIG
# They must be both present, or this is not a valid sig
COUNT=$(echo "${1}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
if [[ ${COUNT} -lt 2 ]]; then
return 1
fi
if [[ -z ${ONLYKEYS} ]]; then
return 0
fi
if $(echo "${1}" | grep -q -E "^\[GNUPG:\] VALIDSIG .* (${ONLYKEYS})\$"); then
return 0
fi
return 1
}
function verify_rev_range {
REVRANGE=${1}
REVFLAGS=${2}
for REV in $(git rev-list ${REVRANGE} ${REVFLAGS}); do
echo "Verifying $REV"
RAWOUT=$(git verify-commit --raw ${REV} 2>&1)
if ! verify_raw_gpg "${RAWOUT}"; then
echo "CRITICAL: ${REV} signature did NOT verify:"
echo "${RAWOUT}"
return 1
fi
done
return 0
}
# Are we running from hooks/pre-push? If so, $1 and $2 should be set.
if [[ -z "${1}${2}" ]]; then
# Not running as a pre-push hook.
if [[ -z ${STARTFROM} ]]; then
# verify the last annotated tag
STARTFROM=$(git describe --abbrev=0)
RAWOUT=$(git verify-tag --raw ${STARTFROM} 2>&1)
else
# verify the arbitrary commit provided
RAWOUT=$(git verify-commit --raw ${STARTFROM} 2>&1)
fi
echo "Verifying ${STARTFROM}"
if ! verify_raw_gpg "${RAWOUT}"; then
echo "CRITICAL: ${STARTFROM} signature did NOT verify:"
echo "${RAWOUT}"
exit 1
fi
REVRANGE="${STARTFROM}.."
if ! verify_rev_range "${REVRANGE}" "${REVFLAGS}"; then
exit 1
fi
else
# We are in a pre-push hook
Z40="0000000000000000000000000000000000000000"
while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do
if [[ ${LOCAL_SHA} == ${Z40} ]]; then
# Ignore delete
continue
fi
if [[ ${REMOTE_SHA} == ${Z40} ]]; then
# New branch, examine all commits
REVRANGE=${LOCAL_SHA}
else
# Update to existing branch, examine new commits
REVRANGE="${REMOTE_SHA}..${LOCAL_SHA}"
fi
if ! verify_rev_range "${REVRANGE}" "${REVFLAGS}"; then
exit 1
fi
done
fi
echo "Verified successfully."
exit 0