~ruther/guix-local

32a1eb802519179eab8ff687e73f26edab28922f — Ludovic Courtès 12 years ago 0f4139e
pk-crypto: Use RFC6979 when signing with an ECC or DSA key.

* guix/pk-crypto.scm (bytevector->hash-data): Add #:key-type parameter.
  Use the 'pkcs1' flag when KEY-TYPE is 'rsa', and 'rfc6979' when
  KEY-TYPE is 'ecc' or 'dsa'.
  (key-type): New procedure.
* guix/scripts/authenticate.scm (read-hash-data): Add 'key-type'
  parameter.  Pass it to 'bytevector->hash-data'.  Adjust caller
  accordingly.
* tests/pk-crypto.scm (%ecc-key-pair): New variable.
  ("key-type"): New test.
  ("sign + verify"): Pass #:key-type to 'bytevector->hash-data'.
  ("sign + verify, Ed25519"): New test.
3 files changed, 65 insertions(+), 11 deletions(-)

M guix/pk-crypto.scm
M guix/scripts/authenticate.scm
M tests/pk-crypto.scm
M guix/pk-crypto.scm => guix/pk-crypto.scm +22 -5
@@ 1,5 1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;


@@ 39,6 39,7 @@
            canonical-sexp-list?
            bytevector->hash-data
            hash-data->bytevector
            key-type
            sign
            verify
            generate-key


@@ 232,15 233,31 @@ Return #f if that element does not exist, or if it's a list."
  "Return an s-expression representing NUMBER."
  (string->canonical-sexp (string-append "#" (number->string number 16) "#")))

(define* (bytevector->hash-data bv #:optional (hash-algo "sha256"))
(define* (bytevector->hash-data bv
                                #:optional
                                (hash-algo "sha256")
                                #:key (key-type 'ecc))
  "Given BV, a bytevector containing a hash, return an s-expression suitable
for use as the data for 'sign'."
for use as the data for 'sign'.  KEY-TYPE must be a symbol: 'dsa, 'ecc, or
'rsa."
  (string->canonical-sexp
   (format #f "(data (flags pkcs1) (hash \"~a\" #~a#))"
   (format #f "(data (flags ~a) (hash \"~a\" #~a#))"
           (case key-type
             ((ecc dsa) "rfc6979")
             ((rsa)     "pkcs1")
             (else (error "unknown key type" key-type)))
           hash-algo
           (bytevector->base16-string bv))))

(define (hash-data->bytevector data)
(define (key-type sexp)
  "Return a symbol denoting the type of key representing by SEXP--e.g., 'rsa',
'ecc'--or #f if SEXP does not denote a valid key."
  (case (canonical-sexp-nth-data sexp 0)
    ((public-key private-key)
     (canonical-sexp-nth-data (canonical-sexp-nth sexp 1) 0))
    (else #f)))

(define* (hash-data->bytevector data)
  "Return two values: the hash value (a bytevector), and the hash algorithm (a
string) extracted from DATA, an sexp as returned by 'bytevector->hash-data'.
Return #f if DATA does not conform."

M guix/scripts/authenticate.scm => guix/scripts/authenticate.scm +5 -4
@@ 39,11 39,12 @@
  (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."
(define (read-hash-data file key-type)
  "Read sha256 hash data from FILE and return it as a gcrypt sexp.  KEY-TYPE
is a symbol representing the type of public key algo being used."
  (let* ((hex (call-with-input-file file get-string-all))
         (bv  (base16-string->bytevector (string-trim-both hex))))
    (bytevector->hash-data bv)))
    (bytevector->hash-data bv #:key-type key-type)))


;;;


@@ 64,7 65,7 @@
                            (leave
                             (_ "cannot find public key for secret key '~a'~%")
                             key)))
            (data       (read-hash-data hash-file))
            (data       (read-hash-data hash-file (key-type public-key)))
            (signature  (signature-sexp data secret-key public-key)))
       (display (canonical-sexp->string signature))
       #t))

M tests/pk-crypto.scm => tests/pk-crypto.scm +38 -2
@@ 1,5 1,5 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;


@@ 31,7 31,7 @@
;; Test the (guix pk-crypto) module.

(define %key-pair
  ;; Key pair that was generated with:
  ;; RSA key pair that was generated with:
  ;;   (generate-key (string->canonical-sexp "(genkey (rsa (nbits 4:1024)))"))
  ;; which takes a bit of time.
  "(key-data


@@ 48,6 48,20 @@
      (q #00E9AD22F158060BC9AE3601DA623AFC60FFF3058795802CA92371C00097335CF9A23D7782DE353C9DBA93D7BB99E6A24A411107605E722481C5C191F80D7EB77F#)
      (u #59B45B95AE01A7A7370FAFDB08FE73A4793CE37F228961B09B1B1E7DDAD9F8D3E28F5C5E8B4B067E6B8E0BBF3F690B42991A79E46108DDCDA2514323A66964DE#))))")

(define %ecc-key-pair
  ;; Ed25519 key pair generated with:
  ;;   (generate-key (string->canonical-sexp "(genkey (ecdsa (curve Ed25519) (flags rfc6979 transient)))"))
  "(key-data
      (public-key
        (ecc
          (curve Ed25519)
          (q #94869C1B9E69DB8DD910B7F7F4D6E56A63A964A59AE8F90F6703ACDDF6F50C81#)))
      (private-key
        (ecc
          (curve Ed25519)
          (q #94869C1B9E69DB8DD910B7F7F4D6E56A63A964A59AE8F90F6703ACDDF6F50C81#)
          (d #6EFB32D0B4EC6B3237B523539F1979379B82726AAA605EB2FBA6775B2B777B78#))))")

(test-begin "pk-crypto")

(let ((sexps '("(foo bar)"


@@ 148,11 162,33 @@
           (and (string=? algo "sha256")
                (bytevector=? value bv))))))

(test-equal "key-type"
  '(rsa ecc)
  (map (compose key-type
                (cut find-sexp-token <> 'public-key)
                string->canonical-sexp)
       (list %key-pair %ecc-key-pair)))

(test-assert "sign + verify"
  (let* ((pair   (string->canonical-sexp %key-pair))
         (secret (find-sexp-token pair 'private-key))
         (public (find-sexp-token pair 'public-key))
         (data   (bytevector->hash-data
                  (sha256 (string->utf8 "Hello, world."))
                  #:key-type (key-type public)))
         (sig    (sign data secret)))
    (and (verify sig data public)
         (not (verify sig
                      (bytevector->hash-data
                       (sha256 (string->utf8 "Hi!"))
                       #:key-type (key-type public))
                      public)))))

(test-assert "sign + verify, Ed25519"
  (let* ((pair   (string->canonical-sexp %ecc-key-pair))
         (secret (find-sexp-token pair 'private-key))
         (public (find-sexp-token pair 'public-key))
         (data   (bytevector->hash-data
                  (sha256 (string->utf8 "Hello, world."))))
         (sig    (sign data secret)))
    (and (verify sig data public)