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/>.
(define-module (guix scripts authenticate)
#:use-module (guix config)
#:use-module (guix scripts)
#:use-module (guix base16)
#:use-module (gcrypt pk-crypto)
@ -40,16 +39,9 @@
;; Read a gcrypt sexp from a port and return it.
(compose string->canonical-sexp read-string))
(define (read-hash-data port key-type)
"Read sha256 hash data from PORT and return it as a gcrypt sexp. KEY-TYPE
is a symbol representing the type of public key algo being used."
(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."
(define (sign-with-key key-file sha256)
"Sign the hash SHA256 (a bytevector) 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))
(public-key (if (string-suffix? ".sec" key-file)
(call-with-input-file
@ -59,18 +51,18 @@ both the hash and the actual signature."
(leave
(G_ "cannot find public key for secret key '~a'~%")
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)))
(display (canonical-sexp->string signature))
#t))
(define (validate-signature port)
"Read the signature from PORT (which is as produced above), check whether
its public key is authorized, verify the signature, and print the signed data
to stdout upon success."
(let* ((signature (read-canonical-sexp port))
(subject (signature-subject signature))
(data (signature-signed-data signature)))
(define (validate-signature signature)
"Validate SIGNATURE, a canonical sexp. Check whether its public key is
authorized, verify the signature, and print the signed data to stdout upon
success."
(let* ((subject (signature-subject signature))
(data (signature-signed-data signature)))
(if (and data subject)
(if (authorized-key? subject)
(if (valid-signature? signature)
@ -86,9 +78,7 @@ to stdout upon success."
;;;
;;; Entry point with 'openssl'-compatible interface. We support this
;;; interface because that's what the daemon expects, and we want to leave it
;;; unmodified currently.
;;; Entry point.
;;;
(define-command (guix-authenticate . args)
@ -105,22 +95,14 @@ to stdout upon success."
(with-fluids ((%default-port-encoding "ISO-8859-1")
(%default-port-conversion-strategy 'error))
(match args
;; As invoked by guix-daemon.
(("rsautl" "-sign" "-inkey" key "-in" hash-file)
(call-with-input-file hash-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)
(("sign" key-file hash)
(sign-with-key key-file (base16-string->bytevector hash)))
(("verify" signature-file)
(call-with-input-file signature-file
(lambda (port)
(validate-signature port))))
;; As invoked by Nix/Crypto.pm (used by Hydra.)
(("rsautl" "-verify" "-inkey" _ "-pubin")
(validate-signature (current-input-port)))
(validate-signature (string->canonical-sexp
(read-string port))))))
(("--help")
(display (G_ "Usage: guix authenticate OPTION...
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);
Path tmpDir = createTempDir();
AutoDelete delTmp(tmpDir);
Path hashFile = tmpDir + "/hash";
writeFile(hashFile, printHash(hash));
Path secretKey = settings.nixConfDir + "/signing-key.sec";
checkSecrecy(secretKey);
Strings args;
args.push_back("rsautl");
args.push_back("-sign");
args.push_back("-inkey");
args.push_back("sign");
args.push_back(secretKey);
args.push_back("-in");
args.push_back(hashFile);
args.push_back(printHash(hash));
string signature = runAuthenticationProgram(args);
@ -1376,12 +1368,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
writeFile(sigFile, signature);
Strings args;
args.push_back("rsautl");
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("verify");
args.push_back(sigFile);
string hash2 = runAuthenticationProgram(args);

View File

@ -1,5 +1,5 @@
# 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.
#
@ -29,34 +29,18 @@ rm -f "$sig" "$hash"
trap 'rm -f "$sig" "$hash"' EXIT
# A hexadecimal string as long as a sha256 hash.
echo "2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb" \
> "$hash"
hash="2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb"
guix authenticate rsautl -sign \
-inkey "$abs_top_srcdir/tests/signing-key.sec" \
-in "$hash" > "$sig"
guix authenticate sign \
"$abs_top_srcdir/tests/signing-key.sec" \
"$hash" > "$sig"
test -f "$sig"
hash2="`guix authenticate rsautl -verify \
-inkey $abs_top_srcdir/tests/signing-key.pub \
-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"`
hash2="`guix authenticate verify "$sig"`"
test "$hash2" = "$hash"
# Detect corrupt signatures.
if guix authenticate rsautl -verify \
-inkey "$abs_top_srcdir/tests/signing-key.pub" \
-pubin -in /dev/null
if guix authenticate verify /dev/null
then false
else true
fi
@ -66,9 +50,7 @@ fi
# modifying this hash.
sed -i "$sig" \
-e's|#[A-Z0-9]\{64\}#|#0000000000000000000000000000000000000000000000000000000000000000#|g'
if guix authenticate rsautl -verify \
-inkey "$abs_top_srcdir/tests/signing-key.pub" \
-pubin -in "$sig"
if guix authenticate verify "$sig"
then false
else true
fi
@ -76,20 +58,14 @@ fi
# Test for <http://bugs.gnu.org/17312>: make sure 'guix authenticate' produces
# valid signatures when run in the C locale.
echo "5eff0b55c9c5f5e87b4e34cd60a2d5654ca1eb78c7b3c67c3179fed1cff07b4c" \
> "$hash"
hash="5eff0b55c9c5f5e87b4e34cd60a2d5654ca1eb78c7b3c67c3179fed1cff07b4c"
LC_ALL=C
export LC_ALL
guix authenticate rsautl -sign \
-inkey "$abs_top_srcdir/tests/signing-key.sec" \
-in "$hash" > "$sig"
guix authenticate sign "$abs_top_srcdir/tests/signing-key.sec" "$hash" \
> "$sig"
guix authenticate rsautl -verify \
-inkey "$abs_top_srcdir/tests/signing-key.pub" \
-pubin -in "$sig"
hash2="`guix authenticate rsautl -verify \
-inkey $abs_top_srcdir/tests/signing-key.pub \
-pubin -in $sig`"
test "$hash2" = `cat "$hash"`
guix authenticate verify "$sig"
hash2="`guix authenticate verify "$sig"`"
test "$hash2" = "$hash"