[PATCH] guix: git-authenticate: Also authenticate the channel intro commit.

OpenSubmitted by Attila Lendvai.
Details
4 participants
  • Attila Lendvai
  • Leo Famulari
  • Ludovic Courtès
  • Maxime Devos
Owner
unassigned
Severity
important
A
A
Attila Lendvai wrote on 26 Sep 12:19 +0200
(address . guix-patches@gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210926101928.3877-1-attila@lendvai.name
* guix/git-authenticate.scm (authenticate-commit): Reword and extend the errormessage to point to the relevant part of the manual.(authenticate-repository): Explicitly authenticate the channel introductioncommit, so that it's also rejected unless it is signed by an authorizedkey. Otherwise only the second commit would yield an error, whichis confusing.---
here's how i tested this:
i set up pulling from a local checkout of guix.in that branch i created a signed dummy commit, and added it as a channelintroduction, replacing guix in my /etc/guix/channels.scm. then tried toguix pull, which worked.
then i added another dummy commit, which resulted in an error when pulling.
then i reset the branch back to only contain the first commit, and addedthis code that then resulted in an error even with a single commit.
i have encountered it while i was trying to set up my local checkout totest my patches on my live guix, and i was utterly confused why my commitwas rejected as unauthenticated (i misunderstood how git-authenticateworks).
guix/git-authenticate.scm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
Toggle diff (31 lines)diff --git a/guix/git-authenticate.scm b/guix/git-authenticate.scmindex ab3fcd8b2f..7d66bf0754 100644--- a/guix/git-authenticate.scm+++ b/guix/git-authenticate.scm@@ -236,8 +236,8 @@ not specify anything, fall back to DEFAULT-AUTHORIZATIONS." (condition (&unauthorized-commit-error (commit id) (signing-key signing-key)))- (formatted-message (G_ "commit ~a not signed by an authorized \-key: ~a")+ (formatted-message (G_ "commit ~a is signed by an unauthorized \+key: ~a\nSee info guix \"Specifying Channel Authorizations\".") (oid->string id) (openpgp-format-fingerprint (openpgp-public-key-fingerprint@@ -424,7 +424,12 @@ denoting the authorized keys for commits whose parent lack the ;; If it's our first time, verify START-COMMIT's signature. (when (null? authenticated-commits) (verify-introductory-commit repository keyring- start-commit signer))+ start-commit signer)+ ;; Explicitly authenticate the channel introduction commit, so that+ ;; it's also rejected unless it's signed by an authorized+ ;; key. Otherwise only the second commit would yield an error, which+ ;; is confusing.+ (authenticate-commits repository (list start-commit))) (let ((stats (call-with-progress-reporter reporter (lambda (report)-- 2.33.0
L
L
Leo Famulari wrote on 26 Sep 20:00 +0200
(no subject)
(address . control@debbugs.gnu.org)
YVC1Uk0UdbZZ9LyF@jasmine.lan
severity 50814 grave
L
L
Leo Famulari wrote on 26 Sep 20:02 +0200
Re: [bug#50814] [PATCH] guix: git-authenticate: Also authenticate the channel intro commit.
(name . Attila Lendvai)(address . attila@lendvai.name)
YVC1pWYSF7ccbSs9@jasmine.lan
On Sun, Sep 26, 2021 at 12:19:29PM +0200, Attila Lendvai wrote:
Toggle quote (25 lines)> * guix/git-authenticate.scm (authenticate-commit): Reword and extend the error> message to point to the relevant part of the manual.> (authenticate-repository): Explicitly authenticate the channel introduction> commit, so that it's also rejected unless it is signed by an authorized> key. Otherwise only the second commit would yield an error, which> is confusing.> ---> > here's how i tested this:> > i set up pulling from a local checkout of guix.> in that branch i created a signed dummy commit, and added it as a channel> introduction, replacing guix in my /etc/guix/channels.scm. then tried to> guix pull, which worked.> > then i added another dummy commit, which resulted in an error when pulling.> > then i reset the branch back to only contain the first commit, and added> this code that then resulted in an error even with a single commit.> > i have encountered it while i was trying to set up my local checkout to> test my patches on my live guix, and i was utterly confused why my commit> was rejected as unauthenticated (i misunderstood how git-authenticate> works).
Thanks for your report.
I've marked the severity as "grave", which in Debbugs parlance means"makes the package in question unusable or mostly so, or causes dataloss, or introduces a security hole allowing access to the accounts ofusers who use the package."
https://debbugs.gnu.org/Developer.html#severities
I'm not sure if that's justified or not but this patch should beprioritized.
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEsFFZSPHn08G5gDigJkb6MLrKfwgFAmFQtaEACgkQJkb6MLrKfwiStg/8D7IUgkR/RBfYkEhlrIbZbOFfx/Iwo9vPZonbtGREFlbCtzJKLtmZjE8/SfDdEseCOHiRqVD6wO026A52zUyyrLywiw54bgAwYHn+AE6iy6i2+dh/Dv3H4sGAqVEt2M1Oh1Fu7Hd+CwXjpE94OCvX/qjn/mOX6S56TkbN5CU5C9VnTsLFux0HvXbQTUpRgOxoe3MyGnA2GAk6gjNI4gOVRSEFf86Zl6id5136yxDDucPt5yptbNFSgwZ2wUvgnxWXpQRAK5QoMLTRZPiJoNk5wo8qAKxcJci6q+t1h5af9AttdDh1Lg2YUe/JJQPa4C7LpcIKTqRdV1EEgZz0PG7qeyIFz3JpDi0AhkmUUoWZuPSuBlBesGP/sJtAIkQcKp7Tka8dy04ID+MXqU9i/nyB+4tXe8jOPp8sG8fblT58uFNb66LEoXvrhW3AffiUZuvf1qDixE2lu9dRhNDjPMLjALffapuxHMLd689Vjp/7lTv0+Kj5JF0iSIr0a29vDtP/hro1J0eOdSMUlVQ7Np7ubY3CIJMk811WbR9pVHOmCSV5HGCmeoYkLeb7k8BGhCdTSIvQFdzs8kQW4GCBfVnnw+mAFov9MntGPRVTe9N1puzEtAzwnZmElKZp0TD6D8c6j2vuGo66pQXlOOc30DuueHBdphW49G4Tp7nFanuo+Js==mvsg-----END PGP SIGNATURE-----

M
M
Maxime Devos wrote on 26 Sep 20:14 +0200
2b0173cc9809ab1e806bf0061fc28a9a85dda6e0.camel@telenet.be
Attila Lendvai schreef op zo 26-09-2021 om 12:19 [+0200]:
Toggle quote (55 lines)> * guix/git-authenticate.scm (authenticate-commit): Reword and extend the error> message to point to the relevant part of the manual.> (authenticate-repository): Explicitly authenticate the channel introduction> commit, so that it's also rejected unless it is signed by an authorized> key. Otherwise only the second commit would yield an error, which> is confusing.> ---> > here's how i tested this:> > i set up pulling from a local checkout of guix.> in that branch i created a signed dummy commit, and added it as a channel> introduction, replacing guix in my /etc/guix/channels.scm. then tried to> guix pull, which worked.> > then i added another dummy commit, which resulted in an error when pulling.> > then i reset the branch back to only contain the first commit, and added> this code that then resulted in an error even with a single commit.> > i have encountered it while i was trying to set up my local checkout to> test my patches on my live guix, and i was utterly confused why my commit> was rejected as unauthenticated (i misunderstood how git-authenticate> works).> > guix/git-authenticate.scm | 11 ++++++++---> 1 file changed, 8 insertions(+), 3 deletions(-)> > diff --git a/guix/git-authenticate.scm b/guix/git-authenticate.scm> index ab3fcd8b2f..7d66bf0754 100644> --- a/guix/git-authenticate.scm> +++ b/guix/git-authenticate.scm> @@ -236,8 +236,8 @@ not specify anything, fall back to DEFAULT-AUTHORIZATIONS."> (condition> (&unauthorized-commit-error (commit id)> (signing-key signing-key)))> - (formatted-message (G_ "commit ~a not signed by an authorized \> -key: ~a")> + (formatted-message (G_ "commit ~a is signed by an unauthorized \> +key: ~a\nSee info guix \"Specifying Channel Authorizations\".")> (oid->string id)> (openpgp-format-fingerprint> (openpgp-public-key-fingerprint> @@ -424,7 +424,12 @@ denoting the authorized keys for commits whose parent lack the> ;; If it's our first time, verify START-COMMIT's signature.> (when (null? authenticated-commits)> (verify-introductory-commit repository keyring> - start-commit signer))> + start-commit signer)> + ;; Explicitly authenticate the channel introduction commit, so that> + ;; it's also rejected unless it's signed by an authorized> + ;; key. Otherwise only the second commit would yield an error, which> + ;; is confusing.> + (authenticate-commits repository (list start-commit)))
Could you add a test to tests/git-authenticate.scm, verifying the right comitis reported? (Maybe use unauthorized-commit-error?, guard andauthenticate-repository.)
I'm not sure explicitely validating the start commit is sufficient. What happensin the following scenario:
(Order of commits) 0. start commit 1. valid (already authenticated?) commit 2. invalid commit 3. invalid commit
Is commit 2 reported, or commit 3 reported? I think commit 2 should be reported,but from your messages on IRC, I think you saw commit 3 being reported?
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVC4iRccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7qZjAQDP3PxstiQvdEIYogONKEK5cV7YS23cCMA+zr00wECX8wD/XEJ4PwOOlWjmfQV/hRD+r63hwNgnMXiUr4JTHicpRwc==F26d-----END PGP SIGNATURE-----

A
A
Attila Lendvai wrote on 27 Sep 20:01 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 50814@debbugs.gnu.org)
MlwY6eiUOkCgaEkZpM6XICOnF_c4o-I_YSlLPKysM412Chnv8lfK1x_AodHD1QZUAgy46Zk3LYfa7Et5QGDH9pOzo6KcEw9SweC7L57f1os=@lendvai.name
just a quick update that i'm working on putting together an extensive test for this, and a fix.
- attilaPGP: 5D5F 45C7 DFCD 0A39
A
A
Attila Lendvai wrote on 27 Sep 20:45 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 50814@debbugs.gnu.org)
XYPPxjyGCdWp4mrarRj4nrI4iZiANxHu1TJEVMd_d2PGw4OmD0yr7HMLd9NXEljnm5TSox8pm75D-alHyaiu29Wre4spahCrmuqZCvkSql8=@lendvai.name
what should happen if a channel-introduction commit does not update the .guix-authorizations file?
this means that this single commit will get through the authentication (channel intro commits are always trusted), but any later commits signed with the same key will be rejected.
this is the situation that got me confused, and thrust me into attempting to fix this (trying to `guix pull` from a local git checkout of guix containing my patches).
i wrote the code for this, but i don't know what should be its UI. how should this be reported to the user?
using `(warning ...)` will just print something to stderr.
i was hoping to raise a continuable condition of type &warning, that i can even check for in the tests, but i have failed to put that together. the scheme/guile condition system is a bit messy/convoluted.
can someone help me out with a hint/outline about how to report this that best fits the rest of guix?
note that it's not really an error, because until another commit is added with the new key, this channel is valid.
- attilaPGP: 5D5F 45C7 DFCD 0A39
A
A
Attila Lendvai wrote on 28 Sep 03:05 +0200
[PATCH 1/4] tests: Smarten up git repository testing framework.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928010537.4241-1-attila@lendvai.name
* guix/tests/git.scm (with-git-repository): New macro that can be used ina nested way under a with-temporary-git-repository.(populate-git-repository): Extend the DSL with (add "some-noise"), (reset"[commit hash]"), (checkout "branch" orphan).* guix/tests/gnupg.scm (key-fingerprint-vector): New function.---
Ready for merging modulo possible feedback, and one TODOregarding the UI of giving feedback to the user.
guix/tests/git.scm | 23 +++++++++++++++++++++-- guix/tests/gnupg.scm | 8 ++++++-- 2 files changed, 27 insertions(+), 4 deletions(-)
Toggle diff (95 lines)diff --git a/guix/tests/git.scm b/guix/tests/git.scmindex 69960284d9..76f5a8b937 100644--- a/guix/tests/git.scm+++ b/guix/tests/git.scm@@ -26,6 +26,7 @@ #:use-module (ice-9 control) #:export (git-command with-temporary-git-repository+ with-git-repository find-commit)) (define git-command@@ -59,8 +60,9 @@ Return DIRECTORY on success." (apply invoke (git-command) "-C" directory command args))))) - (mkdir-p directory)- (git "init")+ (unless (directory-exists? (string-append directory "/.git"))+ (mkdir-p directory)+ (git "init")) (let loop ((directives directives)) (match directives@@ -78,6 +80,9 @@ Return DIRECTORY on success." port))) (git "add" file) (loop rest)))+ ((('add file-name-and-content) rest ...)+ (loop (cons `(add ,file-name-and-content ,file-name-and-content)+ rest))) ((('remove file) rest ...) (git "rm" "-f" file) (loop rest))@@ -99,12 +104,18 @@ Return DIRECTORY on success." ((('checkout branch) rest ...) (git "checkout" branch) (loop rest))+ ((('checkout branch 'orphan) rest ...)+ (git "checkout" "--orphan" branch)+ (loop rest)) ((('merge branch message) rest ...) (git "merge" branch "-m" message) (loop rest)) ((('merge branch message ('signer fingerprint)) rest ...) (git "merge" branch "-m" message (string-append "--gpg-sign=" fingerprint))+ (loop rest))+ ((('reset to) rest ...)+ (git "reset" "--hard" to) (loop rest))))) (define (call-with-temporary-git-repository directives proc)@@ -121,6 +132,14 @@ per DIRECTIVES." (lambda (directory) exp ...))) +(define-syntax-rule (with-git-repository directory+ directives exp ...)+ "Evaluate EXP in a context where DIRECTORY is (further) populated as+per DIRECTIVES."+ (begin+ (populate-git-repository directory directives)+ exp ...))+ (define (find-commit repository message) "Return the commit in REPOSITORY whose message includes MESSAGE, a string." (let/ec returndiff --git a/guix/tests/gnupg.scm b/guix/tests/gnupg.scmindex eb8ff63a43..c7630db912 100644--- a/guix/tests/gnupg.scm+++ b/guix/tests/gnupg.scm@@ -33,6 +33,7 @@ read-openpgp-packet key-fingerprint+ key-fingerprint-vector key-id)) (define gpg-command@@ -76,7 +77,10 @@ process is terminated afterwards." (open-bytevector-input-port (call-with-input-file file read-radix-64)))) +(define key-fingerprint-vector+ (compose openpgp-public-key-fingerprint+ read-openpgp-packet))+ (define key-fingerprint (compose openpgp-format-fingerprint- openpgp-public-key-fingerprint- read-openpgp-packet))+ key-fingerprint-vector))-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 03:05 +0200
[PATCH 2/4] tests: Move keys into ./tests/keys/ and add a third ed25519 key.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928010537.4241-2-attila@lendvai.name
The third key will be used in an upcoming commit.
Rename public keys to .pub.
* guix/tests/gnupg.scm (%ed25519-3-public-key-file): New variable.(%ed25519-3-secret-key-file): New variable.(%ed25519-2-public-key-file): Renamed from %ed25519bis-public-key-file.(%ed25519-2-secret-key-file): Renamed from %ed25519bis-secret-key-file.* tests/keys/ed25519-3.key: New file.* tests/keys/ed25519-3.sec: New file.--- Makefile.am | 20 +++++----- build-aux/test-env.in | 6 +-- guix/tests/gnupg.scm | 22 ++++++---- tests/channels.scm | 18 ++++----- tests/git-authenticate.scm | 23 +++++------ tests/guix-authenticate.sh | 4 +- tests/{civodul.key => keys/civodul.pub} | 0 tests/{dsa.key => keys/dsa.pub} | 0 tests/{ed25519bis.key => keys/ed25519-2.pub} | 0 tests/{ed25519bis.sec => keys/ed25519-2.sec} | 0 tests/keys/ed25519-3.pub | 9 +++++ tests/keys/ed25519-3.sec | 10 +++++ tests/{ed25519.key => keys/ed25519.pub} | 0 tests/{ => keys}/ed25519.sec | 0 tests/{rsa.key => keys/rsa.pub} | 0 tests/{ => keys}/signing-key.pub | 0 tests/{ => keys}/signing-key.sec | 0 tests/openpgp.scm | 42 +++++++++++--------- 18 files changed, 93 insertions(+), 61 deletions(-) rename tests/{civodul.key => keys/civodul.pub} (100%) rename tests/{dsa.key => keys/dsa.pub} (100%) rename tests/{ed25519bis.key => keys/ed25519-2.pub} (100%) rename tests/{ed25519bis.sec => keys/ed25519-2.sec} (100%) create mode 100644 tests/keys/ed25519-3.pub create mode 100644 tests/keys/ed25519-3.sec rename tests/{ed25519.key => keys/ed25519.pub} (100%) rename tests/{ => keys}/ed25519.sec (100%) rename tests/{rsa.key => keys/rsa.pub} (100%) rename tests/{ => keys}/signing-key.pub (100%) rename tests/{ => keys}/signing-key.sec (100%)
Toggle diff (410 lines)diff --git a/Makefile.am b/Makefile.amindex 042cf28464..c0a5b14f02 100644--- a/Makefile.am+++ b/Makefile.am@@ -640,16 +640,18 @@ EXTRA_DIST += \ build-aux/update-guix-package.scm \ build-aux/update-NEWS.scm \ tests/test.drv \- tests/signing-key.pub \- tests/signing-key.sec \ tests/cve-sample.json \- tests/civodul.key \- tests/rsa.key \- tests/dsa.key \- tests/ed25519.key \- tests/ed25519.sec \- tests/ed25519bis.key \- tests/ed25519bis.sec \+ tests/keys/signing-key.pub \+ tests/keys/signing-key.sec \+ tests/keys/civodul.pub \+ tests/keys/rsa.pub \+ tests/keys/dsa.pub \+ tests/keys/ed25519.pub \+ tests/keys/ed25519.sec \+ tests/keys/ed25519-2.pub \+ tests/keys/ed25519-2.sec \+ tests/keys/ed25519-3.pub \+ tests/keys/ed25519-3.sec \ build-aux/config.rpath \ bootstrap \ doc/build.scm \diff --git a/build-aux/test-env.in b/build-aux/test-env.inindex 7efc43206c..ca786437e9 100644--- a/build-aux/test-env.in+++ b/build-aux/test-env.in@@ -73,9 +73,9 @@ then # Copy the keys so that the secret key has the right permissions (the # daemon errors out when this is not the case.) mkdir -p "$GUIX_CONFIGURATION_DIRECTORY"- cp "@abs_top_srcdir@/tests/signing-key.sec" \- "@abs_top_srcdir@/tests/signing-key.pub" \- "$GUIX_CONFIGURATION_DIRECTORY"+ cp "@abs_top_srcdir@/tests/keys/signing-key.sec" \+ "@abs_top_srcdir@/tests/keys/signing-key.pub" \+ "$GUIX_CONFIGURATION_DIRECTORY" chmod 400 "$GUIX_CONFIGURATION_DIRECTORY/signing-key.sec" fi diff --git a/guix/tests/gnupg.scm b/guix/tests/gnupg.scmindex c7630db912..09f02a2b67 100644--- a/guix/tests/gnupg.scm+++ b/guix/tests/gnupg.scm@@ -28,8 +28,10 @@ %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file+ %ed25519-3-public-key-file+ %ed25519-3-secret-key-file read-openpgp-packet key-fingerprint@@ -64,13 +66,17 @@ process is terminated afterwards." (call-with-fresh-gnupg-setup imported (lambda () exp ...))) (define %ed25519-public-key-file- (search-path %load-path "tests/ed25519.key"))+ (search-path %load-path "tests/keys/ed25519.pub")) (define %ed25519-secret-key-file- (search-path %load-path "tests/ed25519.sec"))-(define %ed25519bis-public-key-file- (search-path %load-path "tests/ed25519bis.key"))-(define %ed25519bis-secret-key-file- (search-path %load-path "tests/ed25519bis.sec"))+ (search-path %load-path "tests/keys/ed25519.sec"))+(define %ed25519-2-public-key-file+ (search-path %load-path "tests/keys/ed25519-2.pub"))+(define %ed25519-2-secret-key-file+ (search-path %load-path "tests/keys/ed25519-2.sec"))+(define %ed25519-3-public-key-file+ (search-path %load-path "tests/keys/ed25519-3.pub"))+(define %ed25519-3-secret-key-file+ (search-path %load-path "tests/keys/ed25519-3.sec")) (define (read-openpgp-packet file) (get-openpgp-packetdiff --git a/tests/channels.scm b/tests/channels.scmindex 3e82315b0c..d45c450241 100644--- a/tests/channels.scm+++ b/tests/channels.scm@@ -480,8 +480,8 @@ #t (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add ".guix-channel" ,(object->string@@ -507,7 +507,7 @@ (commit-id-string commit1) (openpgp-public-key-fingerprint (read-openpgp-packet- %ed25519bis-public-key-file)))) ;different key+ %ed25519-2-public-key-file)))) ;different key (channel (channel (name 'example) (url (string-append "file://" directory)) (introduction intro))))@@ -519,7 +519,7 @@ (oid->string (commit-id commit1)) (key-fingerprint %ed25519-public-key-file) (key-fingerprint- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (authenticate-channel channel directory (commit-id-string commit2) #:keyring-reference-prefix "")@@ -530,8 +530,8 @@ #t (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add ".guix-channel" ,(object->string@@ -552,12 +552,12 @@ (signer ,(key-fingerprint %ed25519-public-key-file))) (add "c.txt" "C") (commit "third commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (branch "channel-keyring") (checkout "channel-keyring") (add "signer.key" ,(call-with-input-file %ed25519-public-key-file get-string-all))- (add "other.key" ,(call-with-input-file %ed25519bis-public-key-file+ (add "other.key" ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (commit "keyring commit") (checkout "master"))@@ -588,7 +588,7 @@ (unauthorized-commit-error-signing-key c)) (openpgp-public-key-fingerprint (read-openpgp-packet- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (authenticate-channel channel directory (commit-id-string commit3) #:keyring-reference-prefix "")diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex d87eacc659..f66ef191b0 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -161,14 +161,14 @@ (test-assert "signed commits, .guix-authorizations, unauthorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file get-string-all)) (add "signer2.key"- ,(call-with-input-file %ed25519bis-public-key-file+ ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (add ".guix-authorizations" ,(object->string@@ -184,7 +184,7 @@ (checkout "devel") (add "devel/1.txt" "1") (commit "first devel commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (checkout "master") (add "b.txt" "B") (commit "second commit"@@ -203,7 +203,7 @@ (openpgp-public-key-fingerprint (unauthorized-commit-error-signing-key c)) (openpgp-public-key-fingerprint- (read-openpgp-packet %ed25519bis-public-key-file)))))+ (read-openpgp-packet %ed25519-2-public-key-file))))) (and (authenticate-commits repository (list master1 master2) #:keyring-reference "master")@@ -230,14 +230,14 @@ (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file get-string-all)) (add "signer2.key"- ,(call-with-input-file %ed25519bis-public-key-file+ ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (add ".guix-authorizations" ,(object->string@@ -258,12 +258,12 @@ %ed25519-public-key-file) (name "Alice")) (,(key-fingerprint- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (commit "first devel commit" (signer ,(key-fingerprint %ed25519-public-key-file))) (add "devel/2.txt" "2") (commit "second devel commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (checkout "master") (add "b.txt" "B") (commit "second commit"@@ -273,7 +273,7 @@ ;; After the merge, the second signer is authorized. (add "c.txt" "C") (commit "third commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file))))+ (signer ,(key-fingerprint %ed25519-2-public-key-file)))) (with-repository directory repository (let ((master1 (find-commit repository "first commit")) (master2 (find-commit repository "second commit"))@@ -328,4 +328,3 @@ 'failed))))))) (test-end "git-authenticate")-diff --git a/tests/guix-authenticate.sh b/tests/guix-authenticate.shindex 3a05b232c1..0de6da1878 100644--- a/tests/guix-authenticate.sh+++ b/tests/guix-authenticate.sh@@ -28,7 +28,7 @@ rm -f "$sig" "$hash" trap 'rm -f "$sig" "$hash"' EXIT -key="$abs_top_srcdir/tests/signing-key.sec"+key="$abs_top_srcdir/tests/keys/signing-key.sec" key_len="`echo -n $key | wc -c`" # A hexadecimal string as long as a sha256 hash.@@ -67,7 +67,7 @@ test "$code" -ne 0 # encoded independently of the current locale: <https://bugs.gnu.org/43421>. hash="636166e9636166e9636166e9636166e9636166e9636166e9636166e9636166e9" latin1_cafe="caf$(printf '\351')"-echo "sign 21:tests/signing-key.sec 64:$hash" | guix authenticate \+echo "sign 26:tests/keys/signing-key.sec 64:$hash" | guix authenticate \ | LC_ALL=C grep "hash sha256 \"$latin1_cafe" # Test for <http://bugs.gnu.org/17312>: make sure 'guix authenticate' producesdiff --git a/tests/civodul.key b/tests/keys/civodul.pubsimilarity index 100%rename from tests/civodul.keyrename to tests/keys/civodul.pubdiff --git a/tests/dsa.key b/tests/keys/dsa.pubsimilarity index 100%rename from tests/dsa.keyrename to tests/keys/dsa.pubdiff --git a/tests/ed25519bis.key b/tests/keys/ed25519-2.pubsimilarity index 100%rename from tests/ed25519bis.keyrename to tests/keys/ed25519-2.pubdiff --git a/tests/ed25519bis.sec b/tests/keys/ed25519-2.secsimilarity index 100%rename from tests/ed25519bis.secrename to tests/keys/ed25519-2.secdiff --git a/tests/keys/ed25519-3.pub b/tests/keys/ed25519-3.pubnew file mode 100644index 0000000000..72f311984c--- /dev/null+++ b/tests/keys/ed25519-3.pub@@ -0,0 +1,9 @@+-----BEGIN PGP PUBLIC KEY BLOCK-----++mDMEYVH/7xYJKwYBBAHaRw8BAQdALMLeUhjEG2/UPCJj2j/debFwwAK5gT3G0l5d+ILfFldm0FTxleGFtcGxlQGV4YW1wbGUuY29tPoiWBBMWCAA+FiEEjO6M85jMSK68+7tINGBzA7NyoagkFAmFR/+8CGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC+F4AACgkQGBzA7Nyoagl3lgEAw6yqIlX11lTqwxBGhZk/Oy34O13cbJSZCGv+m0ja++hcA/3DCNOmT+oXjgO/w6enQZUQ1m/d6dUjCc2wOLlLz+ZoG+=+r3i+-----END PGP PUBLIC KEY BLOCK-----diff --git a/tests/keys/ed25519-3.sec b/tests/keys/ed25519-3.secnew file mode 100644index 0000000000..04128a4131--- /dev/null+++ b/tests/keys/ed25519-3.sec@@ -0,0 +1,10 @@+-----BEGIN PGP PRIVATE KEY BLOCK-----++lFgEYVH/7xYJKwYBBAHaRw8BAQdALMLeUhjEG2/UPCJj2j/debFwwAK5gT3G0l5d+ILfFldkAAP92goSbbzQ0ttElr9lr5Cm6rmQtqUZ2Cu/Jk9fvfZROwxI0tBU8ZXhh+bXBsZUBleGFtcGxlLmNvbT6IlgQTFggAPhYhBIzujPOYzEiuvO7SDRgcwOzcqGoJ+BQJhUf/vAhsDBQkDwmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEBgcwOzc+qGoJd5YBAMOsqiJV9dZU6sMQRoWZPzst+Dtd3GyUmQhr/ptI2voXAP9wwjTpk/qF+44Dv8Onp0GVENZv3enVIwnNsDi5S8/maBg==+=EmOt+-----END PGP PRIVATE KEY BLOCK-----diff --git a/tests/ed25519.key b/tests/keys/ed25519.pubsimilarity index 100%rename from tests/ed25519.keyrename to tests/keys/ed25519.pubdiff --git a/tests/ed25519.sec b/tests/keys/ed25519.secsimilarity index 100%rename from tests/ed25519.secrename to tests/keys/ed25519.secdiff --git a/tests/rsa.key b/tests/keys/rsa.pubsimilarity index 100%rename from tests/rsa.keyrename to tests/keys/rsa.pubdiff --git a/tests/signing-key.pub b/tests/keys/signing-key.pubsimilarity index 100%rename from tests/signing-key.pubrename to tests/keys/signing-key.pubdiff --git a/tests/signing-key.sec b/tests/keys/signing-key.secsimilarity index 100%rename from tests/signing-key.secrename to tests/keys/signing-key.secdiff --git a/tests/openpgp.scm b/tests/openpgp.scmindex c2be26fa49..1f20466772 100644--- a/tests/openpgp.scm+++ b/tests/openpgp.scm@@ -59,18 +59,22 @@ vBSFjNSiVHsuAA== (define %civodul-fingerprint "3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5") -(define %civodul-key-id #x090B11993D9AEBB5) ;civodul.key--;; Test keys. They were generated in a container along these lines:-;; guix environment -CP --ad-hoc gnupg pinentry-;; then, within the container:-;; mkdir ~/.gnupg-;; echo pinentry-program ~/.guix-profile/bin/pinentry-tty > ~/.gnupg/gpg-agent.conf-;; gpg --quick-gen-key '<ludo+test-rsa@chbouib.org>' rsa-;; or similar.-(define %rsa-key-id #xAE25DA2A70DEED59) ;rsa.key-(define %dsa-key-id #x587918047BE8BD2C) ;dsa.key-(define %ed25519-key-id #x771F49CBFAAE072D) ;ed25519.key+(define %civodul-key-id #x090B11993D9AEBB5) ;civodul.pub++#|+Test keys in ./tests/keys. They were generated in a container along these lines:+ guix environment -CP --ad-hoc gnupg pinentry coreutils+then, within the container:+ mkdir ~/.gnupg && chmod -R og-rwx ~/.gnupg+ gpg --batch --passphrase '' --quick-gen-key '<example@example.com>' ed25519+ gpg --armor --export example@example.com+ gpg --armor --export-secret-key example@example.com+ # echo pinentry-program ~/.guix-profile/bin/pinentry-curses > ~/.gnupg/gpg-agent.conf+or similar.+|#+(define %rsa-key-id #xAE25DA2A70DEED59) ;rsa.pub+(define %dsa-key-id #x587918047BE8BD2C) ;dsa.pub+(define %ed25519-key-id #x771F49CBFAAE072D) ;ed25519.pub (define %rsa-key-fingerprint (base16-string->bytevector@@ -168,7 +172,7 @@ Pz7oopeN72xgggYUNT37ezqN3MeCqw0= (not (port-ascii-armored? (open-bytevector-input-port %binary-sample)))) (test-assert "get-openpgp-keyring"- (let* ((key (search-path %load-path "tests/civodul.key"))+ (let* ((key (search-path %load-path "tests/keys/civodul.pub")) (keyring (get-openpgp-keyring (open-bytevector-input-port (call-with-input-file key read-radix-64)))))@@ -228,8 +232,10 @@ Pz7oopeN72xgggYUNT37ezqN3MeCqw0= (verify-openpgp-signature signature keyring (open-input-string "Hello!\n")))) (list status (openpgp-public-key-id key)))))- (list "tests/rsa.key" "tests/dsa.key"- "tests/ed25519.key" "tests/ed25519.key" "tests/ed25519.key")+ (list "tests/keys/rsa.pub" "tests/keys/dsa.pub"+ "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub") (list %hello-signature/rsa %hello-signature/dsa %hello-signature/ed25519/sha256 %hello-signature/ed25519/sha512@@ -248,9 +254,9 @@ Pz7oopeN72xgggYUNT37ezqN3MeCqw0= (call-with-input-file key read-radix-64)) keyring))) %empty-keyring- '("tests/rsa.key" "tests/dsa.key"- "tests/ed25519.key" "tests/ed25519.key"- "tests/ed25519.key"))))+ '("tests/keys/rsa.pub" "tests/keys/dsa.pub"+ "tests/keys/ed25519.pub" "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub")))) (map (lambda (signature) (let ((signature (string->openpgp-packet signature))) (let-values (((status key)-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 03:05 +0200
[PATCH 3/4] tests: Add failing test for .guix-authorizations and channel intro.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928010537.4241-3-attila@lendvai.name
Will be fixed in a subsequent commit.
* tests/git-authenticate.scm: New test "signed commits, .guix-authorizations,channel-introduction".--- tests/git-authenticate.scm | 111 +++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+)
Toggle diff (124 lines)diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex f66ef191b0..672aff2177 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -226,6 +226,117 @@ #:keyring-reference "master") #f))))))) +(unless (gpg+git-available?) (test-skip 1))+(test-assert "signed commits, .guix-authorizations, channel-introduction"+ (let* ((result #true)+ (key1 %ed25519-public-key-file)+ (key2 %ed25519-2-public-key-file)+ (key3 %ed25519-3-public-key-file))+ (with-fresh-gnupg-setup (list key1 %ed25519-secret-key-file+ key2 %ed25519-2-secret-key-file+ key3 %ed25519-3-secret-key-file)+ (with-temporary-git-repository dir+ `((checkout "keyring" orphan)+ (add "signer1.key" ,(call-with-input-file key1 get-string-all))+ (add "signer2.key" ,(call-with-input-file key2 get-string-all))+ (add "signer3.key" ,(call-with-input-file key3 get-string-all))+ (commit "keyring commit")++ (checkout "main" orphan)+ (add "noise0")+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))))))+ (commit "commit 0" (signer ,(key-fingerprint key3)))+ (add "noise1")+ (commit "commit 1" (signer ,(key-fingerprint key1)))+ (add "noise2")+ (commit "commit 2" (signer ,(key-fingerprint key1))))+ (with-repository dir repo+ (let* ((commit-0 (find-commit repo "commit 0"))+ (check-from+ (lambda* (commit #:key (should-fail? #false) (key key1)+ (historical-authorizations+ ;; key3 is trusted to authorize commit 0+ (list (key-fingerprint-vector key3))))+ (guard (c ((unauthorized-commit-error? c)+ (if should-fail?+ c+ (let ((port (current-output-port)))+ (format port "FAILURE: Unexpected exception at commit '~s':~%"+ commit)+ (print-exception port (stack-ref (make-stack #t) 1)+ c (exception-args c))+ (set! result #false)+ '()))))+ (format #true "~%~%Checking ~s, should-fail? ~s, repo commits:~%"+ commit should-fail?)+ ;; to be able to inspect in the logs+ (invoke "git" "-C" dir "log" "--reverse" "--pretty=oneline" "main")+ (set! commit (find-commit repo commit))+ (authenticate-repository+ repo+ (commit-id commit)+ (key-fingerprint-vector key)+ #:historical-authorizations historical-authorizations)+ (when should-fail?+ (format #t "FAILURE: Authenticating commit '~s' should have failed.~%" commit)+ (set! result #false))+ '()))))+ (check-from "commit 0" #:key key3)+ (check-from "commit 1")+ (check-from "commit 2")+ (with-git-repository dir+ `((add "noise 3")+ ;; a commit with key2+ (commit "commit 3" (signer ,(key-fingerprint key2))))+ ;; Should fail because it is signed with key2, not key1+ (check-from "commit 3" #:should-fail? #true)+ ;; Specify commit 3 as a channel-introduction signed with+ ;; key2. This is valid, but it should warn the user, because+ ;; .guix-authorizations is not updated to include key2, which+ ;; means that any subsequent commits with the same key will be+ ;; rejected.+ ;;+ ;; TODO we should check somehow that a warning is issued+ (check-from "commit 3" #:key key2))+ (with-git-repository dir+ `((reset ,(oid->string (commit-id (find-commit repo "commit 2"))))+ (add "noise 4")+ ;; set it up properly+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ (,(key-fingerprint key2) (name "Bob"))))))+ (commit "commit 4" (signer ,(key-fingerprint key2))))+ ;; This should fail because even though commit 4 adds key2 to+ ;; .guix-authorizations, the commit itself is not authorized.+ (check-from "commit 1" #:should-fail? #true)+ ;; This should pass, because it's a valid channel intro at commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 5")+ (commit "commit 5" (signer ,(key-fingerprint key2))))+ ;; This is not very intuitive: because commit 4 has once been+ ;; used as a channel intro, it got marked as trusted in the+ ;; ~/.cache/, and because commit 1 is one of its parent, it is+ ;; also trusted.+ (check-from "commit 1")+ (check-from "commit 2")+ ;; Should still be fine, but only when starting from commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 6")+ (commit "commit 6" (signer ,(key-fingerprint key1))))+ (check-from "commit 1")+ (check-from "commit 2")+ (check-from "commit 4" #:key key2))))))+ result))+ (unless (gpg+git-available?) (test-skip 1)) (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 03:05 +0200
[PATCH 4/4] guix: git-authenticate: Fix authenticate-repository.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928010537.4241-4-attila@lendvai.name
Always verify the channel introduction commit, so that no commit can slipthrough that was signed with a different key.
Always update the cache, because it affects the behavior of later calls.
Warn when a channel introduction commit doesn't also update the'.guix-authentications' file.
* guix/git-authenticate.scm (authenticate-commit): Reword and extend the errormessage to point to the relevant part of the manual.(authenticate-repository): Eliminate optimizations to make the code path lessdependent on the input. Always trust the intro-commit itself. Always callverify-introductory-commit.(verify-introductory-commit): Check if the commit contains the key that wasused to sign it, and issue a warning otherwise. This is to avoid the confusioncaused by only the *second* commit yielding an error, because intro-commitsare always trusted.(authenticate-commit): Clarify error message.(authorized-keys-at-commit): Factored out to the toplevel fromcommit-authorized-keys.--- guix/channels.scm | 4 +- guix/git-authenticate.scm | 153 ++++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 66 deletions(-)
Toggle diff (254 lines)diff --git a/guix/channels.scm b/guix/channels.scmindex e4e0428eb5..b84064537f 100644--- a/guix/channels.scm+++ b/guix/channels.scm@@ -347,8 +347,8 @@ commits)...~%") (progress-reporter/bar (length commits))) (define authentic-commits- ;; Consider the currently-used commit of CHANNEL as authentic so- ;; authentication can skip it and all its closure.+ ;; Optimization: consider the currently-used commit of CHANNEL as+ ;; authentic, so that authentication can skip it and all its closure. (match (find (lambda (candidate) (eq? (channel-name candidate) (channel-name channel))) (current-channels))diff --git a/guix/git-authenticate.scm b/guix/git-authenticate.scmindex ab3fcd8b2f..713642d2ea 100644--- a/guix/git-authenticate.scm+++ b/guix/git-authenticate.scm@@ -30,6 +30,7 @@ #:select (cache-directory with-atomic-file-output)) #:use-module ((guix build utils) #:select (mkdir-p))+ #:use-module (guix diagnostics) #:use-module (guix progress) #:use-module (srfi srfi-1) #:use-module (srfi srfi-11)@@ -38,6 +39,7 @@ #:use-module (srfi srfi-35) #:use-module (rnrs bytevectors) #:use-module (rnrs io ports)+ #:use-module (ice-9 exceptions) #:use-module (ice-9 match) #:autoload (ice-9 pretty-print) (pretty-print) #:export (read-authorizations@@ -159,11 +161,10 @@ return a list of authorized fingerprints." (string-downcase (string-filter char-set:graphic fingerprint)))) fingerprints)))) -(define* (commit-authorized-keys repository commit- #:optional (default-authorizations '()))- "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on-authorizations listed in its parent commits. If one of the parent commits-does not specify anything, fall back to DEFAULT-AUTHORIZATIONS."+(define (authorized-keys-at-commit repository commit default-authorizations)+ "Return the list of authorized key fingerprints from the '.guix-authorizations'+file at the given commit."+ (define (parents-have-authorizations-file? commit) ;; Return true if at least one of the parents of COMMIT has the ;; '.guix-authorizations' file.@@ -185,28 +186,35 @@ does not specify anything, fall back to DEFAULT-AUTHORIZATIONS." to remove '.guix-authorizations' file") (oid->string (commit-id commit))))))) - (define (commit-authorizations commit)- (catch 'git-error- (lambda ()- (let* ((tree (commit-tree commit))- (entry (tree-entry-bypath tree ".guix-authorizations"))- (blob (blob-lookup repository (tree-entry-id entry))))- (read-authorizations- (open-bytevector-input-port (blob-content blob)))))- (lambda (key error)- (if (= (git-error-code error) GIT_ENOTFOUND)- (begin- ;; Prevent removal of '.guix-authorizations' since it would make- ;; it trivial to force a fallback to DEFAULT-AUTHORIZATIONS.- (assert-parents-lack-authorizations commit)- default-authorizations)- (throw key error)))))+ (catch 'git-error+ (lambda ()+ (let* ((tree (commit-tree commit))+ (entry (tree-entry-bypath tree ".guix-authorizations"))+ (blob (blob-lookup repository (tree-entry-id entry))))+ (read-authorizations+ (open-bytevector-input-port (blob-content blob)))))+ (lambda (key error)+ (if (= (git-error-code error) GIT_ENOTFOUND)+ (begin+ ;; Prevent removal of '.guix-authorizations' since it would make+ ;; it trivial to force a fallback to DEFAULT-AUTHORIZATIONS.+ (assert-parents-lack-authorizations commit)+ default-authorizations)+ (throw key error))))) +(define* (commit-authorized-keys repository commit+ #:optional (default-authorizations '()))+ "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on+authorizations listed in its parent commits. If one of the parent commits+does not specify anything, fall back to DEFAULT-AUTHORIZATIONS." (match (commit-parents commit) (() default-authorizations) (parents (apply lset-intersection bytevector=?- (map commit-authorizations parents)))))+ (map (lambda (commit)+ (authorized-keys-at-commit repository commit+ default-authorizations))+ parents))))) (define* (authenticate-commit repository commit keyring #:key (default-authorizations '()))@@ -236,8 +244,8 @@ not specify anything, fall back to DEFAULT-AUTHORIZATIONS." (condition (&unauthorized-commit-error (commit id) (signing-key signing-key)))- (formatted-message (G_ "commit ~a not signed by an authorized \-key: ~a")+ (formatted-message (G_ "commit ~a is signed by an unauthorized \+key: ~a\nSee info guix \"Specifying Channel Authorizations\".") (oid->string id) (openpgp-format-fingerprint (openpgp-public-key-fingerprint@@ -356,7 +364,8 @@ authenticated (only COMMIT-ID is written to cache, though)." (base64-encode (sha256 (string->utf8 (repository-directory repository)))))) -(define (verify-introductory-commit repository keyring commit expected-signer)+(define (verify-introductory-commit repository commit expected-signer keyring+ authorizations) "Look up COMMIT in REPOSITORY, and raise an exception if it is not signed by EXPECTED-SIGNER." (define actual-signer@@ -364,13 +373,25 @@ EXPECTED-SIGNER." (commit-signing-key repository (commit-id commit) keyring))) (unless (bytevector=? expected-signer actual-signer)- (raise (formatted-message (G_ "initial commit ~a is signed by '~a' \+ (raise (make-compound-condition+ (condition (&unauthorized-commit-error (commit (commit-id commit))+ (signing-key actual-signer)))+ (formatted-message (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+ (oid->string (commit-id commit))+ (openpgp-format-fingerprint actual-signer)+ (openpgp-format-fingerprint expected-signer)))))+ (unless (member actual-signer+ (authorized-keys-at-commit repository commit authorizations)+ bytevector=?)+ ;; FIXME Is this the right way to tell the user about this situation? It+ ;; would also be nice if the tests could assert for this warning.+ (warning (G_ "initial commit ~a does not add \+the key it is signed with (~a) to the '.guix-authorizations' file.")+ (oid->string (commit-id commit))+ (openpgp-format-fingerprint actual-signer))))++(define* (authenticate-repository repository intro-commit-hash intro-signer #:key (keyring-reference "keyring") (cache-key (repository-cache-key repository))@@ -380,11 +401,12 @@ instead of '~a'") (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. Commits listed in AUTHENTIC-COMMITS and their-closure are considered authentic. Return an alist mapping OpenPGP public keys-to the number of commits signed by that key that have been traversed.+ "Authenticate REPOSITORY up to commit END, an OID. Authentication starts with+commit INTRO-COMMIT-HASH, an OID, which must be signed by INTRO-SIGNER; an+exception is raised if that is not the case. Commits listed in+AUTHENTIC-COMMITS and their closure are considered authentic. 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@@ -393,8 +415,10 @@ 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 intro-commit+ (commit-lookup repository intro-commit-hash))+ (define end-commit (commit-lookup repository end)) @@ -404,36 +428,37 @@ denoting the authorized keys for commits whose parent lack the (define authenticated-commits ;; Previously-authenticated commits that don't need to be checked again. (filter-map (lambda (id)+ ;; We need to tolerate when cached commits disappear due to+ ;; --allow-downgrades. (false-if-git-not-found (commit-lookup repository (string->oid id)))) (append (previously-authenticated-commits cache-key)- authentic-commits)))+ authentic-commits+ ;; The intro commit is unconditionally trusted.+ (list (oid->string intro-commit-hash))))) (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))))+ (commit-difference end-commit intro-commit+ authenticated-commits))++ (verify-introductory-commit repository intro-commit+ intro-signer keyring+ historical-authorizations)++ (let* ((reporter (make-reporter intro-commit end-commit commits))+ (stats (call-with-progress-reporter reporter+ (lambda (report)+ (authenticate-commits repository commits+ #:keyring keyring+ #:default-authorizations+ historical-authorizations+ #:report-progress report)))))+ ;; Note that this will make the then current end commit of any channel,+ ;; that has been used/trusted in the past with a channel introduction,+ ;; remain trusted until the cache is cleared.+ (cache-authenticated-commit cache-key+ (oid->string (commit-id end-commit)))++ stats))-- 2.33.0
M
M
Maxime Devos wrote on 28 Sep 12:02 +0200
Re: [bug#50814] [PATCH] guix: git-authenticate: Also authenticate the channel intro commit.
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 50814@debbugs.gnu.org)
7f921c26c445cfe034ad73bfbd7a9b45e810d673.camel@telenet.be
Attila Lendvai schreef op ma 27-09-2021 om 18:45 [+0000]:
Toggle quote (1 lines)>
[...]
Toggle quote (5 lines)> using `(warning ...)` will just print something to stderr.> > i was hoping to raise a continuable condition of type &warning, that i can even check for in the tests,> but i have failed to put that together. the scheme/guile condition system is a bit messy/convoluted.
Technically, Scheme supports continuable exceptions with 'raise-continuable'and with-exception-hander. E.g., the following Racket example:
(use-modules (rnrs exceptions) (rnrs conditions)) (with-exception-handler (lambda (con) (cond ((not (warning? con)) (raise con)) ((message-condition? con) (display (condition-message con))) (else (display "a warning has been issued"))) 42) (lambda () (+ (raise-continuable (condition (make-warning) (make-message-condition "should be a number"))) 23))) prints: should be a number ⇒ 65
(from https://docs.racket-lang.org/r6rs/r6rs-lib-std/r6rs-lib-Z-H-8.html#node_idx_378)works in Guile
You might need to modify 'call-with-error-handling' in (guix ui) to recognise&warning though, such that the &warning exception will be properly handled.
Alternatively, you can use the procedure 'warning' from (guix diagnostics).To detect the error in this case, you can use parameterise, guix-warning-portand procedures like call-with-output-string. You may need to reset the localefirst (with dynamic-wind?).
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVLoMBccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7o1fAP4pI9fNojskAkSV/vzDIlIvl9WlMPxbt8VHva965NcEjgEAlEMvuk7E/od4vvL6lQxqzIE+XhzhFrDCd2YAVjmWxQY==sQuQ-----END PGP SIGNATURE-----

A
A
Attila Lendvai wrote on 28 Sep 18:24 +0200
[PATCH 1/5] tests: Smarten up git repository testing framework.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928162406.27205-1-attila@lendvai.name
* guix/tests/git.scm (with-git-repository): New macro that can be used ina nested way under a with-temporary-git-repository.(populate-git-repository): Extend the DSL with (add "some-noise"), (reset"[commit hash]"), (checkout "branch" orphan).* guix/tests/gnupg.scm (key-fingerprint-vector): New function.--- guix/tests/git.scm | 23 +++++++++++++++++++++-- guix/tests/gnupg.scm | 8 ++++++-- 2 files changed, 27 insertions(+), 4 deletions(-)
Toggle diff (95 lines)diff --git a/guix/tests/git.scm b/guix/tests/git.scmindex 69960284d9..76f5a8b937 100644--- a/guix/tests/git.scm+++ b/guix/tests/git.scm@@ -26,6 +26,7 @@ #:use-module (ice-9 control) #:export (git-command with-temporary-git-repository+ with-git-repository find-commit)) (define git-command@@ -59,8 +60,9 @@ Return DIRECTORY on success." (apply invoke (git-command) "-C" directory command args))))) - (mkdir-p directory)- (git "init")+ (unless (directory-exists? (string-append directory "/.git"))+ (mkdir-p directory)+ (git "init")) (let loop ((directives directives)) (match directives@@ -78,6 +80,9 @@ Return DIRECTORY on success." port))) (git "add" file) (loop rest)))+ ((('add file-name-and-content) rest ...)+ (loop (cons `(add ,file-name-and-content ,file-name-and-content)+ rest))) ((('remove file) rest ...) (git "rm" "-f" file) (loop rest))@@ -99,12 +104,18 @@ Return DIRECTORY on success." ((('checkout branch) rest ...) (git "checkout" branch) (loop rest))+ ((('checkout branch 'orphan) rest ...)+ (git "checkout" "--orphan" branch)+ (loop rest)) ((('merge branch message) rest ...) (git "merge" branch "-m" message) (loop rest)) ((('merge branch message ('signer fingerprint)) rest ...) (git "merge" branch "-m" message (string-append "--gpg-sign=" fingerprint))+ (loop rest))+ ((('reset to) rest ...)+ (git "reset" "--hard" to) (loop rest))))) (define (call-with-temporary-git-repository directives proc)@@ -121,6 +132,14 @@ per DIRECTIVES." (lambda (directory) exp ...))) +(define-syntax-rule (with-git-repository directory+ directives exp ...)+ "Evaluate EXP in a context where DIRECTORY is (further) populated as+per DIRECTIVES."+ (begin+ (populate-git-repository directory directives)+ exp ...))+ (define (find-commit repository message) "Return the commit in REPOSITORY whose message includes MESSAGE, a string." (let/ec returndiff --git a/guix/tests/gnupg.scm b/guix/tests/gnupg.scmindex eb8ff63a43..c7630db912 100644--- a/guix/tests/gnupg.scm+++ b/guix/tests/gnupg.scm@@ -33,6 +33,7 @@ read-openpgp-packet key-fingerprint+ key-fingerprint-vector key-id)) (define gpg-command@@ -76,7 +77,10 @@ process is terminated afterwards." (open-bytevector-input-port (call-with-input-file file read-radix-64)))) +(define key-fingerprint-vector+ (compose openpgp-public-key-fingerprint+ read-openpgp-packet))+ (define key-fingerprint (compose openpgp-format-fingerprint- openpgp-public-key-fingerprint- read-openpgp-packet))+ key-fingerprint-vector))-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 18:24 +0200
[PATCH 3/5] tests: Add failing test for .guix-authorizations and channel intro.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928162406.27205-3-attila@lendvai.name
Will be fixed in a subsequent commit.
* tests/git-authenticate.scm: New test "signed commits, .guix-authorizations,channel-introduction".--- tests/git-authenticate.scm | 132 +++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+)
Toggle diff (159 lines)diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex f66ef191b0..745a6d6dbe 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -18,6 +18,7 @@ (define-module (test-git-authenticate) #:use-module (git)+ #:use-module (guix diagnostics) #:use-module (guix git) #:use-module (guix git-authenticate) #:use-module (guix openpgp)@@ -28,6 +29,10 @@ #:use-module (srfi srfi-34) #:use-module (srfi srfi-64) #:use-module (rnrs bytevectors)+ #:use-module ((rnrs conditions)+ #:select (warning?))+ #:use-module ((rnrs exceptions)+ #:select (with-exception-handler)) #:use-module (rnrs io ports)) ;; Test the (guix git-authenticate) tools.@@ -226,6 +231,133 @@ #:keyring-reference "master") #f))))))) +(unless (gpg+git-available?) (test-skip 1))+(test-assert "signed commits, .guix-authorizations, channel-introduction"+ (let* ((result #true)+ (key1 %ed25519-public-key-file)+ (key2 %ed25519-2-public-key-file)+ (key3 %ed25519-3-public-key-file))+ (with-fresh-gnupg-setup (list key1 %ed25519-secret-key-file+ key2 %ed25519-2-secret-key-file+ key3 %ed25519-3-secret-key-file)+ (with-temporary-git-repository dir+ `((checkout "keyring" orphan)+ (add "signer1.key" ,(call-with-input-file key1 get-string-all))+ (add "signer2.key" ,(call-with-input-file key2 get-string-all))+ (add "signer3.key" ,(call-with-input-file key3 get-string-all))+ (commit "keyring commit")++ (checkout "main" orphan)+ (add "noise0")+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ (,(key-fingerprint key3) (name "Charlie"))))))+ (commit "commit 0" (signer ,(key-fingerprint key3)))+ (add "noise1")+ (commit "commit 1" (signer ,(key-fingerprint key1)))+ (add "noise2")+ (commit "commit 2" (signer ,(key-fingerprint key1))))+ (with-repository dir repo+ (let* ((commit-0 (find-commit repo "commit 0"))+ (check-from+ (lambda* (commit #:key (should-fail? #false) (key key1)+ (historical-authorizations+ ;; key3 is trusted to authorize commit 0+ (list (key-fingerprint-vector key3))))+ (guard (c ((unauthorized-commit-error? c)+ (if should-fail?+ c+ (let ((port (current-output-port)))+ (format port "FAILURE: Unexpected exception at commit '~s':~%"+ commit)+ (print-exception port (stack-ref (make-stack #t) 1)+ c (exception-args c))+ (set! result #false)+ '()))))+ (format #true "~%~%Checking ~s, should-fail? ~s, repo commits:~%"+ commit should-fail?)+ ;; to be able to inspect in the logs+ (invoke "git" "-C" dir "log" "--reverse" "--pretty=oneline" "main")+ (set! commit (find-commit repo commit))+ (authenticate-repository+ repo+ (commit-id commit)+ (key-fingerprint-vector key)+ #:historical-authorizations historical-authorizations)+ (when should-fail?+ (format #t "FAILURE: Authenticating commit '~s' should have failed.~%" commit)+ (set! result #false))+ '()))))+ (check-from "commit 0" #:key key3)+ (check-from "commit 1")+ (check-from "commit 2")+ (with-git-repository dir+ `((add "noise 3")+ ;; a commit with key2+ (commit "commit 3" (signer ,(key-fingerprint key2))))+ ;; Should fail because it is signed with key2, not key1+ (check-from "commit 3" #:should-fail? #true)+ ;; Specify commit 3 as a channel-introduction signed with+ ;; key2. This is valid, but it should warn the user, because+ ;; .guix-authorizations is not updated to include key2, which+ ;; means that any subsequent commits with the same key will be+ ;; rejected.+ (set! result+ (and result+ (let ((signalled? #false))+ (with-exception-handler+ (lambda (c)+ (cond+ ((not (warning? c))+ (raise c))+ ((formatted-message? c)+ (format #true "warning (expected): ~a~%"+ (apply format #false+ (formatted-message-string c)+ (formatted-message-arguments c)))+ (set! signalled? #true)))+ '())+ (lambda ()+ (check-from "commit 3" #:key key2)+ signalled?))))))+ (with-git-repository dir+ `((reset ,(oid->string (commit-id (find-commit repo "commit 2"))))+ (add "noise 4")+ ;; set it up properly+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ (,(key-fingerprint key2) (name "Bob"))))))+ (commit "commit 4" (signer ,(key-fingerprint key2))))+ ;; This should fail because even though commit 4 adds key2 to+ ;; .guix-authorizations, the commit itself is not authorized.+ (check-from "commit 1" #:should-fail? #true)+ ;; This should pass, because it's a valid channel intro at commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 5")+ (commit "commit 5" (signer ,(key-fingerprint key2))))+ ;; This is not very intuitive: because commit 4 has once been+ ;; used as a channel intro, it got marked as trusted in the+ ;; ~/.cache/, and because commit 1 is one of its parent, it is+ ;; also trusted.+ (check-from "commit 1")+ (check-from "commit 2")+ ;; Should still be fine, but only when starting from commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 6")+ (commit "commit 6" (signer ,(key-fingerprint key1))))+ (check-from "commit 1")+ (check-from "commit 2")+ (check-from "commit 4" #:key key2))))))+ result))+ (unless (gpg+git-available?) (test-skip 1)) (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 18:24 +0200
[PATCH 2/5] tests: Move keys into ./tests/keys/ and add a third ed25519 key.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928162406.27205-2-attila@lendvai.name
The third key will be used in an upcoming commit.
Rename public keys to .pub.
* guix/tests/gnupg.scm (%ed25519-3-public-key-file): New variable.(%ed25519-3-secret-key-file): New variable.(%ed25519-2-public-key-file): Renamed from %ed25519bis-public-key-file.(%ed25519-2-secret-key-file): Renamed from %ed25519bis-secret-key-file.* tests/keys/ed25519-3.key: New file.* tests/keys/ed25519-3.sec: New file.--- Makefile.am | 20 +++++----- build-aux/test-env.in | 6 +-- guix/tests/gnupg.scm | 22 ++++++---- tests/channels.scm | 18 ++++----- tests/git-authenticate.scm | 23 +++++------ tests/guix-authenticate.sh | 4 +- tests/{civodul.key => keys/civodul.pub} | 0 tests/{dsa.key => keys/dsa.pub} | 0 tests/{ed25519bis.key => keys/ed25519-2.pub} | 0 tests/{ed25519bis.sec => keys/ed25519-2.sec} | 0 tests/keys/ed25519-3.pub | 9 +++++ tests/keys/ed25519-3.sec | 10 +++++ tests/{ed25519.key => keys/ed25519.pub} | 0 tests/{ => keys}/ed25519.sec | 0 tests/{rsa.key => keys/rsa.pub} | 0 tests/{ => keys}/signing-key.pub | 0 tests/{ => keys}/signing-key.sec | 0 tests/openpgp.scm | 42 +++++++++++--------- 18 files changed, 93 insertions(+), 61 deletions(-) rename tests/{civodul.key => keys/civodul.pub} (100%) rename tests/{dsa.key => keys/dsa.pub} (100%) rename tests/{ed25519bis.key => keys/ed25519-2.pub} (100%) rename tests/{ed25519bis.sec => keys/ed25519-2.sec} (100%) create mode 100644 tests/keys/ed25519-3.pub create mode 100644 tests/keys/ed25519-3.sec rename tests/{ed25519.key => keys/ed25519.pub} (100%) rename tests/{ => keys}/ed25519.sec (100%) rename tests/{rsa.key => keys/rsa.pub} (100%) rename tests/{ => keys}/signing-key.pub (100%) rename tests/{ => keys}/signing-key.sec (100%)
Toggle diff (410 lines)diff --git a/Makefile.am b/Makefile.amindex b66789fa0b..00604f2f93 100644--- a/Makefile.am+++ b/Makefile.am@@ -643,16 +643,18 @@ EXTRA_DIST += \ build-aux/update-guix-package.scm \ build-aux/update-NEWS.scm \ tests/test.drv \- tests/signing-key.pub \- tests/signing-key.sec \ tests/cve-sample.json \- tests/civodul.key \- tests/rsa.key \- tests/dsa.key \- tests/ed25519.key \- tests/ed25519.sec \- tests/ed25519bis.key \- tests/ed25519bis.sec \+ tests/keys/signing-key.pub \+ tests/keys/signing-key.sec \+ tests/keys/civodul.pub \+ tests/keys/rsa.pub \+ tests/keys/dsa.pub \+ tests/keys/ed25519.pub \+ tests/keys/ed25519.sec \+ tests/keys/ed25519-2.pub \+ tests/keys/ed25519-2.sec \+ tests/keys/ed25519-3.pub \+ tests/keys/ed25519-3.sec \ build-aux/config.rpath \ bootstrap \ doc/build.scm \diff --git a/build-aux/test-env.in b/build-aux/test-env.inindex 7efc43206c..ca786437e9 100644--- a/build-aux/test-env.in+++ b/build-aux/test-env.in@@ -73,9 +73,9 @@ then # Copy the keys so that the secret key has the right permissions (the # daemon errors out when this is not the case.) mkdir -p "$GUIX_CONFIGURATION_DIRECTORY"- cp "@abs_top_srcdir@/tests/signing-key.sec" \- "@abs_top_srcdir@/tests/signing-key.pub" \- "$GUIX_CONFIGURATION_DIRECTORY"+ cp "@abs_top_srcdir@/tests/keys/signing-key.sec" \+ "@abs_top_srcdir@/tests/keys/signing-key.pub" \+ "$GUIX_CONFIGURATION_DIRECTORY" chmod 400 "$GUIX_CONFIGURATION_DIRECTORY/signing-key.sec" fi diff --git a/guix/tests/gnupg.scm b/guix/tests/gnupg.scmindex c7630db912..09f02a2b67 100644--- a/guix/tests/gnupg.scm+++ b/guix/tests/gnupg.scm@@ -28,8 +28,10 @@ %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file+ %ed25519-3-public-key-file+ %ed25519-3-secret-key-file read-openpgp-packet key-fingerprint@@ -64,13 +66,17 @@ process is terminated afterwards." (call-with-fresh-gnupg-setup imported (lambda () exp ...))) (define %ed25519-public-key-file- (search-path %load-path "tests/ed25519.key"))+ (search-path %load-path "tests/keys/ed25519.pub")) (define %ed25519-secret-key-file- (search-path %load-path "tests/ed25519.sec"))-(define %ed25519bis-public-key-file- (search-path %load-path "tests/ed25519bis.key"))-(define %ed25519bis-secret-key-file- (search-path %load-path "tests/ed25519bis.sec"))+ (search-path %load-path "tests/keys/ed25519.sec"))+(define %ed25519-2-public-key-file+ (search-path %load-path "tests/keys/ed25519-2.pub"))+(define %ed25519-2-secret-key-file+ (search-path %load-path "tests/keys/ed25519-2.sec"))+(define %ed25519-3-public-key-file+ (search-path %load-path "tests/keys/ed25519-3.pub"))+(define %ed25519-3-secret-key-file+ (search-path %load-path "tests/keys/ed25519-3.sec")) (define (read-openpgp-packet file) (get-openpgp-packetdiff --git a/tests/channels.scm b/tests/channels.scmindex 3e82315b0c..d45c450241 100644--- a/tests/channels.scm+++ b/tests/channels.scm@@ -480,8 +480,8 @@ #t (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add ".guix-channel" ,(object->string@@ -507,7 +507,7 @@ (commit-id-string commit1) (openpgp-public-key-fingerprint (read-openpgp-packet- %ed25519bis-public-key-file)))) ;different key+ %ed25519-2-public-key-file)))) ;different key (channel (channel (name 'example) (url (string-append "file://" directory)) (introduction intro))))@@ -519,7 +519,7 @@ (oid->string (commit-id commit1)) (key-fingerprint %ed25519-public-key-file) (key-fingerprint- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (authenticate-channel channel directory (commit-id-string commit2) #:keyring-reference-prefix "")@@ -530,8 +530,8 @@ #t (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add ".guix-channel" ,(object->string@@ -552,12 +552,12 @@ (signer ,(key-fingerprint %ed25519-public-key-file))) (add "c.txt" "C") (commit "third commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (branch "channel-keyring") (checkout "channel-keyring") (add "signer.key" ,(call-with-input-file %ed25519-public-key-file get-string-all))- (add "other.key" ,(call-with-input-file %ed25519bis-public-key-file+ (add "other.key" ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (commit "keyring commit") (checkout "master"))@@ -588,7 +588,7 @@ (unauthorized-commit-error-signing-key c)) (openpgp-public-key-fingerprint (read-openpgp-packet- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (authenticate-channel channel directory (commit-id-string commit3) #:keyring-reference-prefix "")diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex d87eacc659..f66ef191b0 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -161,14 +161,14 @@ (test-assert "signed commits, .guix-authorizations, unauthorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file get-string-all)) (add "signer2.key"- ,(call-with-input-file %ed25519bis-public-key-file+ ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (add ".guix-authorizations" ,(object->string@@ -184,7 +184,7 @@ (checkout "devel") (add "devel/1.txt" "1") (commit "first devel commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (checkout "master") (add "b.txt" "B") (commit "second commit"@@ -203,7 +203,7 @@ (openpgp-public-key-fingerprint (unauthorized-commit-error-signing-key c)) (openpgp-public-key-fingerprint- (read-openpgp-packet %ed25519bis-public-key-file)))))+ (read-openpgp-packet %ed25519-2-public-key-file))))) (and (authenticate-commits repository (list master1 master2) #:keyring-reference "master")@@ -230,14 +230,14 @@ (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file get-string-all)) (add "signer2.key"- ,(call-with-input-file %ed25519bis-public-key-file+ ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (add ".guix-authorizations" ,(object->string@@ -258,12 +258,12 @@ %ed25519-public-key-file) (name "Alice")) (,(key-fingerprint- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (commit "first devel commit" (signer ,(key-fingerprint %ed25519-public-key-file))) (add "devel/2.txt" "2") (commit "second devel commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (checkout "master") (add "b.txt" "B") (commit "second commit"@@ -273,7 +273,7 @@ ;; After the merge, the second signer is authorized. (add "c.txt" "C") (commit "third commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file))))+ (signer ,(key-fingerprint %ed25519-2-public-key-file)))) (with-repository directory repository (let ((master1 (find-commit repository "first commit")) (master2 (find-commit repository "second commit"))@@ -328,4 +328,3 @@ 'failed))))))) (test-end "git-authenticate")-diff --git a/tests/guix-authenticate.sh b/tests/guix-authenticate.shindex 3a05b232c1..0de6da1878 100644--- a/tests/guix-authenticate.sh+++ b/tests/guix-authenticate.sh@@ -28,7 +28,7 @@ rm -f "$sig" "$hash" trap 'rm -f "$sig" "$hash"' EXIT -key="$abs_top_srcdir/tests/signing-key.sec"+key="$abs_top_srcdir/tests/keys/signing-key.sec" key_len="`echo -n $key | wc -c`" # A hexadecimal string as long as a sha256 hash.@@ -67,7 +67,7 @@ test "$code" -ne 0 # encoded independently of the current locale: <https://bugs.gnu.org/43421>. hash="636166e9636166e9636166e9636166e9636166e9636166e9636166e9636166e9" latin1_cafe="caf$(printf '\351')"-echo "sign 21:tests/signing-key.sec 64:$hash" | guix authenticate \+echo "sign 26:tests/keys/signing-key.sec 64:$hash" | guix authenticate \ | LC_ALL=C grep "hash sha256 \"$latin1_cafe" # Test for <http://bugs.gnu.org/17312>: make sure 'guix authenticate' producesdiff --git a/tests/civodul.key b/tests/keys/civodul.pubsimilarity index 100%rename from tests/civodul.keyrename to tests/keys/civodul.pubdiff --git a/tests/dsa.key b/tests/keys/dsa.pubsimilarity index 100%rename from tests/dsa.keyrename to tests/keys/dsa.pubdiff --git a/tests/ed25519bis.key b/tests/keys/ed25519-2.pubsimilarity index 100%rename from tests/ed25519bis.keyrename to tests/keys/ed25519-2.pubdiff --git a/tests/ed25519bis.sec b/tests/keys/ed25519-2.secsimilarity index 100%rename from tests/ed25519bis.secrename to tests/keys/ed25519-2.secdiff --git a/tests/keys/ed25519-3.pub b/tests/keys/ed25519-3.pubnew file mode 100644index 0000000000..72f311984c--- /dev/null+++ b/tests/keys/ed25519-3.pub@@ -0,0 +1,9 @@+-----BEGIN PGP PUBLIC KEY BLOCK-----++mDMEYVH/7xYJKwYBBAHaRw8BAQdALMLeUhjEG2/UPCJj2j/debFwwAK5gT3G0l5d+ILfFldm0FTxleGFtcGxlQGV4YW1wbGUuY29tPoiWBBMWCAA+FiEEjO6M85jMSK68+7tINGBzA7NyoagkFAmFR/+8CGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC+F4AACgkQGBzA7Nyoagl3lgEAw6yqIlX11lTqwxBGhZk/Oy34O13cbJSZCGv+m0ja++hcA/3DCNOmT+oXjgO/w6enQZUQ1m/d6dUjCc2wOLlLz+ZoG+=+r3i+-----END PGP PUBLIC KEY BLOCK-----diff --git a/tests/keys/ed25519-3.sec b/tests/keys/ed25519-3.secnew file mode 100644index 0000000000..04128a4131--- /dev/null+++ b/tests/keys/ed25519-3.sec@@ -0,0 +1,10 @@+-----BEGIN PGP PRIVATE KEY BLOCK-----++lFgEYVH/7xYJKwYBBAHaRw8BAQdALMLeUhjEG2/UPCJj2j/debFwwAK5gT3G0l5d+ILfFldkAAP92goSbbzQ0ttElr9lr5Cm6rmQtqUZ2Cu/Jk9fvfZROwxI0tBU8ZXhh+bXBsZUBleGFtcGxlLmNvbT6IlgQTFggAPhYhBIzujPOYzEiuvO7SDRgcwOzcqGoJ+BQJhUf/vAhsDBQkDwmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEBgcwOzc+qGoJd5YBAMOsqiJV9dZU6sMQRoWZPzst+Dtd3GyUmQhr/ptI2voXAP9wwjTpk/qF+44Dv8Onp0GVENZv3enVIwnNsDi5S8/maBg==+=EmOt+-----END PGP PRIVATE KEY BLOCK-----diff --git a/tests/ed25519.key b/tests/keys/ed25519.pubsimilarity index 100%rename from tests/ed25519.keyrename to tests/keys/ed25519.pubdiff --git a/tests/ed25519.sec b/tests/keys/ed25519.secsimilarity index 100%rename from tests/ed25519.secrename to tests/keys/ed25519.secdiff --git a/tests/rsa.key b/tests/keys/rsa.pubsimilarity index 100%rename from tests/rsa.keyrename to tests/keys/rsa.pubdiff --git a/tests/signing-key.pub b/tests/keys/signing-key.pubsimilarity index 100%rename from tests/signing-key.pubrename to tests/keys/signing-key.pubdiff --git a/tests/signing-key.sec b/tests/keys/signing-key.secsimilarity index 100%rename from tests/signing-key.secrename to tests/keys/signing-key.secdiff --git a/tests/openpgp.scm b/tests/openpgp.scmindex c2be26fa49..1f20466772 100644--- a/tests/openpgp.scm+++ b/tests/openpgp.scm@@ -59,18 +59,22 @@ vBSFjNSiVHsuAA== (define %civodul-fingerprint "3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5") -(define %civodul-key-id #x090B11993D9AEBB5) ;civodul.key--;; Test keys. They were generated in a container along these lines:-;; guix environment -CP --ad-hoc gnupg pinentry-;; then, within the container:-;; mkdir ~/.gnupg-;; echo pinentry-program ~/.guix-profile/bin/pinentry-tty > ~/.gnupg/gpg-agent.conf-;; gpg --quick-gen-key '<ludo+test-rsa@chbouib.org>' rsa-;; or similar.-(define %rsa-key-id #xAE25DA2A70DEED59) ;rsa.key-(define %dsa-key-id #x587918047BE8BD2C) ;dsa.key-(define %ed25519-key-id #x771F49CBFAAE072D) ;ed25519.key+(define %civodul-key-id #x090B11993D9AEBB5) ;civodul.pub++#|+Test keys in ./tests/keys. They were generated in a container along these lines:+ guix environment -CP --ad-hoc gnupg pinentry coreutils+then, within the container:+ mkdir ~/.gnupg && chmod -R og-rwx ~/.gnupg+ gpg --batch --passphrase '' --quick-gen-key '<example@example.com>' ed25519+ gpg --armor --export example@example.com+ gpg --armor --export-secret-key example@example.com+ # echo pinentry-program ~/.guix-profile/bin/pinentry-curses > ~/.gnupg/gpg-agent.conf+or similar.+|#+(define %rsa-key-id #xAE25DA2A70DEED59) ;rsa.pub+(define %dsa-key-id #x587918047BE8BD2C) ;dsa.pub+(define %ed25519-key-id #x771F49CBFAAE072D) ;ed25519.pub (define %rsa-key-fingerprint (base16-string->bytevector@@ -168,7 +172,7 @@ Pz7oopeN72xgggYUNT37ezqN3MeCqw0= (not (port-ascii-armored? (open-bytevector-input-port %binary-sample)))) (test-assert "get-openpgp-keyring"- (let* ((key (search-path %load-path "tests/civodul.key"))+ (let* ((key (search-path %load-path "tests/keys/civodul.pub")) (keyring (get-openpgp-keyring (open-bytevector-input-port (call-with-input-file key read-radix-64)))))@@ -228,8 +232,10 @@ Pz7oopeN72xgggYUNT37ezqN3MeCqw0= (verify-openpgp-signature signature keyring (open-input-string "Hello!\n")))) (list status (openpgp-public-key-id key)))))- (list "tests/rsa.key" "tests/dsa.key"- "tests/ed25519.key" "tests/ed25519.key" "tests/ed25519.key")+ (list "tests/keys/rsa.pub" "tests/keys/dsa.pub"+ "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub") (list %hello-signature/rsa %hello-signature/dsa %hello-signature/ed25519/sha256 %hello-signature/ed25519/sha512@@ -248,9 +254,9 @@ Pz7oopeN72xgggYUNT37ezqN3MeCqw0= (call-with-input-file key read-radix-64)) keyring))) %empty-keyring- '("tests/rsa.key" "tests/dsa.key"- "tests/ed25519.key" "tests/ed25519.key"- "tests/ed25519.key"))))+ '("tests/keys/rsa.pub" "tests/keys/dsa.pub"+ "tests/keys/ed25519.pub" "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub")))) (map (lambda (signature) (let ((signature (string->openpgp-packet signature))) (let-values (((status key)-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 18:24 +0200
[PATCH 4/5] guix: Prepare the UI for continuable &warning exceptions.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928162406.27205-4-attila@lendvai.name
* guix/store.scm (call-with-store): Use dynamic-wind so that continuableexceptions are not broken by being re-raised as non-continuable. This isneeded for a later commit that uses continuable exceptions from withingit-authenticate to signal warnings to the user. The reason for this is thatthis way tests can explicitly check that a warning was signalled in certainsituations.* guix/ui.scm (call-with-error-handling): Handle &warning type exceptions byprinting them to the user, and then continuing at the place they weresignalled at.* guix/diagnostics.scm (emit-formatted-warning): New exportedfunction.--- guix/diagnostics.scm | 4 ++++ guix/store.scm | 16 ++++++++++------ guix/ui.scm | 11 ++++++++++- 3 files changed, 24 insertions(+), 7 deletions(-)
Toggle diff (94 lines)diff --git a/guix/diagnostics.scm b/guix/diagnostics.scmindex 6a792febd4..343213fb45 100644--- a/guix/diagnostics.scm+++ b/guix/diagnostics.scm@@ -48,6 +48,7 @@ formatted-message? formatted-message-string formatted-message-arguments+ emit-formatted-warning &fix-hint fix-hint?@@ -161,6 +162,9 @@ messages." (report-error args ...) (exit 1))) +(define* (emit-formatted-warning fmt . args)+ (emit-diagnostic fmt args #:prefix (G_ "warning: ") #:colors %warning-color))+ (define* (emit-diagnostic fmt args #:key location (colors (color)) (prefix "")) "Report diagnostic message FMT with the given ARGS and the specifieddiff --git a/guix/store.scm b/guix/store.scmindex 89a719bcfc..33d4039037 100644--- a/guix/store.scm+++ b/guix/store.scm@@ -45,6 +45,8 @@ #:use-module (srfi srfi-34) #:use-module (srfi srfi-35) #:use-module (srfi srfi-39)+ #:use-module ((rnrs conditions)+ #:select (warning?)) #:use-module (ice-9 match) #:use-module (ice-9 vlist) #:use-module (ice-9 popen)@@ -651,19 +653,21 @@ connection. Use with care." (define (call-with-store proc) "Call PROC with an open store connection."- (let ((store (open-connection)))+ (let ((store '())) (define (thunk) (parameterize ((current-store-protocol-version (store-connection-version store))) (call-with-values (lambda () (proc store)) (lambda results- (close-connection store) (apply values results))))) - (with-exception-handler (lambda (exception)- (close-connection store)- (raise-exception exception))- thunk)))+ (dynamic-wind+ (lambda ()+ (set! store (open-connection)))+ thunk+ (lambda ()+ (close-connection store)+ (set! store '()))))) (define-syntax-rule (with-store store exp ...) "Bind STORE to an open connection to the store and evaluate EXPs;diff --git a/guix/ui.scm b/guix/ui.scmindex 1428c254b3..88940f99ef 100644--- a/guix/ui.scm+++ b/guix/ui.scm@@ -69,6 +69,8 @@ #:use-module (srfi srfi-31) #:use-module (srfi srfi-34) #:use-module (srfi srfi-35)+ #:use-module ((rnrs conditions)+ #:select (warning?)) #:autoload (ice-9 ftw) (scandir) #:use-module (ice-9 match) #:use-module (ice-9 format)@@ -689,7 +691,14 @@ evaluating the tests and bodies of CLAUSES." (and (not (port-closed? port)) (port-filename port))) - (guard* (c ((package-input-error? c)+ (guard* (c ((warning? c)+ (if (formatted-message? c)+ (apply emit-formatted-warning+ (formatted-message-string c)+ (formatted-message-arguments c))+ (emit-formatted-warning "~a" c))+ '())+ ((package-input-error? c) (let* ((package (package-error-package c)) (input (package-error-invalid-input c)) (location (package-location package))-- 2.33.0
A
A
Attila Lendvai wrote on 28 Sep 18:24 +0200
[PATCH 5/5] guix: git-authenticate: Fix authenticate-repository.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20210928162406.27205-5-attila@lendvai.name
Always verify the channel introduction commit, so that no commit can slipthrough that was signed with a different key.
Always update the cache, because it affects the behavior of later calls.
Signal a continuable compound-condition (with type &warning included) when achannel introduction commit doesn't also update the '.guix-authentications'file.
* guix/git-authenticate.scm (authenticate-commit): Reword and extend the errormessage to point to the relevant part of the manual.(authenticate-repository): Eliminate optimizations to make the code path lessdependent on the input. Always trust the intro-commit itself. Always callverify-introductory-commit.(verify-introductory-commit): Check if the commit contains the key that wasused to sign it, and issue a warning otherwise. This is to avoid the confusioncaused by only the *second* commit yielding an error, because intro-commitsare always trusted.(authenticate-commit): Clarify error message.(authorized-keys-at-commit): Factored out to the toplevel fromcommit-authorized-keys.---
An example output with this patch:
$ ./pre-inst-env guix pull --allow-downgradesUpdating channel 'guix' from Git repository at '/path/guix'...guix pull: warning: moving channel 'guix' from 26a979105a58e99c6e0fbb51cb1500dfa2bc2cec to unrelated commit 17fc5e35699d2219e6fae1f0583bb8c2ec3deb25guix pull: warning: initial commit 17fc5e35699d2219e6fae1f0583bb8c2ec3deb25 does not add the key it is signed with (2E4F C7F5 07AB F022 36D3 D51F 31EE D3BE 74EC 3A1F) to the '.guix-authorizations' file.Authenticating channel 'guix', commits 17fc5e3 to 17fc5e3 (0 new commits)...[...]
guix/channels.scm | 4 +- guix/git-authenticate.scm | 156 ++++++++++++++++++++++---------------- 2 files changed, 94 insertions(+), 66 deletions(-)
Toggle diff (258 lines)diff --git a/guix/channels.scm b/guix/channels.scmindex e4e0428eb5..b84064537f 100644--- a/guix/channels.scm+++ b/guix/channels.scm@@ -347,8 +347,8 @@ commits)...~%") (progress-reporter/bar (length commits))) (define authentic-commits- ;; Consider the currently-used commit of CHANNEL as authentic so- ;; authentication can skip it and all its closure.+ ;; Optimization: consider the currently-used commit of CHANNEL as+ ;; authentic, so that authentication can skip it and all its closure. (match (find (lambda (candidate) (eq? (channel-name candidate) (channel-name channel))) (current-channels))diff --git a/guix/git-authenticate.scm b/guix/git-authenticate.scmindex ab3fcd8b2f..b2821a45ad 100644--- a/guix/git-authenticate.scm+++ b/guix/git-authenticate.scm@@ -30,6 +30,7 @@ #:select (cache-directory with-atomic-file-output)) #:use-module ((guix build utils) #:select (mkdir-p))+ #:use-module (guix diagnostics) #:use-module (guix progress) #:use-module (srfi srfi-1) #:use-module (srfi srfi-11)@@ -37,7 +38,10 @@ #:use-module (srfi srfi-34) #:use-module (srfi srfi-35) #:use-module (rnrs bytevectors)+ #:use-module ((rnrs exceptions)+ #:select (raise-continuable)) #:use-module (rnrs io ports)+ #:use-module (ice-9 exceptions) #:use-module (ice-9 match) #:autoload (ice-9 pretty-print) (pretty-print) #:export (read-authorizations@@ -159,11 +163,10 @@ return a list of authorized fingerprints." (string-downcase (string-filter char-set:graphic fingerprint)))) fingerprints)))) -(define* (commit-authorized-keys repository commit- #:optional (default-authorizations '()))- "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on-authorizations listed in its parent commits. If one of the parent commits-does not specify anything, fall back to DEFAULT-AUTHORIZATIONS."+(define (authorized-keys-at-commit repository commit default-authorizations)+ "Return the list of authorized key fingerprints from the '.guix-authorizations'+file at the given commit."+ (define (parents-have-authorizations-file? commit) ;; Return true if at least one of the parents of COMMIT has the ;; '.guix-authorizations' file.@@ -185,28 +188,35 @@ does not specify anything, fall back to DEFAULT-AUTHORIZATIONS." to remove '.guix-authorizations' file") (oid->string (commit-id commit))))))) - (define (commit-authorizations commit)- (catch 'git-error- (lambda ()- (let* ((tree (commit-tree commit))- (entry (tree-entry-bypath tree ".guix-authorizations"))- (blob (blob-lookup repository (tree-entry-id entry))))- (read-authorizations- (open-bytevector-input-port (blob-content blob)))))- (lambda (key error)- (if (= (git-error-code error) GIT_ENOTFOUND)- (begin- ;; Prevent removal of '.guix-authorizations' since it would make- ;; it trivial to force a fallback to DEFAULT-AUTHORIZATIONS.- (assert-parents-lack-authorizations commit)- default-authorizations)- (throw key error)))))+ (catch 'git-error+ (lambda ()+ (let* ((tree (commit-tree commit))+ (entry (tree-entry-bypath tree ".guix-authorizations"))+ (blob (blob-lookup repository (tree-entry-id entry))))+ (read-authorizations+ (open-bytevector-input-port (blob-content blob)))))+ (lambda (key error)+ (if (= (git-error-code error) GIT_ENOTFOUND)+ (begin+ ;; Prevent removal of '.guix-authorizations' since it would make+ ;; it trivial to force a fallback to DEFAULT-AUTHORIZATIONS.+ (assert-parents-lack-authorizations commit)+ default-authorizations)+ (throw key error))))) +(define* (commit-authorized-keys repository commit+ #:optional (default-authorizations '()))+ "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on+authorizations listed in its parent commits. If one of the parent commits+does not specify anything, fall back to DEFAULT-AUTHORIZATIONS." (match (commit-parents commit) (() default-authorizations) (parents (apply lset-intersection bytevector=?- (map commit-authorizations parents)))))+ (map (lambda (commit)+ (authorized-keys-at-commit repository commit+ default-authorizations))+ parents))))) (define* (authenticate-commit repository commit keyring #:key (default-authorizations '()))@@ -236,8 +246,8 @@ not specify anything, fall back to DEFAULT-AUTHORIZATIONS." (condition (&unauthorized-commit-error (commit id) (signing-key signing-key)))- (formatted-message (G_ "commit ~a not signed by an authorized \-key: ~a")+ (formatted-message (G_ "commit ~a is signed by an unauthorized \+key: ~a\nSee info guix \"Specifying Channel Authorizations\".") (oid->string id) (openpgp-format-fingerprint (openpgp-public-key-fingerprint@@ -356,7 +366,8 @@ authenticated (only COMMIT-ID is written to cache, though)." (base64-encode (sha256 (string->utf8 (repository-directory repository)))))) -(define (verify-introductory-commit repository keyring commit expected-signer)+(define (verify-introductory-commit repository commit expected-signer keyring+ authorizations) "Look up COMMIT in REPOSITORY, and raise an exception if it is not signed by EXPECTED-SIGNER." (define actual-signer@@ -364,13 +375,26 @@ EXPECTED-SIGNER." (commit-signing-key repository (commit-id commit) keyring))) (unless (bytevector=? expected-signer actual-signer)- (raise (formatted-message (G_ "initial commit ~a is signed by '~a' \+ (raise (make-compound-condition+ (condition (&unauthorized-commit-error (commit (commit-id commit))+ (signing-key actual-signer)))+ (formatted-message (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+ (oid->string (commit-id commit))+ (openpgp-format-fingerprint actual-signer)+ (openpgp-format-fingerprint expected-signer)))))+ (unless (member actual-signer+ (authorized-keys-at-commit repository commit authorizations)+ bytevector=?)+ (raise-continuable+ (make-compound-condition+ (condition (&warning))+ (formatted-message (G_ "initial commit ~a does not add \+the key it is signed with (~a) to the '.guix-authorizations' file.")+ (oid->string (commit-id commit))+ (openpgp-format-fingerprint actual-signer))))))++(define* (authenticate-repository repository intro-commit-hash intro-signer #:key (keyring-reference "keyring") (cache-key (repository-cache-key repository))@@ -380,11 +404,12 @@ instead of '~a'") (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. Commits listed in AUTHENTIC-COMMITS and their-closure are considered authentic. Return an alist mapping OpenPGP public keys-to the number of commits signed by that key that have been traversed.+ "Authenticate REPOSITORY up to commit END, an OID. Authentication starts with+commit INTRO-COMMIT-HASH, an OID, which must be signed by INTRO-SIGNER; an+exception is raised if that is not the case. Commits listed in+AUTHENTIC-COMMITS and their closure are considered authentic. 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@@ -393,8 +418,10 @@ 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 intro-commit+ (commit-lookup repository intro-commit-hash))+ (define end-commit (commit-lookup repository end)) @@ -404,36 +431,37 @@ denoting the authorized keys for commits whose parent lack the (define authenticated-commits ;; Previously-authenticated commits that don't need to be checked again. (filter-map (lambda (id)+ ;; We need to tolerate when cached commits disappear due to+ ;; --allow-downgrades. (false-if-git-not-found (commit-lookup repository (string->oid id)))) (append (previously-authenticated-commits cache-key)- authentic-commits)))+ authentic-commits+ ;; The intro commit is unconditionally trusted.+ (list (oid->string intro-commit-hash))))) (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))))+ (commit-difference end-commit intro-commit+ authenticated-commits))++ (verify-introductory-commit repository intro-commit+ intro-signer keyring+ historical-authorizations)++ (let* ((reporter (make-reporter intro-commit end-commit commits))+ (stats (call-with-progress-reporter reporter+ (lambda (report)+ (authenticate-commits repository commits+ #:keyring keyring+ #:default-authorizations+ historical-authorizations+ #:report-progress report)))))+ ;; Note that this will make the then current end commit of any channel,+ ;; that has been used/trusted in the past with a channel introduction,+ ;; remain trusted until the cache is cleared.+ (cache-authenticated-commit cache-key+ (oid->string (commit-id end-commit)))++ stats))-- 2.33.0
M
M
Maxime Devos wrote on 29 Sep 15:58 +0200
Re: [bug#50814] [PATCH 3/4] tests: Add failing test for .guix-authorizations and channel intro.
f9e5cc572e6495e2b2a4bd88ef81f84efabda31f.camel@telenet.be
Attila Lendvai schreef op di 28-09-2021 om 03:05 [+0200]:
Toggle quote (5 lines)> Will be fixed in a subsequent commit.> > * tests/git-authenticate.scm: New test "signed commits, .guix-authorizations,> channel-introduction".
I recommend placing the patch fixing the error before the test,to aid bisection.
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVRw8BccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7qXtAQDO2jsIOIrH3f4Ura6HrvtHRr2boairs5IUDmZsMdu37AD+JTr9JPVQWZT5As535QQm0O2i1cBYigwF7hFQ5hz9OAs==VqJm-----END PGP SIGNATURE-----

M
M
Maxime Devos wrote on 29 Sep 16:13 +0200
Re: [bug#50814] [PATCH 4/5] guix: Prepare the UI for continuable &warning exceptions.
9c093db2d9019ef2fe9b27979a3b51848f179a3b.camel@telenet.be
Attila Lendvai schreef op di 28-09-2021 om 18:24 [+0200]:
Toggle quote (24 lines)> (define (call-with-store proc)> "Call PROC with an open store connection."> - (let ((store (open-connection)))> + (let ((store '()))> (define (thunk)> (parameterize ((current-store-protocol-version> (store-connection-version store)))> (call-with-values (lambda () (proc store))> (lambda results> - (close-connection store)> (apply values results)))))> > - (with-exception-handler (lambda (exception)> - (close-connection store)> - (raise-exception exception))> - thunk)))> + (dynamic-wind> + (lambda ()> + (set! store (open-connection)))> + thunk> + (lambda ()> + (close-connection store)> + (set! store '())))))
Do we really need to close and open the connection again every timea continuation is made and resumed? This seems inefficient if a threadingmechanism implemented by continuations is used (such as guile-fibers),and there are two threads (‘fibers’) communicating and waiting with/foreach other in a loop, causing many ‘context switches’ (i.e., many capturedand resumed continuations).
Also note that a connection has some state: to the guix-daemon, it acts asa GC root for everything built with the connection, and everything added tothe store (with add-to-store & friends) with that connection ... Simplyreconnecting isn't sufficient.
Greetings,Maxime
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVR0lhccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7pHcAP90sSXgTCFgHHH0TfD2dUDxQjys03G/1Cix0O9jBBSZ2wD+KVvjBBduVO0t/RijZwAYoRDaykNRGY8suWjYft5TKgY==887w-----END PGP SIGNATURE-----

A
A
Attila Lendvai wrote on 29 Sep 16:50 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 50814@debbugs.gnu.org)
KXhKsjTN2gmW0wKMEmBlxgJN40WGeWtZBwW2Pi9T1QJXVdrbM7bG-7xx0gWCTf5uN1wGgSbx8nARju9N8-oV8roXtPM2gQgTi13XwLpIWvc=@lendvai.name
Toggle quote (12 lines)> Do we really need to close and open the connection again every time> a continuation is made and resumed? This seems inefficient if a threading> mechanism implemented by continuations is used (such as guile-fibers),> and there are two threads (‘fibers’) communicating and waiting with/for> each other in a loop, causing many ‘context switches’ (i.e., many captured> and resumed continuations).>> Also note that a connection has some state: to the guix-daemon, it acts as> a GC root for everything built with the connection, and everything added to> the store (with add-to-store & friends) with that connection ... Simply> reconnecting isn't sufficient.
pardon my ignorance wrt dynamic-wind and call/cc, but does that^ meanthat 1) i should simply leave the wind part of the dynamic-wind emptyand move back the open-connection call into the let... or that 2) theentire idea of replacing the exception handler with an unwind-protectis flawed?
if 2) then i'll try to smarten up the handler to use raise-continuableif the exception is of type &warning.
or any better ideas?
- attilaPGP: 5D5F 45C7 DFCD 0A39
M
M
Maxime Devos wrote on 29 Sep 22:36 +0200
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 50814@debbugs.gnu.org)
929da16ca45605a5bed718dea5d76db7176cf985.camel@telenet.be
Attila Lendvai schreef op wo 29-09-2021 om 14:50 [+0000]:
Toggle quote (18 lines)> > Do we really need to close and open the connection again every time> > a continuation is made and resumed? This seems inefficient if a threading> > mechanism implemented by continuations is used (such as guile-fibers),> > and there are two threads (‘fibers’) communicating and waiting with/for> > each other in a loop, causing many ‘context switches’ (i.e., many captured> > and resumed continuations).> > > > Also note that a connection has some state: to the guix-daemon, it acts as> > a GC root for everything built with the connection, and everything added to> > the store (with add-to-store & friends) with that connection ... Simply> > reconnecting isn't sufficient.> > pardon my ignorance wrt dynamic-wind and call/cc, but does that^ mean> that 1) i should simply leave the wind part of the dynamic-wind empty> and move back the open-connection call into the let... or that 2) the> entire idea of replacing the exception handler with an unwind-protect> is flawed?
About 1): which 'wind part' of dynamic-wind are you referring to?The in-guard or the out-guard?
If the out-guard is empty, then the reference to the old connection willbe overwritten when the fiber is paused and resumed, so the old connectionwill eventually be GC'ed, thus the daemon forgets some GC roots, leadingto a rare GC bug.
If the in-guard is empty, then the after pausing the fiber and resuming it,the connection will be closed while the fiber might still need it.
Toggle quote (3 lines)> if 2) then i'll try to smarten up the handler to use raise-continuable> if the exception is of type &warning.
That should work. Or simpler: always use raise-continuable.
Toggle quote (2 lines)> or any better ideas?
Conventionally, to emit warnings, the procedure 'warning' from(guix diagnostics) is used. See e.g. (guix ci), (guix deprecation), (guix gexp),(guix import ...), various modules under (guix scripts ...), (guix upstream) ...
Is there any reason not to use this pre-existing procedure?
Greetings,Maxime
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVTOPhccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7kuAAQCi8x1NRBBbbxHyFXbLl61sG0ssPuW6GDFBYCce02bXJQD+KB6Al9UEmjJL54d0ZSqL5GHacy/U1mFVBHwJVrwxFQA==C9qm-----END PGP SIGNATURE-----

A
A
Attila Lendvai wrote on 29 Sep 23:22 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 50814@debbugs.gnu.org)
FUwKtKPCBTmnvR7XMsNnfXkl-TyoK-YNruBr7HrCoVYZgwcAJUTmQ187kmE28sip2FXjtY17cBMVPT0WVhGgjUjbl2OsHtTDgqIVDEW6PLI=@lendvai.name
Toggle quote (13 lines)> About 1): which 'wind part' of dynamic-wind are you referring to?>> The in-guard or the out-guard?>> If the out-guard is empty, then the reference to the old connection will> be overwritten when the fiber is paused and resumed, so the old connection> will eventually be GC'ed, thus the daemon forgets some GC roots, leading> to a rare GC bug.>> If the in-guard is empty, then the after pausing the fiber and resuming it,> the connection will be closed while the fiber might still need it.

ok, so this is a no-go. thanks for the clarification!

Toggle quote (7 lines)> Conventionally, to emit warnings, the procedure 'warning' from> (guix diagnostics) is used. See e.g. (guix ci), (guix deprecation), (guix gexp),> (guix import ...), various modules under (guix scripts ...), (guix upstream) ...>> Is there any reason not to use this pre-existing procedure?

in a more advanced UI it might be a different story, but in thecurrent setup the only reason is to be able to assert for the warningin the tests.
is that worth it? shall i just user WARNING and forget about the test?
- attilaPGP: 5D5F 45C7 DFCD 0A39
M
M
Maxime Devos wrote on 30 Sep 00:03 +0200
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 50814@debbugs.gnu.org)
7a5b17dc857d92520df599bcbc592cd416ad71a2.camel@telenet.be
Attila Lendvai schreef op wo 29-09-2021 om 21:22 [+0000]:
Toggle quote (1 lines)> >
[...]
Toggle quote (13 lines)> > > Conventionally, to emit warnings, the procedure 'warning' from> > (guix diagnostics) is used. See e.g. (guix ci), (guix deprecation), (guix gexp),> > (guix import ...), various modules under (guix scripts ...), (guix upstream) ...> > > > Is there any reason not to use this pre-existing procedure?> > in a more advanced UI it might be a different story, but in the> current setup the only reason is to be able to assert for the warning> in the tests.> > is that worth it? shall i just user WARNING and forget about the test?
Testing a warning is emitted seems nice. You could parameterise guix-warning-portand use call-with-output-sting to capture the warning, and use (not (string-null? ...))to verify a warning has been emitted. From a quick grep, it appearstests/transformations.scm and tests/substitute.scm are doing things like this.
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVTiuBccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7oSWAPwLHsXLqm2umA08K19ScaFaJKFIgsGMA29dbbdd6ghqKQEAknynqmSt4jLhzkr8HlbM3fhhbaJTtSJFNm1GU+7PQwI==D2oU-----END PGP SIGNATURE-----

M
M
Maxime Devos wrote on 30 Sep 01:14 +0200
Re: [bug#50814] [PATCH 5/5] guix: git-authenticate: Fix authenticate-repository.
bf6e3898a0df95edd777027767e791fbb91f7cdb.camel@telenet.be
Attila Lendvai schreef op di 28-09-2021 om 18:24 [+0200]:
Toggle quote (12 lines)> [...]> -(define* (commit-authorized-keys repository commit> - #:optional (default-authorizations '()))> - "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on> -authorizations listed in its parent commits. If one of the parent commits> -does not specify anything, fall back to DEFAULT-AUTHORIZATIONS."

> +(define (authorized-keys-at-commit repository commit default-authorizations)> + "Return the list of authorized key fingerprints from the '.guix-authorizations'> +file at the given commit."
Could 'default-authorizations' still be documented?
Anyway, I don't see any problems with this patch (ignoring the warning and thedocstrings), but I'm completely unfamiliar with the internals of channelauthentication, so I don't know what to look for. You'll need to find someoneelse to review this.
Greetings,Maxime
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYVTzZBccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7r1sAPwPBMrVj2xf2/3+qRc2vCdJ99mLKGQv4vjiAmGOHRA6zAD9FPycoQ3VFncbi4+HCqt6WplEYsgLkw5Nneps9E4mYgU==LpmA-----END PGP SIGNATURE-----

L
L
Ludovic Courtès wrote on 9 Oct 12:45 +0200
50814
(address . control@debbugs.gnu.org)
87lf321npg.fsf@inria.fr
severity 50814 importantdone
L
L
Ludovic Courtès wrote on 9 Oct 15:44 +0200
Re: bug#50814: [PATCH] guix: git-authenticate: Also authenticate the channel intro commit.
(name . Leo Famulari)(address . leo@famulari.name)
87k0imxqgn.fsf_-_@gnu.org
Hi!
Leo Famulari <leo@famulari.name> skribis:
Toggle quote (5 lines)> I've marked the severity as "grave", which in Debbugs parlance means> "makes the package in question unusable or mostly so, or causes data> loss, or introduces a security hole allowing access to the accounts of> users who use the package."
This had the unfortunate effect that this patch would not show up inEmacs debbugs.el, which, for some reason, doesn’t know about “grave”.
I’ve changed to “important” and I’d suggest not going beyond “serious”,which is already grave enough. :-)
Ludo’.
L
L
Ludovic Courtès wrote on 9 Oct 15:53 +0200
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 50814@debbugs.gnu.org)
878rz2xq23.fsf@gnu.org
Hi Attila,
Attila Lendvai <attila@lendvai.name> skribis:
Toggle quote (7 lines)> * guix/git-authenticate.scm (authenticate-commit): Reword and extend the error> message to point to the relevant part of the manual.> (authenticate-repository): Explicitly authenticate the channel introduction> commit, so that it's also rejected unless it is signed by an authorized> key. Otherwise only the second commit would yield an error, which> is confusing.
This behavior is intentional and documented (info "(guix) SpecifyingChannel Authorizations"):
Channel introductions answer these questions by describing the first commit of a channel that should be authenticated. The first time a channel is fetched with ‘guix pull’ or ‘guix time-machine’, the command looks up the introductory commit and verifies that it is signed by the specified OpenPGP key. From then on, it authenticates commits according to the rule above.
[…]
The channel introduction, as we saw above, is the commit/key pair—i.e., the commit that introduced ‘.guix-authorizations’, and the fingerprint of the OpenPGP used to sign it.
By definition, parent commits of the introduction do not (notnecessarily) provide ‘.guix-authorizations’. So there’s nothing to bedone here, other than checking that the introductory commit is indeedsigned by the key specified in the introduction.
Does that make sense?
(Other patches you posted in this thread might be useful though, but wecan discuss them independently.)
Thanks,Ludo’.
PS: If you haven’t already, you can take a look at the following pages for more on the design rationale:
https://guix.gnu.org/en/blog/2020/securing-updates/ https://issues.guix.gnu.org/22883#69
A
A
Attila Lendvai wrote on 9 Oct 17:31 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 50814@debbugs.gnu.org)
wd2szjiaeLK46fkuZuw5593yUyqo_N18oULu7hsDbGrZzHWTQw2H2cwlFs7-CemTb5CzENsmcrGKwB0yN2k8od--1tUTIsP-7rvxrAbq-Js=@lendvai.name
Toggle quote (2 lines)> Does that make sense?
there are three main topics of this patchset:
1) adding a (hopefully helpful) warning. the primary goal.2) general cleanups3) IIRC, fixing some actual bugs in the process
as for 1):
what i did was fork guix master, and now i'm pulling my ownauthenticated branch from my own local git checkout, where every oncein a while i merge my various topic branches into my branch, and guixpull it.
when i added my second commit i have spent a disproportionate amountof time trying to figure out what was happening: the first commit wasaccepted, and i thought it's set up all fine. then who knows how muchlater, when i added my second commit, i was staring at the screenwithout a clue why pulling doesn't work anymore.
then i ventured into quickly adding warning, so that others won'twaste their time on this, and went down the rabbit hole, whichresulted in fixing actual bugs, i believe. IIRC, they are exposed bythe test that i have added when run on the current codebase.
as for 3), any actual bugs:
i'll investigate again later by running the test without the fix, and writeup my results here, or better yet, in a better commit message.
--• attila lendvai• PGP: 963F 5D5F 45C7 DFCD 0A39--It should be a grammatical if not legal offense to ascribe thoughts, opinions and decisions to "we" without a signed power of attorney.
A
A
Attila Lendvai wrote on 10 Oct 16:15 +0200
[PATCH] tests: Add test for .guix-authorizations and channel intro.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20211010141502.15716-1-attila@lendvai.name
This test used to fail before a recent fix to authenticate-repository.
* tests/git-authenticate.scm: New test "signed commits, .guix-authorizations,channel-introduction".---
reseding the patch that adds the test (i have extended the comments where thetest fails, and also fixed the check for the warning).
Toggle quote (3 lines)> i'll investigate again later by running the test without the fix, and write> up my results here, or better yet, in a better commit message.
i ran the test without my fix commit, and indeed it fails at two points:
1)
;; Should fail because it is signed with key2, not key1(check-from "commit 3" #:should-fail? #true)
2)
;; It is not very intuitive why commit 1 and 2 should be trusted;; at this point: commit 4 has previously been used as a channel;; intro, thus it got marked as trusted in the ~/.cache/.;; Because commit 1 and 2 are among its parents, it should also;; be trusted at this point because of the cache. Note that;; it's debatable whether this semantics is a good idea, but;; this is how git-authenticate is and has been implemented for;; a while (modulo failing to update the cache in the past when;; taking certain code paths).(check-from "commit 1")
please take a look at the test, and let me know if any of theassumptions encoded into the test is wrong, or if anythingelse needs clarification.
- attila

tests/git-authenticate.scm | 139 +++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
Toggle diff (166 lines)diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex f66ef191b0..7989f46924 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -18,6 +18,7 @@ (define-module (test-git-authenticate) #:use-module (git)+ #:use-module (guix diagnostics) #:use-module (guix git) #:use-module (guix git-authenticate) #:use-module (guix openpgp)@@ -28,6 +29,10 @@ #:use-module (srfi srfi-34) #:use-module (srfi srfi-64) #:use-module (rnrs bytevectors)+ #:use-module ((rnrs conditions)+ #:select (warning?))+ #:use-module ((rnrs exceptions)+ #:select (with-exception-handler)) #:use-module (rnrs io ports)) ;; Test the (guix git-authenticate) tools.@@ -226,6 +231,140 @@ #:keyring-reference "master") #f))))))) +(unless (gpg+git-available?) (test-skip 1))+(test-assert "signed commits, .guix-authorizations, channel-introduction"+ (let* ((result #true)+ (key1 %ed25519-public-key-file)+ (key2 %ed25519-2-public-key-file)+ (key3 %ed25519-3-public-key-file))+ (with-fresh-gnupg-setup (list key1 %ed25519-secret-key-file+ key2 %ed25519-2-secret-key-file+ key3 %ed25519-3-secret-key-file)+ (with-temporary-git-repository dir+ `((checkout "keyring" orphan)+ (add "signer1.key" ,(call-with-input-file key1 get-string-all))+ (add "signer2.key" ,(call-with-input-file key2 get-string-all))+ (add "signer3.key" ,(call-with-input-file key3 get-string-all))+ (commit "keyring commit")++ (checkout "main" orphan)+ (add "noise0")+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ (,(key-fingerprint key3) (name "Charlie"))))))+ (commit "commit 0" (signer ,(key-fingerprint key3)))+ (add "noise1")+ (commit "commit 1" (signer ,(key-fingerprint key1)))+ (add "noise2")+ (commit "commit 2" (signer ,(key-fingerprint key1))))+ (with-repository dir repo+ (let* ((commit-0 (find-commit repo "commit 0"))+ (check-from+ (lambda* (commit #:key (should-fail? #false) (key key1)+ (historical-authorizations+ ;; key3 is trusted to authorize commit 0+ (list (key-fingerprint-vector key3))))+ (guard (c ((unauthorized-commit-error? c)+ (if should-fail?+ c+ (let ((port (current-output-port)))+ (format port "FAILURE: Unexpected exception at commit '~s':~%"+ commit)+ (print-exception port (stack-ref (make-stack #t) 1)+ c (exception-args c))+ (set! result #false)+ '()))))+ (format #true "~%~%Checking ~s, should-fail? ~s, repo commits:~%"+ commit should-fail?)+ ;; to be able to inspect in the logs+ (invoke "git" "-C" dir "log" "--reverse" "--pretty=oneline" "main")+ (set! commit (find-commit repo commit))+ (authenticate-repository+ repo+ (commit-id commit)+ (key-fingerprint-vector key)+ #:historical-authorizations historical-authorizations)+ (when should-fail?+ (format #t "FAILURE: Authenticating commit '~s' should have failed.~%" commit)+ (set! result #false))+ '()))))+ (check-from "commit 0" #:key key3)+ (check-from "commit 1")+ (check-from "commit 2")+ (with-git-repository dir+ `((add "noise 3")+ ;; a commit with key2+ (commit "commit 3" (signer ,(key-fingerprint key2))))+ ;; Should fail because it is signed with key2, not key1+ (check-from "commit 3" #:should-fail? #true)+ ;; Specify commit 3 as a channel-introduction signed with+ ;; key2. This is valid, but it should warn the user, because+ ;; .guix-authorizations is not updated to include key2, which+ ;; means that any subsequent commits with the same key will be+ ;; rejected.+ (set! result+ (and (let ((signalled? #false))+ (with-exception-handler+ (lambda (c)+ (cond+ ((not (warning? c))+ (raise c))+ ((formatted-message? c)+ (format #true "warning (expected): ~a~%"+ (apply format #false+ (formatted-message-string c)+ (formatted-message-arguments c)))+ (set! signalled? #true)))+ '())+ (lambda ()+ (check-from "commit 3" #:key key2)+ (unless signalled?+ (format #t "FAILURE: No warning signalled for commit 3~%"))+ signalled?)))+ result)))+ (with-git-repository dir+ `((reset ,(oid->string (commit-id (find-commit repo "commit 2"))))+ (add "noise 4")+ ;; set it up properly+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ (,(key-fingerprint key2) (name "Bob"))))))+ (commit "commit 4" (signer ,(key-fingerprint key2))))+ ;; This should fail because even though commit 4 adds key2 to+ ;; .guix-authorizations, the commit itself is not authorized.+ (check-from "commit 1" #:should-fail? #true)+ ;; This should pass, because it's a valid channel intro at commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 5")+ (commit "commit 5" (signer ,(key-fingerprint key2))))+ ;; It is not very intuitive why commit 1 and 2 should be trusted+ ;; at this point: commit 4 has previously been used as a channel+ ;; intro, thus it got marked as trusted in the ~/.cache/.+ ;; Because commit 1 and 2 are among its parents, it should also+ ;; be trusted at this point because of the cache. Note that+ ;; it's debatable whether this semantics is a good idea, but+ ;; this is how git-authenticate is and has been implemented for+ ;; a while (modulo failing to update the cache in the past when+ ;; taking certain code paths).+ (check-from "commit 1")+ (check-from "commit 2")+ ;; Should still be fine, but only when starting from commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 6")+ (commit "commit 6" (signer ,(key-fingerprint key1))))+ (check-from "commit 1")+ (check-from "commit 2")+ (check-from "commit 4" #:key key2))))))+ result))+ (unless (gpg+git-available?) (test-skip 1)) (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file-- 2.33.0
L
L
Ludovic Courtès wrote on 12 Oct 11:39 +0200
Re: bug#50814: [PATCH] guix: git-authenticate: Also authenticate the channel intro commit.
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 50814@debbugs.gnu.org)
87r1cqwpi3.fsf@gnu.org
Hi,
Attila Lendvai <attila@lendvai.name> skribis:
Toggle quote (6 lines)> there are three main topics of this patchset:>> 1) adding a (hopefully helpful) warning. the primary goal.> 2) general cleanups> 3) IIRC, fixing some actual bugs in the process
Alright. Please next time open one issue per topic: that’s a good wayto maximize the chances that review happens in a timely fashion. :-)
Toggle quote (18 lines)> as for 1):>> what i did was fork guix master, and now i'm pulling my own> authenticated branch from my own local git checkout, where every once> in a while i merge my various topic branches into my branch, and guix> pull it.>> when i added my second commit i have spent a disproportionate amount> of time trying to figure out what was happening: the first commit was> accepted, and i thought it's set up all fine. then who knows how much> later, when i added my second commit, i was staring at the screen> without a clue why pulling doesn't work anymore.>> then i ventured into quickly adding warning, so that others won't> waste their time on this, and went down the rabbit hole, which> resulted in fixing actual bugs, i believe. IIRC, they are exposed by> the test that i have added when run on the current codebase.
I understand the behavior was surprising to you, but I’d like to see ifwe can pinpoint why. Can you think of anything that could be added tothe documentation?
https://guix.gnu.org/manual/en/html_node/Specifying-Channel-Authorizations.html
Toggle quote (5 lines)> as for 3), any actual bugs:>> i'll investigate again later by running the test without the fix, and write> up my results here, or better yet, in a better commit message.
Yes please. In general, please start by reporting the bug: what youget, what you expected, and how to reproduce. That makes it easier tounderstand and evaluate proposed fixes.
Thanks!
Ludo’.
L
L
Leo Famulari wrote on 12 Oct 17:17 +0200
(name . Ludovic Courtès)(address . ludo@gnu.org)
YWWnDFynmZ8TmgHX@jasmine.lan
On Sat, Oct 09, 2021 at 03:44:40PM +0200, Ludovic Courtès wrote:
Toggle quote (6 lines)> This had the unfortunate effect that this patch would not show up in> Emacs debbugs.el, which, for some reason, doesn’t know about “grave”.> > I’ve changed to “important” and I’d suggest not going beyond “serious”,> which is already grave enough. :-)
Noted!
A
A
Attila Lendvai wrote 3 days ago
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 50814@debbugs.gnu.org)
RQVMMoKLN91IL7OY4XhljYSCt4hyEDqgKIcY9kOpNk6OHBTK46-oU78I6WzxpldpaaUSExfyl1vVXXpCaw0orq5fEYRhQOflzH1xM-snJU8=@lendvai.name
Toggle quote (3 lines)> i'll investigate again later by running the test without the fix, and write> up my results here, or better yet, in a better commit message.
i ran the test without my fix, and indeed it fails at two points:
1)
;; Should fail because it is signed with key2, not key1(check-from "commit 3" #:should-fail? #true)
2)
;; It is not very intuitive why commit 1 and 2 should be trusted;; at this point: commit 4 has previously been used as a channel;; intro, thus it got marked as trusted in the ~/.cache/.;; Because commit 1 and 2 are among its parents, it should also;; be trusted at this point because of the cache. Note that;; it's debatable whether this semantics is a good idea, but;; this is how git-authenticate is and has been implemented for;; a while (modulo failing to update the cache in the past when;; taking certain code paths).(check-from "commit 1")(check-from "commit 2")
note that i have extended the above comments compared to what's in thecommits that i have sent previously (and i also fixed the check forthe warning). i suspect there are still things to discuss, so i'llwait for any feedback before i resend the patches. i did not touch thetest code itself, so you can easily find these points in it.

Toggle quote (5 lines)> Yes please. In general, please start by reporting the bug: what you> get, what you expected, and how to reproduce. That makes it easier> to understand and evaluate proposed fixes.

understood. the problem is that it all started out as adding awarning, and the rest were just side-quests... :)

Toggle quote (5 lines)> Alright. Please next time open one issue per topic: that’s a good> way to maximize the chances that review happens in a timely fashion.> :-)

can i mark dependencies between issues/patchsets?
because all that i could do here is split this into two sets ofcommits (because of the dependencies between the commits):
1) the 3 test commits, and2) the 2 guix commits.
i thought that separating the test that is exhibiting the bug, fromthe fix that fixes it, would only hinder the process.

Toggle quote (5 lines)> I understand the behavior was surprising to you, but I’d like to see> if we can pinpoint why. Can you think of anything that could be> added to the documentation?

if we assume that everyone reads and internalizes every page of thedocumentation of every software that they use, then i guess nothingneeds to be added.
but if our goal is to maximize the effectiveness of the users, then noamount of static, free-flowing text can compete with a warning that issignalled in close context to the issue.
i think the right question to ask here is how often would this warningbe superfluous. my assumption is that very rarely, if ever, but i maynot be aware of some use-cases.
looking forward to any feedback on how to improve this.
--• attila lendvai• PGP: 963F 5D5F 45C7 DFCD 0A39--If the source of fear is the unknown, and fear is the only way to be controlled, then knowledge is the only way to be free.
L
L
Ludovic Courtès wrote 2 days ago
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 50814@debbugs.gnu.org)
878ryqbsvk.fsf@gnu.org
Hi Attila,
Attila Lendvai <attila@lendvai.name> skribis:
Toggle quote (5 lines)>> i'll investigate again later by running the test without the fix, and write>> up my results here, or better yet, in a better commit message.>> i ran the test without my fix, and indeed it fails at two points:
Sorry, which test is failing? Is that part of the patches you sent?I need more context. :-)
[...]
Toggle quote (16 lines)>> Alright. Please next time open one issue per topic: that’s a good>> way to maximize the chances that review happens in a timely fashion.>> :-)>>> can i mark dependencies between issues/patchsets?>> because all that i could do here is split this into two sets of> commits (because of the dependencies between the commits):>> 1) the 3 test commits, and> 2) the 2 guix commits.>> i thought that separating the test that is exhibiting the bug, from> the fix that fixes it, would only hinder the process.
Yes, in general it’s best to have the test and the fix in the samecommit.
However, at this point, I’m not sure which “bug” we’re talking about.What you described in your initial message is not a bug in my view:
https://issues.guix.gnu.org/50814#28
Toggle quote (13 lines)>> I understand the behavior was surprising to you, but I’d like to see>> if we can pinpoint why. Can you think of anything that could be>> added to the documentation?>>> if we assume that everyone reads and internalizes every page of the> documentation of every software that they use, then i guess nothing> needs to be added.>> but if our goal is to maximize the effectiveness of the users, then no> amount of static, free-flowing text can compete with a warning that is> signalled in close context to the issue.
Sure, I agree.
However, you’re clearly a power user at this point :-), and we’retalking about one of the most sensitive pieces of code in Guix. I thinkit’s important to make sure we’re on the same level of understanding ofthe design and current implementation; I also think it’s notunreasonable to expect channel writers to pay attention to documentationon these matters.
I’m not saying that we should not change anything, but rather that it’snot like a simple usability/UX issue.
I hope this makes sense!
Ludo’.
A
A
Attila Lendvai wrote 42 hours ago
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 50814@debbugs.gnu.org)
HYmFugT4oHoOLRIfzLRZyEtR3aJeuSjmKed_5Okw0HqRj6pP8A6ITOV_6Ut55WzXhtxilgUeVoqlWQXSfRPvi89xdZnUOKiloJlk-MaPF1A=@lendvai.name
hi Ludo,

Toggle quote (7 lines)> > i ran the test without my fix, and indeed it fails at two points:>> Sorry, which test is failing? Is that part of the patches you sent?>> I need more context. :-)

i have sent 5 patches. three of them are prefixed with 'test:', andtwo of those are test idempotent test infrastructure changes. thethird of them adds a new test that tests git-authenticate. this is thetest that i'm talking about.
if you apply only these 3 test related commits, and run the new teston the unpatched codebase, then you'll see the two failures that i'mtalking about in my previous mail.
search the test log for 'FAILURE' (the test runs fully, but fails incase any of the tests fail).
one of the two failures is a more serious issue because a channelintro commit is accepted while it shouldn't be.

Toggle quote (19 lines)> > > Alright. Please next time open one issue per topic: that’s a good> > > way to maximize the chances that review happens in a timely fashion.> > >> > > :-)> >> > can i mark dependencies between issues/patchsets?> > because all that i could do here is split this into two sets of> > commits (because of the dependencies between the commits):> >> > 1. the 3 test commits, and> > 2. the 2 guix commits.> >> > i thought that separating the test that is exhibiting the bug, from> > the fix that fixes it, would only hinder the process.>> Yes, in general it’s best to have the test and the fix in the same> commit.

i cut the fix and the test in separate commits (but sent them in thesame patchset/issue), so that it's possible to partially apply onlythe test commits, and study its behavior on the current codebase.

Toggle quote (7 lines)> However, at this point, I’m not sure which “bug” we’re talking about.>> What you described in your initial message is not a bug in my view:>> https://issues.guix.gnu.org/50814#28

the bug is described, formally, by the test that i have added (unlessthe test itself is wrong, that is). IIRC, i started putting togetherthis new test to expose the bugs that i have suspected while reviewingthe implementation of git-authenticate, and then to support my effortto fix them afterwards.
i think the best next-action is for someone qualified to take a lookat the test that i have added, and see if any of the assumptionsencoded in it is wrong. i think i understand this part of the codebasepretty well now, but i may have erred.
if the test seems to be valid, then proceed to review the rest of thecommits.

Toggle quote (6 lines)> I’m not saying that we should not change anything, but rather that it’s> not like a simple usability/UX issue.>> I hope this makes sense!

yes, it does! actually, i welcome the reluctance to haphazardly applypatches to this part of the codebase. i was kinda expecting this, andthat's why i have prepared the commits so that the test can be appliedand tried separately.
hope this clarifies the situation,
--• attila lendvai• PGP: 963F 5D5F 45C7 DFCD 0A39--“Everything can be taken from a man but one thing: the last of the human freedoms—to choose one’s attitude in any given set of circumstances, to choose one’s own way.” — Viktor E. Frankl (1905–1997), 'Man's Search for Meaning' (1946)
A
A
Attila Lendvai wrote 42 hours ago
[PATCH 1/5] tests: Smarten up git repository testing framework.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20211018155734.5175-1-attila@lendvai.name
* guix/tests/git.scm (with-git-repository): New macro that can be used ina nested way under a with-temporary-git-repository.(populate-git-repository): Extend the DSL with (add "some-noise"), (reset"[commit hash]"), (checkout "branch" orphan).* guix/tests/gnupg.scm (key-fingerprint-vector): New function.--- guix/tests/git.scm | 23 +++++++++++++++++++++-- guix/tests/gnupg.scm | 8 ++++++-- 2 files changed, 27 insertions(+), 4 deletions(-)
Toggle diff (95 lines)diff --git a/guix/tests/git.scm b/guix/tests/git.scmindex 69960284d9..76f5a8b937 100644--- a/guix/tests/git.scm+++ b/guix/tests/git.scm@@ -26,6 +26,7 @@ (define-module (guix tests git) #:use-module (ice-9 control) #:export (git-command with-temporary-git-repository+ with-git-repository find-commit)) (define git-command@@ -59,8 +60,9 @@ (define (git command . args) (apply invoke (git-command) "-C" directory command args))))) - (mkdir-p directory)- (git "init")+ (unless (directory-exists? (string-append directory "/.git"))+ (mkdir-p directory)+ (git "init")) (let loop ((directives directives)) (match directives@@ -78,6 +80,9 @@ (define (git command . args) port))) (git "add" file) (loop rest)))+ ((('add file-name-and-content) rest ...)+ (loop (cons `(add ,file-name-and-content ,file-name-and-content)+ rest))) ((('remove file) rest ...) (git "rm" "-f" file) (loop rest))@@ -99,12 +104,18 @@ (define (git command . args) ((('checkout branch) rest ...) (git "checkout" branch) (loop rest))+ ((('checkout branch 'orphan) rest ...)+ (git "checkout" "--orphan" branch)+ (loop rest)) ((('merge branch message) rest ...) (git "merge" branch "-m" message) (loop rest)) ((('merge branch message ('signer fingerprint)) rest ...) (git "merge" branch "-m" message (string-append "--gpg-sign=" fingerprint))+ (loop rest))+ ((('reset to) rest ...)+ (git "reset" "--hard" to) (loop rest))))) (define (call-with-temporary-git-repository directives proc)@@ -121,6 +132,14 @@ (define-syntax-rule (with-temporary-git-repository directory (lambda (directory) exp ...))) +(define-syntax-rule (with-git-repository directory+ directives exp ...)+ "Evaluate EXP in a context where DIRECTORY is (further) populated as+per DIRECTIVES."+ (begin+ (populate-git-repository directory directives)+ exp ...))+ (define (find-commit repository message) "Return the commit in REPOSITORY whose message includes MESSAGE, a string." (let/ec returndiff --git a/guix/tests/gnupg.scm b/guix/tests/gnupg.scmindex eb8ff63a43..c7630db912 100644--- a/guix/tests/gnupg.scm+++ b/guix/tests/gnupg.scm@@ -33,6 +33,7 @@ (define-module (guix tests gnupg) read-openpgp-packet key-fingerprint+ key-fingerprint-vector key-id)) (define gpg-command@@ -76,7 +77,10 @@ (define (read-openpgp-packet file) (open-bytevector-input-port (call-with-input-file file read-radix-64)))) +(define key-fingerprint-vector+ (compose openpgp-public-key-fingerprint+ read-openpgp-packet))+ (define key-fingerprint (compose openpgp-format-fingerprint- openpgp-public-key-fingerprint- read-openpgp-packet))+ key-fingerprint-vector))-- 2.33.0
A
A
Attila Lendvai wrote 42 hours ago
[PATCH 2/5] tests: Move keys into ./tests/keys/ and add a third ed25519 key.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20211018155734.5175-2-attila@lendvai.name
The third key will be used in an upcoming commit.
Rename public keys to .pub.
* guix/tests/gnupg.scm (%ed25519-3-public-key-file): New variable.(%ed25519-3-secret-key-file): New variable.(%ed25519-2-public-key-file): Renamed from %ed25519bis-public-key-file.(%ed25519-2-secret-key-file): Renamed from %ed25519bis-secret-key-file.* tests/keys/ed25519-3.key: New file.* tests/keys/ed25519-3.sec: New file.--- Makefile.am | 20 +++++----- build-aux/test-env.in | 6 +-- guix/tests/gnupg.scm | 22 ++++++---- tests/channels.scm | 18 ++++----- tests/git-authenticate.scm | 23 +++++------ tests/guix-authenticate.sh | 4 +- tests/{civodul.key => keys/civodul.pub} | 0 tests/{dsa.key => keys/dsa.pub} | 0 tests/{ed25519bis.key => keys/ed25519-2.pub} | 0 tests/{ed25519bis.sec => keys/ed25519-2.sec} | 0 tests/keys/ed25519-3.pub | 9 +++++ tests/keys/ed25519-3.sec | 10 +++++ tests/{ed25519.key => keys/ed25519.pub} | 0 tests/{ => keys}/ed25519.sec | 0 tests/{rsa.key => keys/rsa.pub} | 0 tests/{ => keys}/signing-key.pub | 0 tests/{ => keys}/signing-key.sec | 0 tests/openpgp.scm | 42 +++++++++++--------- 18 files changed, 93 insertions(+), 61 deletions(-) rename tests/{civodul.key => keys/civodul.pub} (100%) rename tests/{dsa.key => keys/dsa.pub} (100%) rename tests/{ed25519bis.key => keys/ed25519-2.pub} (100%) rename tests/{ed25519bis.sec => keys/ed25519-2.sec} (100%) create mode 100644 tests/keys/ed25519-3.pub create mode 100644 tests/keys/ed25519-3.sec rename tests/{ed25519.key => keys/ed25519.pub} (100%) rename tests/{ => keys}/ed25519.sec (100%) rename tests/{rsa.key => keys/rsa.pub} (100%) rename tests/{ => keys}/signing-key.pub (100%) rename tests/{ => keys}/signing-key.sec (100%)
Toggle diff (410 lines)diff --git a/Makefile.am b/Makefile.amindex 635147efc1..95c6597c17 100644--- a/Makefile.am+++ b/Makefile.am@@ -645,16 +645,18 @@ EXTRA_DIST += \ build-aux/update-guix-package.scm \ build-aux/update-NEWS.scm \ tests/test.drv \- tests/signing-key.pub \- tests/signing-key.sec \ tests/cve-sample.json \- tests/civodul.key \- tests/rsa.key \- tests/dsa.key \- tests/ed25519.key \- tests/ed25519.sec \- tests/ed25519bis.key \- tests/ed25519bis.sec \+ tests/keys/signing-key.pub \+ tests/keys/signing-key.sec \+ tests/keys/civodul.pub \+ tests/keys/rsa.pub \+ tests/keys/dsa.pub \+ tests/keys/ed25519.pub \+ tests/keys/ed25519.sec \+ tests/keys/ed25519-2.pub \+ tests/keys/ed25519-2.sec \+ tests/keys/ed25519-3.pub \+ tests/keys/ed25519-3.sec \ build-aux/config.rpath \ bootstrap \ doc/build.scm \diff --git a/build-aux/test-env.in b/build-aux/test-env.inindex 7efc43206c..ca786437e9 100644--- a/build-aux/test-env.in+++ b/build-aux/test-env.in@@ -73,9 +73,9 @@ then # Copy the keys so that the secret key has the right permissions (the # daemon errors out when this is not the case.) mkdir -p "$GUIX_CONFIGURATION_DIRECTORY"- cp "@abs_top_srcdir@/tests/signing-key.sec" \- "@abs_top_srcdir@/tests/signing-key.pub" \- "$GUIX_CONFIGURATION_DIRECTORY"+ cp "@abs_top_srcdir@/tests/keys/signing-key.sec" \+ "@abs_top_srcdir@/tests/keys/signing-key.pub" \+ "$GUIX_CONFIGURATION_DIRECTORY" chmod 400 "$GUIX_CONFIGURATION_DIRECTORY/signing-key.sec" fi diff --git a/guix/tests/gnupg.scm b/guix/tests/gnupg.scmindex c7630db912..09f02a2b67 100644--- a/guix/tests/gnupg.scm+++ b/guix/tests/gnupg.scm@@ -28,8 +28,10 @@ (define-module (guix tests gnupg) %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file+ %ed25519-3-public-key-file+ %ed25519-3-secret-key-file read-openpgp-packet key-fingerprint@@ -64,13 +66,17 @@ (define-syntax-rule (with-fresh-gnupg-setup imported exp ...) (call-with-fresh-gnupg-setup imported (lambda () exp ...))) (define %ed25519-public-key-file- (search-path %load-path "tests/ed25519.key"))+ (search-path %load-path "tests/keys/ed25519.pub")) (define %ed25519-secret-key-file- (search-path %load-path "tests/ed25519.sec"))-(define %ed25519bis-public-key-file- (search-path %load-path "tests/ed25519bis.key"))-(define %ed25519bis-secret-key-file- (search-path %load-path "tests/ed25519bis.sec"))+ (search-path %load-path "tests/keys/ed25519.sec"))+(define %ed25519-2-public-key-file+ (search-path %load-path "tests/keys/ed25519-2.pub"))+(define %ed25519-2-secret-key-file+ (search-path %load-path "tests/keys/ed25519-2.sec"))+(define %ed25519-3-public-key-file+ (search-path %load-path "tests/keys/ed25519-3.pub"))+(define %ed25519-3-secret-key-file+ (search-path %load-path "tests/keys/ed25519-3.sec")) (define (read-openpgp-packet file) (get-openpgp-packetdiff --git a/tests/channels.scm b/tests/channels.scmindex 3e82315b0c..d45c450241 100644--- a/tests/channels.scm+++ b/tests/channels.scm@@ -480,8 +480,8 @@ (define (find-commit* message) #t (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add ".guix-channel" ,(object->string@@ -507,7 +507,7 @@ (define (find-commit* message) (commit-id-string commit1) (openpgp-public-key-fingerprint (read-openpgp-packet- %ed25519bis-public-key-file)))) ;different key+ %ed25519-2-public-key-file)))) ;different key (channel (channel (name 'example) (url (string-append "file://" directory)) (introduction intro))))@@ -519,7 +519,7 @@ (define (find-commit* message) (oid->string (commit-id commit1)) (key-fingerprint %ed25519-public-key-file) (key-fingerprint- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (authenticate-channel channel directory (commit-id-string commit2) #:keyring-reference-prefix "")@@ -530,8 +530,8 @@ (define (find-commit* message) #t (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add ".guix-channel" ,(object->string@@ -552,12 +552,12 @@ (define (find-commit* message) (signer ,(key-fingerprint %ed25519-public-key-file))) (add "c.txt" "C") (commit "third commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (branch "channel-keyring") (checkout "channel-keyring") (add "signer.key" ,(call-with-input-file %ed25519-public-key-file get-string-all))- (add "other.key" ,(call-with-input-file %ed25519bis-public-key-file+ (add "other.key" ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (commit "keyring commit") (checkout "master"))@@ -588,7 +588,7 @@ (define (find-commit* message) (unauthorized-commit-error-signing-key c)) (openpgp-public-key-fingerprint (read-openpgp-packet- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (authenticate-channel channel directory (commit-id-string commit3) #:keyring-reference-prefix "")diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex d87eacc659..f66ef191b0 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -161,14 +161,14 @@ (define (gpg+git-available?) (test-assert "signed commits, .guix-authorizations, unauthorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file get-string-all)) (add "signer2.key"- ,(call-with-input-file %ed25519bis-public-key-file+ ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (add ".guix-authorizations" ,(object->string@@ -184,7 +184,7 @@ (define (gpg+git-available?) (checkout "devel") (add "devel/1.txt" "1") (commit "first devel commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (checkout "master") (add "b.txt" "B") (commit "second commit"@@ -203,7 +203,7 @@ (define (correct? c commit) (openpgp-public-key-fingerprint (unauthorized-commit-error-signing-key c)) (openpgp-public-key-fingerprint- (read-openpgp-packet %ed25519bis-public-key-file)))))+ (read-openpgp-packet %ed25519-2-public-key-file))))) (and (authenticate-commits repository (list master1 master2) #:keyring-reference "master")@@ -230,14 +230,14 @@ (define (correct? c commit) (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file %ed25519-secret-key-file- %ed25519bis-public-key-file- %ed25519bis-secret-key-file)+ %ed25519-2-public-key-file+ %ed25519-2-secret-key-file) (with-temporary-git-repository directory `((add "signer1.key" ,(call-with-input-file %ed25519-public-key-file get-string-all)) (add "signer2.key"- ,(call-with-input-file %ed25519bis-public-key-file+ ,(call-with-input-file %ed25519-2-public-key-file get-string-all)) (add ".guix-authorizations" ,(object->string@@ -258,12 +258,12 @@ (define (correct? c commit) %ed25519-public-key-file) (name "Alice")) (,(key-fingerprint- %ed25519bis-public-key-file))))))+ %ed25519-2-public-key-file)))))) (commit "first devel commit" (signer ,(key-fingerprint %ed25519-public-key-file))) (add "devel/2.txt" "2") (commit "second devel commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file)))+ (signer ,(key-fingerprint %ed25519-2-public-key-file))) (checkout "master") (add "b.txt" "B") (commit "second commit"@@ -273,7 +273,7 @@ (define (correct? c commit) ;; After the merge, the second signer is authorized. (add "c.txt" "C") (commit "third commit"- (signer ,(key-fingerprint %ed25519bis-public-key-file))))+ (signer ,(key-fingerprint %ed25519-2-public-key-file)))) (with-repository directory repository (let ((master1 (find-commit repository "first commit")) (master2 (find-commit repository "second commit"))@@ -328,4 +328,3 @@ (define (correct? c commit) 'failed))))))) (test-end "git-authenticate")-diff --git a/tests/guix-authenticate.sh b/tests/guix-authenticate.shindex 3a05b232c1..0de6da1878 100644--- a/tests/guix-authenticate.sh+++ b/tests/guix-authenticate.sh@@ -28,7 +28,7 @@ rm -f "$sig" "$hash" trap 'rm -f "$sig" "$hash"' EXIT -key="$abs_top_srcdir/tests/signing-key.sec"+key="$abs_top_srcdir/tests/keys/signing-key.sec" key_len="`echo -n $key | wc -c`" # A hexadecimal string as long as a sha256 hash.@@ -67,7 +67,7 @@ test "$code" -ne 0 # encoded independently of the current locale: <https://bugs.gnu.org/43421>. hash="636166e9636166e9636166e9636166e9636166e9636166e9636166e9636166e9" latin1_cafe="caf$(printf '\351')"-echo "sign 21:tests/signing-key.sec 64:$hash" | guix authenticate \+echo "sign 26:tests/keys/signing-key.sec 64:$hash" | guix authenticate \ | LC_ALL=C grep "hash sha256 \"$latin1_cafe" # Test for <http://bugs.gnu.org/17312>: make sure 'guix authenticate' producesdiff --git a/tests/civodul.key b/tests/keys/civodul.pubsimilarity index 100%rename from tests/civodul.keyrename to tests/keys/civodul.pubdiff --git a/tests/dsa.key b/tests/keys/dsa.pubsimilarity index 100%rename from tests/dsa.keyrename to tests/keys/dsa.pubdiff --git a/tests/ed25519bis.key b/tests/keys/ed25519-2.pubsimilarity index 100%rename from tests/ed25519bis.keyrename to tests/keys/ed25519-2.pubdiff --git a/tests/ed25519bis.sec b/tests/keys/ed25519-2.secsimilarity index 100%rename from tests/ed25519bis.secrename to tests/keys/ed25519-2.secdiff --git a/tests/keys/ed25519-3.pub b/tests/keys/ed25519-3.pubnew file mode 100644index 0000000000..72f311984c--- /dev/null+++ b/tests/keys/ed25519-3.pub@@ -0,0 +1,9 @@+-----BEGIN PGP PUBLIC KEY BLOCK-----++mDMEYVH/7xYJKwYBBAHaRw8BAQdALMLeUhjEG2/UPCJj2j/debFwwAK5gT3G0l5d+ILfFldm0FTxleGFtcGxlQGV4YW1wbGUuY29tPoiWBBMWCAA+FiEEjO6M85jMSK68+7tINGBzA7NyoagkFAmFR/+8CGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgEC+F4AACgkQGBzA7Nyoagl3lgEAw6yqIlX11lTqwxBGhZk/Oy34O13cbJSZCGv+m0ja++hcA/3DCNOmT+oXjgO/w6enQZUQ1m/d6dUjCc2wOLlLz+ZoG+=+r3i+-----END PGP PUBLIC KEY BLOCK-----diff --git a/tests/keys/ed25519-3.sec b/tests/keys/ed25519-3.secnew file mode 100644index 0000000000..04128a4131--- /dev/null+++ b/tests/keys/ed25519-3.sec@@ -0,0 +1,10 @@+-----BEGIN PGP PRIVATE KEY BLOCK-----++lFgEYVH/7xYJKwYBBAHaRw8BAQdALMLeUhjEG2/UPCJj2j/debFwwAK5gT3G0l5d+ILfFldkAAP92goSbbzQ0ttElr9lr5Cm6rmQtqUZ2Cu/Jk9fvfZROwxI0tBU8ZXhh+bXBsZUBleGFtcGxlLmNvbT6IlgQTFggAPhYhBIzujPOYzEiuvO7SDRgcwOzcqGoJ+BQJhUf/vAhsDBQkDwmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEBgcwOzc+qGoJd5YBAMOsqiJV9dZU6sMQRoWZPzst+Dtd3GyUmQhr/ptI2voXAP9wwjTpk/qF+44Dv8Onp0GVENZv3enVIwnNsDi5S8/maBg==+=EmOt+-----END PGP PRIVATE KEY BLOCK-----diff --git a/tests/ed25519.key b/tests/keys/ed25519.pubsimilarity index 100%rename from tests/ed25519.keyrename to tests/keys/ed25519.pubdiff --git a/tests/ed25519.sec b/tests/keys/ed25519.secsimilarity index 100%rename from tests/ed25519.secrename to tests/keys/ed25519.secdiff --git a/tests/rsa.key b/tests/keys/rsa.pubsimilarity index 100%rename from tests/rsa.keyrename to tests/keys/rsa.pubdiff --git a/tests/signing-key.pub b/tests/keys/signing-key.pubsimilarity index 100%rename from tests/signing-key.pubrename to tests/keys/signing-key.pubdiff --git a/tests/signing-key.sec b/tests/keys/signing-key.secsimilarity index 100%rename from tests/signing-key.secrename to tests/keys/signing-key.secdiff --git a/tests/openpgp.scm b/tests/openpgp.scmindex c2be26fa49..1f20466772 100644--- a/tests/openpgp.scm+++ b/tests/openpgp.scm@@ -59,18 +59,22 @@ (define %binary-sample (define %civodul-fingerprint "3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5") -(define %civodul-key-id #x090B11993D9AEBB5) ;civodul.key--;; Test keys. They were generated in a container along these lines:-;; guix environment -CP --ad-hoc gnupg pinentry-;; then, within the container:-;; mkdir ~/.gnupg-;; echo pinentry-program ~/.guix-profile/bin/pinentry-tty > ~/.gnupg/gpg-agent.conf-;; gpg --quick-gen-key '<ludo+test-rsa@chbouib.org>' rsa-;; or similar.-(define %rsa-key-id #xAE25DA2A70DEED59) ;rsa.key-(define %dsa-key-id #x587918047BE8BD2C) ;dsa.key-(define %ed25519-key-id #x771F49CBFAAE072D) ;ed25519.key+(define %civodul-key-id #x090B11993D9AEBB5) ;civodul.pub++#|+Test keys in ./tests/keys. They were generated in a container along these lines:+ guix environment -CP --ad-hoc gnupg pinentry coreutils+then, within the container:+ mkdir ~/.gnupg && chmod -R og-rwx ~/.gnupg+ gpg --batch --passphrase '' --quick-gen-key '<example@example.com>' ed25519+ gpg --armor --export example@example.com+ gpg --armor --export-secret-key example@example.com+ # echo pinentry-program ~/.guix-profile/bin/pinentry-curses > ~/.gnupg/gpg-agent.conf+or similar.+|#+(define %rsa-key-id #xAE25DA2A70DEED59) ;rsa.pub+(define %dsa-key-id #x587918047BE8BD2C) ;dsa.pub+(define %ed25519-key-id #x771F49CBFAAE072D) ;ed25519.pub (define %rsa-key-fingerprint (base16-string->bytevector@@ -168,7 +172,7 @@ (define %hello-signature/ed25519/sha1 ;digest-algo: sha1 (not (port-ascii-armored? (open-bytevector-input-port %binary-sample)))) (test-assert "get-openpgp-keyring"- (let* ((key (search-path %load-path "tests/civodul.key"))+ (let* ((key (search-path %load-path "tests/keys/civodul.pub")) (keyring (get-openpgp-keyring (open-bytevector-input-port (call-with-input-file key read-radix-64)))))@@ -228,8 +232,10 @@ (define %hello-signature/ed25519/sha1 ;digest-algo: sha1 (verify-openpgp-signature signature keyring (open-input-string "Hello!\n")))) (list status (openpgp-public-key-id key)))))- (list "tests/rsa.key" "tests/dsa.key"- "tests/ed25519.key" "tests/ed25519.key" "tests/ed25519.key")+ (list "tests/keys/rsa.pub" "tests/keys/dsa.pub"+ "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub") (list %hello-signature/rsa %hello-signature/dsa %hello-signature/ed25519/sha256 %hello-signature/ed25519/sha512@@ -248,9 +254,9 @@ (define %hello-signature/ed25519/sha1 ;digest-algo: sha1 (call-with-input-file key read-radix-64)) keyring))) %empty-keyring- '("tests/rsa.key" "tests/dsa.key"- "tests/ed25519.key" "tests/ed25519.key"- "tests/ed25519.key"))))+ '("tests/keys/rsa.pub" "tests/keys/dsa.pub"+ "tests/keys/ed25519.pub" "tests/keys/ed25519.pub"+ "tests/keys/ed25519.pub")))) (map (lambda (signature) (let ((signature (string->openpgp-packet signature))) (let-values (((status key)-- 2.33.0
A
A
Attila Lendvai wrote 42 hours ago
[PATCH 4/5] guix: git-authenticate: Fix authenticate-repository.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20211018155734.5175-4-attila@lendvai.name
Always verify the channel introduction commit, so that no commit can slipthrough that was signed with a different key.
Always update the cache, because it affects the behavior of later calls.
Signal a continuable compound-condition (with type &warning included) when achannel introduction commit doesn't also update the '.guix-authentications'file.
* guix/git-authenticate.scm (authenticate-commit): Reword and extend the errormessage to point to the relevant part of the manual.(authenticate-repository): Eliminate optimizations to make the code path lessdependent on the input. Always trust the intro-commit itself. Always callverify-introductory-commit.(verify-introductory-commit): Check if the commit contains the key that wasused to sign it, and issue a warning otherwise. This is to avoid the confusioncaused by only the *second* commit yielding an error, because intro-commitsare always trusted.(authenticate-commit): Clarify error message.(authorized-keys-at-commit): Factored out to the toplevel fromcommit-authorized-keys.--- guix/channels.scm | 4 +- guix/git-authenticate.scm | 158 +++++++++++++++++++++++--------------- 2 files changed, 96 insertions(+), 66 deletions(-)
Toggle diff (260 lines)diff --git a/guix/channels.scm b/guix/channels.scmindex e4e0428eb5..b84064537f 100644--- a/guix/channels.scm+++ b/guix/channels.scm@@ -347,8 +347,8 @@ (define (make-reporter start-commit end-commit commits) (progress-reporter/bar (length commits))) (define authentic-commits- ;; Consider the currently-used commit of CHANNEL as authentic so- ;; authentication can skip it and all its closure.+ ;; Optimization: consider the currently-used commit of CHANNEL as+ ;; authentic, so that authentication can skip it and all its closure. (match (find (lambda (candidate) (eq? (channel-name candidate) (channel-name channel))) (current-channels))diff --git a/guix/git-authenticate.scm b/guix/git-authenticate.scmindex ab3fcd8b2f..a667863d65 100644--- a/guix/git-authenticate.scm+++ b/guix/git-authenticate.scm@@ -30,6 +30,7 @@ (define-module (guix git-authenticate) #:select (cache-directory with-atomic-file-output)) #:use-module ((guix build utils) #:select (mkdir-p))+ #:use-module (guix diagnostics) #:use-module (guix progress) #:use-module (srfi srfi-1) #:use-module (srfi srfi-11)@@ -37,7 +38,10 @@ (define-module (guix git-authenticate) #:use-module (srfi srfi-34) #:use-module (srfi srfi-35) #:use-module (rnrs bytevectors)+ #:use-module ((rnrs exceptions)+ #:select (raise-continuable)) #:use-module (rnrs io ports)+ #:use-module (ice-9 exceptions) #:use-module (ice-9 match) #:autoload (ice-9 pretty-print) (pretty-print) #:export (read-authorizations@@ -159,11 +163,12 @@ (define (read-authorizations port) (string-downcase (string-filter char-set:graphic fingerprint)))) fingerprints)))) -(define* (commit-authorized-keys repository commit- #:optional (default-authorizations '()))- "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on-authorizations listed in its parent commits. If one of the parent commits-does not specify anything, fall back to DEFAULT-AUTHORIZATIONS."+(define (authorized-keys-at-commit repository commit default-value)+ "Return the list of authorized key fingerprints in REPOSITORY as encoded in+the '.guix-authorizations' file at the point denoted by COMMIT. If the file is+not present, then assert that it has never been there (i.e. do not allow+its removal), and return DEFAULT-VALUE."+ (define (parents-have-authorizations-file? commit) ;; Return true if at least one of the parents of COMMIT has the ;; '.guix-authorizations' file.@@ -185,28 +190,35 @@ (define (assert-parents-lack-authorizations commit) to remove '.guix-authorizations' file") (oid->string (commit-id commit))))))) - (define (commit-authorizations commit)- (catch 'git-error- (lambda ()- (let* ((tree (commit-tree commit))- (entry (tree-entry-bypath tree ".guix-authorizations"))- (blob (blob-lookup repository (tree-entry-id entry))))- (read-authorizations- (open-bytevector-input-port (blob-content blob)))))- (lambda (key error)- (if (= (git-error-code error) GIT_ENOTFOUND)- (begin- ;; Prevent removal of '.guix-authorizations' since it would make- ;; it trivial to force a fallback to DEFAULT-AUTHORIZATIONS.- (assert-parents-lack-authorizations commit)- default-authorizations)- (throw key error)))))+ (catch 'git-error+ (lambda ()+ (let* ((tree (commit-tree commit))+ (entry (tree-entry-bypath tree ".guix-authorizations"))+ (blob (blob-lookup repository (tree-entry-id entry))))+ (read-authorizations+ (open-bytevector-input-port (blob-content blob)))))+ (lambda (key error)+ (if (= (git-error-code error) GIT_ENOTFOUND)+ (begin+ ;; Prevent removal of '.guix-authorizations' since it would make+ ;; it trivial to force a fallback to DEFAULT-VALUE.+ (assert-parents-lack-authorizations commit)+ default-value)+ (throw key error))))) +(define* (commit-authorized-keys repository commit+ #:optional (default-authorizations '()))+ "Return the list of OpenPGP fingerprints authorized to sign COMMIT, based on+authorizations listed in its parent commits. If one of the parent commits+does not specify anything, fall back to DEFAULT-AUTHORIZATIONS." (match (commit-parents commit) (() default-authorizations) (parents (apply lset-intersection bytevector=?- (map commit-authorizations parents)))))+ (map (lambda (commit)+ (authorized-keys-at-commit repository commit+ default-authorizations))+ parents))))) (define* (authenticate-commit repository commit keyring #:key (default-authorizations '()))@@ -236,8 +248,8 @@ (define signing-key (condition (&unauthorized-commit-error (commit id) (signing-key signing-key)))- (formatted-message (G_ "commit ~a not signed by an authorized \-key: ~a")+ (formatted-message (G_ "commit ~a is signed by an unauthorized \+key: ~a\nSee info guix \"Specifying Channel Authorizations\".") (oid->string id) (openpgp-format-fingerprint (openpgp-public-key-fingerprint@@ -356,7 +368,8 @@ (define (repository-cache-key repository) (base64-encode (sha256 (string->utf8 (repository-directory repository)))))) -(define (verify-introductory-commit repository keyring commit expected-signer)+(define (verify-introductory-commit repository commit expected-signer keyring+ authorizations) "Look up COMMIT in REPOSITORY, and raise an exception if it is not signed by EXPECTED-SIGNER." (define actual-signer@@ -364,13 +377,26 @@ (define actual-signer (commit-signing-key repository (commit-id commit) keyring))) (unless (bytevector=? expected-signer actual-signer)- (raise (formatted-message (G_ "initial commit ~a is signed by '~a' \+ (raise (make-compound-condition+ (condition (&unauthorized-commit-error (commit (commit-id commit))+ (signing-key actual-signer)))+ (formatted-message (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+ (oid->string (commit-id commit))+ (openpgp-format-fingerprint actual-signer)+ (openpgp-format-fingerprint expected-signer)))))+ (unless (member actual-signer+ (authorized-keys-at-commit repository commit authorizations)+ bytevector=?)+ (raise-continuable+ (make-compound-condition+ (condition (&warning))+ (formatted-message (G_ "initial commit ~a does not add \+the key it is signed with (~a) to the '.guix-authorizations' file.")+ (oid->string (commit-id commit))+ (openpgp-format-fingerprint actual-signer))))))++(define* (authenticate-repository repository intro-commit-hash intro-signer #:key (keyring-reference "keyring") (cache-key (repository-cache-key repository))@@ -380,11 +406,12 @@ (define* (authenticate-repository repository start signer (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. Commits listed in AUTHENTIC-COMMITS and their-closure are considered authentic. Return an alist mapping OpenPGP public keys-to the number of commits signed by that key that have been traversed.+ "Authenticate REPOSITORY up to commit END, an OID. Authentication starts with+commit INTRO-COMMIT-HASH, an OID, which must be signed by INTRO-SIGNER; an+exception is raised if that is not the case. Commits listed in+AUTHENTIC-COMMITS and their closure are considered authentic. 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@@ -393,8 +420,10 @@ (define* (authenticate-repository repository start signer 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 intro-commit+ (commit-lookup repository intro-commit-hash))+ (define end-commit (commit-lookup repository end)) @@ -404,36 +433,37 @@ (define keyring (define authenticated-commits ;; Previously-authenticated commits that don't need to be checked again. (filter-map (lambda (id)+ ;; We need to tolerate when cached commits disappear due to+ ;; --allow-downgrades. (false-if-git-not-found (commit-lookup repository (string->oid id)))) (append (previously-authenticated-commits cache-key)- authentic-commits)))+ authentic-commits+ ;; The intro commit is unconditionally trusted.+ (list (oid->string intro-commit-hash))))) (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))))+ (commit-difference end-commit intro-commit+ authenticated-commits))++ (verify-introductory-commit repository intro-commit+ intro-signer keyring+ historical-authorizations)++ (let* ((reporter (make-reporter intro-commit end-commit commits))+ (stats (call-with-progress-reporter reporter+ (lambda (report)+ (authenticate-commits repository commits+ #:keyring keyring+ #:default-authorizations+ historical-authorizations+ #:report-progress report)))))+ ;; Note that this will make the then current end commit of any channel,+ ;; that has been used/trusted in the past with a channel introduction,+ ;; remain trusted until the cache is cleared.+ (cache-authenticated-commit cache-key+ (oid->string (commit-id end-commit)))++ stats))-- 2.33.0
A
A
Attila Lendvai wrote 42 hours ago
[PATCH 3/5] guix: Prepare the UI for continuable &warning exceptions.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20211018155734.5175-3-attila@lendvai.name
* guix/store.scm (call-with-store): Use dynamic-wind so that continuableexceptions are not broken by being re-raised as non-continuable. This isneeded for a later commit that uses continuable exceptions from withingit-authenticate to signal warnings to the user. The reason for this is thatthis way tests can explicitly check that a warning was signalled in certainsituations.* guix/ui.scm (call-with-error-handling): Handle &warning type exceptions byprinting them to the user, and then continuing at the place they weresignalled at.* guix/diagnostics.scm (emit-formatted-warning): New exportedfunction.--- guix/diagnostics.scm | 4 ++++ guix/store.scm | 7 +++++-- guix/ui.scm | 11 ++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-)
Toggle diff (78 lines)diff --git a/guix/diagnostics.scm b/guix/diagnostics.scmindex 6a792febd4..343213fb45 100644--- a/guix/diagnostics.scm+++ b/guix/diagnostics.scm@@ -48,6 +48,7 @@ (define-module (guix diagnostics) formatted-message? formatted-message-string formatted-message-arguments+ emit-formatted-warning &fix-hint fix-hint?@@ -161,6 +162,9 @@ (define-syntax-rule (leave args ...) (report-error args ...) (exit 1))) +(define* (emit-formatted-warning fmt . args)+ (emit-diagnostic fmt args #:prefix (G_ "warning: ") #:colors %warning-color))+ (define* (emit-diagnostic fmt args #:key location (colors (color)) (prefix "")) "Report diagnostic message FMT with the given ARGS and the specifieddiff --git a/guix/store.scm b/guix/store.scmindex 89a719bcfc..1b177cc952 100644--- a/guix/store.scm+++ b/guix/store.scm@@ -34,6 +34,8 @@ (define-module (guix store) #:use-module (guix profiling) #:autoload (guix build syscalls) (terminal-columns) #:use-module (rnrs bytevectors)+ #:use-module ((rnrs conditions) #:select (warning?))+ #:use-module ((rnrs exceptions) #:select (raise-continuable)) #:use-module (ice-9 binary-ports) #:use-module ((ice-9 control) #:select (let/ec)) #:use-module (ice-9 atomic)@@ -661,8 +663,9 @@ (define (thunk) (apply values results))))) (with-exception-handler (lambda (exception)- (close-connection store)- (raise-exception exception))+ (unless (warning? exception)+ (close-connection store))+ (raise-continuable exception)) thunk))) (define-syntax-rule (with-store store exp ...)diff --git a/guix/ui.scm b/guix/ui.scmindex 1428c254b3..88940f99ef 100644--- a/guix/ui.scm+++ b/guix/ui.scm@@ -69,6 +69,8 @@ (define-module (guix ui) #:use-module (srfi srfi-31) #:use-module (srfi srfi-34) #:use-module (srfi srfi-35)+ #:use-module ((rnrs conditions)+ #:select (warning?)) #:autoload (ice-9 ftw) (scandir) #:use-module (ice-9 match) #:use-module (ice-9 format)@@ -689,7 +691,14 @@ (define (port-filename* port) (and (not (port-closed? port)) (port-filename port))) - (guard* (c ((package-input-error? c)+ (guard* (c ((warning? c)+ (if (formatted-message? c)+ (apply emit-formatted-warning+ (formatted-message-string c)+ (formatted-message-arguments c))+ (emit-formatted-warning "~a" c))+ '())+ ((package-input-error? c) (let* ((package (package-error-package c)) (input (package-error-invalid-input c)) (location (package-location package))-- 2.33.0
A
A
Attila Lendvai wrote 42 hours ago
[PATCH 5/5] tests: Add test for .guix-authorizations and channel intro.
(address . 50814@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20211018155734.5175-5-attila@lendvai.name
This test used to fail before a recent fix to authenticate-repository.
* tests/git-authenticate.scm: New test "signed commits, .guix-authorizations,channel-introduction".--- tests/git-authenticate.scm | 150 +++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+)
Toggle diff (177 lines)diff --git a/tests/git-authenticate.scm b/tests/git-authenticate.scmindex f66ef191b0..25b4962ea4 100644--- a/tests/git-authenticate.scm+++ b/tests/git-authenticate.scm@@ -18,6 +18,7 @@ (define-module (test-git-authenticate) #:use-module (git)+ #:use-module (guix diagnostics) #:use-module (guix git) #:use-module (guix git-authenticate) #:use-module (guix openpgp)@@ -28,6 +29,10 @@ (define-module (test-git-authenticate) #:use-module (srfi srfi-34) #:use-module (srfi srfi-64) #:use-module (rnrs bytevectors)+ #:use-module ((rnrs conditions)+ #:select (warning?))+ #:use-module ((rnrs exceptions)+ #:select (with-exception-handler)) #:use-module (rnrs io ports)) ;; Test the (guix git-authenticate) tools.@@ -226,6 +231,151 @@ (define (correct? c commit) #:keyring-reference "master") #f))))))) +(unless (gpg+git-available?) (test-skip 1))+(test-assert "signed commits, .guix-authorizations, channel-introduction"+ (let* ((result #true)+ (key1 %ed25519-public-key-file)+ (key2 %ed25519-2-public-key-file)+ (key3 %ed25519-3-public-key-file))+ (with-fresh-gnupg-setup (list key1 %ed25519-secret-key-file+ key2 %ed25519-2-secret-key-file+ key3 %ed25519-3-secret-key-file)+ (with-temporary-git-repository dir+ `((checkout "keyring" orphan)+ (add "signer1.key" ,(call-with-input-file key1 get-string-all))+ (add "signer2.key" ,(call-with-input-file key2 get-string-all))+ (add "signer3.key" ,(call-with-input-file key3 get-string-all))+ (commit "keyring commit")++ (checkout "main" orphan)+ (add "noise0")+ (add ".guix-authorizations"+ ,(object->string+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ ;; Notice that key2 is not authorized at this point.+ (,(key-fingerprint key3) (name "Charlie"))))))+ (commit "commit 0" (signer ,(key-fingerprint key3)))+ (add "noise1")+ (commit "commit 1" (signer ,(key-fingerprint key1)))+ (add "noise2")+ (commit "commit 2" (signer ,(key-fingerprint key1))))+ (with-repository dir repo+ (let* ((commit-0 (find-commit repo "commit 0"))+ (check-from+ (lambda* (commit #:key (should-fail? #false) (key key1)+ (historical-authorizations+ ;; Let's mark key3 to be trusted+ ;; unconditionally, so that it authorizes+ ;; commit 0.+ (list (key-fingerprint-vector key3))))+ (guard (c ((unauthorized-commit-error? c)+ (if should-fail?+ c+ (let ((port (current-output-port)))+ (format port "FAILURE: Unexpected exception at commit '~s':~%"+ commit)+ (print-exception port (stack-ref (make-stack #t) 1)+ c (exception-args c))+ (set! result #false)+ '()))))+ (format #true "~%~%Checking ~s, should-fail? ~s, repo commits:~%"+ commit should-fail?)+ ;; To be able to inspect git's state in the logs.+ (invoke "git" "-C" dir "log" "--reverse" "--pretty=oneline" "main")+ (set! commit (find-commit repo commit))+ (authenticate-repository repo+ (commit-id commit)+ (key-fingerprint-vector key)+ #:historical-authorizations+ historical-authorizations)+ (when should-fail?+ (format #t "FAILURE: Authenticating commit '~s' should have failed.~%" commit)+ (set! result #false))+ '()))))+ (check-from "commit 0" #:key key3)+ (check-from "commit 1")+ (check-from "commit 2")+ (with-git-repository dir+ `((add "noise 3")+ (commit "commit 3" (signer ,(key-fingerprint key2))))+ ;; This should fail because it is signed by key2, i.e. an+ ;; unauthorized key.+ (check-from "commit 3" #:should-fail? #true)+ ;; Specify commit 3 as a channel-introduction signed with+ ;; key2. This is valid, but it should warn the user, because+ ;; .guix-authorizations is not updated to include key2, which+ ;; means that any subsequent commits with the same key will be+ ;; rejected.+ (set! result+ (and (let ((signalled? #false))+ (with-exception-handler+ (lambda (c)+ (cond+ ((not (warning? c))+ (raise c))+ ((formatted-message? c)+ (format #true "warning (expected): ~a~%"+ (apply format #false+ (formatted-message-string c)+ (formatted-message-arguments c)))+ (set! signalled? #true)))+ '())+ (lambda ()+ (check-from "commit 3" #:key key2)+ (unless signalled?+ (format #t "FAILURE: No warning signalled for commit 3~%"))+ signalled?)))+ result)))+ (with-git-repository dir+ ;; Drop the faulty commit 3+ `((reset ,(oid->string (commit-id (find-commit repo "commit 2"))))+ (add "noise 4")+ (add ".guix-authorizations"+ ,(object->string+ ;; Remove key3, add key2.+ `(authorizations+ (version 0)+ ((,(key-fingerprint key1) (name "Alice"))+ (,(key-fingerprint key2) (name "Bob"))))))+ (commit "commit 4" (signer ,(key-fingerprint key2))))+ ;; This should fail because even though commit 4 adds key2 to+ ;; .guix-authorizations, but commit 1 was created prior to that,+ ;; therefore it is not authorized.+ (check-from "commit 1" #:should-fail? #true)+ ;; This should pass, because it's a valid channel intro at commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 5")+ (commit "commit 5" (signer ,(key-fingerprint key2))))+ ;; It is not very intuitive why commit 1 and 2 should be trusted+ ;; at this point: commit 4 has previously been used as a channel+ ;; intro, thus it got marked as trusted in the ~/.cache/.+ ;; Because commit 1 and 2 are among its parents, it should also+ ;; be trusted at this point because of the cache. Note that+ ;; it's debatable whether this semantics is a good idea, but+ ;; this is how git-authenticate is and has been implemented for+ ;; a while (modulo failing to update the cache in the past when+ ;; taking certain code paths).+ (check-from "commit 1")+ (check-from "commit 2")+ ;; Should still be fine, but only when starting from commit 4+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 6")+ (commit "commit 6" (signer ,(key-fingerprint key1))))+ (check-from "commit 1")+ (check-from "commit 2")+ (check-from "commit 4" #:key key2))+ (with-git-repository dir+ `((add "noise 7")+ (commit "commit 7" (signer ,(key-fingerprint key3))))+ ;; This should fail because key3 is not among the authorized+ ;; keys anymore, and commit 7 is signed by it.+ (check-from "commit 6" #:should-fail? #true))))))+ result))+ (unless (gpg+git-available?) (test-skip 1)) (test-assert "signed commits, .guix-authorizations, authorized merge" (with-fresh-gnupg-setup (list %ed25519-public-key-file-- 2.33.0
?
Your comment

Commenting via the web interface is currently disabled.

To comment on this conversation send email to 50814@debbugs.gnu.org