authenticate.scm 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014, 2015, 2016, 2017 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/>.
  18. (define-module (guix scripts authenticate)
  19. #:use-module (guix config)
  20. #:use-module (guix base16)
  21. #:use-module (gcrypt pk-crypto)
  22. #:use-module (guix pki)
  23. #:use-module (guix ui)
  24. #:use-module (ice-9 binary-ports)
  25. #:use-module (ice-9 rdelim)
  26. #:use-module (ice-9 match)
  27. #:export (guix-authenticate))
  28. ;;; Commentary:
  29. ;;;
  30. ;;; This program is used internally by the daemon to sign exported archive
  31. ;;; (the 'export-paths' RPC), and to authenticate imported archives (the
  32. ;;; 'import-paths' RPC.)
  33. ;;;
  34. ;;; Code:
  35. (define read-canonical-sexp
  36. ;; Read a gcrypt sexp from a port and return it.
  37. (compose string->canonical-sexp read-string))
  38. (define (read-hash-data port key-type)
  39. "Read sha256 hash data from PORT and return it as a gcrypt sexp. KEY-TYPE
  40. is a symbol representing the type of public key algo being used."
  41. (let* ((hex (read-string port))
  42. (bv (base16-string->bytevector (string-trim-both hex))))
  43. (bytevector->hash-data bv #:key-type key-type)))
  44. (define (sign-with-key key-file port)
  45. "Sign the hash read from PORT with KEY-FILE, and write an sexp that includes
  46. both the hash and the actual signature."
  47. (let* ((secret-key (call-with-input-file key-file read-canonical-sexp))
  48. (public-key (if (string-suffix? ".sec" key-file)
  49. (call-with-input-file
  50. (string-append (string-drop-right key-file 4)
  51. ".pub")
  52. read-canonical-sexp)
  53. (leave
  54. (G_ "cannot find public key for secret key '~a'~%")
  55. key-file)))
  56. (data (read-hash-data port (key-type public-key)))
  57. (signature (signature-sexp data secret-key public-key)))
  58. (display (canonical-sexp->string signature))
  59. #t))
  60. (define (validate-signature port)
  61. "Read the signature from PORT (which is as produced above), check whether
  62. its public key is authorized, verify the signature, and print the signed data
  63. to stdout upon success."
  64. (let* ((signature (read-canonical-sexp port))
  65. (subject (signature-subject signature))
  66. (data (signature-signed-data signature)))
  67. (if (and data subject)
  68. (if (authorized-key? subject)
  69. (if (valid-signature? signature)
  70. (let ((hash (hash-data->bytevector data)))
  71. (display (bytevector->base16-string hash))
  72. #t) ; success
  73. (leave (G_ "error: invalid signature: ~a~%")
  74. (canonical-sexp->string signature)))
  75. (leave (G_ "error: unauthorized public key: ~a~%")
  76. (canonical-sexp->string subject)))
  77. (leave (G_ "error: corrupt signature data: ~a~%")
  78. (canonical-sexp->string signature)))))
  79. ;;;
  80. ;;; Entry point with 'openssl'-compatible interface. We support this
  81. ;;; interface because that's what the daemon expects, and we want to leave it
  82. ;;; unmodified currently.
  83. ;;;
  84. (define (guix-authenticate . args)
  85. ;; Signature sexps written to stdout may contain binary data, so force
  86. ;; ISO-8859-1 encoding so that things are not mangled. See
  87. ;; <http://bugs.gnu.org/17312> for details.
  88. (set-port-encoding! (current-output-port) "ISO-8859-1")
  89. (set-port-conversion-strategy! (current-output-port) 'error)
  90. ;; Same goes for input ports.
  91. (with-fluids ((%default-port-encoding "ISO-8859-1")
  92. (%default-port-conversion-strategy 'error))
  93. (match args
  94. ;; As invoked by guix-daemon.
  95. (("rsautl" "-sign" "-inkey" key "-in" hash-file)
  96. (call-with-input-file hash-file
  97. (lambda (port)
  98. (sign-with-key key port))))
  99. ;; As invoked by Nix/Crypto.pm (used by Hydra.)
  100. (("rsautl" "-sign" "-inkey" key)
  101. (sign-with-key key (current-input-port)))
  102. ;; As invoked by guix-daemon.
  103. (("rsautl" "-verify" "-inkey" _ "-pubin" "-in" signature-file)
  104. (call-with-input-file signature-file
  105. (lambda (port)
  106. (validate-signature port))))
  107. ;; As invoked by Nix/Crypto.pm (used by Hydra.)
  108. (("rsautl" "-verify" "-inkey" _ "-pubin")
  109. (validate-signature (current-input-port)))
  110. (("--help")
  111. (display (G_ "Usage: guix authenticate OPTION...
  112. Sign or verify the signature on the given file. This tool is meant to
  113. be used internally by 'guix-daemon'.\n")))
  114. (("--version")
  115. (show-version-and-exit "guix authenticate"))
  116. (else
  117. (leave (G_ "wrong arguments"))))))
  118. ;;; authenticate.scm ends here