Signature verification; private-key loading.

This commit is contained in:
Tony Garnock-Jones 2011-10-20 14:19:29 -04:00
parent 526d3b9afe
commit 8676d74702
3 changed files with 113 additions and 42 deletions

View File

@ -1,6 +1,8 @@
#lang racket/base
(require racket/match)
(require racket/port)
(require net/base64)
(require (planet vyzo/crypto))
(require (planet tonyg/bitsyntax))
@ -9,7 +11,9 @@
(require rackunit)
(provide (struct-out rsa-public-key)
(provide (struct-out rsa-private-key)
(struct-out dsa-private-key)
(struct-out rsa-public-key)
(struct-out dsa-public-key)
public-key->pieces
@ -18,37 +22,78 @@
host-key-algorithm->keys
host-key-algorithm->digest-type
host-key-signature
verify-host-key-signature!
pieces->ssh-host-key
ssh-host-key->pieces)
(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)
(struct rsa-public-key (n e) #:transparent)
(struct dsa-public-key (y p q g) #:transparent)
;; (define (private-key->pieces key)
;; (match (asn1-ber-decode-all (private-key->bytes key))
;; (`(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)))
;; ...)
;; (`(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
;; ...)))
;; 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))
(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)))))))))
(define (public-key->pieces key)
(match (asn1-ber-decode-all (public-key->bytes key))
(`(0 16 ((0 2 ,n-bytes)
@ -79,15 +124,14 @@
(define (host-key-algorithm->keys host-key-alg)
(case host-key-alg
((ssh-rsa) (values host-key-rsa-private host-key-rsa-public))
((ssh-dss) (values host-key-dsa-private host-key-dsa-public))
(else (error 'host-key-algorithm->keys "Unknown host-key-alg ~v" host-key-alg))))
(else (error 'host-key-algorithm->keys "Unsupported host-key-alg ~v" host-key-alg))))
(define (host-key-algorithm->digest-type host-key-alg)
(case host-key-alg
((ssh-rsa) digest:sha1)
((ssh-dss) digest:dss1)
(else (error 'host-key-algorithm->digest-type "Unknown host-key-alg ~v" host-key-alg))))
(else (error 'host-key-algorithm->digest-type "Unsupported host-key-alg ~v" host-key-alg))))
(define (host-key-signature private-key host-key-alg exchange-hash)
(case host-key-alg
@ -104,6 +148,26 @@
((bs->n s-bytes) :: big-endian integer bits 160))
:: (t:string))))))))
(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")))))
(define (pieces->ssh-host-key pieces)
(match pieces
((struct rsa-public-key (n e))
@ -119,11 +183,11 @@
(define (ssh-host-key->pieces blob)
(bit-string-case blob
([ (= #"ssh-rsa" :: (t:string))
([ (= #"ssh-rsa" :: (t:string #:pack))
(e :: (t:mpint))
(n :: (t:mpint)) ]
(rsa-public-key n e))
([ (= #"ssh-dss" :: (t:string))
([ (= #"ssh-dss" :: (t:string #:pack))
(p :: (t:mpint))
(q :: (t:mpint))
(g :: (t:mpint))
@ -132,23 +196,16 @@
;; TODO: proper store for keys
(define host-key-rsa-private
(bytes->private-key
pkey:rsa
#"0\202\4\243\2\1\0\2\202\1\1\0\251\261\357\350:\371\361li\31\331\225\215\\}:\315T\245g(\322\341\313)f\201\2406\201\r1r=\313\2eU\f\263W\310\240~c\0234>\364\211\310&\363\16\226\a\301\240\245\266\232\313\354\240Vp\352\262\201\345\246\277R\262\17\350\1\272b\16o9{\301.IY\313\re\305i\304\302z7\244m \332\245\252\201\6/\vb4\327\221|-%\342\361\255>\252Yg\326F[\330S\"\2237\215\271\364\270XfB\317\200\276\22?u\377\372\377\201\273\217/\317\367p\0266u\5\232\326Q\205YL\243\251\375&\236\23\0\214\3404d\bI\247\246\353\353\320\302\315\326/a\34\31\3026s1\312\325+\272\303c\260\352;`W\365\361\v\262\240\371\224\267\346.\363\244D?a\177(\372\352\24\31\367\253\200A\304\261\232\n\203\361\237F\271\224\331\270Y\314\237\e\222`\326P\276o\273\332\346\216)~\303\217\2\3\1\0\1\2\202\1\0LK4\332\213Q~\212\310B\30\2V\31\0040\240\312r\307\v\242\254c\244\fs1-\360\35\227b\216\207\254_\24\272\356\23\4\237\253\323-z*\347\324\336\211,t\35^\5_\r\302(\234\220\231\221\343\230\221\2037V\35\344\267+\321\232\235J\242\304\274\352w\3630\205Ov\211\220\25:\242YB\225\t\177`\356d\3277\211\205+\265(J?\263(\272|\377\360\21\264\272\253\252\334vI\2]\177<\31\301:\232V\322\255Xih\255\2T\266)\271\310}\305I\237\230\t\306\t\221\373\351\27-\312KK\177V\351u \31FN\373\341\255\272\f\332\272\252\4v\276\330\1\314\354\344\37\261\272\331V\360\221\\t\230\357Wi\307a\327%\374\235<t\200=\305sE\rL\16>\31c\213\177mV\240#\26B\226\222\e]\262\265\254\37\376N\362\272x$J\360%Y\323\251\25\310nva\256\305\216a\271\2\201\201\0\326pr\bo\351\240\364\31\316hI +\212\314D\255\210\5\214HL\273\f\264es\310\276[\325t\331\215\20<\216\347\337\313\214\247\262\257\377\21+e\2064\211\233\362\353O\342\2521,2\251b&j\356]X\252T6\211\354N\361\377\34\323\246#\306\321\245pQI\211\372Gaw\314\321\257\3417V3|\323H\244\344\332x\350\306Y\177\201aOC\252\axV\261\230*^?\375\30u\205\217\r\2\201\201\0\312\225zW\341\337\21\374\207\2332\34\320\311\351\230\374\302z\207\250\3605!\16%\225I\274h\200\367\221\353\223 8f\256==\221\2?\177D\361\257l\232\207\207\256\234.x\4\234U\b\n`\323\264\241\351R}\266#d\271 \200\271\327&|\276\226m\343\324\221\16-\v5\16\24'\4\322\e\374\24#UyS!\341\352M5\262-\336\230\227\311\4\336\25\325\332~0ZO)\364x,\n\260\226\v\2\201\201\0\220\217\313\200>\231\334\204\322\344\340N\363\336\6\273\0M\220\26Ud\344=q*'\376\257E\r9\320\205\\\340\246B\234\264\265OD:e\5i\370\274\177\205\351\202\365w\207*B\e\3264\355\25\232\332\231\210\332^1Yw\17\224\204\365\273\344>\353}|\25\260\253L\r5ik\236\314\270K~c\304\273\377\31\234\256u\r\207W=hVY\315U\27C\207\267\253\222\34\a\353\363\355V\320\333Q\375\2\201\200\3\254\344\224U\227\273v\316k\5d\272p\377X\3644\377\16\31\5\212\2417\345|\250\342\317.\217\317H\353\331h\314\214\32\266T<+4\276\346w\235K\314k\254\302N\241\204\313\345K\t\r*\252\eI\345b<|\265\346\31\355\333\361HTy\f\267\237\253\212\232,=\b\320a\2237\246,5\315\223\272\210A\366\332gj\321/\223_w{A}b\36\31L&\177\360\23\201\232Z\327\311\265\334s\2\201\200Q\6k\324O\5\205d\226\331:2B\332W?\341\311\331\251\246/2j\242P\216\e\261\216\202\266\301\365v\274\303b\315\30\37568\376\331\355GNa;\344mS\362\225H}-\375\226\324\277\351\0\2J\206\363\3278H\353J\326\247\322\3313(}\263\374t8\376\322\350\245\201\320\360\305^\235\350]\304j\352\201k\23\213\201\b\316(\267nn\372.M;\312\202S/)\312\260\270\36\342\356b\225\237"))
(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)
#"")))))
(define host-key-rsa-public
(pkey->public-key host-key-rsa-private))
(define host-key-dsa-private (load-private-key "test-dsa-key"))
(define host-key-dsa-public (pkey->public-key host-key-dsa-private))
(define host-key-dsa-private
(bytes->private-key
pkey:dsa
#"0\202\3=\2\1\0\2\202\1\1\0\347,\332\312\361\22\272\211jV\224r\4O`\350h\v#BO\307A\355\22K.\2062A\337\341\317eT>%p\251\303\3657iG{\220\32\253TB\23\b\250*=\245\\\354jt\2163\331\220\256\v\206\250\225q\377\315\311\233-F7E~\372\274\a\r5\364\265Y\364\276(\261|y\221\3\345\16\2552\1V+\242p]\243\225(-\202, gL\330\252\303\256x\307\213\300^\232\200\265/(\356\234j\275xa\345\202\350\210F\1*}\300H\337\367\372\215\343f\333\337[\311a\352\307j\321n\364W\367\273\275]\234\267<\35\253\372\373hN\206Z\4a\211\206g\2\e\201\23~\3702\215;\2632K^\205\332/\245\2\3510\374\16\342pQ\3465\277\220 B\347\214\345H\266\265yRL\r)-\307b\211M\250Y\210\362\347\3631\263\177\373\3175M\32M\344`\22\210\244\16+\271\16\340I\243\2\25\0\373\344\244\30)\246\214BB5\343\351\250\301S\370\3208\310\231\2\202\1\0Q\3443\245\0P_\327E\320\253Q\4\a\300\347Vu\331\24\31p\235\341K\252\314Q\2Pv|\0\246\213\n)\31#K\16\366\305\264\306\363\312\223\r3\224\227\215\330u\203!\333z.\23\345\310\4^\302G\2154u\252\223.\251^x\343\3320\354#Tl\30\202LR\325\3\354\261\276;\6\27+\254S\332\337\2370\16+q\354g\315\336\32\212se\233\346\226\354\245\\2\323eR\237\321\202\320\274h\"t\343D+\374\262\262\361\202\27l\345\256w\a\260\322\20\263m\276\25\271[\213O\a\370\251ob\306\223\vg_\261'r\205\375\217\310\226I\3340\363\345\277\177\361|o\214\371\246\240\204y\257\361\35\n\230%}\30:{\202\rH!\204\6K\367\305\272\26\313q7Tg\346a\271\365\177\323\23\5\23u\200\360\225\320\270\357\326b\317\372\340K\3064\177u&\37\334\262~7\266\237\0254\330\264+\374\2\202\1\0?\250\b\373\331[\352\255VwF\374\263\226\274\310\303#d\231m-2\315\251\"\17\5\321\306\356\6\302@_9\372\227\305\267\205\305\24\"\36\226\5\272/0\a|\e[\257\4\23O.=l\366\235\237\334<\300j\376\231\235\223\222\310\253\25\244\263\25\245\356\322\254\340&:\310\232%\255\246\331\273\227Gk\303\206\351~'C\377\321\336\251\210\6\201\336A\241)\3749j\355\256\363C{\302\224\312@\271G\3716\271\222}\344?\371\217p\335\236\201\\K\305\262\0307\27\230\312\362\241\274\351\370\327\226B\24\a\374\375\220\261\35\254\355\311$\310x\330\23\330\307\e4\254M7\316\316\273\300\360\352B(\30\f\357%G\252\273Fs\v\225YXv\237zK]\364t\375\33087\300\233\304\361QKa\344T \312\332\266_\352\243~\216\221\177x\263@\27\337(h\271\266}\240\342gA\30W\352-u\346Na$T+\2\24k\32\227(\203\254\250\313\272b+\223\245\221\361N=y\0\330"))
(define host-key-dsa-public
(pkey->public-key host-key-dsa-private))
(check-equal? (public-key->bytes (pieces->public-key (public-key->pieces host-key-rsa-public)))
(public-key->bytes host-key-rsa-public))
(check-equal? (public-key->bytes (pieces->public-key (public-key->pieces host-key-dsa-public)))
(public-key->bytes host-key-dsa-private))

View File

@ -66,6 +66,8 @@
(define-syntax t:string
(syntax-rules ()
((_ #t #:pack) (lambda (input ks kf)
((t:string #t) input (lambda (v rest) (ks (bit-string->bytes v) rest)) kf)))
((_ #t) (lambda (input ks kf)
(bit-string-case input
([ (length :: integer bits 32) (body :: binary bytes length) (rest :: binary) ]

12
test-dsa-key Normal file
View File

@ -0,0 +1,12 @@
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQCEQ1YvOR7/MQByCPJt/FSO7NN7YO1VLqy7A95M07q6AaG5FZ2A
m9s8KZPlNFPrNhG8pRxxHhWgfBczoIObZi2saXeXQyTCUtHUejQBk+Xl31I+0SYU
/m5fIP3Q9UY3cR8LucsIQkJIcuLVpoMmtFA/EtxYs+roxm+wtMlgk/8HkQIVAObN
DEIjvgKwW9MKzRz8VXms/aDDAoGAeMnKQxj/iBSfQ3Wsd4ipCi3PdoLJ0+TJuiFG
0tmbxLxwC0YCR24YMeobva/SpSu6y48+2rjv9Wc9ZKwISbrdO6xrNgDJtoCZLGK+
C2DHEC3rBYFicOgpoysk/HsS/to3GtMnPyA2NJDR/cjUdgWBRg+4eAx1ZsVPjaJT
A5Z60tECgYAkhzk5oi/b3zxPEPoFYki2apR4mciJso/1mYvb6fpd+rzlihNrkFAA
LL+6uOofkyf32FIQhEN+JXDNMfaHreJkLPxGXIJ4FyUbrrZcxbmgJdh9NHd0L/mI
yIHlo+SImp1DLCEtRP1GwKv8Lm0/rFNpY/z5Os3qeXKw1swDvEMfywIVANtH4mhn
F6JfX/4/cJ4cpGlcgrWe
-----END DSA PRIVATE KEY-----