authenticate: Support reading the hash or key from stdin.
[jackhill/guix/guix.git] / guix / scripts / authenticate.scm
CommitLineData
526382ff 1;;; GNU Guix --- Functional package management for GNU
d28684b5 2;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
526382ff
LC
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/>.
18
19(define-module (guix scripts authenticate)
20 #:use-module (guix config)
21 #:use-module (guix utils)
22 #:use-module (guix pk-crypto)
96e5085c 23 #:use-module (guix pki)
526382ff
LC
24 #:use-module (guix ui)
25 #:use-module (rnrs io ports)
26 #:use-module (ice-9 match)
27 #:export (guix-authenticate))
28
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:
36
9dbe6e43
LC
37(define read-canonical-sexp
38 ;; Read a gcrypt sexp from a port and return it.
39 (compose string->canonical-sexp get-string-all))
526382ff 40
9dbe6e43
LC
41(define (read-hash-data port key-type)
42 "Read sha256 hash data from PORT and return it as a gcrypt sexp. KEY-TYPE
32a1eb80 43is a symbol representing the type of public key algo being used."
9dbe6e43 44 (let* ((hex (get-string-all port))
526382ff 45 (bv (base16-string->bytevector (string-trim-both hex))))
32a1eb80 46 (bytevector->hash-data bv #:key-type key-type)))
526382ff 47
9dbe6e43
LC
48(define (sign-with-key key-file port)
49 "Sign the hash read from PORT with KEY-FILE, and write an sexp that includes
50both 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))
64
65(define (validate-signature port)
66 "Read the signature from PORT (which is as produced above), check whether
67its public key is authorized, verify the signature, and print the signed data
68to 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)))))
526382ff
LC
84\f
85;;;
86;;; Entry point with 'openssl'-compatible interface. We support this
87;;; interface because that's what the daemon expects, and we want to leave it
88;;; unmodified currently.
89;;;
90
91(define (guix-authenticate . args)
92 (match args
9b0a2233 93 ;; As invoked by guix-daemon.
526382ff 94 (("rsautl" "-sign" "-inkey" key "-in" hash-file)
9dbe6e43
LC
95 (call-with-input-file hash-file
96 (lambda (port)
97 (sign-with-key key port))))
9b0a2233
LC
98 ;; As invoked by Nix/Crypto.pm (used by Hydra.)
99 (("rsautl" "-sign" "-inkey" key)
100 (sign-with-key key (current-input-port)))
101 ;; As invoked by guix-daemon.
96e5085c 102 (("rsautl" "-verify" "-inkey" _ "-pubin" "-in" signature-file)
9dbe6e43
LC
103 (call-with-input-file signature-file
104 (lambda (port)
105 (validate-signature port))))
9b0a2233
LC
106 ;; As invoked by Nix/Crypto.pm (used by Hydra.)
107 (("rsautl" "-verify" "-inkey" _ "-pubin")
108 (validate-signature (current-input-port)))
526382ff
LC
109 (("--help")
110 (display (_ "Usage: guix authenticate OPTION...
111Sign or verify the signature on the given file. This tool is meant to
112be used internally by 'guix-daemon'.\n")))
113 (("--version")
114 (show-version-and-exit "guix authenticate"))
115 (else
116 (leave (_ "wrong arguments")))))
117
118;;; authenticate.scm ends here