Skip to content
Snippets Groups Projects
Unverified Commit 051a45e6 authored by Ludovic Courtès's avatar Ludovic Courtès
Browse files

git-authenticate: Use (guix openpgp).

It can now authenticate 14K+ commits in 23s instead of 4mn20.

* build-aux/git-authenticate.scm (%authorized-signing-keys): Turn
fingerprints into bytevectors.
(with-temporary-files): Remove.
(commit-signing-key): Add 'keyring' parameter.  Use
'string->openpgp-packet' and 'verify-openpgp-signature' instead of (guix
gnupg) procedures.
(authenticate-commit): Add 'keyring' parameter.  Pass it to
'commit-signing-key'.  Adjust to SIGNING-KEY being an <openpgp-public-key>.
(authenticate-commits): Remove 'parameterize'.  Load keyring with
'get-openpgp-keyring'.
(git-authenticate): When printing stats, adjust to SIGNER being an
<openpgp-public-key>.
parent b835e158
No related branches found
No related tags found
No related merge requests found
...@@ -23,8 +23,9 @@ ...@@ -23,8 +23,9 @@
(use-modules (git) (use-modules (git)
(guix git) (guix git)
(guix gnupg) (guix openpgp)
(guix utils) ((guix utils) #:select (config-directory))
(guix base16)
((guix build utils) #:select (mkdir-p)) ((guix build utils) #:select (mkdir-p))
(guix i18n) (guix i18n)
(guix progress) (guix progress)
...@@ -215,7 +216,8 @@ (define %authorized-signing-keys ...@@ -215,7 +216,8 @@ (define %authorized-signing-keys
;; Fingerprint of authorized signing keys. ;; Fingerprint of authorized signing keys.
(map (match-lambda (map (match-lambda
((name fingerprint) ((name fingerprint)
(string-filter char-set:graphic fingerprint))) (base16-string->bytevector
(string-downcase (string-filter char-set:graphic fingerprint)))))
%committers)) %committers))
(define %commits-with-bad-signature (define %commits-with-bad-signature
...@@ -226,75 +228,63 @@ (define %unsigned-commits ...@@ -226,75 +228,63 @@ (define %unsigned-commits
;; Commits lacking a signature. ;; Commits lacking a signature.
'()) '())
(define-syntax-rule (with-temporary-files file1 file2 exp ...) (define (commit-signing-key repo commit-id keyring)
(call-with-temporary-output-file "Return the OpenPGP key that signed COMMIT-ID (an OID). Raise an exception
(lambda (file1 port1) if the commit is unsigned, has an invalid signature, or if its signing key is
(call-with-temporary-output-file not in KEYRING."
(lambda (file2 port2)
exp ...)))))
(define (commit-signing-key repo commit-id)
"Return the OpenPGP key ID that signed COMMIT-ID (an OID). Raise an
exception if the commit is unsigned or has an invalid signature."
(let-values (((signature signed-data) (let-values (((signature signed-data)
(catch 'git-error (catch 'git-error
(lambda () (lambda ()
(commit-extract-signature repo commit-id)) (commit-extract-signature repo commit-id))
(lambda _ (lambda _
(values #f #f))))) (values #f #f)))))
(if (not signature) (unless signature
(raise (condition (raise (condition
(&message (&message
(message (format #f (G_ "commit ~a lacks a signature") (message (format #f (G_ "commit ~a lacks a signature")
commit-id))))) commit-id))))))
(begin
(with-fluids ((%default-port-encoding "UTF-8")) (let ((signature (string->openpgp-packet signature)))
(with-temporary-files data-file signature-file (with-fluids ((%default-port-encoding "UTF-8"))
(call-with-output-file data-file (let-values (((status data)
(cut display signed-data <>)) (verify-openpgp-signature signature keyring
(call-with-output-file signature-file (open-input-string signed-data))))
(cut display signature <>)) (match status
('bad-signature
(let-values (((status data) ;; There's a signature but it's invalid.
(with-error-to-port (%make-void-port "w") (raise (condition
(lambda () (&message
(gnupg-verify* signature-file data-file (message (format #f (G_ "signature verification failed \
#:key-download 'always)))))
(match status
('invalid-signature
;; There's a signature but it's invalid.
(raise (condition
(&message
(message (format #f (G_ "signature verification failed \
for commit ~a") for commit ~a")
(oid->string commit-id))))))) (oid->string commit-id)))))))
('missing-key ('missing-key
(raise (condition (raise (condition
(&message (&message
(message (format #f (G_ "could not authenticate \ (message (format #f (G_ "could not authenticate \
commit ~a: key ~a is missing") commit ~a: key ~a is missing")
(oid->string commit-id) (oid->string commit-id)
data)))))) data))))))
('valid-signature ('good-signature data)))))))
(match data
((fingerprint . user) (define (authenticate-commit repository commit keyring)
fingerprint)))))))))))
(define (authenticate-commit repository commit)
"Authenticate COMMIT from REPOSITORY and return the signing key fingerprint. "Authenticate COMMIT from REPOSITORY and return the signing key fingerprint.
Raise an error when authentication fails." Raise an error when authentication fails."
(define id (define id
(commit-id commit)) (commit-id commit))
(define signing-key (define signing-key
(commit-signing-key repository id)) (commit-signing-key repository id keyring))
(unless (member signing-key %authorized-signing-keys) (unless (member (openpgp-public-key-fingerprint signing-key)
%authorized-signing-keys)
(raise (condition (raise (condition
(&message (&message
(message (format #f (G_ "commit ~a not signed by an authorized \ (message (format #f (G_ "commit ~a not signed by an authorized \
key: ~a") key: ~a")
(oid->string id) signing-key)))))) (oid->string id)
(openpgp-format-fingerprint
(openpgp-public-key-fingerprint
signing-key))))))))
signing-key) signing-key)
...@@ -302,17 +292,21 @@ (define* (authenticate-commits repository commits ...@@ -302,17 +292,21 @@ (define* (authenticate-commits repository commits
#:key (report-progress (const #t))) #:key (report-progress (const #t)))
"Authenticate COMMITS, a list of commit objects, calling REPORT-PROGRESS for "Authenticate COMMITS, a list of commit objects, calling REPORT-PROGRESS for
each of them. Return an alist showing the number of occurrences of each key." each of them. Return an alist showing the number of occurrences of each key."
(parameterize ((current-keyring (string-append (config-directory) (define keyring-file
"/keyrings/channels/guix.kbx"))) (string-append (config-directory) "/keyrings/channels/guix.kbx"))
(fold (lambda (commit stats)
(report-progress) (define keyring
(let ((signer (authenticate-commit repository commit))) (call-with-input-file keyring-file get-openpgp-keyring))
(match (assoc signer stats)
(#f (cons `(,signer . 1) stats)) (fold (lambda (commit stats)
((_ . count) (cons `(,signer . ,(+ count 1)) (report-progress)
(alist-delete signer stats)))))) (let ((signer (authenticate-commit repository commit keyring)))
'() (match (assq signer stats)
commits))) (#f (cons `(,signer . 1) stats))
((_ . count) (cons `(,signer . ,(+ count 1))
(alist-delete signer stats))))))
'()
commits))
(define commit-short-id (define commit-short-id
(compose (cut string-take <> 7) oid->string commit-id)) (compose (cut string-take <> 7) oid->string commit-id))
...@@ -409,7 +403,10 @@ (define reporter ...@@ -409,7 +403,10 @@ (define reporter
(format #t (G_ "Signing statistics:~%")) (format #t (G_ "Signing statistics:~%"))
(for-each (match-lambda (for-each (match-lambda
((signer . count) ((signer . count)
(format #t " ~a ~10d~%" signer count))) (format #t " ~a ~10d~%"
(openpgp-format-fingerprint
(openpgp-public-key-fingerprint signer))
count)))
(sort stats (sort stats
(match-lambda* (match-lambda*
(((_ . count1) (_ . count2)) (((_ . count1) (_ . count2))
...@@ -423,7 +420,3 @@ (define reporter ...@@ -423,7 +420,3 @@ (define reporter
(G_ "Usage: git-authenticate START [END] (G_ "Usage: git-authenticate START [END]
Authenticate commits START to END or the current head.\n")))))) Authenticate commits START to END or the current head.\n"))))))
;;; Local Variables:
;;; eval: (put 'with-temporary-files 'scheme-indent-function 2)
;;; End:
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment