diff options
author | Martin Willi <martin@revosec.ch> | 2010-02-05 14:25:38 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2010-08-03 15:39:25 +0200 |
commit | 84d67ead4e673bb9eaf43836f9b25ea3ad638cfe (patch) | |
tree | 2f982b3059b93393677abd35f53aa7dfe2ffa1ba | |
parent | 3e7e7779414f17ebf735e12e19c99ca1e8235ac7 (diff) | |
download | strongswan-84d67ead4e673bb9eaf43836f9b25ea3ad638cfe.tar.bz2 strongswan-84d67ead4e673bb9eaf43836f9b25ea3ad638cfe.tar.xz |
Refactored common used operations into TLS crypto helper
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls_crypto.c | 153 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls_crypto.h | 43 | ||||
-rw-r--r-- | src/charon/plugins/eap_tls/tls/tls_peer.c | 192 |
3 files changed, 212 insertions, 176 deletions
diff --git a/src/charon/plugins/eap_tls/tls/tls_crypto.c b/src/charon/plugins/eap_tls/tls/tls_crypto.c index 5a23fb559..719003e05 100644 --- a/src/charon/plugins/eap_tls/tls/tls_crypto.c +++ b/src/charon/plugins/eap_tls/tls/tls_crypto.c @@ -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, }, diff --git a/src/charon/plugins/eap_tls/tls/tls_crypto.h b/src/charon/plugins/eap_tls/tls/tls_crypto.h index 4b29652a8..e3363cde9 100644 --- a/src/charon/plugins/eap_tls/tls/tls_crypto.h +++ b/src/charon/plugins/eap_tls/tls/tls_crypto.h @@ -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. diff --git a/src/charon/plugins/eap_tls/tls/tls_peer.c b/src/charon/plugins/eap_tls/tls/tls_peer.c index 2c96d0267..6272b90e4 100644 --- a/src/charon/plugins/eap_tls/tls/tls_peer.c +++ b/src/charon/plugins/eap_tls/tls/tls_peer.c @@ -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); |