aboutsummaryrefslogtreecommitdiffstats
path: root/git-verify-to-tip
blob: 76411fd4cb30371ef7a3e7db9654b81334a8b1aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/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