guix describe: Adjust test to latest changes.
[jackhill/guix/guix.git] / tests / git-authenticate.scm
CommitLineData
c83eedba 1;;; GNU Guix --- Functional package management for GNU
36cb04df 2;;; Copyright © 2020, 2022 Ludovic Courtès <ludo@gnu.org>
c83eedba
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (test-git-authenticate)
20 #:use-module (git)
21 #:use-module (guix git)
22 #:use-module (guix git-authenticate)
36cb04df
LC
23 #:use-module ((guix channels) #:select (openpgp-fingerprint))
24 #:use-module ((guix diagnostics)
25 #:select (formatted-message? formatted-message-arguments))
c83eedba 26 #:use-module (guix openpgp)
36cb04df 27 #:use-module ((guix tests) #:select (random-text))
c83eedba
LC
28 #:use-module (guix tests git)
29 #:use-module (guix tests gnupg)
30 #:use-module (guix build utils)
31 #:use-module (srfi srfi-1)
32 #:use-module (srfi srfi-34)
36cb04df 33 #:use-module (srfi srfi-35)
c83eedba
LC
34 #:use-module (srfi srfi-64)
35 #:use-module (rnrs bytevectors)
36 #:use-module (rnrs io ports))
37
38;; Test the (guix git-authenticate) tools.
39
c83eedba
LC
40(define (gpg+git-available?)
41 (and (which (git-command))
42 (which (gpg-command)) (which (gpgconf-command))))
43
44\f
45(test-begin "git-authenticate")
46
47(unless (which (git-command)) (test-skip 1))
48(test-assert "unsigned commits"
49 (with-temporary-git-repository directory
50 '((add "a.txt" "A")
51 (commit "first commit")
52 (add "b.txt" "B")
53 (commit "second commit"))
54 (with-repository directory repository
55 (let ((commit1 (find-commit repository "first"))
56 (commit2 (find-commit repository "second")))
57 (guard (c ((unsigned-commit-error? c)
58 (oid=? (git-authentication-error-commit c)
59 (commit-id commit1))))
60 (authenticate-commits repository (list commit1 commit2)
61 #:keyring-reference "master")
62 'failed)))))
63
0a8dd8aa 64(unless (gpg+git-available?) (test-skip 1))
52c529ff
LC
65(test-assert "signed commits, SHA1 signature"
66 (with-fresh-gnupg-setup (list %ed25519-public-key-file
67 %ed25519-secret-key-file)
68 ;; Force use of SHA1 for signatures.
69 (call-with-output-file (string-append (getenv "GNUPGHOME") "/gpg.conf")
70 (lambda (port)
71 (display "digest-algo sha1" port)))
72
73 (with-temporary-git-repository directory
74 `((add "a.txt" "A")
75 (add "signer.key" ,(call-with-input-file %ed25519-public-key-file
76 get-string-all))
77 (add ".guix-authorizations"
78 ,(object->string
79 `(authorizations (version 0)
80 ((,(key-fingerprint %ed25519-public-key-file)
81 (name "Charlie"))))))
82 (commit "first commit"
83 (signer ,(key-fingerprint %ed25519-public-key-file))))
84 (with-repository directory repository
85 (let ((commit (find-commit repository "first")))
86 (guard (c ((unsigned-commit-error? c)
87 (oid=? (git-authentication-error-commit c)
88 (commit-id commit))))
89 (authenticate-commits repository (list commit)
90 #:keyring-reference "master")
91 'failed))))))
92
c83eedba
LC
93(unless (gpg+git-available?) (test-skip 1))
94(test-assert "signed commits, default authorizations"
95 (with-fresh-gnupg-setup (list %ed25519-public-key-file
96 %ed25519-secret-key-file)
97 (with-temporary-git-repository directory
98 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
99 get-string-all))
100 (commit "zeroth commit")
101 (add "a.txt" "A")
102 (commit "first commit"
103 (signer ,(key-fingerprint %ed25519-public-key-file)))
104 (add "b.txt" "B")
105 (commit "second commit"
106 (signer ,(key-fingerprint %ed25519-public-key-file))))
107 (with-repository directory repository
108 (let ((commit1 (find-commit repository "first"))
109 (commit2 (find-commit repository "second")))
110 (authenticate-commits repository (list commit1 commit2)
111 #:default-authorizations
112 (list (openpgp-public-key-fingerprint
113 (read-openpgp-packet
114 %ed25519-public-key-file)))
115 #:keyring-reference "master"))))))
116
117(unless (gpg+git-available?) (test-skip 1))
118(test-assert "signed commits, .guix-authorizations"
119 (with-fresh-gnupg-setup (list %ed25519-public-key-file
120 %ed25519-secret-key-file)
121 (with-temporary-git-repository directory
122 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
123 get-string-all))
124 (add ".guix-authorizations"
125 ,(object->string
126 `(authorizations (version 0)
127 ((,(key-fingerprint
128 %ed25519-public-key-file)
129 (name "Charlie"))))))
130 (commit "zeroth commit")
131 (add "a.txt" "A")
132 (commit "first commit"
133 (signer ,(key-fingerprint %ed25519-public-key-file)))
134 (add ".guix-authorizations"
135 ,(object->string `(authorizations (version 0) ()))) ;empty
136 (commit "second commit"
137 (signer ,(key-fingerprint %ed25519-public-key-file)))
138 (add "b.txt" "B")
139 (commit "third commit"
140 (signer ,(key-fingerprint %ed25519-public-key-file))))
141 (with-repository directory repository
142 (let ((commit1 (find-commit repository "first"))
143 (commit2 (find-commit repository "second"))
144 (commit3 (find-commit repository "third")))
145 ;; COMMIT1 and COMMIT2 are fine.
146 (and (authenticate-commits repository (list commit1 commit2)
147 #:keyring-reference "master")
148
149 ;; COMMIT3 is signed by an unauthorized key according to its
150 ;; parent's '.guix-authorizations' file.
151 (guard (c ((unauthorized-commit-error? c)
152 (and (oid=? (git-authentication-error-commit c)
153 (commit-id commit3))
154 (bytevector=?
155 (openpgp-public-key-fingerprint
156 (unauthorized-commit-error-signing-key c))
157 (openpgp-public-key-fingerprint
158 (read-openpgp-packet
159 %ed25519-public-key-file))))))
160 (authenticate-commits repository
161 (list commit1 commit2 commit3)
162 #:keyring-reference "master")
163 'failed)))))))
164
165(unless (gpg+git-available?) (test-skip 1))
166(test-assert "signed commits, .guix-authorizations, unauthorized merge"
167 (with-fresh-gnupg-setup (list %ed25519-public-key-file
168 %ed25519-secret-key-file
9ebc9ca0
AL
169 %ed25519-2-public-key-file
170 %ed25519-2-secret-key-file)
c83eedba
LC
171 (with-temporary-git-repository directory
172 `((add "signer1.key"
173 ,(call-with-input-file %ed25519-public-key-file
174 get-string-all))
175 (add "signer2.key"
9ebc9ca0 176 ,(call-with-input-file %ed25519-2-public-key-file
c83eedba
LC
177 get-string-all))
178 (add ".guix-authorizations"
179 ,(object->string
180 `(authorizations (version 0)
181 ((,(key-fingerprint
182 %ed25519-public-key-file)
183 (name "Alice"))))))
184 (commit "zeroth commit")
185 (add "a.txt" "A")
186 (commit "first commit"
187 (signer ,(key-fingerprint %ed25519-public-key-file)))
188 (branch "devel")
189 (checkout "devel")
190 (add "devel/1.txt" "1")
191 (commit "first devel commit"
9ebc9ca0 192 (signer ,(key-fingerprint %ed25519-2-public-key-file)))
c83eedba
LC
193 (checkout "master")
194 (add "b.txt" "B")
195 (commit "second commit"
196 (signer ,(key-fingerprint %ed25519-public-key-file)))
197 (merge "devel" "merge"
198 (signer ,(key-fingerprint %ed25519-public-key-file))))
199 (with-repository directory repository
200 (let ((master1 (find-commit repository "first commit"))
201 (master2 (find-commit repository "second commit"))
202 (devel1 (find-commit repository "first devel commit"))
203 (merge (find-commit repository "merge")))
204 (define (correct? c commit)
205 (and (oid=? (git-authentication-error-commit c)
206 (commit-id commit))
207 (bytevector=?
208 (openpgp-public-key-fingerprint
209 (unauthorized-commit-error-signing-key c))
210 (openpgp-public-key-fingerprint
9ebc9ca0 211 (read-openpgp-packet %ed25519-2-public-key-file)))))
c83eedba
LC
212
213 (and (authenticate-commits repository (list master1 master2)
214 #:keyring-reference "master")
215
216 ;; DEVEL1 is signed by an unauthorized key according to its
217 ;; parent's '.guix-authorizations' file.
218 (guard (c ((unauthorized-commit-error? c)
219 (correct? c devel1)))
220 (authenticate-commits repository
221 (list master1 devel1)
222 #:keyring-reference "master")
223 #f)
224
225 ;; MERGE is authorized but one of its ancestors is not.
226 (guard (c ((unauthorized-commit-error? c)
227 (correct? c devel1)))
228 (authenticate-commits repository
229 (list master1 master2
230 devel1 merge)
231 #:keyring-reference "master")
232 #f)))))))
233
234(unless (gpg+git-available?) (test-skip 1))
235(test-assert "signed commits, .guix-authorizations, authorized merge"
236 (with-fresh-gnupg-setup (list %ed25519-public-key-file
237 %ed25519-secret-key-file
9ebc9ca0
AL
238 %ed25519-2-public-key-file
239 %ed25519-2-secret-key-file)
c83eedba
LC
240 (with-temporary-git-repository directory
241 `((add "signer1.key"
242 ,(call-with-input-file %ed25519-public-key-file
243 get-string-all))
244 (add "signer2.key"
9ebc9ca0 245 ,(call-with-input-file %ed25519-2-public-key-file
c83eedba
LC
246 get-string-all))
247 (add ".guix-authorizations"
248 ,(object->string
249 `(authorizations (version 0)
250 ((,(key-fingerprint
251 %ed25519-public-key-file)
252 (name "Alice"))))))
253 (commit "zeroth commit")
254 (add "a.txt" "A")
255 (commit "first commit"
256 (signer ,(key-fingerprint %ed25519-public-key-file)))
257 (branch "devel")
258 (checkout "devel")
259 (add ".guix-authorizations"
260 ,(object->string ;add the second signer
261 `(authorizations (version 0)
262 ((,(key-fingerprint
263 %ed25519-public-key-file)
264 (name "Alice"))
265 (,(key-fingerprint
9ebc9ca0 266 %ed25519-2-public-key-file))))))
c83eedba
LC
267 (commit "first devel commit"
268 (signer ,(key-fingerprint %ed25519-public-key-file)))
269 (add "devel/2.txt" "2")
270 (commit "second devel commit"
9ebc9ca0 271 (signer ,(key-fingerprint %ed25519-2-public-key-file)))
c83eedba
LC
272 (checkout "master")
273 (add "b.txt" "B")
274 (commit "second commit"
275 (signer ,(key-fingerprint %ed25519-public-key-file)))
276 (merge "devel" "merge"
277 (signer ,(key-fingerprint %ed25519-public-key-file)))
278 ;; After the merge, the second signer is authorized.
279 (add "c.txt" "C")
280 (commit "third commit"
9ebc9ca0 281 (signer ,(key-fingerprint %ed25519-2-public-key-file))))
c83eedba
LC
282 (with-repository directory repository
283 (let ((master1 (find-commit repository "first commit"))
284 (master2 (find-commit repository "second commit"))
285 (devel1 (find-commit repository "first devel commit"))
286 (devel2 (find-commit repository "second devel commit"))
287 (merge (find-commit repository "merge"))
288 (master3 (find-commit repository "third commit")))
289 (authenticate-commits repository
290 (list master1 master2 devel1 devel2
291 merge master3)
292 #:keyring-reference "master"))))))
293
e7827560
LC
294(unless (gpg+git-available?) (test-skip 1))
295(test-assert "signed commits, .guix-authorizations removed"
296 (with-fresh-gnupg-setup (list %ed25519-public-key-file
297 %ed25519-secret-key-file)
298 (with-temporary-git-repository directory
299 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
300 get-string-all))
301 (add ".guix-authorizations"
302 ,(object->string
303 `(authorizations (version 0)
304 ((,(key-fingerprint
305 %ed25519-public-key-file)
306 (name "Charlie"))))))
307 (commit "zeroth commit")
308 (add "a.txt" "A")
309 (commit "first commit"
310 (signer ,(key-fingerprint %ed25519-public-key-file)))
311 (remove ".guix-authorizations")
312 (commit "second commit"
313 (signer ,(key-fingerprint %ed25519-public-key-file)))
314 (add "b.txt" "B")
315 (commit "third commit"
316 (signer ,(key-fingerprint %ed25519-public-key-file))))
317 (with-repository directory repository
318 (let ((commit1 (find-commit repository "first"))
319 (commit2 (find-commit repository "second"))
320 (commit3 (find-commit repository "third")))
321 ;; COMMIT1 and COMMIT2 are fine.
322 (and (authenticate-commits repository (list commit1 commit2)
323 #:keyring-reference "master")
324
325 ;; COMMIT3 is rejected because COMMIT2 removes
326 ;; '.guix-authorizations'.
327 (guard (c ((unauthorized-commit-error? c)
328 (oid=? (git-authentication-error-commit c)
329 (commit-id commit2))))
330 (authenticate-commits repository
331 (list commit1 commit2 commit3)
332 #:keyring-reference "master")
333 'failed)))))))
334
36cb04df
LC
335(unless (gpg+git-available?) (test-skip 1))
336(test-assert "introductory commit, valid signature"
337 (with-fresh-gnupg-setup (list %ed25519-public-key-file
338 %ed25519-secret-key-file)
339 (let ((fingerprint (key-fingerprint %ed25519-public-key-file)))
340 (with-temporary-git-repository directory
341 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
342 get-string-all))
343 (add ".guix-authorizations"
344 ,(object->string
345 `(authorizations (version 0)
346 ((,(key-fingerprint
347 %ed25519-public-key-file)
348 (name "Charlie"))))))
349 (commit "zeroth commit" (signer ,fingerprint))
350 (add "a.txt" "A")
351 (commit "first commit" (signer ,fingerprint)))
352 (with-repository directory repository
353 (let ((commit0 (find-commit repository "zero"))
354 (commit1 (find-commit repository "first")))
355 ;; COMMIT0 is signed with the right key, and COMMIT1 is fine.
356 (authenticate-repository repository
357 (commit-id commit0)
358 (openpgp-fingerprint fingerprint)
359 #:keyring-reference "master"
360 #:cache-key (random-text))))))))
361
362(unless (gpg+git-available?) (test-skip 1))
363(test-equal "introductory commit, missing signature"
364 'intro-lacks-signature
365 (with-fresh-gnupg-setup (list %ed25519-public-key-file
366 %ed25519-secret-key-file)
367 (let ((fingerprint (key-fingerprint %ed25519-public-key-file)))
368 (with-temporary-git-repository directory
369 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
370 get-string-all))
371 (add ".guix-authorizations"
372 ,(object->string
373 `(authorizations (version 0)
374 ((,(key-fingerprint
375 %ed25519-public-key-file)
376 (name "Charlie"))))))
377 (commit "zeroth commit") ;unsigned!
378 (add "a.txt" "A")
379 (commit "first commit" (signer ,fingerprint)))
380 (with-repository directory repository
381 (let ((commit0 (find-commit repository "zero")))
382 ;; COMMIT0 is not signed.
383 (guard (c ((formatted-message? c)
384 ;; Message like "commit ~a lacks a signature".
385 (and (equal? (formatted-message-arguments c)
386 (list (oid->string (commit-id commit0))))
387 'intro-lacks-signature)))
388 (authenticate-repository repository
389 (commit-id commit0)
390 (openpgp-fingerprint fingerprint)
391 #:keyring-reference "master"
392 #:cache-key (random-text)))))))))
393
394(unless (gpg+git-available?) (test-skip 1))
395(test-equal "introductory commit, wrong signature"
396 'wrong-intro-signing-key
397 (with-fresh-gnupg-setup (list %ed25519-public-key-file
398 %ed25519-secret-key-file
399 %ed25519-2-public-key-file
400 %ed25519-2-secret-key-file)
401 (let ((fingerprint (key-fingerprint %ed25519-public-key-file))
402 (wrong-fingerprint (key-fingerprint %ed25519-2-public-key-file)))
403 (with-temporary-git-repository directory
404 `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file
405 get-string-all))
406 (add "signer2.key" ,(call-with-input-file %ed25519-2-public-key-file
407 get-string-all))
408 (add ".guix-authorizations"
409 ,(object->string
410 `(authorizations (version 0)
411 ((,(key-fingerprint
412 %ed25519-public-key-file)
413 (name "Charlie"))))))
414 (commit "zeroth commit" (signer ,wrong-fingerprint))
415 (add "a.txt" "A")
416 (commit "first commit" (signer ,fingerprint)))
417 (with-repository directory repository
418 (let ((commit0 (find-commit repository "zero"))
419 (commit1 (find-commit repository "first")))
420 ;; COMMIT0 is signed with the wrong key--not the one passed as the
421 ;; SIGNER argument to 'authenticate-repository'.
422 (guard (c ((formatted-message? c)
423 ;; Message like "commit ~a signed by ~a instead of ~a".
424 (and (equal? (formatted-message-arguments c)
425 (list (oid->string (commit-id commit0))
426 wrong-fingerprint fingerprint))
427 'wrong-intro-signing-key)))
428 (authenticate-repository repository
429 (commit-id commit0)
430 (openpgp-fingerprint fingerprint)
431 #:keyring-reference "master"
432 #:cache-key (random-text)))))))))
433
ca87601d
LC
434(unless (gpg+git-available?) (test-skip 1))
435(test-equal "authenticate-repository, target not a descendant of intro"
436 'target-commit-not-a-descendant-of-intro
437 (with-fresh-gnupg-setup (list %ed25519-public-key-file
438 %ed25519-secret-key-file)
439 (let ((fingerprint (key-fingerprint %ed25519-public-key-file)))
440 (with-temporary-git-repository directory
441 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
442 get-string-all))
443 (add ".guix-authorizations"
444 ,(object->string
445 `(authorizations (version 0)
446 ((,(key-fingerprint
447 %ed25519-public-key-file)
448 (name "Charlie"))))))
449 (commit "zeroth commit" (signer ,fingerprint))
450 (branch "pre-intro-branch")
451 (checkout "pre-intro-branch")
452 (add "b.txt" "B")
453 (commit "alternate commit" (signer ,fingerprint))
454 (checkout "master")
455 (add "a.txt" "A")
456 (commit "first commit" (signer ,fingerprint))
457 (add "c.txt" "C")
458 (commit "second commit" (signer ,fingerprint)))
459 (with-repository directory repository
460 (let ((commit1 (find-commit repository "first"))
461 (commit-alt
462 (commit-lookup repository
463 (reference-target
464 (branch-lookup repository
465 "pre-intro-branch")))))
466 (guard (c ((formatted-message? c)
467 (and (equal? (formatted-message-arguments c)
468 (list (oid->string (commit-id commit-alt))
469 (oid->string (commit-id commit1))))
470 'target-commit-not-a-descendant-of-intro)))
471 (authenticate-repository repository
472 (commit-id commit1)
473 (openpgp-fingerprint fingerprint)
474 #:end (commit-id commit-alt)
475 #:keyring-reference "master"
476 #:cache-key (random-text)))))))))
477
c83eedba 478(test-end "git-authenticate")