diff options
author | Martin Willi <martin@revosec.ch> | 2010-02-04 11:17:48 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2010-08-03 15:39:25 +0200 |
commit | 18010de23dfa057e53cf047ba02c2a97c4483a35 (patch) | |
tree | 68676f4eabfb1e506fa46b369ff88e36efac3382 | |
parent | 149b7e6d015cdad60cbbef68faa422b5e02a99af (diff) | |
download | strongswan-18010de23dfa057e53cf047ba02c2a97c4483a35.tar.bz2 strongswan-18010de23dfa057e53cf047ba02c2a97c4483a35.tar.xz |
Derive master secret, create Finished message
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls.c | 2 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls_crypto.c | 155 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls_crypto.h | 32 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls_peer.c | 82 |
4 files changed, 241 insertions, 30 deletions
diff --git a/src/charon/plugins/eap_tls/tls/tls.c b/src/charon/plugins/eap_tls/tls/tls.c index 407438a4b..9181eacd1 100644 --- a/src/charon/plugins/eap_tls/tls/tls.c +++ b/src/charon/plugins/eap_tls/tls/tls.c @@ -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, diff --git a/src/charon/plugins/eap_tls/tls/tls_crypto.c b/src/charon/plugins/eap_tls/tls/tls_crypto.c index 10add151c..6b2038198 100644 --- a/src/charon/plugins/eap_tls/tls/tls_crypto.c +++ b/src/charon/plugins/eap_tls/tls/tls_crypto.c @@ -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; } diff --git a/src/charon/plugins/eap_tls/tls/tls_crypto.h b/src/charon/plugins/eap_tls/tls/tls_crypto.h index 538ab1de0..c2cb1757f 100644 --- a/src/charon/plugins/eap_tls/tls/tls_crypto.h +++ b/src/charon/plugins/eap_tls/tls/tls_crypto.h @@ -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_ @}*/ diff --git a/src/charon/plugins/eap_tls/tls/tls_peer.c b/src/charon/plugins/eap_tls/tls/tls_peer.c index 77e1f1a7f..5f6888eaa 100644 --- a/src/charon/plugins/eap_tls/tls/tls_peer.c +++ b/src/charon/plugins/eap_tls/tls/tls_peer.c @@ -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, ¤t, &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; } |