You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

135 lines
5.4 KiB

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