]> git.ipfire.org Git - thirdparty/git.git/blob - t/t7528-signed-commit-ssh.sh
The third batch
[thirdparty/git.git] / t / t7528-signed-commit-ssh.sh
1 #!/bin/sh
2
3 test_description='ssh signed commit tests'
4 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6
7 . ./test-lib.sh
8 GNUPGHOME_NOT_USED=$GNUPGHOME
9 . "$TEST_DIRECTORY/lib-gpg.sh"
10
11 test_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
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 &&
85 eval $(ssh-agent) &&
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")" &&
90 echo 2 >file &&
91 test_config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
92 git commit -a -m rsa-config -S &&
93 ssh-add "${GPGSSH_KEY_ECDSA}" &&
94 echo 3 >file &&
95 git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
96 echo 4 >file &&
97 test_config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
98 git commit -a -m ecdsa-config -S
99 '
100
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 &&
104
105 echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
106 git tag expired-signed &&
107
108 echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
109 git tag notyetvalid-signed &&
110
111 echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
112 git tag timeboxedvalid-signed &&
113
114 echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
115 git tag timeboxedinvalid-signed
116 '
117
118 test_expect_success GPGSSH 'verify and show signatures' '
119 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
120 test_config gpg.mintrustlevel UNDEFINED &&
121 (
122 for commit in initial second merge fourth-signed \
123 fifth-signed sixth-signed seventh-signed tenth-signed \
124 eleventh-signed
125 do
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
131 done
132 ) &&
133 (
134 for commit in merge^2 fourth-unsigned sixth-unsigned \
135 seventh-unsigned ninth-unsigned
136 do
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
142 done
143 ) &&
144 (
145 for commit in eighth-signed-alt twelfth-signed-alt
146 do
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
152 done
153 )
154 '
155
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
162 '
163
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
168 '
169
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
174 '
175
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
181 '
182
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
187 '
188
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
193 '
194
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
199 '
200
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
204 '
205
206 test_expect_success GPGSSH 'verify signatures with --raw' '
207 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
208 (
209 for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
210 do
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
215 done
216 ) &&
217 (
218 for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
219 do
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
224 done
225 ) &&
226 (
227 for commit in eighth-signed-alt
228 do
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
233 done
234 )
235 '
236
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
240 '
241
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
254 '
255
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
266 '
267
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 &&
271 cat raw >forged2 &&
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
278 '
279
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
290 '
291
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 &&
296 G
297 FINGERPRINT
298 principal with number 1
299 FINGERPRINT
300
301 EOF
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
305 '
306
307 test_expect_success GPGSSH 'show bad signature with custom format' '
308 test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
309 cat >expect <<-\EOF &&
310 B
311
312
313
314
315 EOF
316 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
317 test_cmp expect actual
318 '
319
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 &&
323 U
324 FINGERPRINT
325
326 FINGERPRINT
327
328 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
333 '
334
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 &&
338 undefined
339 FINGERPRINT
340
341 FINGERPRINT
342
343 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
348 '
349
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 &&
353 fully
354 FINGERPRINT
355 principal with number 1
356 FINGERPRINT
357
358 EOF
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
363 '
364
365 test_expect_success GPGSSH 'show lack of signature with custom format' '
366 cat >expect <<-\EOF &&
367 N
368
369
370
371
372 EOF
373 git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
374 test_cmp expect actual
375 '
376
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
382 '
383
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"
391 '
392
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
407 '
408
409 test_expect_failure GPGSSH 'show double signature with custom format (TODO)' '
410 cat >expect <<-\EOF &&
411 E
412
413
414
415
416 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
419 '
420
421
422 test_expect_failure GPGSSH 'verify-commit verifies multiply signed commits (TODO)' '
423 git init multiply-signed &&
424 cd multiply-signed &&
425 test_commit first &&
426 echo 1 >second &&
427 git add second &&
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 &&
434 Qtree $tree
435 Qparent $parent
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-----
439 QZ
440 Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
441 Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
442 Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
443 Q =tQ0N
444 Q -----END PGP SIGNATURE-----
445 Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
446 QZ
447 Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
448 Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
449 Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
450 Q =pIwP
451 Q -----END PGP SIGNATURE-----
452 Q
453 Qsecond
454 EOF
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
460 '
461
462 test_done