Git fork
1#!/bin/sh
2
3test_description='ssh signed commit tests'
4GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6
7. ./test-lib.sh
8GNUPGHOME_NOT_USED=$GNUPGHOME
9. "$TEST_DIRECTORY/lib-gpg.sh"
10
11test_expect_success GPGSSH 'create signed commits' '
12 test_oid_cache <<-\EOF &&
13 header sha1:gpgsig
14 header sha256:gpgsig-sha256
15 EOF
16
17 test_when_finished "test_unconfig commit.gpgsign" &&
18 test_config gpg.format ssh &&
19 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
20
21 echo 1 >file && git add file &&
22 test_tick && git commit -S -m initial &&
23 git tag initial &&
24 git branch side &&
25
26 echo 2 >file && test_tick && git commit -a -S -m second &&
27 git tag second &&
28
29 git checkout side &&
30 echo 3 >elif && git add elif &&
31 test_tick && git commit -m "third on side" &&
32
33 git checkout main &&
34 test_tick && git merge -S side &&
35 git tag merge &&
36
37 echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
38 git tag fourth-unsigned &&
39
40 test_tick && git commit --amend -S -m "fourth signed" &&
41 git tag fourth-signed &&
42
43 git config commit.gpgsign true &&
44 echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
45 git tag fifth-signed &&
46
47 git config commit.gpgsign false &&
48 echo 6 >file && test_tick && git commit -a -m "sixth" &&
49 git tag sixth-unsigned &&
50
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 &&
54
55 test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
56 git tag seventh-signed &&
57
58 echo 8 >file && test_tick && git commit -a -m eighth -S"${GPGSSH_KEY_UNTRUSTED}" &&
59 git tag eighth-signed-alt &&
60
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) &&
69
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) &&
77
78 echo 13>file && test_tick && git commit -a -m thirteenth -S"${GPGSSH_KEY_ECDSA}" &&
79 git tag thirteenth-signed-ecdsa
80'
81
82test_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 &&
85 eval $(ssh-agent -T || ssh-agent) &&
86 test_when_finished "kill ${SSH_AGENT_PID}" &&
87 test_when_finished "test_unconfig user.signingkey" &&
88 mkdir tmpdir &&
89 TMPDIR="$(pwd)/tmpdir" &&
90 (
91 export TMPDIR &&
92 ssh-add "${GPGSSH_KEY_PRIMARY}" &&
93 echo 1 >file && git add file &&
94 git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
95 echo 2 >file &&
96 git config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
97 git commit -a -m rsa-config -S &&
98 ssh-add "${GPGSSH_KEY_ECDSA}" &&
99 echo 3 >file &&
100 git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
101 echo 4 >file &&
102 git config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
103 git commit -a -m ecdsa-config -S
104 ) &&
105 find tmpdir -type f >tmpfiles &&
106 test_must_be_empty tmpfiles
107'
108
109test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' '
110 test_when_finished "test_unconfig commit.gpgsign" &&
111 test_config gpg.format ssh &&
112
113 echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
114 git tag expired-signed &&
115
116 echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
117 git tag notyetvalid-signed &&
118
119 echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
120 git tag timeboxedvalid-signed &&
121
122 echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
123 git tag timeboxedinvalid-signed
124'
125
126test_expect_success GPGSSH 'verify and show signatures' '
127 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
128 test_config gpg.mintrustlevel UNDEFINED &&
129 (
130 for commit in initial second merge fourth-signed \
131 fifth-signed sixth-signed seventh-signed tenth-signed \
132 eleventh-signed
133 do
134 git verify-commit $commit &&
135 git show --pretty=short --show-signature $commit >actual &&
136 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
137 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
138 echo $commit OK || exit 1
139 done
140 ) &&
141 (
142 for commit in merge^2 fourth-unsigned sixth-unsigned \
143 seventh-unsigned ninth-unsigned
144 do
145 test_must_fail git verify-commit $commit &&
146 git show --pretty=short --show-signature $commit >actual &&
147 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
148 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
149 echo $commit OK || exit 1
150 done
151 ) &&
152 (
153 for commit in eighth-signed-alt twelfth-signed-alt
154 do
155 git show --pretty=short --show-signature $commit >actual &&
156 grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
157 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
158 grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
159 echo $commit OK || exit 1
160 done
161 )
162'
163
164test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature' '
165 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
166 test_must_fail git verify-commit eighth-signed-alt 2>actual &&
167 grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
168 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
169 grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
170'
171
172test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on expired signature key' '
173 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
174 test_must_fail git verify-commit expired-signed 2>actual &&
175 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
176'
177
178test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on not yet valid signature key' '
179 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
180 test_must_fail git verify-commit notyetvalid-signed 2>actual &&
181 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
182'
183
184test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit succeeds with commit date and key validity matching' '
185 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
186 git verify-commit timeboxedvalid-signed 2>actual &&
187 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
188 ! grep "${GPGSSH_BAD_SIGNATURE}" actual
189'
190
191test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure with commit date outside of key validity' '
192 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
193 test_must_fail git verify-commit timeboxedinvalid-signed 2>actual &&
194 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
195'
196
197test_expect_success GPGSSH 'verify-commit exits success with matching minTrustLevel' '
198 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
199 test_config gpg.minTrustLevel fully &&
200 git verify-commit sixth-signed
201'
202
203test_expect_success GPGSSH 'verify-commit exits success with low minTrustLevel' '
204 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
205 test_config gpg.minTrustLevel marginal &&
206 git verify-commit sixth-signed
207'
208
209test_expect_success GPGSSH 'verify-commit exits failure with high minTrustLevel' '
210 test_config gpg.minTrustLevel ultimate &&
211 test_must_fail git verify-commit eighth-signed-alt
212'
213
214test_expect_success GPGSSH 'verify signatures with --raw' '
215 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
216 (
217 for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
218 do
219 git verify-commit --raw $commit 2>actual &&
220 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
221 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
222 echo $commit OK || exit 1
223 done
224 ) &&
225 (
226 for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
227 do
228 test_must_fail git verify-commit --raw $commit 2>actual &&
229 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
230 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
231 echo $commit OK || exit 1
232 done
233 ) &&
234 (
235 for commit in eighth-signed-alt
236 do
237 test_must_fail git verify-commit --raw $commit 2>actual &&
238 grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
239 ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
240 echo $commit OK || exit 1
241 done
242 )
243'
244
245test_expect_success GPGSSH 'proper header is used for hash algorithm' '
246 git cat-file commit fourth-signed >output &&
247 grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output
248'
249
250test_expect_success GPGSSH 'show signed commit with signature' '
251 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
252 git show -s initial >commit &&
253 git show -s --show-signature initial >show &&
254 git verify-commit -v initial >verify.1 2>verify.2 &&
255 git cat-file commit initial >cat &&
256 grep -v -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.commit &&
257 grep -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.gpg &&
258 grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
259 test_cmp show.commit commit &&
260 test_cmp show.gpg verify.2 &&
261 test_cmp cat.commit verify.1
262'
263
264test_expect_success GPGSSH 'detect fudged signature' '
265 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
266 git cat-file commit seventh-signed >raw &&
267 sed -e "s/^seventh/7th forged/" raw >forged1 &&
268 git hash-object -w -t commit forged1 >forged1.commit &&
269 test_must_fail git verify-commit $(cat forged1.commit) &&
270 git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
271 grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
272 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
273 ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
274'
275
276test_expect_success GPGSSH 'detect fudged signature with NUL' '
277 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
278 git cat-file commit seventh-signed >raw &&
279 cat raw >forged2 &&
280 echo Qwik | tr "Q" "\000" >>forged2 &&
281 git hash-object --literally -w -t commit forged2 >forged2.commit &&
282 test_must_fail git verify-commit $(cat forged2.commit) &&
283 git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
284 grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
285 ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2
286'
287
288test_expect_success GPGSSH 'amending already signed commit' '
289 test_config gpg.format ssh &&
290 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
291 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
292 git checkout -f fourth-signed^0 &&
293 git commit --amend -S --no-edit &&
294 git verify-commit HEAD &&
295 git show -s --show-signature HEAD >actual &&
296 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
297 ! grep "${GPGSSH_BAD_SIGNATURE}" actual
298'
299
300test_expect_success GPGSSH 'show good signature with custom format' '
301 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
302 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
303 cat >expect.tmpl <<-\EOF &&
304 G
305 FINGERPRINT
306 principal with number 1
307 FINGERPRINT
308
309 EOF
310 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
311 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
312 test_cmp expect actual
313'
314
315test_expect_success GPGSSH 'show bad signature with custom format' '
316 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
317 cat >expect <<-\EOF &&
318 B
319
320
321
322
323 EOF
324 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
325 test_cmp expect actual
326'
327
328test_expect_success GPGSSH 'show untrusted signature with custom format' '
329 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
330 cat >expect.tmpl <<-\EOF &&
331 U
332 FINGERPRINT
333
334 FINGERPRINT
335
336 EOF
337 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
338 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
339 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
340 test_cmp expect actual
341'
342
343test_expect_success GPGSSH 'show untrusted signature with undefined trust level' '
344 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
345 cat >expect.tmpl <<-\EOF &&
346 undefined
347 FINGERPRINT
348
349 FINGERPRINT
350
351 EOF
352 git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
353 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
354 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
355 test_cmp expect actual
356'
357
358test_expect_success GPGSSH 'show untrusted signature with ultimate trust level' '
359 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
360 cat >expect.tmpl <<-\EOF &&
361 fully
362 FINGERPRINT
363 principal with number 1
364 FINGERPRINT
365
366 EOF
367 git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
368 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
369 sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
370 test_cmp expect actual
371'
372
373test_expect_success GPGSSH 'show lack of signature with custom format' '
374 cat >expect <<-\EOF &&
375 N
376
377
378
379
380 EOF
381 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
382 test_cmp expect actual
383'
384
385test_expect_success GPGSSH 'log.showsignature behaves like --show-signature' '
386 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
387 test_config log.showsignature true &&
388 git show initial >actual &&
389 grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
390'
391
392test_expect_success GPGSSH 'check config gpg.format values' '
393 test_config gpg.format ssh &&
394 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
395 test_config gpg.format ssh &&
396 git commit -S --amend -m "success" &&
397 test_config gpg.format OpEnPgP &&
398 test_must_fail git commit -S --amend -m "fail"
399'
400
401test_expect_failure GPGSSH 'detect fudged commit with double signature (TODO)' '
402 sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
403 sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
404 sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
405 gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
406 cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
407 sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
408 double-combined.asc > double-gpgsig &&
409 sed -e "/committer/r double-gpgsig" double-base >double-commit &&
410 git hash-object -w -t commit double-commit >double-commit.commit &&
411 test_must_fail git verify-commit $(cat double-commit.commit) &&
412 git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
413 grep "BAD signature from" double-actual &&
414 grep "Good signature from" double-actual
415'
416
417test_expect_failure GPGSSH 'show double signature with custom format (TODO)' '
418 cat >expect <<-\EOF &&
419 E
420
421
422
423
424 EOF
425 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual &&
426 test_cmp expect actual
427'
428
429
430test_expect_failure GPGSSH 'verify-commit verifies multiply signed commits (TODO)' '
431 git init multiply-signed &&
432 cd multiply-signed &&
433 test_commit first &&
434 echo 1 >second &&
435 git add second &&
436 tree=$(git write-tree) &&
437 parent=$(git rev-parse HEAD^{commit}) &&
438 git commit --gpg-sign -m second &&
439 git cat-file commit HEAD &&
440 # Avoid trailing whitespace.
441 sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
442 Qtree $tree
443 Qparent $parent
444 Qauthor A U Thor <author@example.com> 1112912653 -0700
445 Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
446 Qgpgsig -----BEGIN PGP SIGNATURE-----
447 QZ
448 Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
449 Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
450 Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
451 Q =tQ0N
452 Q -----END PGP SIGNATURE-----
453 Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
454 QZ
455 Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
456 Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
457 Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
458 Q =pIwP
459 Q -----END PGP SIGNATURE-----
460 Q
461 Qsecond
462 EOF
463 head=$(git hash-object -t commit -w commit) &&
464 git reset --hard $head &&
465 git verify-commit $head 2>actual &&
466 grep "Good signature from" actual &&
467 ! grep "BAD signature from" actual
468'
469
470test_done