From: "Ludovic Courtès" <ludo@gnu.org>
To: 42225@debbugs.gnu.org
Cc: "Ludovic Courtès" <ludo@gnu.org>
Subject: [bug#42225] [PATCH 1/5] git-authenticate: Factorize 'authenticate-repository'.
Date: Mon, 6 Jul 2020 10:43:52 +0200 [thread overview]
Message-ID: <20200706084356.13013-1-ludo@gnu.org> (raw)
In-Reply-To: <20200706083755.11778-1-ludo@gnu.org>
* guix/git-authenticate.scm (repository-cache-key)
(verify-introductory-commit, authenticate-repository): New procedures.
* guix/channels.scm (verify-introductory-commit): Remove.
(authenticate-channel): Rewrite in terms of 'authenticate-repository'.
---
guix/channels.scm | 118 ++++++++++----------------------------
guix/git-authenticate.scm | 101 +++++++++++++++++++++++++++++++-
2 files changed, 131 insertions(+), 88 deletions(-)
diff --git a/guix/channels.scm b/guix/channels.scm
index 500c956f0f..bbabf654a9 100644
--- a/guix/channels.scm
+++ b/guix/channels.scm
@@ -315,100 +315,44 @@ result is unspecified."
(define commit-short-id
(compose (cut string-take <> 7) oid->string commit-id))
-(define (verify-introductory-commit repository introduction keyring)
- "Raise an exception if the first commit described in INTRODUCTION doesn't
-have the expected signer."
- (define commit-id
- (channel-introduction-first-signed-commit introduction))
-
- (define actual-signer
- (openpgp-public-key-fingerprint
- (commit-signing-key repository (string->oid commit-id)
- keyring)))
-
- (define expected-signer
- (channel-introduction-first-commit-signer introduction))
-
- (unless (bytevector=? expected-signer actual-signer)
- (raise (condition
- (&message
- (message (format #f (G_ "initial commit ~a is signed by '~a' \
-instead of '~a'")
- commit-id
- (openpgp-format-fingerprint actual-signer)
- (openpgp-format-fingerprint expected-signer))))))))
-
(define* (authenticate-channel channel checkout commit
#:key (keyring-reference-prefix "origin/"))
"Authenticate the given COMMIT of CHANNEL, available at CHECKOUT, a
directory containing a CHANNEL checkout. Raise an error if authentication
fails."
+ (define intro
+ (channel-introduction channel))
+
+ (define cache-key
+ (string-append "channels/" (symbol->string (channel-name channel))))
+
+ (define keyring-reference
+ (channel-metadata-keyring-reference
+ (read-channel-metadata-from-source checkout)))
+
+ (define (make-reporter start-commit end-commit commits)
+ (format (current-error-port)
+ (G_ "Authenticating channel '~a', commits ~a to ~a (~h new \
+commits)...~%")
+ (channel-name channel)
+ (commit-short-id start-commit)
+ (commit-short-id end-commit)
+ (length commits))
+
+ (progress-reporter/bar (length commits)))
+
;; XXX: Too bad we need to re-open CHECKOUT.
(with-repository checkout repository
- (define start-commit
- (commit-lookup repository
- (string->oid
- (channel-introduction-first-signed-commit
- (channel-introduction channel)))))
-
- (define end-commit
- (commit-lookup repository (string->oid commit)))
-
- (define cache-key
- (string-append "channels/" (symbol->string (channel-name channel))))
-
- (define keyring-reference
- (channel-metadata-keyring-reference
- (read-channel-metadata-from-source checkout)))
-
- (define keyring
- (load-keyring-from-reference repository
- (string-append keyring-reference-prefix
- keyring-reference)))
-
- (define authenticated-commits
- ;; Previously-authenticated commits that don't need to be checked again.
- (filter-map (lambda (id)
- (false-if-exception
- (commit-lookup repository (string->oid id))))
- (previously-authenticated-commits cache-key)))
-
- (define commits
- ;; Commits to authenticate, excluding the closure of
- ;; AUTHENTICATED-COMMITS.
- (commit-difference end-commit start-commit
- authenticated-commits))
-
- (define reporter
- (progress-reporter/bar (length commits)))
-
- ;; When COMMITS is empty, it's because END-COMMIT is in the closure of
- ;; START-COMMIT and/or AUTHENTICATED-COMMITS, in which case it's known to
- ;; be authentic already.
- (unless (null? commits)
- (format (current-error-port)
- (G_ "Authenticating channel '~a', \
-commits ~a to ~a (~h new commits)...~%")
- (channel-name channel)
- (commit-short-id start-commit)
- (commit-short-id end-commit)
- (length commits))
-
- ;; If it's our first time, verify CHANNEL's introductory commit.
- (when (null? authenticated-commits)
- (verify-introductory-commit repository
- (channel-introduction channel)
- keyring))
-
- (call-with-progress-reporter reporter
- (lambda (report)
- (authenticate-commits repository commits
- #:keyring keyring
- #:report-progress report)))
-
- (cache-authenticated-commit cache-key
- (oid->string
- (commit-id end-commit))))))
+ (authenticate-repository repository
+ (string->oid
+ (channel-introduction-first-signed-commit intro))
+ (channel-introduction-first-commit-signer intro)
+ #:end (string->oid commit)
+ #:keyring-reference
+ (string-append keyring-reference-prefix
+ keyring-reference)
+ #:make-reporter make-reporter
+ #:cache-key cache-key)))
(define* (latest-channel-instance store channel
#:key (patches %patches)
diff --git a/guix/git-authenticate.scm b/guix/git-authenticate.scm
index 082c44ee06..99fd9c3594 100644
--- a/guix/git-authenticate.scm
+++ b/guix/git-authenticate.scm
@@ -18,14 +18,18 @@
(define-module (guix git-authenticate)
#:use-module (git)
+ #:autoload (gcrypt hash) (sha256)
#:use-module (guix base16)
- #:use-module ((guix git) #:select (false-if-git-not-found))
+ #:autoload (guix base64) (base64-encode)
+ #:use-module ((guix git)
+ #:select (commit-difference false-if-git-not-found))
#:use-module (guix i18n)
#:use-module (guix openpgp)
#:use-module ((guix utils)
#:select (cache-directory with-atomic-file-output))
#:use-module ((guix build utils)
#:select (mkdir-p))
+ #:use-module (guix progress)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-26)
@@ -44,6 +48,9 @@
previously-authenticated-commits
cache-authenticated-commit
+ repository-cache-key
+ authenticate-repository
+
git-authentication-error?
git-authentication-error-commit
unsigned-commit-error?
@@ -339,3 +346,95 @@ authenticated (only COMMIT-ID is written to cache, though)."
(display ";; List of previously-authenticated commits.\n\n"
port)
(pretty-print lst port))))))
+
+\f
+;;;
+;;; High-level interface.
+;;;
+
+(define (repository-cache-key repository)
+ "Return a unique key to store the authenticate commit cache for REPOSITORY."
+ (string-append "checkouts/"
+ (base64-encode
+ (sha256 (string->utf8 (repository-directory repository))))))
+
+(define (verify-introductory-commit repository keyring commit expected-signer)
+ "Look up COMMIT in REPOSITORY, and raise an exception if it is not signed by
+EXPECTED-SIGNER."
+ (define actual-signer
+ (openpgp-public-key-fingerprint
+ (commit-signing-key repository (commit-id commit) keyring)))
+
+ (unless (bytevector=? expected-signer actual-signer)
+ (raise (condition
+ (&message
+ (message (format #f (G_ "initial commit ~a is signed by '~a' \
+instead of '~a'")
+ (oid->string (commit-id commit))
+ (openpgp-format-fingerprint actual-signer)
+ (openpgp-format-fingerprint expected-signer))))))))
+
+(define* (authenticate-repository repository start signer
+ #:key
+ (keyring-reference "keyring")
+ (cache-key (repository-cache-key repository))
+ (end (reference-target
+ (repository-head repository)))
+ (historical-authorizations '())
+ (make-reporter
+ (const progress-reporter/silent)))
+ "Authenticate REPOSITORY up to commit END, an OID. Authentication starts
+with commit START, an OID, which must be signed by SIGNER; an exception is
+raised if that is not the case. Return an alist mapping OpenPGP public keys
+to the number of commits signed by that key that have been traversed.
+
+The OpenPGP keyring is loaded from KEYRING-REFERENCE in REPOSITORY, where
+KEYRING-REFERENCE is the name of a branch. The list of authenticated commits
+is cached in the authentication cache under CACHE-KEY.
+
+HISTORICAL-AUTHORIZATIONS must be a list of OpenPGP fingerprints (bytevectors)
+denoting the authorized keys for commits whose parent lack the
+'.guix-authorizations' file."
+ (define start-commit
+ (commit-lookup repository start))
+ (define end-commit
+ (commit-lookup repository end))
+
+ (define keyring
+ (load-keyring-from-reference repository keyring-reference))
+
+ (define authenticated-commits
+ ;; Previously-authenticated commits that don't need to be checked again.
+ (filter-map (lambda (id)
+ (false-if-git-not-found
+ (commit-lookup repository (string->oid id))))
+ (previously-authenticated-commits cache-key)))
+
+ (define commits
+ ;; Commits to authenticate, excluding the closure of
+ ;; AUTHENTICATED-COMMITS.
+ (commit-difference end-commit start-commit
+ authenticated-commits))
+
+ ;; When COMMITS is empty, it's because END-COMMIT is in the closure of
+ ;; START-COMMIT and/or AUTHENTICATED-COMMITS, in which case it's known to
+ ;; be authentic already.
+ (if (null? commits)
+ '()
+ (let ((reporter (make-reporter start-commit end-commit commits)))
+ ;; If it's our first time, verify START-COMMIT's signature.
+ (when (null? authenticated-commits)
+ (verify-introductory-commit repository keyring
+ start-commit signer))
+
+ (let ((stats (call-with-progress-reporter reporter
+ (lambda (report)
+ (authenticate-commits repository commits
+ #:keyring keyring
+ #:default-authorizations
+ historical-authorizations
+ #:report-progress report)))))
+ (cache-authenticated-commit cache-key
+ (oid->string (commit-id end-commit)))
+
+ stats))))
--
2.26.2
next prev parent reply other threads:[~2020-07-06 8:45 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-07-06 8:37 [bug#42225] [PATCH 0/5] Add 'guix git-authenticate' command Ludovic Courtès
2020-07-06 8:43 ` Ludovic Courtès [this message]
2020-07-06 8:43 ` [bug#42225] [PATCH 2/5] Add 'etc/historical-authorizations' Ludovic Courtès
2020-07-06 8:43 ` [bug#42225] [PATCH 3/5] git: Add 'with-git-error-handling' Ludovic Courtès
2020-07-06 8:43 ` [bug#42225] [PATCH 4/5] Add 'guix git-authenticate' Ludovic Courtès
2020-07-06 8:43 ` [bug#42225] [PATCH 5/5] maint: Remove 'build-aux/git-authenticate.scm' Ludovic Courtès
2020-07-06 9:24 ` [bug#42225] [PATCH 0/5] Add 'guix git-authenticate' command zimoun
2020-07-06 12:46 ` Ludovic Courtès
2020-07-06 13:27 ` zimoun
2020-07-06 19:48 ` Ludovic Courtès
2020-07-06 21:27 ` zimoun
2020-07-11 11:01 ` bug#42225: " Ludovic Courtès
2020-07-16 0:12 ` [bug#42225] " zimoun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://guix.gnu.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200706084356.13013-1-ludo@gnu.org \
--to=ludo@gnu.org \
--cc=42225@debbugs.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/guix.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).