From 8676d7470207fbba4e64faf417e86b214608829e Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 20 Oct 2011 14:19:29 -0400 Subject: [PATCH] Signature verification; private-key loading. --- host-key.rkt | 141 +++++++++++++++++++++++++++++------------- ssh-message-types.rkt | 2 + test-dsa-key | 12 ++++ 3 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 test-dsa-key diff --git a/host-key.rkt b/host-key.rkt index f0f1ff4..fd9a252 100644 --- a/host-key.rkt +++ b/host-key.rkt @@ -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\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)) diff --git a/ssh-message-types.rkt b/ssh-message-types.rkt index 91aa609..afd4ab3 100644 --- a/ssh-message-types.rkt +++ b/ssh-message-types.rkt @@ -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) ] diff --git a/test-dsa-key b/test-dsa-key new file mode 100644 index 0000000..94350ee --- /dev/null +++ b/test-dsa-key @@ -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-----