Derive master secret, create Finished message
authorMartin Willi <martin@revosec.ch>
Thu, 4 Feb 2010 10:17:48 +0000 (11:17 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Aug 2010 13:39:25 +0000 (15:39 +0200)
src/charon/plugins/eap_tls/tls/tls.c
src/charon/plugins/eap_tls/tls/tls_crypto.c
src/charon/plugins/eap_tls/tls/tls_crypto.h
src/charon/plugins/eap_tls/tls/tls_peer.c

index 407438a..9181eac 100644 (file)
@@ -157,10 +157,10 @@ tls_t *tls_create(bool is_server, identification_t *server,
                        .destroy = _destroy,
                },
                .is_server = is_server,
-               .crypto = tls_crypto_create(),
                .version = TLS_1_2,
        );
 
+       this->crypto = tls_crypto_create(&this->public);
        if (is_server)
        {
                this->handshake = &tls_server_create(&this->public, this->crypto,
index 10add15..6b20381 100644 (file)
@@ -15,6 +15,8 @@
 
 #include "tls_crypto.h"
 
+#include <daemon.h>
+
 typedef struct private_tls_crypto_t private_tls_crypto_t;
 
 /**
@@ -26,16 +28,43 @@ struct private_tls_crypto_t {
         * Public tls_crypto_t interface.
         */
        tls_crypto_t public;
+
+       /**
+        * List of supported/acceptable cipher suites
+        */
+       tls_cipher_suite_t *suites;
+
+       /**
+        * Number of supported suites
+        */
+       int suite_count;
+
+       /**
+        * Selected cipher suite
+        */
+       tls_cipher_suite_t suite;
+
+       /**
+        * TLS context
+        */
+       tls_t *tls;
+
+       /**
+        * Connection state TLS PRF
+        */
+       tls_prf_t *prf;
 };
 
-METHOD(tls_crypto_t, get_cipher_suites, int,
-       private_tls_crypto_t *this, tls_cipher_suite_t **suites)
+/**
+ * Initialize the cipher suite list
+ */
+static void build_cipher_suite_list(private_tls_crypto_t *this)
 {
        encryption_algorithm_t encr;
        integrity_algorithm_t mac;
        enumerator_t *encrs, *macs;
-       tls_cipher_suite_t buf[64];
-       int count = 0, i, j, res = 0;
+       tls_cipher_suite_t supported[64], unique[64];
+       int count = 0, i, j;
 
        /* we assume that we support RSA, but no DHE yet */
        macs = lib->crypto->create_signer_enumerator(lib->crypto);
@@ -44,13 +73,13 @@ METHOD(tls_crypto_t, get_cipher_suites, int,
                switch (mac)
                {
                        case AUTH_HMAC_SHA1_160:
-                               buf[count++] = TLS_RSA_WITH_NULL_SHA;
+                               supported[count++] = TLS_RSA_WITH_NULL_SHA;
                                break;
                        case AUTH_HMAC_SHA2_256_256:
-                               buf[count++] = TLS_RSA_WITH_NULL_SHA256;
+                               supported[count++] = TLS_RSA_WITH_NULL_SHA256;
                                break;
                        case AUTH_HMAC_MD5_128:
-                               buf[count++] = TLS_RSA_WITH_NULL_MD5;
+                               supported[count++] = TLS_RSA_WITH_NULL_MD5;
                                break;
                        default:
                                break;
@@ -64,12 +93,12 @@ METHOD(tls_crypto_t, get_cipher_suites, int,
                                        switch (mac)
                                        {
                                                case AUTH_HMAC_SHA1_160:
-                                                       buf[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
-                                                       buf[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+                                                       supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+                                                       supported[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
                                                        break;
                                                case AUTH_HMAC_SHA2_256_256:
-                                                       buf[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
-                                                       buf[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+                                                       supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+                                                       supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
                                                        break;
                                                default:
                                                        break;
@@ -79,7 +108,7 @@ METHOD(tls_crypto_t, get_cipher_suites, int,
                                        switch (mac)
                                        {
                                                case AUTH_HMAC_SHA1_160:
-                                                       buf[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+                                                       supported[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
                                                        break;
                                                default:
                                                        break;
@@ -94,14 +123,14 @@ METHOD(tls_crypto_t, get_cipher_suites, int,
        macs->destroy(macs);
 
        /* remove duplicates */
-       *suites = malloc(sizeof(tls_cipher_suite_t) * count);
+       this->suite_count = 0;
        for (i = 0; i < count; i++)
        {
                bool match = FALSE;
 
-               for (j = 0; j < res; j++)
+               for (j = 0; j < this->suite_count; j++)
                {
-                       if (buf[i] == (*suites)[j])
+                       if (supported[i] == unique[j])
                        {
                                match = TRUE;
                                break;
@@ -109,32 +138,122 @@ METHOD(tls_crypto_t, get_cipher_suites, int,
                }
                if (!match)
                {
-                       (*suites)[res++] = buf[i];
+                       unique[this->suite_count++] = supported[i];
+               }
+       }
+       free(this->suites);
+       this->suites = malloc(sizeof(tls_cipher_suite_t) * this->suite_count);
+       memcpy(this->suites, unique, sizeof(tls_cipher_suite_t) * this->suite_count);
+}
+
+METHOD(tls_crypto_t, get_cipher_suites, int,
+       private_tls_crypto_t *this, tls_cipher_suite_t **suites)
+{
+       *suites = this->suites;
+       return this->suite_count;
+}
+
+METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t,
+       private_tls_crypto_t *this, tls_cipher_suite_t *suites, int count)
+{
+       int i, j;
+
+       for (i = 0; i < this->suite_count; i++)
+       {
+               for (j = 0; j < count; j++)
+               {
+                       if (this->suites[i] == suites[j])
+                       {
+                               this->suite = this->suites[i];
+                               return this->suite;
+                       }
+               }
+       }
+       return 0;
+}
+
+METHOD(tls_crypto_t, derive_master_secret, void,
+       private_tls_crypto_t *this, chunk_t premaster,
+       chunk_t client_random, chunk_t server_random)
+{
+       if (!this->prf)
+       {
+               if (this->tls->get_version(this->tls) < TLS_1_2)
+               {
+                       this->prf = tls_prf_create_10();
+               }
+               else
+               {
+                       switch (this->suite)
+                       {
+                               case TLS_RSA_WITH_NULL_MD5:
+                                       this->prf = tls_prf_create_12(PRF_HMAC_MD5);
+                                       break;
+                               case TLS_RSA_WITH_AES_128_CBC_SHA:
+                               case TLS_RSA_WITH_AES_256_CBC_SHA:
+                               case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+                               case TLS_RSA_WITH_NULL_SHA:
+                                       this->prf = tls_prf_create_12(PRF_HMAC_SHA1);
+                                       break;
+                               case TLS_RSA_WITH_AES_128_CBC_SHA256:
+                               case TLS_RSA_WITH_NULL_SHA256:
+                                       this->prf = tls_prf_create_12(PRF_HMAC_SHA2_256);
+                                       break;
+                               default:
+                                       DBG1(DBG_IKE, "PRF for cipher suite unknown");
+                                       break;
+                       }
                }
        }
-       return res;
+       if (this->prf)
+       {
+               char master[48];
+               chunk_t seed;
+
+               seed = chunk_cata("cc", client_random, server_random);
+               this->prf->set_key(this->prf, premaster);
+               this->prf->get_bytes(this->prf, "master secret", seed,
+                                                        sizeof(master), master);
+
+               this->prf->set_key(this->prf, chunk_from_thing(master));
+               memset(master, 0, sizeof(master));
+       }
 }
 
+METHOD(tls_crypto_t, get_prf, tls_prf_t*,
+       private_tls_crypto_t *this)
+{
+
+       return this->prf;
+}
 
 METHOD(tls_crypto_t, destroy, void,
        private_tls_crypto_t *this)
 {
+       free(this->suites);
+       DESTROY_IF(this->prf);
        free(this);
 }
 
 /**
  * See header
  */
-tls_crypto_t *tls_crypto_create()
+tls_crypto_t *tls_crypto_create(tls_t *tls)
 {
        private_tls_crypto_t *this;
 
        INIT(this,
                .public = {
                        .get_cipher_suites = _get_cipher_suites,
+                       .select_cipher_suite = _select_cipher_suite,
+                       .derive_master_secret = _derive_master_secret,
+                       .get_prf = _get_prf,
                        .destroy = _destroy,
                },
+               .tls = tls,
        );
 
+       build_cipher_suite_list(this);
+
        return &this->public;
 }
index 538ab1d..c2cb175 100644 (file)
@@ -24,6 +24,7 @@
 typedef struct tls_crypto_t tls_crypto_t;
 
 #include "tls.h"
+#include "tls_prf.h"
 
 /**
  * TLS crypto helper functions.
@@ -33,12 +34,39 @@ struct tls_crypto_t {
        /**
         * Get a list of supported TLS cipher suites.
         *
-        * @param suites                allocated list of suites
+        * @param suites                list of suites, points to internal data
         * @return                              number of suites returned
         */
        int (*get_cipher_suites)(tls_crypto_t *this, tls_cipher_suite_t **suites);
 
        /**
+        * Select and store a cipher suite from a given list of candidates.
+        *
+        * @param suites                list of candidates to select from
+        * @param count                 number of suites
+        * @return                              selected suite, 0 if none acceptable
+        */
+       tls_cipher_suite_t (*select_cipher_suite)(tls_crypto_t *this,
+                                                                               tls_cipher_suite_t *suites, int count);
+
+       /**
+        * Derive the master secret and load it into the PRF.
+        *
+        * @param premaster             premaster secret
+        * @param client_random random data from client hello
+        * @param server_random random data from server hello
+        */
+       void (*derive_master_secret)(tls_crypto_t *this, chunk_t premaster,
+                                                                chunk_t client_random, chunk_t server_random);
+
+       /**
+        * Get the connection state PRF.
+        *
+        * @return                              PRF, NULL if not supported
+        */
+       tls_prf_t* (*get_prf)(tls_crypto_t *this);
+
+       /**
         * Destroy a tls_crypto_t.
         */
        void (*destroy)(tls_crypto_t *this);
@@ -47,6 +75,6 @@ struct tls_crypto_t {
 /**
  * Create a tls_crypto instance.
  */
-tls_crypto_t *tls_crypto_create();
+tls_crypto_t *tls_crypto_create(tls_t *tls);
 
 #endif /** TLS_CRYPTO_H_ @}*/
index 77e1f1a..5f6888e 100644 (file)
@@ -73,6 +73,16 @@ struct private_tls_peer_t {
        chunk_t handshake;
 
        /**
+        * Hello random data selected by client
+        */
+       char client_random[32];
+
+       /**
+        * Hello random data selected by server
+        */
+       char server_random[32];
+
+       /**
         * Auth helper for peer authentication
         */
        auth_cfg_t *peer_auth;
@@ -110,14 +120,13 @@ static status_t process_server_hello(private_tls_peer_t *this,
 {
        u_int8_t compression;
        u_int16_t version, cipher;
-       u_int32_t gmt;
        chunk_t random, session, ext = chunk_empty;
+       tls_cipher_suite_t suite;
 
        append_handshake(this, TLS_SERVER_HELLO, reader->peek(reader));
 
        if (!reader->read_uint16(reader, &version) ||
-               !reader->read_uint32(reader, &gmt) ||
-               !reader->read_data(reader, 28, &random) ||
+               !reader->read_data(reader, sizeof(this->server_random), &random) ||
                !reader->read_data8(reader, &session) ||
                !reader->read_uint16(reader, &cipher) ||
                !reader->read_uint8(reader, &compression) ||
@@ -126,10 +135,19 @@ static status_t process_server_hello(private_tls_peer_t *this,
                DBG1(DBG_IKE, "received invalid ServerHello");
                return FAILED;
        }
+
+       memcpy(this->server_random, random.ptr, sizeof(this->server_random));
+
        if (version < this->tls->get_version(this->tls))
        {
                this->tls->set_version(this->tls, version);
        }
+       suite = cipher;
+       if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1))
+       {
+               DBG1(DBG_IKE, "received cipher suite inacceptable");
+               return FAILED;
+       }
        return NEED_MORE;
 }
 
@@ -289,19 +307,18 @@ static status_t send_hello(private_tls_peer_t *this,
        tls_cipher_suite_t *suite;
        int count, i;
        rng_t *rng;
-       char random[28];
 
+       htoun32(&this->client_random, time(NULL));
        rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
        if (!rng)
        {
                return FAILED;
        }
-       rng->get_bytes(rng, sizeof(random), random);
+       rng->get_bytes(rng, sizeof(this->client_random) - 4, this->client_random + 4);
        rng->destroy(rng);
 
        writer->write_uint16(writer, this->tls->get_version(this->tls));
-       writer->write_uint32(writer, time(NULL));
-       writer->write_data(writer, chunk_from_thing(random));
+       writer->write_data(writer, chunk_from_thing(this->client_random));
        /* session identifier => none */
        writer->write_data8(writer, chunk_empty);
 
@@ -311,7 +328,6 @@ static status_t send_hello(private_tls_peer_t *this,
        {
                writer->write_uint16(writer, suite[i]);
        }
-       free(suite);
        /* NULL compression only */
        writer->write_uint8(writer, 1);
        writer->write_uint8(writer, 0);
@@ -399,6 +415,10 @@ static status_t send_key_exchange(private_tls_peer_t *this,
        rng->destroy(rng);
        htoun16(premaster, TLS_1_2);
 
+       this->crypto->derive_master_secret(this->crypto, chunk_from_thing(premaster),
+                                                                          chunk_from_thing(this->client_random),
+                                                                          chunk_from_thing(this->server_random));
+
        enumerator = charon->credentials->create_public_enumerator(
                                charon->credentials, KEY_ANY, this->server, this->server_auth);
        while (enumerator->enumerate(enumerator, &current, &auth))
@@ -501,9 +521,53 @@ static status_t send_certificate_verify(private_tls_peer_t *this,
 static status_t send_finished(private_tls_peer_t *this,
                                                          tls_handshake_type_t *type, tls_writer_t *writer)
 {
+       chunk_t seed;
+       tls_prf_t *prf;
+       char data[12];
+
+       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       {
+               /* TODO: use hash of cipher suite only */
+               seed = chunk_empty;
+       }
+       else
+       {
+               hasher_t *md5, *sha1;
+               char buf[HASH_SIZE_MD5 + HASH_SIZE_SHA1];
+
+               md5 = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+               if (!md5)
+               {
+                       DBG1(DBG_IKE, "unable to create %N Finished, MD5 not supported",
+                                tls_version_names, this->tls->get_version(this->tls));
+                       return FAILED;
+               }
+               md5->get_hash(md5, this->handshake, buf);
+               md5->destroy(md5);
+               sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+               if (!sha1)
+               {
+                       DBG1(DBG_IKE, "unable to sign %N Finished, SHA1 not supported",
+                                tls_version_names, this->tls->get_version(this->tls));
+                       return FAILED;
+               }
+               sha1->get_hash(sha1, this->handshake, buf + HASH_SIZE_MD5);
+               sha1->destroy(sha1);
+
+               seed = chunk_clonea(chunk_from_thing(buf));
+       }
+
+       prf = this->crypto->get_prf(this->crypto);
+       if (!prf)
+       {
+               return FAILED;
+       }
+       prf->get_bytes(prf, "client finished", seed, sizeof(data), data);
+
+       writer->write_data(writer, chunk_from_thing(data));
+
        *type = TLS_FINISHED;
        this->state = STATE_FINISHED_SENT;
-       /* TODO: finished message */
        return NEED_MORE;
 }