Refactored common used operations into TLS crypto helper
authorMartin Willi <martin@revosec.ch>
Fri, 5 Feb 2010 13:25:38 +0000 (14:25 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Aug 2010 13:39:25 +0000 (15:39 +0200)
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 5a23fb5..719003e 100644 (file)
@@ -50,6 +50,11 @@ struct private_tls_crypto_t {
        tls_t *tls;
 
        /**
+        * All handshake data concatentated
+        */
+       chunk_t handshake;
+
+       /**
         * Connection state TLS PRF
         */
        tls_prf_t *prf;
@@ -346,7 +351,128 @@ METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t,
        return 0;
 }
 
-METHOD(tls_crypto_t, derive_master_secret, void,
+METHOD(tls_crypto_t, append_handshake, void,
+       private_tls_crypto_t *this, tls_handshake_type_t type, chunk_t data)
+{
+       u_int32_t header;
+
+       /* reconstruct handshake header */
+       header = htonl(data.len | (type << 24));
+       this->handshake = chunk_cat("mcc", this->handshake,
+                                                               chunk_from_thing(header), data);
+}
+
+/**
+ * Create a hash of the stored handshake data
+ */
+static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash)
+{
+       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       {
+               hasher_t *hasher;
+               suite_algs_t *alg;
+
+               alg = find_suite(this->suite);
+               if (!alg)
+               {
+                       return FALSE;
+               }
+               hasher = lib->crypto->create_hasher(lib->crypto, alg->hash);
+               if (!hasher)
+               {
+                       DBG1(DBG_IKE, "%N not supported", hash_algorithm_names, alg->hash);
+                       return FALSE;
+               }
+               hasher->allocate_hash(hasher, this->handshake, hash);
+               hasher->destroy(hasher);
+       }
+       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, "%N not supported", hash_algorithm_names, HASH_MD5);
+                       return FALSE;
+               }
+               md5->get_hash(md5, this->handshake, buf);
+               md5->destroy(md5);
+               sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+               if (!sha1)
+               {
+                       DBG1(DBG_IKE, "%N not supported", hash_algorithm_names, HASH_SHA1);
+                       return FALSE;
+               }
+               sha1->get_hash(sha1, this->handshake, buf + HASH_SIZE_MD5);
+               sha1->destroy(sha1);
+
+               *hash = chunk_clone(chunk_from_thing(buf));
+       }
+       return TRUE;
+}
+
+METHOD(tls_crypto_t, sign_handshake, bool,
+       private_tls_crypto_t *this, private_key_t *key, chunk_t *sig)
+{
+       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       {
+               u_int16_t length;
+               u_int8_t hash_alg;
+               u_int8_t sig_alg;
+
+               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
+               {
+                       return FALSE;
+               }
+               /* TODO: signature scheme to hashsign algorithm mapping */
+               hash_alg = 2; /* sha1 */
+               sig_alg = 1; /* RSA */
+               length = htons(sig->len);
+               *sig = chunk_cat("cccm", chunk_from_thing(hash_alg),
+                                       chunk_from_thing(sig_alg), chunk_from_thing(length), *sig);
+       }
+       else
+       {
+               u_int16_t length;
+               chunk_t hash;
+
+               if (!hash_handshake(this, &hash))
+               {
+                       return FALSE;
+               }
+               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
+               {
+                       free(hash.ptr);
+                       return FALSE;
+               }
+               free(hash.ptr);
+               length = htons(sig->len);
+               *sig = chunk_cat("cm", chunk_from_thing(length), *sig);
+       }
+       return TRUE;
+}
+
+METHOD(tls_crypto_t, calculate_finished, bool,
+       private_tls_crypto_t *this, char *label, char out[12])
+{
+       chunk_t seed;
+
+       if (!this->prf)
+       {
+               return FALSE;
+       }
+       if (!hash_handshake(this, &seed))
+       {
+               return FALSE;
+       }
+       this->prf->get_bytes(this->prf, label, seed, 12, out);
+       free(seed.ptr);
+       return TRUE;
+}
+
+METHOD(tls_crypto_t, derive_secrets, void,
        private_tls_crypto_t *this, chunk_t premaster,
        chunk_t client_random, chunk_t server_random)
 {
@@ -363,11 +489,6 @@ METHOD(tls_crypto_t, derive_master_secret, void,
        this->prf->set_key(this->prf, chunk_from_thing(master));
        memset(master, 0, sizeof(master));
 
-       /* MSK for EAP-TLS */
-       this->msk = chunk_alloc(64);
-       this->prf->get_bytes(this->prf, "client EAP encryption", seed,
-                                                this->msk.len, this->msk.ptr);
-
        /* derive key block for key expansion */
        mks = this->signer_out->get_key_size(this->signer_out);
        if (this->crypter_out)
@@ -452,10 +573,16 @@ METHOD(tls_crypto_t, change_cipher, void,
        }
 }
 
-METHOD(tls_crypto_t, get_prf, tls_prf_t*,
-       private_tls_crypto_t *this)
+METHOD(tls_crypto_t, derive_eap_msk, void,
+       private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random)
 {
-       return this->prf;
+       chunk_t seed;
+
+       seed = chunk_cata("cc", client_random, server_random);
+       free(this->msk.ptr);
+       this->msk = chunk_alloc(64);
+       this->prf->get_bytes(this->prf, "client EAP encryption", seed,
+                                                this->msk.len, this->msk.ptr);
 }
 
 METHOD(tls_crypto_t, get_eap_msk, chunk_t,
@@ -473,6 +600,7 @@ METHOD(tls_crypto_t, destroy, void,
        DESTROY_IF(this->crypter_out);
        free(this->iv_in.ptr);
        free(this->iv_out.ptr);
+       free(this->handshake.ptr);
        free(this->msk.ptr);
        DESTROY_IF(this->prf);
        free(this->suites);
@@ -490,9 +618,12 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                .public = {
                        .get_cipher_suites = _get_cipher_suites,
                        .select_cipher_suite = _select_cipher_suite,
-                       .derive_master_secret = _derive_master_secret,
+                       .append_handshake = _append_handshake,
+                       .sign_handshake = _sign_handshake,
+                       .calculate_finished = _calculate_finished,
+                       .derive_secrets = _derive_secrets,
                        .change_cipher = _change_cipher,
-                       .get_prf = _get_prf,
+                       .derive_eap_msk = _derive_eap_msk,
                        .get_eap_msk = _get_eap_msk,
                        .destroy = _destroy,
                },
index 4b29652..e3363cd 100644 (file)
@@ -26,6 +26,8 @@ typedef struct tls_crypto_t tls_crypto_t;
 #include "tls.h"
 #include "tls_prf.h"
 
+#include <credentials/keys/private_key.h>
+
 /**
  * TLS crypto helper functions.
  */
@@ -50,14 +52,41 @@ struct tls_crypto_t {
                                                                                tls_cipher_suite_t *suites, int count);
 
        /**
-        * Derive the master secret and load it into the PRF.
+        * Store exchanged handshake data, used for cryptographic operations.
+        *
+        * @param type                  handshake sub type
+        * @param data                  data to append to handshake buffer
+        */
+       void (*append_handshake)(tls_crypto_t *this,
+                                                        tls_handshake_type_t type, chunk_t data);
+
+       /**
+        * Create a signature of the handshake data using a given private key.
+        *
+        * @param key                   private key to use for signature
+        * @param sig                   allocated signature
+        * @return                              TRUE if signature create successfully
+        */
+       bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, chunk_t *sig);
+
+       /**
+        * Calculate the data of a TLS finished message.
+        *
+        * @param label                 ASCII label to use for calculation
+        * @param out                   buffer to write finished data to
+        * @return                              TRUE if calculation successful
+        */
+       bool (*calculate_finished)(tls_crypto_t *this, char *label, char out[12]);
+
+       /**
+        * Derive the master secret, MAC and encryption keys.
         *
         * @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);
+       void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster,
+                                                  chunk_t client_random, chunk_t server_random);
 
        /**
         * Change the cipher used at protection layer.
@@ -67,11 +96,13 @@ struct tls_crypto_t {
        void (*change_cipher)(tls_crypto_t *this, bool inbound);
 
        /**
-        * Get the connection state PRF.
+        * Derive the EAP-TLS MSK.
         *
-        * @return                              PRF, NULL if not supported
+        * @param client_random random data from client hello
+        * @param server_random random data from server hello
         */
-       tls_prf_t* (*get_prf)(tls_crypto_t *this);
+       void (*derive_eap_msk)(tls_crypto_t *this,
+                                                  chunk_t client_random, chunk_t server_random);
 
        /**
         * Get the MSK to use in EAP-TLS.
index 2c96d02..6272b90 100644 (file)
@@ -70,11 +70,6 @@ struct private_tls_peer_t {
        peer_state_t state;
 
        /**
-        * All handshake data concatentated
-        */
-       chunk_t handshake;
-
-       /**
         * Hello random data selected by client
         */
        char client_random[32];
@@ -101,20 +96,6 @@ struct private_tls_peer_t {
 };
 
 /**
- * Append a handshake message to the handshake data buffer
- */
-static void append_handshake(private_tls_peer_t *this,
-                                                        tls_handshake_type_t type, chunk_t data)
-{
-       u_int32_t header;
-
-       /* reconstruct handshake header */
-       header = htonl(data.len | (type << 24));
-       this->handshake = chunk_cat("mcc", this->handshake,
-                                                               chunk_from_thing(header), data);
-}
-
-/**
  * Process a server hello message
  */
 static status_t process_server_hello(private_tls_peer_t *this,
@@ -125,7 +106,8 @@ static status_t process_server_hello(private_tls_peer_t *this,
        chunk_t random, session, ext = chunk_empty;
        tls_cipher_suite_t suite;
 
-       append_handshake(this, TLS_SERVER_HELLO, reader->peek(reader));
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_SERVER_HELLO, reader->peek(reader));
 
        if (!reader->read_uint16(reader, &version) ||
                !reader->read_data(reader, sizeof(this->server_random), &random) ||
@@ -164,7 +146,8 @@ static status_t process_certificate(private_tls_peer_t *this,
        chunk_t data;
        bool first = TRUE;
 
-       append_handshake(this, TLS_CERTIFICATE, reader->peek(reader));
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_CERTIFICATE, reader->peek(reader));
 
        if (!reader->read_data24(reader, &data))
        {
@@ -217,7 +200,8 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader)
        identification_t *id;
        certificate_t *cert;
 
-       append_handshake(this, TLS_CERTIFICATE_REQUEST, reader->peek(reader));
+       this->crypto->append_handshake(this->crypto,
+                                                               TLS_CERTIFICATE_REQUEST, reader->peek(reader));
 
        if (!reader->read_data8(reader, &types))
        {
@@ -267,7 +251,8 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader)
 static status_t process_hello_done(private_tls_peer_t *this,
                                                                   tls_reader_t *reader)
 {
-       append_handshake(this, TLS_SERVER_HELLO_DONE, reader->peek(reader));
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_SERVER_HELLO_DONE, reader->peek(reader));
        this->state = STATE_HELLO_DONE;
        return NEED_MORE;
 }
@@ -277,61 +262,28 @@ static status_t process_hello_done(private_tls_peer_t *this,
  */
 static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader)
 {
-       chunk_t seed, received;
-       tls_prf_t *prf;
-       char data[12];
+       chunk_t received;
+       char buf[12];
 
-       if (!reader->read_data(reader, sizeof(data), &received))
+       if (!reader->read_data(reader, sizeof(buf), &received))
        {
                DBG1(DBG_IKE, "received server finished too short");
                return FAILED;
        }
-
-       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)
+       if (!this->crypto->calculate_finished(this->crypto, "server finished", buf))
        {
+               DBG1(DBG_IKE, "calculating server finished failed");
                return FAILED;
        }
-       prf->get_bytes(prf, "server finished", seed, sizeof(data), data);
-
-       if (!chunk_equals(received, chunk_from_thing(data)))
+       if (!chunk_equals(received, chunk_from_thing(buf)))
        {
                DBG1(DBG_IKE, "received server finished invalid");
                return FAILED;
        }
        this->state = STATE_COMPLETE;
+       this->crypto->derive_eap_msk(this->crypto,
+                                                                chunk_from_thing(this->client_random),
+                                                                chunk_from_thing(this->server_random));
        return NEED_MORE;
 }
 
@@ -408,7 +360,7 @@ static status_t send_hello(private_tls_peer_t *this,
 
        *type = TLS_CLIENT_HELLO;
        this->state = STATE_HELLO_SENT;
-       append_handshake(this, *type, writer->get_buf(writer));
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;
 }
 
@@ -462,7 +414,7 @@ static status_t send_certificate(private_tls_peer_t *this,
 
        *type = TLS_CERTIFICATE;
        this->state = STATE_CERT_SENT;
-       append_handshake(this, *type, writer->get_buf(writer));
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;
 }
 
@@ -489,9 +441,9 @@ 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));
+       this->crypto->derive_secrets(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);
@@ -520,7 +472,7 @@ static status_t send_key_exchange(private_tls_peer_t *this,
 
        *type = TLS_CLIENT_KEY_EXCHANGE;
        this->state = STATE_KEY_EXCHANGE_SENT;
-       append_handshake(this, *type, writer->get_buf(writer));
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;
 }
 
@@ -532,60 +484,18 @@ static status_t send_certificate_verify(private_tls_peer_t *this,
 {
        chunk_t signature;
 
-       if (!this->private)
+       if (!this->private ||
+               !this->crypto->sign_handshake(this->crypto, this->private, &signature))
        {
+               DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed");
                return FAILED;
        }
-
-       if (this->tls->get_version(this->tls) >= TLS_1_2)
-       {
-               if (!this->private->sign(this->private, SIGN_RSA_EMSA_PKCS1_SHA1,
-                                                                this->handshake, &signature))
-               {
-                       DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed");
-                       return FAILED;
-               }
-               /* TODO: signature scheme to hashsign algorithm mapping */
-               writer->write_uint8(writer, 2); /* sha1 */
-               writer->write_uint8(writer, 1); /* RSA */
-       }
-       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 sign %N Verify, 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 Verify, 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);
-
-               if (!this->private->sign(this->private, SIGN_RSA_EMSA_PKCS1_NULL,
-                                                                chunk_from_thing(buf), &signature))
-               {
-                       DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed");
-                       return FAILED;
-               }
-       }
-       writer->write_data16(writer, signature);
+       writer->write_data(writer, signature);
        free(signature.ptr);
 
        *type = TLS_CERTIFICATE_VERIFY;
        this->state = STATE_VERIFY_SENT;
-       append_handshake(this, *type, writer->get_buf(writer));
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;
 }
 
@@ -595,54 +505,19 @@ 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));
-       }
+       char buf[12];
 
-       prf = this->crypto->get_prf(this->crypto);
-       if (!prf)
+       if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
        {
+               DBG1(DBG_IKE, "calculating client finished data failed");
                return FAILED;
        }
-       prf->get_bytes(prf, "client finished", seed, sizeof(data), data);
 
-       writer->write_data(writer, chunk_from_thing(data));
+       writer->write_data(writer, chunk_from_thing(buf));
 
        *type = TLS_FINISHED;
        this->state = STATE_FINISHED_SENT;
-       append_handshake(this, *type, writer->get_buf(writer));
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
        return NEED_MORE;
 }
 
@@ -696,7 +571,6 @@ METHOD(tls_handshake_t, destroy, void,
        private_tls_peer_t *this)
 {
        DESTROY_IF(this->private);
-       free(this->handshake.ptr);
        this->peer_auth->destroy(this->peer_auth);
        this->server_auth->destroy(this->server_auth);
        free(this);