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.
			
			
This commit is contained in:
		
							parent
							
								
									0f4139e97e
								
							
						
					
					
						commit
						32a1eb8025
					
				
					 3 changed files with 65 additions and 11 deletions
				
			
		|  | @ -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." | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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,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 | ||||
|  |  | |||
		Reference in a new issue