pk-crypto: Add canonical-sexp to sexp conversion procedures.

* guix/pk-crypto.scm (canonical-sexp-fold, canonical-sexp->sexp,
  sexp->canonical-sexp): New procedures.
* tests/pk-crypto.scm ("canonical-sexp->sexp",
  "sexp->canonical-sexp->sexp"): New tests.
This commit is contained in:
Ludovic Courtès 2013-12-28 16:16:00 +01:00
parent 363ae1da82
commit 9501d7745e
2 changed files with 108 additions and 4 deletions

View File

@ -40,7 +40,9 @@
sign sign
verify verify
generate-key generate-key
find-sexp-token)) find-sexp-token
canonical-sexp->sexp
sexp->canonical-sexp))
;;; Commentary: ;;; Commentary:
@ -48,9 +50,13 @@
;;; Public key cryptographic routines from GNU Libgcrypt. ;;; Public key cryptographic routines from GNU Libgcrypt.
;;;; ;;;;
;;; Libgcrypt uses "canonical s-expressions" to represent key material, ;;; Libgcrypt uses "canonical s-expressions" to represent key material,
;;; parameters, and data. We keep it as an opaque object rather than ;;; parameters, and data. We keep it as an opaque object to map them to
;;; attempting to map them to Scheme s-expressions because (1) Libgcrypt sexps ;;; Scheme s-expressions because (1) Libgcrypt sexps may be stored in secure
;;; are stored in secure memory, and (2) the read syntax is different. ;;; memory, and (2) the read syntax is different.
;;;
;;; A 'canonical-sexp->sexp' procedure is provided nevertheless, for use in
;;; cases where it is safe to move data out of Libgcrypt---e.g., when
;;; processing ACL entries, public keys, etc.
;;; ;;;
;;; Canonical sexps were defined by Rivest et al. in the IETF draft at ;;; Canonical sexps were defined by Rivest et al. in the IETF draft at
;;; <http://people.csail.mit.edu/rivest/Sexp.txt> for the purposes of SPKI ;;; <http://people.csail.mit.edu/rivest/Sexp.txt> for the purposes of SPKI
@ -283,4 +289,56 @@ return #f if not found."
(or (canonical-sexp-null? sexp) (or (canonical-sexp-null? sexp)
(> (canonical-sexp-length sexp) 0))) (> (canonical-sexp-length sexp) 0)))
(define (canonical-sexp-fold proc seed sexp)
"Fold PROC (as per SRFI-1) over SEXP, a canonical sexp."
(if (canonical-sexp-list? sexp)
(let ((len (canonical-sexp-length sexp)))
(let loop ((index 0)
(result seed))
(if (= index len)
result
(loop (+ 1 index)
(proc (or (canonical-sexp-nth sexp index)
(canonical-sexp-nth-data sexp index))
result)))))
(error "sexp is not a list" sexp)))
(define (canonical-sexp->sexp sexp)
"Return a Scheme sexp corresponding to SEXP. This is particularly useful to
compare sexps (since Libgcrypt does not provide an 'equal?' procedure), or to
use pattern matching."
(if (canonical-sexp-list? sexp)
(reverse
(canonical-sexp-fold (lambda (item result)
(cons (if (canonical-sexp? item)
(canonical-sexp->sexp item)
item)
result))
'()
sexp))
(canonical-sexp->string sexp))) ; XXX: not very useful
(define (sexp->canonical-sexp sexp)
"Return a canonical sexp equivalent to SEXP, a Scheme sexp as returned by
'canonical-sexp->sexp'."
;; XXX: This is inefficient, but the Libgcrypt API doesn't allow us to do
;; much better.
(string->canonical-sexp
(call-with-output-string
(lambda (port)
(define (write item)
(cond ((list? item)
(display "(" port)
(for-each write item)
(display ")" port))
((symbol? item)
(format port " ~a" item))
((bytevector? item)
(format port " #~a#"
(bytevector->base16-string item)))
(else
(error "unsupported sexp item type" item))))
(write sexp)))))
;;; pk-crypto.scm ends here ;;; pk-crypto.scm ends here

View File

@ -163,6 +163,52 @@
(gc) (gc)
(test-equal "canonical-sexp->sexp"
`((data
(flags pkcs1)
(hash sha256
,(base16-string->bytevector
"2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb")))
(public-key
(rsa
(n ,(base16-string->bytevector
(string-downcase
"00C1F764069F54FFE93A126B02328903E984E4AE3AF6DF402B5B6B3907911B88C385F1BA76A002EC9DEA109A5228EF0E62EE31A06D1A5861CAB474F6C857AC66EB65A1905F25BBA1869579E73A3B7FED13AF5A1667326F88CDFC2FF24B03C14FD1384AA7E73CA89572880B606E3A974E15347963FC7B6378574936A47580DBCB45")))
(e ,(base16-string->bytevector
"010001")))))
(list (canonical-sexp->sexp
(string->canonical-sexp
"(data
(flags pkcs1)
(hash \"sha256\"
#2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb#))"))
(canonical-sexp->sexp
(find-sexp-token (string->canonical-sexp %key-pair)
'public-key))))
(let ((lst
`((data
(flags pkcs1)
(hash sha256
,(base16-string->bytevector
"2749f0ea9f26c6c7be746a9cff8fa4c2f2a02b000070dba78429e9a11f87c6eb")))
(public-key
(rsa
(n ,(base16-string->bytevector
(string-downcase
"00C1F764069F54FFE93A126B02328903E984E4AE3AF6DF402B5B6B3907911B88C385F1BA76A002EC9DEA109A5228EF0E62EE31A06D1A5861CAB474F6C857AC66EB65A1905F25BBA1869579E73A3B7FED13AF5A1667326F88CDFC2FF24B03C14FD1384AA7E73CA89572880B606E3A974E15347963FC7B6378574936A47580DBCB45")))
(e ,(base16-string->bytevector
"010001")))))))
(test-equal "sexp->canonical-sexp->sexp"
lst
(map (compose canonical-sexp->sexp sexp->canonical-sexp)
lst)))
(test-end) (test-end)