Send a ClientHello to start TLS negotiation
authorMartin Willi <martin@revosec.ch>
Mon, 25 Jan 2010 11:23:59 +0000 (11:23 +0000)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Aug 2010 13:39:24 +0000 (15:39 +0200)
src/charon/plugins/eap_tls/tls/tls_peer.c

index 737f9b3..17f19f1 100644 (file)
 
 typedef struct private_tls_peer_t private_tls_peer_t;
 
+typedef enum {
+       STATE_INIT,
+       STATE_HELLO_SENT,
+} peer_state_t;
+
 /**
  * Private data of an tls_peer_t object.
  */
@@ -35,6 +40,11 @@ struct private_tls_peer_t {
         * TLS crypto context
         */
        tls_crypto_t *crypto;
+
+       /**
+        * State we are in
+        */
+       peer_state_t state;
 };
 
 METHOD(tls_handshake_t, process, status_t,
@@ -43,10 +53,84 @@ METHOD(tls_handshake_t, process, status_t,
        return NEED_MORE;
 }
 
+/**
+ * Build the Client Hello using a given set of ciphers
+ */
+static chunk_t build_hello(int count, tls_cipher_suite_t *suite, rng_t *rng)
+{
+       int i;
+
+       struct __attribute__((packed)) {
+               u_int16_t version;
+               struct __attribute__((packed)) {
+                       u_int32_t gmt;
+                       u_int8_t bytes[28];
+               } random;
+               struct __attribute__((packed)) {
+                       /* never send a session identifier */
+                       u_int8_t len;
+                       u_int8_t id[0];
+               } session;
+               struct __attribute__((packed)) {
+                       u_int16_t len;
+                       u_int16_t suite[count];
+               } cipher;
+               struct __attribute__((packed)) {
+                       /* currently NULL compression only */
+                       u_int8_t len;
+                       u_int8_t method[1];
+               } compression;
+               u_int8_t extensions[0];
+       } hello;
+
+       htoun16(&hello.session.len, 0);
+       htoun16(&hello.version, TLS_1_2);
+       htoun32(&hello.random.gmt, time(NULL));
+       rng->get_bytes(rng, sizeof(hello.random.bytes), (char*)&hello.random.bytes);
+       htoun16(&hello.cipher.len, count * 2);
+       for (i = 0; i < count; i++)
+       {
+               htoun16(&hello.cipher.suite[i], suite[i]);
+       }
+       hello.compression.len = 1;
+       hello.compression.method[0] = 0;
+       return chunk_clone(chunk_create((char*)&hello, sizeof(hello)));
+}
+
+/**
+ * Send a client hello
+ */
+static status_t send_hello(private_tls_peer_t *this,
+                                                  tls_handshake_type_t *type, chunk_t *data)
+{
+       tls_cipher_suite_t *suite;
+       int count;
+       rng_t *rng;
+
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng)
+       {
+               return FAILED;
+       }
+       count = this->crypto->get_cipher_suites(this->crypto, &suite);
+       *data = build_hello(count, suite, rng);
+       *type = TLS_CLIENT_HELLO;
+       free(suite);
+       rng->destroy(rng);
+       return NEED_MORE;
+}
+
 METHOD(tls_handshake_t, build, status_t,
        private_tls_peer_t *this, tls_handshake_type_t *type, chunk_t *data)
 {
-       return INVALID_STATE;
+       switch (this->state)
+       {
+               case STATE_INIT:
+                       this->state = STATE_HELLO_SENT;
+                       return send_hello(this, type, data);
+               default:
+                       return INVALID_STATE;
+       }
 }
 
 METHOD(tls_handshake_t, destroy, void,
@@ -68,6 +152,7 @@ tls_peer_t *tls_peer_create(tls_crypto_t *crypto)
                        .build = _build,
                        .destroy = _destroy,
                },
+               .state = STATE_INIT,
                .crypto = crypto,
        );