2011-10-20 13:43:41 +00:00
|
|
|
#lang racket/base
|
|
|
|
|
|
|
|
(require racket/match)
|
2011-10-20 18:19:29 +00:00
|
|
|
(require racket/port)
|
|
|
|
(require net/base64)
|
2011-10-20 13:43:41 +00:00
|
|
|
|
|
|
|
(require (planet vyzo/crypto))
|
|
|
|
(require (planet tonyg/bitsyntax))
|
|
|
|
(require "asn1-ber.rkt")
|
|
|
|
(require "ssh-message-types.rkt")
|
|
|
|
|
|
|
|
(require rackunit)
|
|
|
|
|
2011-10-20 18:19:29 +00:00
|
|
|
(provide (struct-out rsa-private-key)
|
|
|
|
(struct-out dsa-private-key)
|
|
|
|
(struct-out rsa-public-key)
|
2011-10-20 13:43:41 +00:00
|
|
|
(struct-out dsa-public-key)
|
|
|
|
|
|
|
|
public-key->pieces
|
|
|
|
pieces->public-key
|
|
|
|
|
|
|
|
host-key-algorithm->keys
|
|
|
|
host-key-algorithm->digest-type
|
|
|
|
host-key-signature
|
2011-10-20 18:19:29 +00:00
|
|
|
verify-host-key-signature!
|
2011-10-20 13:43:41 +00:00
|
|
|
|
|
|
|
pieces->ssh-host-key
|
|
|
|
ssh-host-key->pieces)
|
|
|
|
|
2011-10-20 18:19:29 +00:00
|
|
|
(struct rsa-private-key (version n e d p q dmp1 dmq1 iqmp) #:transparent)
|
|
|
|
(struct dsa-private-key (version p q g y x) #:transparent)
|
2011-10-20 13:43:41 +00:00
|
|
|
(struct rsa-public-key (n e) #:transparent)
|
|
|
|
(struct dsa-public-key (y p q g) #:transparent)
|
|
|
|
|
|
|
|
;; ASN.1 BER integers are signed.
|
|
|
|
(define (bs->n bs) (bit-string->integer bs #t #t))
|
|
|
|
(define (n->bs n) (integer->bit-string n (* 8 (mpint-width n)) #t))
|
|
|
|
|
2011-10-20 18:19:29 +00:00
|
|
|
(define (private-key->pieces key)
|
|
|
|
(bytes->private-key-pieces (private-key->bytes key)))
|
|
|
|
|
|
|
|
(define (bytes->private-key-pieces bs)
|
|
|
|
(match (asn1-ber-decode-all bs)
|
|
|
|
(`(0 16 ((0 2 ,version-bytes)
|
|
|
|
(0 2 ,n-bytes)
|
|
|
|
(0 2 ,e-bytes)
|
|
|
|
(0 2 ,d-bytes)
|
|
|
|
(0 2 ,p-bytes)
|
|
|
|
(0 2 ,q-bytes)
|
|
|
|
(0 2 ,dmp1-bytes)
|
|
|
|
(0 2 ,dmq1-bytes)
|
|
|
|
(0 2 ,iqmp-bytes)))
|
|
|
|
(rsa-private-key (bs->n version-bytes)
|
|
|
|
(bs->n n-bytes)
|
|
|
|
(bs->n e-bytes)
|
|
|
|
(bs->n d-bytes)
|
|
|
|
(bs->n p-bytes)
|
|
|
|
(bs->n q-bytes)
|
|
|
|
(bs->n dmp1-bytes)
|
|
|
|
(bs->n dmq1-bytes)
|
|
|
|
(bs->n iqmp-bytes)))
|
|
|
|
(`(0 16 ((0 2 ,version-bytes)
|
|
|
|
(0 2 ,p-bytes)
|
|
|
|
(0 2 ,q-bytes)
|
|
|
|
(0 2 ,g-bytes)
|
|
|
|
(0 2 ,public-key-bytes) ;; y
|
|
|
|
(0 2 ,private-key-bytes))) ;; x
|
|
|
|
(dsa-private-key (bs->n version-bytes)
|
|
|
|
(bs->n p-bytes)
|
|
|
|
(bs->n q-bytes)
|
|
|
|
(bs->n g-bytes)
|
|
|
|
(bs->n public-key-bytes)
|
|
|
|
(bs->n private-key-bytes)))))
|
|
|
|
|
|
|
|
(define (pieces->private-key p)
|
|
|
|
(match p
|
|
|
|
((struct rsa-private-key (version n e d p q dmp1 dmq1 iqmp))
|
|
|
|
(bytes->private-key pkey:rsa
|
|
|
|
(asn1-ber-encode `(0 16 ((0 2 ,(n->bs version))
|
|
|
|
(0 2 ,(n->bs n))
|
|
|
|
(0 2 ,(n->bs e))
|
|
|
|
(0 2 ,(n->bs d))
|
|
|
|
(0 2 ,(n->bs p))
|
|
|
|
(0 2 ,(n->bs q))
|
|
|
|
(0 2 ,(n->bs dmp1))
|
|
|
|
(0 2 ,(n->bs dmq1))
|
|
|
|
(0 2 ,(n->bs iqmp)))))))
|
|
|
|
((struct dsa-private-key (version p q g y x))
|
|
|
|
(bytes->private-key pkey:dsa
|
|
|
|
(asn1-ber-encode `(0 16 ((0 2 ,(n->bs version))
|
|
|
|
(0 2 ,(n->bs p))
|
|
|
|
(0 2 ,(n->bs q))
|
|
|
|
(0 2 ,(n->bs g))
|
|
|
|
(0 2 ,(n->bs y))
|
|
|
|
(0 2 ,(n->bs x)))))))))
|
|
|
|
|
2011-10-20 13:43:41 +00:00
|
|
|
(define (public-key->pieces key)
|
|
|
|
(match (asn1-ber-decode-all (public-key->bytes key))
|
|
|
|
(`(0 16 ((0 2 ,n-bytes)
|
|
|
|
(0 2 ,e-bytes)))
|
|
|
|
(rsa-public-key (bs->n n-bytes)
|
|
|
|
(bs->n e-bytes)))
|
|
|
|
(`(0 16 ((0 2 ,public-key-bytes) ;; y
|
|
|
|
(0 2 ,p-bytes)
|
|
|
|
(0 2 ,q-bytes)
|
|
|
|
(0 2 ,g-bytes)))
|
|
|
|
(dsa-public-key (bs->n public-key-bytes)
|
|
|
|
(bs->n p-bytes)
|
|
|
|
(bs->n q-bytes)
|
|
|
|
(bs->n g-bytes)))))
|
|
|
|
|
|
|
|
(define (pieces->public-key p)
|
|
|
|
(match p
|
|
|
|
((struct rsa-public-key (n e))
|
|
|
|
(bytes->public-key pkey:rsa
|
|
|
|
(asn1-ber-encode `(0 16 ((0 2 ,(n->bs n))
|
|
|
|
(0 2 ,(n->bs e)))))))
|
|
|
|
((struct dsa-public-key (y p q g))
|
|
|
|
(bytes->public-key pkey:dsa
|
|
|
|
(asn1-ber-encode `(0 16 ((0 2 ,(n->bs y))
|
|
|
|
(0 2 ,(n->bs p))
|
|
|
|
(0 2 ,(n->bs q))
|
|
|
|
(0 2 ,(n->bs g)))))))))
|
|
|
|
|
|
|
|
(define (host-key-algorithm->keys host-key-alg)
|
|
|
|
(case host-key-alg
|
|
|
|
((ssh-dss) (values host-key-dsa-private host-key-dsa-public))
|
2011-10-20 18:19:29 +00:00
|
|
|
(else (error 'host-key-algorithm->keys "Unsupported host-key-alg ~v" host-key-alg))))
|
2011-10-20 13:43:41 +00:00
|
|
|
|
|
|
|
(define (host-key-algorithm->digest-type host-key-alg)
|
|
|
|
(case host-key-alg
|
|
|
|
((ssh-rsa) digest:sha1)
|
|
|
|
((ssh-dss) digest:dss1)
|
2011-10-20 18:19:29 +00:00
|
|
|
(else (error 'host-key-algorithm->digest-type "Unsupported host-key-alg ~v" host-key-alg))))
|
2011-10-20 13:43:41 +00:00
|
|
|
|
|
|
|
(define (host-key-signature private-key host-key-alg exchange-hash)
|
|
|
|
(case host-key-alg
|
|
|
|
((ssh-rsa)
|
|
|
|
;; TODO: offer ssh-rsa. See comment in definition of
|
|
|
|
;; local-algorithm-list in ssh-transport.rkt.
|
|
|
|
(error 'host-key-signature "ssh-rsa host key signatures unimplemented"))
|
|
|
|
((ssh-dss)
|
|
|
|
(match (asn1-ber-decode-all (sign private-key digest:dss1 exchange-hash))
|
|
|
|
(`(0 16 ((0 2 ,r-bytes)
|
|
|
|
(0 2 ,s-bytes)))
|
|
|
|
(bit-string (#"ssh-dss" :: (t:string))
|
|
|
|
((bit-string ((bs->n r-bytes) :: big-endian integer bits 160)
|
|
|
|
((bs->n s-bytes) :: big-endian integer bits 160))
|
|
|
|
:: (t:string))))))))
|
|
|
|
|
2011-10-20 18:19:29 +00:00
|
|
|
(define (verify-host-key-signature! public-key host-key-alg exchange-hash h-signature)
|
|
|
|
;; TODO: If we are *re*keying, worth checking here that the key hasn't *changed* either.
|
|
|
|
(write `(TODO check-host-key!)) (newline) (flush-output) ;; actually check the identity TOFU/POP
|
|
|
|
(case host-key-alg
|
|
|
|
((ssh-rsa)
|
|
|
|
;; TODO: offer ssh-rsa. See comment in definition of
|
|
|
|
;; local-algorithm-list in ssh-transport.rkt.
|
|
|
|
(error 'verify-host-key-signature! "ssh-rsa host key signatures unimplemented"))
|
|
|
|
((ssh-dss)
|
|
|
|
(define signature (bit-string-case h-signature
|
|
|
|
([ (= #"ssh-dss" :: (t:string #:pack))
|
|
|
|
(r-and-s :: (t:string)) ]
|
|
|
|
(bit-string-case r-and-s
|
|
|
|
([ (r :: big-endian integer bits 160)
|
|
|
|
(s :: big-endian integer bits 160) ]
|
|
|
|
(asn1-ber-encode `(0 16 ((0 2 ,(n->bs r))
|
|
|
|
(0 2 ,(n->bs s))))))))))
|
|
|
|
(when (not (verify public-key digest:dss1 signature exchange-hash))
|
|
|
|
(error 'verify-host-key-signature! "Signature mismatch")))))
|
|
|
|
|
2011-10-20 13:43:41 +00:00
|
|
|
(define (pieces->ssh-host-key pieces)
|
|
|
|
(match pieces
|
|
|
|
((struct rsa-public-key (n e))
|
|
|
|
(bit-string (#"ssh-rsa" :: (t:string))
|
|
|
|
(e :: (t:mpint))
|
|
|
|
(n :: (t:mpint))))
|
|
|
|
((struct dsa-public-key (y p q g))
|
|
|
|
(bit-string (#"ssh-dss" :: (t:string))
|
|
|
|
(p :: (t:mpint))
|
|
|
|
(q :: (t:mpint))
|
|
|
|
(g :: (t:mpint))
|
|
|
|
(y :: (t:mpint))))))
|
|
|
|
|
|
|
|
(define (ssh-host-key->pieces blob)
|
|
|
|
(bit-string-case blob
|
2011-10-20 18:19:29 +00:00
|
|
|
([ (= #"ssh-rsa" :: (t:string #:pack))
|
2011-10-20 13:43:41 +00:00
|
|
|
(e :: (t:mpint))
|
|
|
|
(n :: (t:mpint)) ]
|
|
|
|
(rsa-public-key n e))
|
2011-10-20 18:19:29 +00:00
|
|
|
([ (= #"ssh-dss" :: (t:string #:pack))
|
2011-10-20 13:43:41 +00:00
|
|
|
(p :: (t:mpint))
|
|
|
|
(q :: (t:mpint))
|
|
|
|
(g :: (t:mpint))
|
|
|
|
(y :: (t:mpint)) ]
|
|
|
|
(dsa-public-key y p q g))))
|
|
|
|
|
|
|
|
;; TODO: proper store for keys
|
|
|
|
|
2011-10-20 18:19:29 +00:00
|
|
|
(define (load-private-key filename)
|
|
|
|
(pieces->private-key
|
|
|
|
(bytes->private-key-pieces
|
|
|
|
(base64-decode
|
|
|
|
(regexp-replace* #rx"(?m:^-.*-$)"
|
|
|
|
(call-with-input-file filename port->bytes)
|
|
|
|
#"")))))
|
2011-10-20 13:43:41 +00:00
|
|
|
|
2011-10-20 18:19:29 +00:00
|
|
|
(define host-key-dsa-private (load-private-key "test-dsa-key"))
|
|
|
|
(define host-key-dsa-public (pkey->public-key host-key-dsa-private))
|
2011-10-20 13:43:41 +00:00
|
|
|
|
|
|
|
(check-equal? (public-key->bytes (pieces->public-key (public-key->pieces host-key-dsa-public)))
|
|
|
|
(public-key->bytes host-key-dsa-private))
|