check-available-binaries: Use 'substitutable-paths'.
[guix.git] / guix / scripts / authenticate.scm
blobeedebb4baca3a4e3f1720526f0698d6a8b174dd5
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
10 ;;;
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;;; GNU General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
19 (define-module (guix scripts authenticate)
20   #:use-module (guix config)
21   #:use-module (guix utils)
22   #:use-module (guix pk-crypto)
23   #:use-module (guix pki)
24   #:use-module (guix ui)
25   #:use-module (rnrs io ports)
26   #:use-module (ice-9 match)
27   #:export (guix-authenticate))
29 ;;; Commentary:
30 ;;;
31 ;;; This program is used internally by the daemon to sign exported archive
32 ;;; (the 'export-paths' RPC), and to authenticate imported archives (the
33 ;;; 'import-paths' RPC.)
34 ;;;
35 ;;; Code:
37 (define read-canonical-sexp
38   ;; Read a gcrypt sexp from a port and return it.
39   (compose string->canonical-sexp get-string-all))
41 (define (read-hash-data port key-type)
42   "Read sha256 hash data from PORT and return it as a gcrypt sexp.  KEY-TYPE
43 is a symbol representing the type of public key algo being used."
44   (let* ((hex (get-string-all port))
45          (bv  (base16-string->bytevector (string-trim-both hex))))
46     (bytevector->hash-data bv #:key-type key-type)))
48 (define (sign-with-key key-file port)
49   "Sign the hash read from PORT with KEY-FILE, and write an sexp that includes
50 both the hash and the actual signature."
51   (let* ((secret-key (call-with-input-file key-file read-canonical-sexp))
52          (public-key (if (string-suffix? ".sec" key-file)
53                          (call-with-input-file
54                              (string-append (string-drop-right key-file 4)
55                                             ".pub")
56                            read-canonical-sexp)
57                          (leave
58                           (_ "cannot find public key for secret key '~a'~%")
59                           key-file)))
60          (data       (read-hash-data port (key-type public-key)))
61          (signature  (signature-sexp data secret-key public-key)))
62     (display (canonical-sexp->string signature))
63     #t))
65 (define (validate-signature port)
66   "Read the signature from PORT (which is as produced above), check whether
67 its public key is authorized, verify the signature, and print the signed data
68 to stdout upon success."
69   (let* ((signature (read-canonical-sexp port))
70          (subject   (signature-subject signature))
71          (data      (signature-signed-data signature)))
72     (if (and data subject)
73         (if (authorized-key? subject)
74             (if (valid-signature? signature)
75                 (let ((hash (hash-data->bytevector data)))
76                   (display (bytevector->base16-string hash))
77                   #t)                              ; success
78                 (leave (_ "error: invalid signature: ~a~%")
79                        (canonical-sexp->string signature)))
80             (leave (_ "error: unauthorized public key: ~a~%")
81                    (canonical-sexp->string subject)))
82         (leave (_ "error: corrupt signature data: ~a~%")
83                (canonical-sexp->string signature)))))
86 ;;;
87 ;;; Entry point with 'openssl'-compatible interface.  We support this
88 ;;; interface because that's what the daemon expects, and we want to leave it
89 ;;; unmodified currently.
90 ;;;
92 (define (guix-authenticate . args)
93   ;; Signature sexps written to stdout may contain binary data, so force
94   ;; ISO-8859-1 encoding so that things are not mangled.  See
95   ;; <http://bugs.gnu.org/17312> for details.
96   (set-port-encoding! (current-output-port) "ISO-8859-1")
97   (set-port-conversion-strategy! (current-output-port) 'error)
99   ;; Same goes for input ports.
100   (with-fluids ((%default-port-encoding "ISO-8859-1")
101                 (%default-port-conversion-strategy 'error))
102     (match args
103       ;; As invoked by guix-daemon.
104       (("rsautl" "-sign" "-inkey" key "-in" hash-file)
105        (call-with-input-file hash-file
106          (lambda (port)
107            (sign-with-key key port))))
108       ;; As invoked by Nix/Crypto.pm (used by Hydra.)
109       (("rsautl" "-sign" "-inkey" key)
110        (sign-with-key key (current-input-port)))
111       ;; As invoked by guix-daemon.
112       (("rsautl" "-verify" "-inkey" _ "-pubin" "-in" signature-file)
113        (call-with-input-file signature-file
114          (lambda (port)
115            (validate-signature port))))
116       ;; As invoked by Nix/Crypto.pm (used by Hydra.)
117       (("rsautl" "-verify" "-inkey" _ "-pubin")
118        (validate-signature (current-input-port)))
119       (("--help")
120        (display (_ "Usage: guix authenticate OPTION...
121 Sign or verify the signature on the given file.  This tool is meant to
122 be used internally by 'guix-daemon'.\n")))
123       (("--version")
124        (show-version-and-exit "guix authenticate"))
125       (else
126        (leave (_ "wrong arguments"))))))
128 ;;; authenticate.scm ends here