daemon: Simplify interface with 'guix authenticate'.

There's no reason at this point to mimic the calling convention of the
'openssl' command.

* nix/libstore/local-store.cc (LocalStore::exportPath): Add only "sign"
and HASH to ARGS.  Remove 'tmpDir' and 'hashFile'.
(LocalStore::importPath): Add only "verify" and SIGNATURE to
* guix/scripts/authenticate.scm (guix-authenticate): Adjust
accordingly; remove the OpenSSL-style clauses.
(read-hash-data): Remove.
(sign-with-key): Replace 'port' with 'sha256' and adjust accordingly.
(validate-signature): Export SIGNATURE to be a canonical sexp.
* tests/guix-authenticate.sh: Adjust tests accordingly.
This commit is contained in:
Ludovic Courtès 2020-09-08 15:00:29 +02:00
parent 7a68d3ccad
commit 6dd8ffc574
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
3 changed files with 36 additions and 91 deletions

View file

@ -17,7 +17,6 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (guix scripts authenticate) (define-module (guix scripts authenticate)
#:use-module (guix config)
#:use-module (guix scripts) #:use-module (guix scripts)
#:use-module (guix base16) #:use-module (guix base16)
#:use-module (gcrypt pk-crypto) #:use-module (gcrypt pk-crypto)
@ -40,16 +39,9 @@ (define read-canonical-sexp
;; Read a gcrypt sexp from a port and return it. ;; Read a gcrypt sexp from a port and return it.
(compose string->canonical-sexp read-string)) (compose string->canonical-sexp read-string))
(define (read-hash-data port key-type) (define (sign-with-key key-file sha256)
"Read sha256 hash data from PORT and return it as a gcrypt sexp. KEY-TYPE "Sign the hash SHA256 (a bytevector) with KEY-FILE, and write an sexp that
is a symbol representing the type of public key algo being used." includes both the hash and the actual signature."
(let* ((hex (read-string port))
(bv (base16-string->bytevector (string-trim-both hex))))
(bytevector->hash-data bv #:key-type key-type)))
(define (sign-with-key key-file port)
"Sign the hash read from PORT with KEY-FILE, and write an sexp that includes
both the hash and the actual signature."
(let* ((secret-key (call-with-input-file key-file read-canonical-sexp)) (let* ((secret-key (call-with-input-file key-file read-canonical-sexp))
(public-key (if (string-suffix? ".sec" key-file) (public-key (if (string-suffix? ".sec" key-file)
(call-with-input-file (call-with-input-file
@ -59,18 +51,18 @@ (define (sign-with-key key-file port)
(leave (leave
(G_ "cannot find public key for secret key '~a'~%") (G_ "cannot find public key for secret key '~a'~%")
key-file))) key-file)))
(data (read-hash-data port (key-type public-key))) (data (bytevector->hash-data sha256
#:key-type (key-type public-key)))
(signature (signature-sexp data secret-key public-key))) (signature (signature-sexp data secret-key public-key)))
(display (canonical-sexp->string signature)) (display (canonical-sexp->string signature))
#t)) #t))
(define (validate-signature port) (define (validate-signature signature)
"Read the signature from PORT (which is as produced above), check whether "Validate SIGNATURE, a canonical sexp. Check whether its public key is
its public key is authorized, verify the signature, and print the signed data authorized, verify the signature, and print the signed data to stdout upon
to stdout upon success." success."
(let* ((signature (read-canonical-sexp port)) (let* ((subject (signature-subject signature))
(subject (signature-subject signature)) (data (signature-signed-data signature)))
(data (signature-signed-data signature)))
(if (and data subject) (if (and data subject)
(if (authorized-key? subject) (if (authorized-key? subject)
(if (valid-signature? signature) (if (valid-signature? signature)
@ -86,9 +78,7 @@ (define (validate-signature port)
;;; ;;;
;;; Entry point with 'openssl'-compatible interface. We support this ;;; Entry point.
;;; interface because that's what the daemon expects, and we want to leave it
;;; unmodified currently.
;;; ;;;
(define-command (guix-authenticate . args) (define-command (guix-authenticate . args)
@ -105,22 +95,14 @@ (define-command (guix-authenticate . args)
(with-fluids ((%default-port-encoding "ISO-8859-1") (with-fluids ((%default-port-encoding "ISO-8859-1")
(%default-port-conversion-strategy 'error)) (%default-port-conversion-strategy 'error))
(match args (match args
;; As invoked by guix-daemon. (("sign" key-file hash)
(("rsautl" "-sign" "-inkey" key "-in" hash-file) (sign-with-key key-file (base16-string->bytevector hash)))
(call-with-input-file hash-file (("verify" signature-file)
(lambda (port)
(sign-with-key key port))))
;; As invoked by Nix/Crypto.pm (used by Hydra.)
(("rsautl" "-sign" "-inkey" key)
(sign-with-key key (current-input-port)))
;; As invoked by guix-daemon.
(("rsautl" "-verify" "-inkey" _ "-pubin" "-in" signature-file)
(call-with-input-file signature-file (call-with-input-file signature-file
(lambda (port) (lambda (port)
(validate-signature port)))) (validate-signature (string->canonical-sexp
;; As invoked by Nix/Crypto.pm (used by Hydra.) (read-string port))))))
(("rsautl" "-verify" "-inkey" _ "-pubin")
(validate-signature (current-input-port)))
(("--help") (("--help")
(display (G_ "Usage: guix authenticate OPTION... (display (G_ "Usage: guix authenticate OPTION...
Sign or verify the signature on the given file. This tool is meant to Sign or verify the signature on the given file. This tool is meant to

View file

@ -1277,21 +1277,13 @@ void LocalStore::exportPath(const Path & path, bool sign,
writeInt(1, hashAndWriteSink); writeInt(1, hashAndWriteSink);
Path tmpDir = createTempDir();
AutoDelete delTmp(tmpDir);
Path hashFile = tmpDir + "/hash";
writeFile(hashFile, printHash(hash));
Path secretKey = settings.nixConfDir + "/signing-key.sec"; Path secretKey = settings.nixConfDir + "/signing-key.sec";
checkSecrecy(secretKey); checkSecrecy(secretKey);
Strings args; Strings args;
args.push_back("rsautl"); args.push_back("sign");
args.push_back("-sign");
args.push_back("-inkey");
args.push_back(secretKey); args.push_back(secretKey);
args.push_back("-in"); args.push_back(printHash(hash));
args.push_back(hashFile);
string signature = runAuthenticationProgram(args); string signature = runAuthenticationProgram(args);
@ -1376,12 +1368,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
writeFile(sigFile, signature); writeFile(sigFile, signature);
Strings args; Strings args;
args.push_back("rsautl"); args.push_back("verify");
args.push_back("-verify");
args.push_back("-inkey");
args.push_back(settings.nixConfDir + "/signing-key.pub");
args.push_back("-pubin");
args.push_back("-in");
args.push_back(sigFile); args.push_back(sigFile);
string hash2 = runAuthenticationProgram(args); string hash2 = runAuthenticationProgram(args);

View file

@ -1,5 +1,5 @@
# GNU Guix --- Functional package management for GNU # GNU Guix --- Functional package management for GNU
# Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org> # Copyright © 2013, 2014, 2020 Ludovic Courtès <ludo@gnu.org>
# #
# This file is part of GNU Guix. # This file is part of GNU Guix.
# #
@ -29,34 +29,18 @@ rm -f "$sig" "$hash"
trap 'rm -f "$sig" "$hash"' EXIT trap 'rm -f "$sig" "$hash"' EXIT
# A hexadecimal string as long as a sha256 hash. # A hexadecimal string as long as a sha256 hash.
echo "2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb" \ hash="2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb"
> "$hash"
guix authenticate rsautl -sign \ guix authenticate sign \
-inkey "$abs_top_srcdir/tests/signing-key.sec" \ "$abs_top_srcdir/tests/signing-key.sec" \
-in "$hash" > "$sig" "$hash" > "$sig"
test -f "$sig" test -f "$sig"
hash2="`guix authenticate rsautl -verify \ hash2="`guix authenticate verify "$sig"`"
-inkey $abs_top_srcdir/tests/signing-key.pub \ test "$hash2" = "$hash"
-pubin -in $sig`"
test "$hash2" = `cat "$hash"`
# Same thing in a pipeline, using the command line syntax that Nix/Crypto.pm
# uses.
hash2="` \
cat "$hash" \
| guix authenticate rsautl -sign \
-inkey "$abs_top_srcdir/tests/signing-key.sec" \
| guix authenticate rsautl -verify \
-inkey $abs_top_srcdir/tests/signing-key.pub \
-pubin`"
test "$hash2" = `cat "$hash"`
# Detect corrupt signatures. # Detect corrupt signatures.
if guix authenticate rsautl -verify \ if guix authenticate verify /dev/null
-inkey "$abs_top_srcdir/tests/signing-key.pub" \
-pubin -in /dev/null
then false then false
else true else true
fi fi
@ -66,9 +50,7 @@ fi
# modifying this hash. # modifying this hash.
sed -i "$sig" \ sed -i "$sig" \
-e's|#[A-Z0-9]\{64\}#|#0000000000000000000000000000000000000000000000000000000000000000#|g' -e's|#[A-Z0-9]\{64\}#|#0000000000000000000000000000000000000000000000000000000000000000#|g'
if guix authenticate rsautl -verify \ if guix authenticate verify "$sig"
-inkey "$abs_top_srcdir/tests/signing-key.pub" \
-pubin -in "$sig"
then false then false
else true else true
fi fi
@ -76,20 +58,14 @@ fi
# Test for <http://bugs.gnu.org/17312>: make sure 'guix authenticate' produces # Test for <http://bugs.gnu.org/17312>: make sure 'guix authenticate' produces
# valid signatures when run in the C locale. # valid signatures when run in the C locale.
echo "5eff0b55c9c5f5e87b4e34cd60a2d5654ca1eb78c7b3c67c3179fed1cff07b4c" \ hash="5eff0b55c9c5f5e87b4e34cd60a2d5654ca1eb78c7b3c67c3179fed1cff07b4c"
> "$hash"
LC_ALL=C LC_ALL=C
export LC_ALL export LC_ALL
guix authenticate rsautl -sign \ guix authenticate sign "$abs_top_srcdir/tests/signing-key.sec" "$hash" \
-inkey "$abs_top_srcdir/tests/signing-key.sec" \ > "$sig"
-in "$hash" > "$sig"
guix authenticate rsautl -verify \ guix authenticate verify "$sig"
-inkey "$abs_top_srcdir/tests/signing-key.pub" \ hash2="`guix authenticate verify "$sig"`"
-pubin -in "$sig" test "$hash2" = "$hash"
hash2="`guix authenticate rsautl -verify \
-inkey $abs_top_srcdir/tests/signing-key.pub \
-pubin -in $sig`"
test "$hash2" = `cat "$hash"`