guix-play/guix/scripts/authenticate.scm
Ludovic Courtès 6df1fb8991 authenticate: Store the public key as part of the signature.
* guix/scripts/authenticate.scm (signature-sexp): New procedure.
  (guix-authenticate): Use it to produce the signature.  Adjust
  verification code accordingly.
* tests/store.scm ("import corrupt path"): Adjust test accordingly.
2013-12-29 15:57:23 +01:00

113 lines
4.5 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (guix scripts authenticate)
#:use-module (guix config)
#:use-module (guix utils)
#:use-module (guix pk-crypto)
#:use-module (guix ui)
#:use-module (rnrs io ports)
#:use-module (ice-9 match)
#:export (guix-authenticate))
;;; Commentary:
;;;
;;; This program is used internally by the daemon to sign exported archive
;;; (the 'export-paths' RPC), and to authenticate imported archives (the
;;; 'import-paths' RPC.)
;;;
;;; Code:
(define (read-canonical-sexp file)
"Read a gcrypt sexp from FILE and return it."
(call-with-input-file file
(compose string->canonical-sexp get-string-all)))
(define (read-hash-data file)
"Read sha256 hash data from FILE and return it as a gcrypt sexp."
(let* ((hex (call-with-input-file file get-string-all))
(bv (base16-string->bytevector (string-trim-both hex))))
(bytevector->hash-data bv)))
(define (signature-sexp data secret-key public-key)
"Return a SPKI-style sexp for the signature of DATA with SECRET-KEY that
includes DATA, the actual signature value (with a 'sig-val' tag), and
PUBLIC-KEY (see <http://theworld.com/~cme/spki.txt> for examples.)"
(string->canonical-sexp
(format #f
"(signature ~a ~a ~a)"
(canonical-sexp->string data)
(canonical-sexp->string (sign data secret-key))
(canonical-sexp->string public-key))))
;;;
;;; 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.
;;;
(define (guix-authenticate . args)
(match args
(("rsautl" "-sign" "-inkey" key "-in" hash-file)
;; Sign the hash in HASH-FILE with KEY, and return an sexp that includes
;; both the hash and the actual signature.
(let* ((secret-key (read-canonical-sexp key))
(public-key (if (string-suffix? ".sec" key)
(read-canonical-sexp
(string-append (string-drop-right key 4) ".pub"))
(leave (_ "cannot find public key for secret key '~a'")
key)))
(data (read-hash-data hash-file))
(signature (signature-sexp data secret-key public-key)))
(display (canonical-sexp->string signature))
#t))
(("rsautl" "-verify" "-inkey" key "-pubin" "-in" signature-file)
;; Read the signature as produced above, check it against KEY, and print
;; the signed data to stdout upon success.
(let* ((public-key (read-canonical-sexp key))
(sig+data (read-canonical-sexp signature-file))
(data (find-sexp-token sig+data 'data))
(signature (find-sexp-token sig+data 'sig-val)))
(if (and data signature)
(if (verify signature data public-key)
(begin
(display (bytevector->base16-string
(hash-data->bytevector data)))
#t) ; success
(begin
(format (current-error-port)
"error: invalid signature: ~a~%"
(canonical-sexp->string signature))
(exit 1)))
(begin
(format (current-error-port)
"error: corrupt signature data: ~a~%"
(canonical-sexp->string sig+data))
(exit 1)))))
(("--help")
(display (_ "Usage: guix authenticate OPTION...
Sign or verify the signature on the given file. This tool is meant to
be used internally by 'guix-daemon'.\n")))
(("--version")
(show-version-and-exit "guix authenticate"))
(else
(leave (_ "wrong arguments")))))
;;; authenticate.scm ends here