3 test_description
='ssh signed commit tests'
4 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
=main
5 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
8 GNUPGHOME_NOT_USED
=$GNUPGHOME
9 .
"$TEST_DIRECTORY/lib-gpg.sh"
11 test_expect_success GPGSSH
'create signed commits' '
12 test_oid_cache <<-\EOF &&
14 header sha256:gpgsig-sha256
17 test_when_finished "test_unconfig commit.gpgsign" &&
18 test_config gpg.format ssh &&
19 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
21 echo 1 >file && git add file &&
22 test_tick && git commit -S -m initial &&
26 echo 2 >file && test_tick && git commit -a -S -m second &&
30 echo 3 >elif && git add elif &&
31 test_tick && git commit -m "third on side" &&
34 test_tick && git merge -S side &&
37 echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
38 git tag fourth-unsigned &&
40 test_tick && git commit --amend -S -m "fourth signed" &&
41 git tag fourth-signed &&
43 git config commit.gpgsign true &&
44 echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
45 git tag fifth-signed &&
47 git config commit.gpgsign false &&
48 echo 6 >file && test_tick && git commit -a -m "sixth" &&
49 git tag sixth-unsigned &&
51 git config commit.gpgsign true &&
52 echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign &&
53 git tag seventh-unsigned &&
55 test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
56 git tag seventh-signed &&
58 echo 8 >file && test_tick && git commit -a -m eighth -S"${GPGSSH_KEY_UNTRUSTED}" &&
59 git tag eighth-signed-alt &&
61 # commit.gpgsign is still on but this must not be signed
62 echo 9 | git commit-tree HEAD^{tree} >oid &&
63 test_line_count = 1 oid &&
64 git tag ninth-unsigned $(cat oid) &&
65 # explicit -S of course must sign.
66 echo 10 | git commit-tree -S HEAD^{tree} >oid &&
67 test_line_count = 1 oid &&
68 git tag tenth-signed $(cat oid) &&
70 # --gpg-sign[=<key-id>] must sign.
71 echo 11 | git commit-tree --gpg-sign HEAD^{tree} >oid &&
72 test_line_count = 1 oid &&
73 git tag eleventh-signed $(cat oid) &&
74 echo 12 | git commit-tree --gpg-sign="${GPGSSH_KEY_UNTRUSTED}" HEAD^{tree} >oid &&
75 test_line_count = 1 oid &&
76 git tag twelfth-signed-alt $(cat oid) &&
78 echo 13>file && test_tick && git commit -a -m thirteenth -S"${GPGSSH_KEY_ECDSA}" &&
79 git tag thirteenth-signed-ecdsa
82 test_expect_success GPGSSH
'sign commits using literal public keys with ssh-agent' '
83 test_when_finished "test_unconfig commit.gpgsign" &&
84 test_config gpg.format ssh &&
86 test_when_finished "kill ${SSH_AGENT_PID}" &&
87 ssh-add "${GPGSSH_KEY_PRIMARY}" &&
88 echo 1 >file && git add file &&
89 git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
91 test_config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
92 git commit -a -m rsa-config -S &&
93 ssh-add "${GPGSSH_KEY_ECDSA}" &&
95 git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
97 test_config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
98 git commit -a -m ecdsa-config -S
101 test_expect_success GPGSSH
,GPGSSH_VERIFYTIME
'create signed commits with keys having defined lifetimes' '
102 test_when_finished "test_unconfig commit.gpgsign" &&
103 test_config gpg.format ssh &&
105 echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
106 git tag expired-signed &&
108 echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
109 git tag notyetvalid-signed &&
111 echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
112 git tag timeboxedvalid-signed &&
114 echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
115 git tag timeboxedinvalid-signed
118 test_expect_success GPGSSH
'verify and show signatures' '
119 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
120 test_config gpg.mintrustlevel UNDEFINED &&
122 for commit in initial second merge fourth-signed \
123 fifth-signed sixth-signed seventh-signed tenth-signed \
126 git verify-commit $commit &&
127 git show --pretty=short --show-signature $commit >actual &&
128 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
129 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
130 echo $commit OK || exit 1
134 for commit in merge^2 fourth-unsigned sixth-unsigned \
135 seventh-unsigned ninth-unsigned
137 test_must_fail git verify-commit $commit &&
138 git show --pretty=short --show-signature $commit >actual &&
139 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
140 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
141 echo $commit OK || exit 1
145 for commit in eighth-signed-alt twelfth-signed-alt
147 git show --pretty=short --show-signature $commit >actual &&
148 grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
149 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
150 grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
151 echo $commit OK || exit 1
156 test_expect_success GPGSSH
'verify-commit exits failure on untrusted signature' '
157 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
158 test_must_fail git verify-commit eighth-signed-alt 2>actual &&
159 grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
160 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
161 grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
164 test_expect_success GPGSSH
,GPGSSH_VERIFYTIME
'verify-commit exits failure on expired signature key' '
165 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
166 test_must_fail git verify-commit expired-signed 2>actual &&
167 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
170 test_expect_success GPGSSH
,GPGSSH_VERIFYTIME
'verify-commit exits failure on not yet valid signature key' '
171 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
172 test_must_fail git verify-commit notyetvalid-signed 2>actual &&
173 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
176 test_expect_success GPGSSH
,GPGSSH_VERIFYTIME
'verify-commit succeeds with commit date and key validity matching' '
177 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
178 git verify-commit timeboxedvalid-signed 2>actual &&
179 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
180 ! grep "${GPGSSH_BAD_SIGNATURE}" actual
183 test_expect_success GPGSSH
,GPGSSH_VERIFYTIME
'verify-commit exits failure with commit date outside of key validity' '
184 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
185 test_must_fail git verify-commit timeboxedinvalid-signed 2>actual &&
186 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
189 test_expect_success GPGSSH
'verify-commit exits success with matching minTrustLevel' '
190 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
191 test_config gpg.minTrustLevel fully &&
192 git verify-commit sixth-signed
195 test_expect_success GPGSSH
'verify-commit exits success with low minTrustLevel' '
196 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
197 test_config gpg.minTrustLevel marginal &&
198 git verify-commit sixth-signed
201 test_expect_success GPGSSH
'verify-commit exits failure with high minTrustLevel' '
202 test_config gpg.minTrustLevel ultimate &&
203 test_must_fail git verify-commit eighth-signed-alt
206 test_expect_success GPGSSH
'verify signatures with --raw' '
207 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
209 for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
211 git verify-commit --raw $commit 2>actual &&
212 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
213 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
214 echo $commit OK || exit 1
218 for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
220 test_must_fail git verify-commit --raw $commit 2>actual &&
221 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
222 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
223 echo $commit OK || exit 1
227 for commit in eighth-signed-alt
229 test_must_fail git verify-commit --raw $commit 2>actual &&
230 grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
231 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
232 echo $commit OK || exit 1
237 test_expect_success GPGSSH
'proper header is used for hash algorithm' '
238 git cat-file commit fourth-signed >output &&
239 grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output
242 test_expect_success GPGSSH
'show signed commit with signature' '
243 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
244 git show -s initial >commit &&
245 git show -s --show-signature initial >show &&
246 git verify-commit -v initial >verify.1 2>verify.2 &&
247 git cat-file commit initial >cat &&
248 grep -v -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.commit &&
249 grep -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.gpg &&
250 grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
251 test_cmp show.commit commit &&
252 test_cmp show.gpg verify.2 &&
253 test_cmp cat.commit verify.1
256 test_expect_success GPGSSH
'detect fudged signature' '
257 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
258 git cat-file commit seventh-signed >raw &&
259 sed -e "s/^seventh/7th forged/" raw >forged1 &&
260 git hash-object -w -t commit forged1 >forged1.commit &&
261 test_must_fail git verify-commit $(cat forged1.commit) &&
262 git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
263 grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
264 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
265 ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
268 test_expect_success GPGSSH
'detect fudged signature with NUL' '
269 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
270 git cat-file commit seventh-signed >raw &&
272 echo Qwik | tr "Q" "\000" >>forged2 &&
273 git hash-object --literally -w -t commit forged2 >forged2.commit &&
274 test_must_fail git verify-commit $(cat forged2.commit) &&
275 git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
276 grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
277 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2
280 test_expect_success GPGSSH
'amending already signed commit' '
281 test_config gpg.format ssh &&
282 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
283 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
284 git checkout -f fourth-signed^0 &&
285 git commit --amend -S --no-edit &&
286 git verify-commit HEAD &&
287 git show -s --show-signature HEAD >actual &&
288 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
289 ! grep "${GPGSSH_BAD_SIGNATURE}" actual
292 test_expect_success GPGSSH
'show good signature with custom format' '
293 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
294 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
295 cat >expect.tmpl <<-\EOF &&
298 principal with number 1
302 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
303 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
304 test_cmp expect actual
307 test_expect_success GPGSSH
'show bad signature with custom format' '
308 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
309 cat >expect <<-\EOF &&
316 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
317 test_cmp expect actual
320 test_expect_success GPGSSH
'show untrusted signature with custom format' '
321 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
322 cat >expect.tmpl <<-\EOF &&
329 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
330 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
331 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
332 test_cmp expect actual
335 test_expect_success GPGSSH
'show untrusted signature with undefined trust level' '
336 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
337 cat >expect.tmpl <<-\EOF &&
344 git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
345 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
346 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
347 test_cmp expect actual
350 test_expect_success GPGSSH
'show untrusted signature with ultimate trust level' '
351 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
352 cat >expect.tmpl <<-\EOF &&
355 principal with number 1
359 git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
360 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
361 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
362 test_cmp expect actual
365 test_expect_success GPGSSH
'show lack of signature with custom format' '
366 cat >expect <<-\EOF &&
373 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
374 test_cmp expect actual
377 test_expect_success GPGSSH
'log.showsignature behaves like --show-signature' '
378 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
379 test_config log.showsignature true &&
380 git show initial >actual &&
381 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
384 test_expect_success GPGSSH
'check config gpg.format values' '
385 test_config gpg.format ssh &&
386 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
387 test_config gpg.format ssh &&
388 git commit -S --amend -m "success" &&
389 test_config gpg.format OpEnPgP &&
390 test_must_fail git commit -S --amend -m "fail"
393 test_expect_failure GPGSSH
'detect fudged commit with double signature (TODO)' '
394 sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
395 sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
396 sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
397 gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
398 cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
399 sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
400 double-combined.asc > double-gpgsig &&
401 sed -e "/committer/r double-gpgsig" double-base >double-commit &&
402 git hash-object -w -t commit double-commit >double-commit.commit &&
403 test_must_fail git verify-commit $(cat double-commit.commit) &&
404 git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
405 grep "BAD signature from" double-actual &&
406 grep "Good signature from" double-actual
409 test_expect_failure GPGSSH
'show double signature with custom format (TODO)' '
410 cat >expect <<-\EOF &&
417 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual &&
418 test_cmp expect actual
422 test_expect_failure GPGSSH
'verify-commit verifies multiply signed commits (TODO)' '
423 git init multiply-signed &&
424 cd multiply-signed &&
428 tree=$(git write-tree) &&
429 parent=$(git rev-parse HEAD^{commit}) &&
430 git commit --gpg-sign -m second &&
431 git cat-file commit HEAD &&
432 # Avoid trailing whitespace.
433 sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
436 Qauthor A U Thor <author@example.com> 1112912653 -0700
437 Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
438 Qgpgsig -----BEGIN PGP SIGNATURE-----
440 Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
441 Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
442 Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
444 Q -----END PGP SIGNATURE-----
445 Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
447 Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
448 Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
449 Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
451 Q -----END PGP SIGNATURE-----
455 head=$(git hash-object -t commit -w commit) &&
456 git reset --hard $head &&
457 git verify-commit $head 2>actual &&
458 grep "Good signature from" actual &&
459 ! grep "BAD signature from" actual