gnu: Add bsnes.
[jackhill/guix/guix.git] / tests / git-authenticate.scm
CommitLineData
c83eedba
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
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)
23 #:use-module (guix openpgp)
24 #:use-module (guix tests git)
25 #:use-module (guix tests gnupg)
26 #:use-module (guix build utils)
27 #:use-module (srfi srfi-1)
28 #:use-module (srfi srfi-34)
29 #:use-module (srfi srfi-64)
30 #:use-module (rnrs bytevectors)
31 #:use-module (rnrs io ports))
32
33;; Test the (guix git-authenticate) tools.
34
35(define %ed25519-public-key-file
36 (search-path %load-path "tests/ed25519.key"))
37(define %ed25519-secret-key-file
38 (search-path %load-path "tests/ed25519.sec"))
39(define %ed25519bis-public-key-file
40 (search-path %load-path "tests/ed25519bis.key"))
41(define %ed25519bis-secret-key-file
42 (search-path %load-path "tests/ed25519bis.sec"))
43
44(define (read-openpgp-packet file)
45 (get-openpgp-packet
46 (open-bytevector-input-port
47 (call-with-input-file file read-radix-64))))
48
49(define key-fingerprint
50 (compose openpgp-format-fingerprint
51 openpgp-public-key-fingerprint
52 read-openpgp-packet))
53
54(define (key-id file)
55 (define id
56 (openpgp-public-key-id (read-openpgp-packet)))
57
58 (string-pad (number->string id 16) 16 #\0))
59
60(define (gpg+git-available?)
61 (and (which (git-command))
62 (which (gpg-command)) (which (gpgconf-command))))
63
64\f
65(test-begin "git-authenticate")
66
67(unless (which (git-command)) (test-skip 1))
68(test-assert "unsigned commits"
69 (with-temporary-git-repository directory
70 '((add "a.txt" "A")
71 (commit "first commit")
72 (add "b.txt" "B")
73 (commit "second commit"))
74 (with-repository directory repository
75 (let ((commit1 (find-commit repository "first"))
76 (commit2 (find-commit repository "second")))
77 (guard (c ((unsigned-commit-error? c)
78 (oid=? (git-authentication-error-commit c)
79 (commit-id commit1))))
80 (authenticate-commits repository (list commit1 commit2)
81 #:keyring-reference "master")
82 'failed)))))
83
52c529ff
LC
84(unless (which (git-command)) (test-skip 1))
85(test-assert "signed commits, SHA1 signature"
86 (with-fresh-gnupg-setup (list %ed25519-public-key-file
87 %ed25519-secret-key-file)
88 ;; Force use of SHA1 for signatures.
89 (call-with-output-file (string-append (getenv "GNUPGHOME") "/gpg.conf")
90 (lambda (port)
91 (display "digest-algo sha1" port)))
92
93 (with-temporary-git-repository directory
94 `((add "a.txt" "A")
95 (add "signer.key" ,(call-with-input-file %ed25519-public-key-file
96 get-string-all))
97 (add ".guix-authorizations"
98 ,(object->string
99 `(authorizations (version 0)
100 ((,(key-fingerprint %ed25519-public-key-file)
101 (name "Charlie"))))))
102 (commit "first commit"
103 (signer ,(key-fingerprint %ed25519-public-key-file))))
104 (with-repository directory repository
105 (let ((commit (find-commit repository "first")))
106 (guard (c ((unsigned-commit-error? c)
107 (oid=? (git-authentication-error-commit c)
108 (commit-id commit))))
109 (authenticate-commits repository (list commit)
110 #:keyring-reference "master")
111 'failed))))))
112
c83eedba
LC
113(unless (gpg+git-available?) (test-skip 1))
114(test-assert "signed commits, default authorizations"
115 (with-fresh-gnupg-setup (list %ed25519-public-key-file
116 %ed25519-secret-key-file)
117 (with-temporary-git-repository directory
118 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
119 get-string-all))
120 (commit "zeroth commit")
121 (add "a.txt" "A")
122 (commit "first commit"
123 (signer ,(key-fingerprint %ed25519-public-key-file)))
124 (add "b.txt" "B")
125 (commit "second commit"
126 (signer ,(key-fingerprint %ed25519-public-key-file))))
127 (with-repository directory repository
128 (let ((commit1 (find-commit repository "first"))
129 (commit2 (find-commit repository "second")))
130 (authenticate-commits repository (list commit1 commit2)
131 #:default-authorizations
132 (list (openpgp-public-key-fingerprint
133 (read-openpgp-packet
134 %ed25519-public-key-file)))
135 #:keyring-reference "master"))))))
136
137(unless (gpg+git-available?) (test-skip 1))
138(test-assert "signed commits, .guix-authorizations"
139 (with-fresh-gnupg-setup (list %ed25519-public-key-file
140 %ed25519-secret-key-file)
141 (with-temporary-git-repository directory
142 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
143 get-string-all))
144 (add ".guix-authorizations"
145 ,(object->string
146 `(authorizations (version 0)
147 ((,(key-fingerprint
148 %ed25519-public-key-file)
149 (name "Charlie"))))))
150 (commit "zeroth commit")
151 (add "a.txt" "A")
152 (commit "first commit"
153 (signer ,(key-fingerprint %ed25519-public-key-file)))
154 (add ".guix-authorizations"
155 ,(object->string `(authorizations (version 0) ()))) ;empty
156 (commit "second commit"
157 (signer ,(key-fingerprint %ed25519-public-key-file)))
158 (add "b.txt" "B")
159 (commit "third commit"
160 (signer ,(key-fingerprint %ed25519-public-key-file))))
161 (with-repository directory repository
162 (let ((commit1 (find-commit repository "first"))
163 (commit2 (find-commit repository "second"))
164 (commit3 (find-commit repository "third")))
165 ;; COMMIT1 and COMMIT2 are fine.
166 (and (authenticate-commits repository (list commit1 commit2)
167 #:keyring-reference "master")
168
169 ;; COMMIT3 is signed by an unauthorized key according to its
170 ;; parent's '.guix-authorizations' file.
171 (guard (c ((unauthorized-commit-error? c)
172 (and (oid=? (git-authentication-error-commit c)
173 (commit-id commit3))
174 (bytevector=?
175 (openpgp-public-key-fingerprint
176 (unauthorized-commit-error-signing-key c))
177 (openpgp-public-key-fingerprint
178 (read-openpgp-packet
179 %ed25519-public-key-file))))))
180 (authenticate-commits repository
181 (list commit1 commit2 commit3)
182 #:keyring-reference "master")
183 'failed)))))))
184
185(unless (gpg+git-available?) (test-skip 1))
186(test-assert "signed commits, .guix-authorizations, unauthorized merge"
187 (with-fresh-gnupg-setup (list %ed25519-public-key-file
188 %ed25519-secret-key-file
189 %ed25519bis-public-key-file
190 %ed25519bis-secret-key-file)
191 (with-temporary-git-repository directory
192 `((add "signer1.key"
193 ,(call-with-input-file %ed25519-public-key-file
194 get-string-all))
195 (add "signer2.key"
196 ,(call-with-input-file %ed25519bis-public-key-file
197 get-string-all))
198 (add ".guix-authorizations"
199 ,(object->string
200 `(authorizations (version 0)
201 ((,(key-fingerprint
202 %ed25519-public-key-file)
203 (name "Alice"))))))
204 (commit "zeroth commit")
205 (add "a.txt" "A")
206 (commit "first commit"
207 (signer ,(key-fingerprint %ed25519-public-key-file)))
208 (branch "devel")
209 (checkout "devel")
210 (add "devel/1.txt" "1")
211 (commit "first devel commit"
212 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
213 (checkout "master")
214 (add "b.txt" "B")
215 (commit "second commit"
216 (signer ,(key-fingerprint %ed25519-public-key-file)))
217 (merge "devel" "merge"
218 (signer ,(key-fingerprint %ed25519-public-key-file))))
219 (with-repository directory repository
220 (let ((master1 (find-commit repository "first commit"))
221 (master2 (find-commit repository "second commit"))
222 (devel1 (find-commit repository "first devel commit"))
223 (merge (find-commit repository "merge")))
224 (define (correct? c commit)
225 (and (oid=? (git-authentication-error-commit c)
226 (commit-id commit))
227 (bytevector=?
228 (openpgp-public-key-fingerprint
229 (unauthorized-commit-error-signing-key c))
230 (openpgp-public-key-fingerprint
231 (read-openpgp-packet %ed25519bis-public-key-file)))))
232
233 (and (authenticate-commits repository (list master1 master2)
234 #:keyring-reference "master")
235
236 ;; DEVEL1 is signed by an unauthorized key according to its
237 ;; parent's '.guix-authorizations' file.
238 (guard (c ((unauthorized-commit-error? c)
239 (correct? c devel1)))
240 (authenticate-commits repository
241 (list master1 devel1)
242 #:keyring-reference "master")
243 #f)
244
245 ;; MERGE is authorized but one of its ancestors is not.
246 (guard (c ((unauthorized-commit-error? c)
247 (correct? c devel1)))
248 (authenticate-commits repository
249 (list master1 master2
250 devel1 merge)
251 #:keyring-reference "master")
252 #f)))))))
253
254(unless (gpg+git-available?) (test-skip 1))
255(test-assert "signed commits, .guix-authorizations, authorized merge"
256 (with-fresh-gnupg-setup (list %ed25519-public-key-file
257 %ed25519-secret-key-file
258 %ed25519bis-public-key-file
259 %ed25519bis-secret-key-file)
260 (with-temporary-git-repository directory
261 `((add "signer1.key"
262 ,(call-with-input-file %ed25519-public-key-file
263 get-string-all))
264 (add "signer2.key"
265 ,(call-with-input-file %ed25519bis-public-key-file
266 get-string-all))
267 (add ".guix-authorizations"
268 ,(object->string
269 `(authorizations (version 0)
270 ((,(key-fingerprint
271 %ed25519-public-key-file)
272 (name "Alice"))))))
273 (commit "zeroth commit")
274 (add "a.txt" "A")
275 (commit "first commit"
276 (signer ,(key-fingerprint %ed25519-public-key-file)))
277 (branch "devel")
278 (checkout "devel")
279 (add ".guix-authorizations"
280 ,(object->string ;add the second signer
281 `(authorizations (version 0)
282 ((,(key-fingerprint
283 %ed25519-public-key-file)
284 (name "Alice"))
285 (,(key-fingerprint
286 %ed25519bis-public-key-file))))))
287 (commit "first devel commit"
288 (signer ,(key-fingerprint %ed25519-public-key-file)))
289 (add "devel/2.txt" "2")
290 (commit "second devel commit"
291 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
292 (checkout "master")
293 (add "b.txt" "B")
294 (commit "second commit"
295 (signer ,(key-fingerprint %ed25519-public-key-file)))
296 (merge "devel" "merge"
297 (signer ,(key-fingerprint %ed25519-public-key-file)))
298 ;; After the merge, the second signer is authorized.
299 (add "c.txt" "C")
300 (commit "third commit"
301 (signer ,(key-fingerprint %ed25519bis-public-key-file))))
302 (with-repository directory repository
303 (let ((master1 (find-commit repository "first commit"))
304 (master2 (find-commit repository "second commit"))
305 (devel1 (find-commit repository "first devel commit"))
306 (devel2 (find-commit repository "second devel commit"))
307 (merge (find-commit repository "merge"))
308 (master3 (find-commit repository "third commit")))
309 (authenticate-commits repository
310 (list master1 master2 devel1 devel2
311 merge master3)
312 #:keyring-reference "master"))))))
313
e7827560
LC
314(unless (gpg+git-available?) (test-skip 1))
315(test-assert "signed commits, .guix-authorizations removed"
316 (with-fresh-gnupg-setup (list %ed25519-public-key-file
317 %ed25519-secret-key-file)
318 (with-temporary-git-repository directory
319 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
320 get-string-all))
321 (add ".guix-authorizations"
322 ,(object->string
323 `(authorizations (version 0)
324 ((,(key-fingerprint
325 %ed25519-public-key-file)
326 (name "Charlie"))))))
327 (commit "zeroth commit")
328 (add "a.txt" "A")
329 (commit "first commit"
330 (signer ,(key-fingerprint %ed25519-public-key-file)))
331 (remove ".guix-authorizations")
332 (commit "second commit"
333 (signer ,(key-fingerprint %ed25519-public-key-file)))
334 (add "b.txt" "B")
335 (commit "third commit"
336 (signer ,(key-fingerprint %ed25519-public-key-file))))
337 (with-repository directory repository
338 (let ((commit1 (find-commit repository "first"))
339 (commit2 (find-commit repository "second"))
340 (commit3 (find-commit repository "third")))
341 ;; COMMIT1 and COMMIT2 are fine.
342 (and (authenticate-commits repository (list commit1 commit2)
343 #:keyring-reference "master")
344
345 ;; COMMIT3 is rejected because COMMIT2 removes
346 ;; '.guix-authorizations'.
347 (guard (c ((unauthorized-commit-error? c)
348 (oid=? (git-authentication-error-commit c)
349 (commit-id commit2))))
350 (authenticate-commits repository
351 (list commit1 commit2 commit3)
352 #:keyring-reference "master")
353 'failed)))))))
354
c83eedba
LC
355(test-end "git-authenticate")
356