1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
4 ;;; This file is part of GNU Guix.
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.
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.
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/>.
19 (define-module (test-git-authenticate)
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))
33 ;; Test the (guix git-authenticate) tools.
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"))
44 (define (read-openpgp-packet file)
46 (open-bytevector-input-port
47 (call-with-input-file file read-radix-64))))
49 (define key-fingerprint
50 (compose openpgp-format-fingerprint
51 openpgp-public-key-fingerprint
56 (openpgp-public-key-id (read-openpgp-packet)))
58 (string-pad (number->string id 16) 16 #\0))
60 (define (gpg+git-available?)
61 (and (which (git-command))
62 (which (gpg-command)) (which (gpgconf-command))))
65 (test-begin "git-authenticate")
67 (unless (which (git-command)) (test-skip 1))
68 (test-assert "unsigned commits"
69 (with-temporary-git-repository directory
71 (commit "first commit")
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")
84 (unless (gpg+git-available?) (test-skip 1))
85 (test-assert "signed commits, default authorizations"
86 (with-fresh-gnupg-setup (list %ed25519-public-key-file
87 %ed25519-secret-key-file)
88 (with-temporary-git-repository directory
89 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
91 (commit "zeroth commit")
93 (commit "first commit"
94 (signer ,(key-fingerprint %ed25519-public-key-file)))
96 (commit "second commit"
97 (signer ,(key-fingerprint %ed25519-public-key-file))))
98 (with-repository directory repository
99 (let ((commit1 (find-commit repository "first"))
100 (commit2 (find-commit repository "second")))
101 (authenticate-commits repository (list commit1 commit2)
102 #:default-authorizations
103 (list (openpgp-public-key-fingerprint
105 %ed25519-public-key-file)))
106 #:keyring-reference "master"))))))
108 (unless (gpg+git-available?) (test-skip 1))
109 (test-assert "signed commits, .guix-authorizations"
110 (with-fresh-gnupg-setup (list %ed25519-public-key-file
111 %ed25519-secret-key-file)
112 (with-temporary-git-repository directory
113 `((add "signer.key" ,(call-with-input-file %ed25519-public-key-file
115 (add ".guix-authorizations"
117 `(authorizations (version 0)
119 %ed25519-public-key-file)
120 (name "Charlie"))))))
121 (commit "zeroth commit")
123 (commit "first commit"
124 (signer ,(key-fingerprint %ed25519-public-key-file)))
125 (add ".guix-authorizations"
126 ,(object->string `(authorizations (version 0) ()))) ;empty
127 (commit "second commit"
128 (signer ,(key-fingerprint %ed25519-public-key-file)))
130 (commit "third commit"
131 (signer ,(key-fingerprint %ed25519-public-key-file))))
132 (with-repository directory repository
133 (let ((commit1 (find-commit repository "first"))
134 (commit2 (find-commit repository "second"))
135 (commit3 (find-commit repository "third")))
136 ;; COMMIT1 and COMMIT2 are fine.
137 (and (authenticate-commits repository (list commit1 commit2)
138 #:keyring-reference "master")
140 ;; COMMIT3 is signed by an unauthorized key according to its
141 ;; parent's '.guix-authorizations' file.
142 (guard (c ((unauthorized-commit-error? c)
143 (and (oid=? (git-authentication-error-commit c)
146 (openpgp-public-key-fingerprint
147 (unauthorized-commit-error-signing-key c))
148 (openpgp-public-key-fingerprint
150 %ed25519-public-key-file))))))
151 (authenticate-commits repository
152 (list commit1 commit2 commit3)
153 #:keyring-reference "master")
156 (unless (gpg+git-available?) (test-skip 1))
157 (test-assert "signed commits, .guix-authorizations, unauthorized merge"
158 (with-fresh-gnupg-setup (list %ed25519-public-key-file
159 %ed25519-secret-key-file
160 %ed25519bis-public-key-file
161 %ed25519bis-secret-key-file)
162 (with-temporary-git-repository directory
164 ,(call-with-input-file %ed25519-public-key-file
167 ,(call-with-input-file %ed25519bis-public-key-file
169 (add ".guix-authorizations"
171 `(authorizations (version 0)
173 %ed25519-public-key-file)
175 (commit "zeroth commit")
177 (commit "first commit"
178 (signer ,(key-fingerprint %ed25519-public-key-file)))
181 (add "devel/1.txt" "1")
182 (commit "first devel commit"
183 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
186 (commit "second commit"
187 (signer ,(key-fingerprint %ed25519-public-key-file)))
188 (merge "devel" "merge"
189 (signer ,(key-fingerprint %ed25519-public-key-file))))
190 (with-repository directory repository
191 (let ((master1 (find-commit repository "first commit"))
192 (master2 (find-commit repository "second commit"))
193 (devel1 (find-commit repository "first devel commit"))
194 (merge (find-commit repository "merge")))
195 (define (correct? c commit)
196 (and (oid=? (git-authentication-error-commit c)
199 (openpgp-public-key-fingerprint
200 (unauthorized-commit-error-signing-key c))
201 (openpgp-public-key-fingerprint
202 (read-openpgp-packet %ed25519bis-public-key-file)))))
204 (and (authenticate-commits repository (list master1 master2)
205 #:keyring-reference "master")
207 ;; DEVEL1 is signed by an unauthorized key according to its
208 ;; parent's '.guix-authorizations' file.
209 (guard (c ((unauthorized-commit-error? c)
210 (correct? c devel1)))
211 (authenticate-commits repository
212 (list master1 devel1)
213 #:keyring-reference "master")
216 ;; MERGE is authorized but one of its ancestors is not.
217 (guard (c ((unauthorized-commit-error? c)
218 (correct? c devel1)))
219 (authenticate-commits repository
220 (list master1 master2
222 #:keyring-reference "master")
225 (unless (gpg+git-available?) (test-skip 1))
226 (test-assert "signed commits, .guix-authorizations, authorized merge"
227 (with-fresh-gnupg-setup (list %ed25519-public-key-file
228 %ed25519-secret-key-file
229 %ed25519bis-public-key-file
230 %ed25519bis-secret-key-file)
231 (with-temporary-git-repository directory
233 ,(call-with-input-file %ed25519-public-key-file
236 ,(call-with-input-file %ed25519bis-public-key-file
238 (add ".guix-authorizations"
240 `(authorizations (version 0)
242 %ed25519-public-key-file)
244 (commit "zeroth commit")
246 (commit "first commit"
247 (signer ,(key-fingerprint %ed25519-public-key-file)))
250 (add ".guix-authorizations"
251 ,(object->string ;add the second signer
252 `(authorizations (version 0)
254 %ed25519-public-key-file)
257 %ed25519bis-public-key-file))))))
258 (commit "first devel commit"
259 (signer ,(key-fingerprint %ed25519-public-key-file)))
260 (add "devel/2.txt" "2")
261 (commit "second devel commit"
262 (signer ,(key-fingerprint %ed25519bis-public-key-file)))
265 (commit "second commit"
266 (signer ,(key-fingerprint %ed25519-public-key-file)))
267 (merge "devel" "merge"
268 (signer ,(key-fingerprint %ed25519-public-key-file)))
269 ;; After the merge, the second signer is authorized.
271 (commit "third commit"
272 (signer ,(key-fingerprint %ed25519bis-public-key-file))))
273 (with-repository directory repository
274 (let ((master1 (find-commit repository "first commit"))
275 (master2 (find-commit repository "second commit"))
276 (devel1 (find-commit repository "first devel commit"))
277 (devel2 (find-commit repository "second devel commit"))
278 (merge (find-commit repository "merge"))
279 (master3 (find-commit repository "third commit")))
280 (authenticate-commits repository
281 (list master1 master2 devel1 devel2
283 #:keyring-reference "master"))))))
285 (test-end "git-authenticate")