From 32a1eb802519179eab8ff687e73f26edab28922f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Wed, 19 Mar 2014 21:40:10 +0100 Subject: [PATCH] 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. --- guix/pk-crypto.scm | 27 ++++++++++++++++++----- guix/scripts/authenticate.scm | 9 ++++---- tests/pk-crypto.scm | 40 +++++++++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/guix/pk-crypto.scm b/guix/pk-crypto.scm index 50f709418c..b9ab02861c 100644 --- a/guix/pk-crypto.scm +++ b/guix/pk-crypto.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2013 Ludovic Courtès +;;; Copyright © 2013, 2014 Ludovic Courtès ;;; ;;; 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." diff --git a/guix/scripts/authenticate.scm b/guix/scripts/authenticate.scm index 27580dedff..927dbe8afc 100644 --- a/guix/scripts/authenticate.scm +++ b/guix/scripts/authenticate.scm @@ -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)) diff --git a/tests/pk-crypto.scm b/tests/pk-crypto.scm index 6774dd4157..4d498020f5 100644 --- a/tests/pk-crypto.scm +++ b/tests/pk-crypto.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2013 Ludovic Courtès +;;; Copyright © 2013, 2014 Ludovic Courtès ;;; ;;; 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,8 +162,30 @@ (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