commit c8063ea4b3b94cd7947da06815a1a27b94ca1b79
parent 05b035aca3bd5a96b9e42a415e39e955d7388d43
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sun, 21 Sep 2014 17:48:58 +0200
move tls a bit forward
can parse server key exchange and write client key exchange but
doesn't compute correct values yet
Diffstat:
| M | tls.lisp |  |  | 625 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- | 
1 file changed, 401 insertions(+), 224 deletions(-)
diff --git a/tls.lisp b/tls.lisp
@@ -29,6 +29,10 @@
 ;;gnutls-cli wikipedia.org
 
 ;; https://en.wikipedia.org/wiki/Transport_Layer_Security
+;; http://msdn.microsoft.com/en-us/library/windows/desktop/aa380513(v=vs.85).aspx
+;; http://technet.microsoft.com/en-us/library/cc785811(v=ws.10).aspx
+;; https://tools.ietf.org/html/rfc5246
+;; https://tools.ietf.org/html/rfc4492
 
 (defun next-u8 (reader)
   (rw:next-u8 reader))
@@ -271,31 +275,90 @@
   (sha384 . 5)
   (sha512 . 6))
 
-(defenum $SignatureAlgorithm (:nbits 8)
-  (anonymous . 0)
-  (rsa       . 1)
-  (dsa       . 2)
-  (ecdsa     . 3))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defenum $ECCurveType (:nbits 8)
+  (explicit_prime . 1)
+  (explicit_char2 . 2)
+  (named_curve    . 3)
+  #+nil(reserved 248..255))
+
+(defenum $NamedCurve (:nbits 16)
+  (sect163k1 .  1)
+  (sect163r1 .  2)
+  (sect163r2 .  3)
+  (sect193r1 .  4)
+  (sect193r2 .  5)
+  (sect233k1 .  6)
+  (sect233r1 .  7)
+  (sect239k1 .  8)
+  (sect283k1 .  9)
+  (sect283r1 . 10)
+  (sect409k1 . 11)
+  (sect409r1 . 12)
+  (sect571k1 . 13)
+  (sect571r1 . 14)
+  (secp160k1 . 15)
+  (secp160r1 . 16)
+  (secp160r2 . 17)
+  (secp192k1 . 18)
+  (secp192r1 . 19)
+  (secp224k1 . 20)
+  (secp224r1 . 21)
+  (secp256k1 . 22)
+  (secp256r1 . 23)
+  (secp384r1 . 24)
+  (secp521r1 . 25)
+  ;;reserved (0xfe00..0xfeff)
+  (arbitrary_explicit_prime_curves . #xff01)
+  (arbitrary_explicit_char2_curves . #xff02))
+
+(defenum %$SignatureHashAlgorithmHash (:nbits 8)
+  (sha256 . 4))
+
+(defenum %$SignatureHashAlgorithmSignature (:nbits 8)
+  (rsa . 1))
 
 (defun PublicValueEncoding ()
-  ;; If the client has sent a certificate which contains a suitable
-  ;; Diffie-Hellman key (for fixed_dh client authentication), then
-  ;; Yc is implicit and does not need to be sent again.  In this
-  ;; case, the client key exchange message will be sent, but it MUST
-  ;; be empty.
-  'implicit)
+  ;;'implicit
+  'explicit)
 
 (defun KeyExchangeAlgorithm ()
-  ;; dhe_dss dhe_rsa dh_anon rsa dh_dss dh_rsa
-  'dhe_rsa)
-
-;;;;;;;;;;;;;;
+  ;;'ecdhe_rsa
+  'ec_diffie_hellman)
+
+;; (defenum $KeyExchangeAlgorithm (:nbits 8)
+;;   (dhe_dss . 0)
+;;   (dhe_rsa . 1)
+;;   (dh_anon . 2)
+;;   (rsa     . 3)
+;;   (dh_dss  . 4)
+;;   (dh_rsa  . 5))
+
+;; ECDH_ECDSA          Fixed ECDH with ECDSA-signed certificates.
+;; ECDHE_ECDSA         Ephemeral ECDH with ECDSA signatures.
+;; ECDH_RSA            Fixed ECDH with RSA-signed certificates.
+;; ECDHE_RSA           Ephemeral ECDH with RSA signatures.
+;; ECDH_anon           Anonymous ECDH, no signatures.
+
+;; (defun ECBasisType ()
+;;   'ec_basis_trinomial
+;;   'ec_basis_pentanomial)
+
+(defun SignatureAlgorithm ()
+  ;;'anonymous
+  ;;'rsa
+  ;;'dsa
+  'ecdsa)
 
 (defun aname (struc &optional slot)
   (intern (format nil "~a-~a" struc slot)))
 
+(defun make-octet-buffer (length)
+  (make-array length
+              :element-type '(unsigned-byte 8)
+              :initial-element 0
+              :adjustable t
+              :fill-pointer 0))
+
 (defun defun-rname-slot (slot)
   (destructuring-bind (ty na &key length size min max compute next) slot
     `(,na
@@ -425,13 +488,6 @@
 (defstruc $Certificate ()
   (%$Certificate list :length u24 :min 0 :max #.(1- (expt 2 24)) :size t))
 
-(defstruc $ClientDiffieHellmanPublic ()
-  (computed type :compute (PublicValueEncoding))
-  ((ecase type
-     (implicit)
-     (explicit $dh_Yc))
-   dh_public))
-
 (defstruc $ClientHello ()
   ($ContentVersion #+nil $ProtocolVersion version)
   ($Random random)
@@ -440,19 +496,6 @@
   ($CompressionMethod compression_methods :length u8 :min 1 :max #.(1- (expt 2 8)) :size t)
   ($Extension extensions :length u16 :min 0 :max #.(1- (expt 2 16)) :size t))
 
-(defstruc $ClientKeyExchange ()
-  (computed type :compute (KeyExchangeAlgorithm))
-  ((ecase type
-     (rsa $EncryptedPreMasterSecret)
-     ((dhe_dss dhe_rsa dh_dss dh_rsa dh_anon) $ClientDiffieHellmanPublic))
-   keys))
-
-(defstruc $dh_Yc ()
-  (u8 data :length u16 :min 1 :max #.(1- (expt 2 16))))
-
-(defstruc $EncryptedPreMasterSecret ()
-  ($PreMasterSecret pubkey_encrypted))
-
 (defstruc $Extension ()
   ($ExtensionType type)
   #+nil
@@ -466,6 +509,134 @@
     (signature_algorithms))
   (u8 data :length u16 :min 0 :max #.(1- (expt 2 16)) :size t))
 
+(defstruc $ServerDHParams ()
+  (u8 dh_p :length u16 :min 1 :max #.(1- (expt 2 16)))
+  (u8 dh_g :length u16 :min 1 :max #.(1- (expt 2 16)))
+  (u8 dh_Ys :length u16 :min 1 :max #.(1- (expt 2 16))))
+
+(defstruc $signed_params ()
+  (u8 client_random :size 32)
+  (u8 server_random :size 32)
+  ($ServerDHParams params2))
+
+(defstruc %$ServerDHParams ()
+  ($ServerDHParams params)
+  ($signed_params signed_params))
+
+(defstruc $ECCurve ()
+  (u8 a :length u8 :min 1)
+  (u8 b :length u8 :min 1))
+
+(defstruc $ECPoint ()
+  (u8 data :length u8 :min 1 :size t))
+
+;; (defstruc %$ExplicitPrime ()
+;;   opaque      prime_p <1..2^8-1>
+;;   ECCurve     curve
+;;   ECPoint     base
+;;   opaque      order <1..2^8-1>
+;;   opaque      cofactor <1..2^8-1>)
+
+;; (defstruc %$ExplicitChar2 ()
+;;   uint16      m
+;;   ECBasisType basis
+;;   select (basis) {
+;;   case ec_trinomial:
+;;   opaque  k <1..2^8-1>
+;;   case ec_pentanomial:
+;;   opaque  k1 <1..2^8-1>
+;;   opaque  k2 <1..2^8-1>
+;;   opaque  k3 <1..2^8-1>
+;;   }
+;;   ECCurve     curve
+;;   ECPoint     base
+;;   opaque      order <1..2^8-1>
+;;   opaque      cofactor <1..2^8-1>)
+
+(defstruc $ECParameters ()
+  ($ECCurveType curve_type)
+  ((ecase curve_type
+     ;;(explicit_prime %$ExplicitPrime)
+     ;;(explicit_char2 %$ExplicitChar2)
+     (named_curve $NamedCurve))
+   data))
+
+(defstruc $ServerECDHParams ()
+  ($ECParameters curve_params)
+  ($ECPoint public))
+
+(defstruc %$ECSASignature () ;; digitally-signed
+  (%$SignatureHashAlgorithm algorithm)
+  (u16 length #+nil :compute #+nil(ShaSize))
+  (u8 sha_hash :size length))
+
+(defstruc %$SignatureHashAlgorithm ()
+  (%$SignatureHashAlgorithmHash hash)
+  (%$SignatureHashAlgorithmSignature signature))
+
+(defstruc $Signature ()
+  (computed algorithm :compute (SignatureAlgorithm))
+  ((ecase algorithm
+     (ecdsa %$ECSASignature))
+   data))
+
+(defstruc %$ServerECDHParams ()
+  ($ServerECDHParams params)
+  ($Signature signed_params))
+
+(defstruc $ServerKeyExchange ()
+  ;;(u8 data :length u16 :min 1 :max #.(1- (expt 2 16)))
+  (computed type :compute (KeyExchangeAlgorithm))
+  ((ecase type
+     ;;(dh_anon $ServerDHParams)
+     ;;((dhe_dss dhe_rsa) %$ServerDHParams)
+     (ec_diffie_hellman %$ServerECDHParams)
+     #+nil((rsa dh_dss dh_rsa)))
+   data))
+
+(defstruc $SessionID ()
+  (u8 data :length u8 :min 0 :max 32 :size t))
+
+(defstruc $Random ()
+  (u32 gmt_unix_time)
+  (u8 random_bytes :size 28))
+
+(defstruc $ServerHello ()
+  ($ContentVersion #+nil ProtocolVersion version)
+  ($Random random)
+  ($SessionID session_id)
+  ($CipherSuite cipher_suite)
+  ($CompressionMethod compression_method)
+  ($Extension extensions :length u16 :min 0 :max #.(1- (expt 2 16)) :size t))
+
+(defstruc $dh_Yc ()
+  (u8 data :length u16 :min 1 :max #.(1- (expt 2 16))))
+
+(defstruc $EncryptedPreMasterSecret ()
+  ($PreMasterSecret pubkey_encrypted))
+
+(defstruc $ClientDiffieHellmanPublic ()
+  (computed type :compute (PublicValueEncoding))
+  ((ecase type
+     (implicit)
+     (explicit $dh_Yc))
+   dh_public))
+
+(defstruc $ClientECDiffieHellmanPublic ()
+  (computed type :compute (PublicValueEncoding))
+  ((ecase type
+     (implicit)
+     (explicit $ECPoint)) ;; ecdh_Yc
+   ecdh_public))
+
+(defstruc $ClientKeyExchange ()
+  (computed type :compute (KeyExchangeAlgorithm))
+  ((ecase type
+     ;;(rsa $EncryptedPreMasterSecret)
+     ;;((dhe_dss dhe_rsa dh_dss dh_rsa dh_anon) $ClientDiffieHellmanPublic)
+     (ec_diffie_hellman $ClientECDiffieHellmanPublic))
+   data))
+
 (defstruc $Handshake ()
   ($HandshakeType type)
   ((ecase type
@@ -474,17 +645,13 @@
      (CLIENT_KEY_EXCHANGE $ClientKeyExchange)
      (SERVER_HELLO $ServerHello)
      (SERVER_HELLO_DONE)
-     (SERVER_KEY_EXCHANGE #+nil $ServerKeyExchange))
+     (SERVER_KEY_EXCHANGE $ServerKeyExchange))
    data :length u24))
 
 (defstruc $PreMasterSecret ()
   ($ContentVersion #+nil ProtocolVersion client_version)
   (u8 random :size 46))
 
-(defstruc $Random ()
-  (u32 gmt_unix_time)
-  (u8 random_bytes :size 28))
-
 (defstruc $Record ()
   ($ContentType type)
   (u16 #+nil ContentVersion version)
@@ -495,44 +662,6 @@
   #+nil
   (u8 data :length u16 :min 1 :max 16383 :size t))
 
-#+nil
-(defstruc $ServerDHParams () ;;;;
-  (u8 dh_p :min 1 :max #.(1- (expt 2 16)) :size t)
-  (u8 dh_g :min 1 :max #.(1- (expt 2 16)) :size t)
-  (u8 dh_Ys :min 1 :max #.(1- (expt 2 16)) :size t))
-
-(defstruc %$ServerDHParams2 ()
-  ($ServerDHParams params)
-  ($signed_params signed_params))
-
-(defstruc $ServerHello ()
-  ($ContentVersion #+nil ProtocolVersion version)
-  ($Random random)
-  ($SessionID session_id)
-  ($CipherSuite cipher_suite)
-  ($CompressionMethod compression_method)
-  ($Extension extensions :length u16 :min 0 :max #.(1- (expt 2 16)) :size t))
-
-#+nil
-(defstruc $ServerKeyExchange ()
-  (u8 data :min 1 :max #.(1- (expt 2 16)) :size t)
-  #+nil
-  ($KeyExchangeAlgorithm type)
-  #+nil
-  ((ecase type
-     (dh_anon $ServerDHParams)
-     ((dhe_dss dhe_rsa) %$ServerDHParams2)
-     ((rsa dh_dss dh_rsa)))
-   data))
-
-(defstruc $SessionID ()
-  (u8 data :length u8 :min 0 :max 32 :size t))
-
-(defstruc $signed_params ()
-  (u8 client_random :size 32)
-  (u8 server_random :size 32)
-  ($ServerDHParams params2))
-
 ;; struct {
 ;;          HashAlgorithm hash
 ;;          SignatureAlgorithm signature
@@ -541,13 +670,6 @@
 ;;    SignatureAndHashAlgorithm
 ;;     supported_signature_algorithms<2..2^16-1>
 
-(defun make-octet-buffer (length)
-  (make-array length
-              :element-type '(unsigned-byte 8)
-              :initial-element 0
-              :adjustable t
-              :fill-pointer 0))
-
 (defun random-octets (length)
   (loop
      for i from 0 below length
@@ -558,122 +680,156 @@
 
 ;;(universal-time-to-unix (encode-universal-time 19 22 23 28 7 2014 0)) ;; TODO broken
 
-(defun test ()
-  (let ((b (make-octet-buffer 100)))
-    (write-$Record
-     (rw:writer b)
-     (make-$Record
-      :type 'HANDSHAKE
-      :version ($ContentVersion 'SSL3.0)
-      :data (make-$Handshake
-             :type 'CLIENT_HELLO
-             :data (make-$ClientHello
-                    :version 'TLS1.2
-                    :random (make-$Random
-                             :gmt_unix_time (universal-time-to-unix (get-universal-time))
-                             :random_bytes (random-octets 28))
-                    :session_id (make-$SessionID #+nil :data #+nil(random-octets 32))
-                    :cipher_suites '(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
-                                     TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
-                                     TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
-                                     TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
-                                     TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
-                                     TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
-                                     TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
-                                     TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
-                                     TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
-                                     TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
-                                     TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
-                                     TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
-                                     TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
-                                     TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
-                                     TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
-                                     TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
-                                     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
-                                     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
-                                     TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
-                                     TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
-                                     TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
-                                     TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
-                                     TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
-                                     TLS_ECDHE_RSA_WITH_RC4_128_SHA
-                                     TLS_RSA_WITH_AES_128_GCM_SHA256
-                                     TLS_RSA_WITH_AES_256_GCM_SHA384
-                                     TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
-                                     TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
-                                     TLS_RSA_WITH_AES_128_CBC_SHA
-                                     TLS_RSA_WITH_AES_128_CBC_SHA256
-                                     TLS_RSA_WITH_AES_256_CBC_SHA
-                                     TLS_RSA_WITH_AES_256_CBC_SHA256
-                                     TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
-                                     TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
-                                     TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
-                                     TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
-                                     TLS_RSA_WITH_3DES_EDE_CBC_SHA
-                                     TLS_RSA_WITH_RC4_128_SHA
-                                     TLS_RSA_WITH_RC4_128_MD5
-                                     TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
-                                     TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
-                                     TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
-                                     TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
-                                     TLS_DHE_RSA_WITH_AES_128_CBC_SHA
-                                     TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
-                                     TLS_DHE_RSA_WITH_AES_256_CBC_SHA
-                                     TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
-                                     TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
-                                     TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
-                                     TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
-                                     TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
-                                     TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
-                                     TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
-                                     TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
-                                     TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256
-                                     TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384
-                                     TLS_DHE_DSS_WITH_AES_128_CBC_SHA
-                                     TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
-                                     TLS_DHE_DSS_WITH_AES_256_CBC_SHA
-                                     TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
-                                     TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
-                                     TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256
-                                     TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
-                                     TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256
-                                     TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
-                                     TLS_DHE_DSS_WITH_RC4_128_SHA)
-                    :compression_methods (list 'null)
-                    :extensions (list
-                                 (make-$Extension :type 'status_request
-                                                  :data '(1 0 0 0 0))
-                                 (make-$Extension :type 'server_name
-                                                  :data '(0 #x10 0 0 #xd #x77 #x69 #x6b #x69 #x70 #x65 #x64 #x69 #x61 #x2e #x6f #x72 #x67))
-                                 (make-$Extension :type 'renegotiation_info
-                                                  :data '(0))
-                                 (make-$Extension :type 'SessionTicket_TLS :data nil)
-                                 (make-$Extension :type 'elliptic_curves
-                                                  :data '(0 10 0 #x13 0 #x15 0 #x17 0 #x18 0 #x19))
-                                 (make-$Extension :type 'ec_point_formats
-                                                  :data '(1 0))
-                                 (make-$Extension :type 'signature_algorithms
-                                                  :data '(0 #x1a 4 1 4 2 4 3 5 1 5 3 6 1 6 3 3 1 3 2 3 3 2 1 2 2 2 3)))))))
-    b))
-
-;;(print (test))
-
-(defun test2 ()
-  (let ((b (make-octet-buffer 100)))
-    (write-$Record
-     (rw:writer b)
-     (make-$Record
-      :type 'HANDSHAKE
-      :version ($ContentVersion 'SSL3.0)
-      :data (make-$Handshake
-             :type 'CLIENT_KEY_EXCHANGE
-             :data (make-$ClientKeyExchange
-                    :keys (make-$ClientDiffieHellmanPublic
-                           :dh_public nil)))))
-    b))
-
-;;(print (test2))
+(defun write-client-hello (writer client-random client-hello-time)
+  (write-$Record
+   writer
+   (make-$Record
+    :type 'HANDSHAKE
+    :version ($ContentVersion 'SSL3.0)
+    :data (make-$Handshake
+           :type 'CLIENT_HELLO
+           :data (make-$ClientHello
+                  :version 'TLS1.2
+                  :random (make-$Random
+                           :gmt_unix_time client-hello-time
+                           :random_bytes client-random)
+                  :session_id (make-$SessionID #+nil :data #+nil(random-octets 32))
+                  :cipher_suites '(
+                                   ;; TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+                                   ;; TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+                                   ;; TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+                                   ;; TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+                                   ;; TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+                                   ;; TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
+                                   ;; TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+                                   ;; TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
+                                   ;; TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+                                   ;; TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+                                   ;; TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
+                                   ;; TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
+                                   TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+                                   ;; TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+                                   ;; TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+                                   ;; TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+                                   ;; TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+                                   ;; TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+                                   ;; TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+                                   ;; TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
+                                   ;; TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+                                   ;; TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
+                                   ;; TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+                                   ;; TLS_ECDHE_RSA_WITH_RC4_128_SHA
+                                   ;; TLS_RSA_WITH_AES_128_GCM_SHA256
+                                   ;; TLS_RSA_WITH_AES_256_GCM_SHA384
+                                   ;; TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
+                                   ;; TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
+                                   ;; TLS_RSA_WITH_AES_128_CBC_SHA
+                                   ;; TLS_RSA_WITH_AES_128_CBC_SHA256
+                                   ;; TLS_RSA_WITH_AES_256_CBC_SHA
+                                   ;; TLS_RSA_WITH_AES_256_CBC_SHA256
+                                   ;; TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
+                                   ;; TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
+                                   ;; TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
+                                   ;; TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
+                                   ;; TLS_RSA_WITH_3DES_EDE_CBC_SHA
+                                   ;; TLS_RSA_WITH_RC4_128_SHA
+                                   ;; TLS_RSA_WITH_RC4_128_MD5
+                                   ;; TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+                                   ;; TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
+                                   ;; TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+                                   ;; TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+                                   ;; TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+                                   ;; TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+                                   ;; TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+                                   ;; TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
+                                   ;; TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
+                                   ;; TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+                                   ;; TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
+                                   ;; TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
+                                   ;; TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+                                   ;; TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
+                                   ;; TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
+                                   ;; TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256
+                                   ;; TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384
+                                   ;; TLS_DHE_DSS_WITH_AES_128_CBC_SHA
+                                   ;; TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
+                                   ;; TLS_DHE_DSS_WITH_AES_256_CBC_SHA
+                                   ;; TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
+                                   ;; TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
+                                   ;; TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256
+                                   ;; TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
+                                   ;; TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256
+                                   ;; TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+                                   ;; TLS_DHE_DSS_WITH_RC4_128_SHA
+                                   )
+                  :compression_methods (list 'null)
+                  :extensions (list
+                               (make-$Extension :type 'status_request
+                                                :data '(1 0 0 0 0))
+                               (make-$Extension :type 'server_name
+                                                :data '(0 #x10 0 0 #xd #x77 #x69 #x6b #x69 #x70 #x65 #x64 #x69 #x61 #x2e #x6f #x72 #x67))
+                               (make-$Extension :type 'renegotiation_info
+                                                :data '(0))
+                               (make-$Extension :type 'SessionTicket_TLS :data nil)
+                               (make-$Extension :type 'elliptic_curves
+                                                :data '(0 10 0 #x13 0 #x15 0 #x17 0 #x18 0 #x19))
+                               (make-$Extension :type 'ec_point_formats
+                                                :data '(1 0))
+                               (make-$Extension :type 'signature_algorithms
+                                                :data '(0 #x1a 4 1 4 2 4 3 5 1 5 3 6 1 6 3 3 1 3 2 3 3 2 1 2 2 2 3))))))))
+
+(defun write-client-key-exchange (writer)
+  (write-$Record
+   writer
+   (make-$Record
+    :type 'HANDSHAKE
+    :version ($ContentVersion 'SSL3.0)
+    :data (make-$Handshake
+           :type 'CLIENT_KEY_EXCHANGE
+           :data (make-$ClientKeyExchange
+                  :type 'dhe_rsa
+                  :data (make-$ClientECDiffieHellmanPublic
+                         :type 'explicit
+                         :ecdh_public (make-$ECPoint
+                                       :data #(3 1 4 1 5 9)))))))) ;; TODO compute properly
+
+(defun next-server-hello (reader client-hello-time)
+  (let ((x (next-$Record reader)))
+    ;;(print x)
+    (let ((x ($Record-data x)))
+      (let ((x ($Handshake-data x)))
+        (assert (eq 'TLS1.2 ($ServerHello-version x)))
+        (assert (eq 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+                    ($ServerHello-cipher_suite x)))
+        (let ((x ($ServerHello-random x)))
+          (assert (<= 0 (- ($Random-gmt_unix_time x) client-hello-time) 1))
+          ($Random-random_bytes x))))))
+
+(defun next-server-certificate (reader)
+  (let ((x (next-$Record reader)))
+    ;;(print x)
+    (let ((x ($Record-data x)))
+      (let ((x ($Handshake-data x)))
+        (loop
+           for x in ($Certificate-list x)
+           collect (%$Certificate-der x))))))
+
+(defun next-server-key-exchange (reader) ;; TODO
+  (let ((x (next-$Record reader)))
+    ;;(print x)
+    (let ((x ($Record-data x)))
+      (let ((x ($Handshake-data x)))
+        (etypecase x
+          ($ServerKeyExchange (print x)))))))
+
+(defun next-server-hello-done (reader)
+  (let ((x (next-$Record reader)))
+    ;;(print x)
+    (let ((x ($Record-data x)))
+      (assert (eq 'SERVER_HELLO_DONE ($Handshake-type x)))
+      (assert (not ($Handshake-data x))))))
 
+#+nil
 (let ((saved (test)))
   (with-open-file (s "/tmp/a"
                      :direction :output
@@ -685,25 +841,46 @@
     (next-$Record (rw:byte-reader s))
     #+nil(next-$ClientHello (record-reader (rw:byte-reader s)))))
 
-(with-open-stream (s (rw.socket:make-tcp-client-socket "wikipedia.org" 443))
-  (write-sequence (test) s) ;; client hello
-  (finish-output s)
-  (print
-   (list (next-$Record (rw:byte-reader s)) ;; server hello
-         (next-$Record (rw:byte-reader s)) ;; certificate
-         (next-$Record (rw:byte-reader s)) ;; server key exchange
-         (next-$Record (rw:byte-reader s)) ;; server hello done
-         ))
-  ;;TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
-  ;; client key exchange
-  (write-sequence (test2) s)
-  ;; change cipher spec
-  ;; multiple handshake messages
-  (finish-output)
-  #+nil
-  (list (next-$Record (rw:byte-reader s)) ;; new session ticket
-        (next-$Record (rw:byte-reader s)) ;; change cipher spec
-        (next-$Record (rw:byte-reader s)) ;; encrypted handshake message
-        )
-  ;; encrypted app data
-  )
+(defun packet-writer (stream)
+  (let ((b (make-octet-buffer 42)))
+    (lambda (x)
+      (case x
+        (flush
+         (write-sequence b stream)
+         (finish-output stream)
+         (setf (fill-pointer b) 0))
+        (t
+         (vector-push-extend x b)))
+      x)))
+
+(defun flush (writer)
+  (funcall writer 'flush))
+
+(defun %tls-connect (reader writer)
+  (let ((client-random (random-octets 28))
+        (client-hello-time (universal-time-to-unix (get-universal-time))))
+    (write-client-hello writer client-random client-hello-time)
+    (flush writer)
+    (let ((server-random (next-server-hello reader client-hello-time))
+          (server-certificates (next-server-certificate reader)))
+      (next-server-key-exchange reader)
+      ;; TODO certificate request
+      (next-server-hello-done reader)
+      ;; TODO certificate
+      (write-client-key-exchange writer)
+      ;; TODO certificate verify
+      ;; change cipher spec <<<<<<<<<<<<<<<<<<<<
+      ;; multiple handshake messages
+      (flush writer)
+      (next-$Record reader) ;; expecting alert handshake failure
+      #+nil
+      (list (next-$Record r) ;; new session ticket
+            (next-$Record r) ;; change cipher spec
+            (next-$Record r) ;; encrypted handshake message
+            ))))
+
+(defun tls-connect (hostname &optional (port 443))
+  (with-open-stream (s (rw.socket:make-tcp-client-socket hostname port))
+    (%tls-connect (rw:byte-reader s) (packet-writer s))))
+
+;;(tls-connect "wikipedia.org")