diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 04cebb672..b14abc530 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -74,6 +74,7 @@ sa/ike_sa.c sa/ike_sa.h \ sa/ike_sa_id.c sa/ike_sa_id.h \ sa/ike_sa_manager.c sa/ike_sa_manager.h \ sa/task_manager.c sa/task_manager.h \ +sa/keymat.c sa/keymat.h \ sa/tasks/child_create.c sa/tasks/child_create.h \ sa/tasks/child_delete.c sa/tasks/child_delete.h \ sa/tasks/child_rekey.c sa/tasks/child_rekey.h \ diff --git a/src/charon/plugins/stroke/stroke_list.c b/src/charon/plugins/stroke/stroke_list.c index 555239fb5..b4963a7ad 100644 --- a/src/charon/plugins/stroke/stroke_list.c +++ b/src/charon/plugins/stroke/stroke_list.c @@ -79,14 +79,18 @@ static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) if (all) { - proposal_t *ike_proposal = ike_sa->get_proposal(ike_sa); - + keymat_t *keymat; + proposal_t *ike_proposal; + + keymat = ike_sa->get_keymat(ike_sa); + ike_proposal = keymat->get_proposal(keymat); + fprintf(out, "%12s[%d]: IKE SPIs: %.16llx_i%s %.16llx_r%s", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), id->get_initiator_spi(id), id->is_initiator(id) ? "*" : "", id->get_responder_spi(id), id->is_initiator(id) ? "" : "*"); - - + + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) { u_int32_t rekey, reauth, now; @@ -112,7 +116,7 @@ static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) } } fprintf(out, "\n"); - + if (ike_proposal) { char buf[BUF_LEN]; @@ -121,7 +125,7 @@ static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) fprintf(out, "%12s[%d]: IKE proposal: %s\n", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), buf+4); - } + } } } diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c index 04204cb0f..70d56f074 100644 --- a/src/charon/sa/authenticators/eap_authenticator.c +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Martin Willi + * Copyright (C) 2006-2008 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -70,36 +70,24 @@ struct private_eap_authenticator_t { */ u_int32_t vendor; }; - -/** - * reuse shared key signature function from PSK authenticator - */ -extern chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, - chunk_t secret, identification_t *id, - chunk_t skp, prf_t *prf); /** * Implementation of authenticator_t.verify. */ static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init, chunk_t my_nonce, auth_payload_t *auth_payload) { - chunk_t auth_data, recv_auth_data, secret; - identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa); + chunk_t auth_data, recv_auth_data; + identification_t *other_id; + keymat_t *keymat; - if (this->msk.len) - { /* use MSK if EAP method established one... */ - secret = this->msk; - } - else - { /* ... or use SKp if not */ - secret = this->ike_sa->get_skp_verify(this->ike_sa); - } - auth_data = build_shared_key_signature(ike_sa_init, my_nonce, secret, - other_id, this->ike_sa->get_skp_verify(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa)); + other_id = this->ike_sa->get_other_id(this->ike_sa); + keymat = this->ike_sa->get_keymat(this->ike_sa); + + auth_data = keymat->get_psk_sig(keymat, TRUE, ike_sa_init, my_nonce, + this->msk, other_id); recv_auth_data = auth_payload->get_data(auth_payload); - if (!chunk_equals(auth_data, recv_auth_data)) + if (!auth_data.len || !chunk_equals(auth_data, recv_auth_data)) { DBG1(DBG_IKE, "verification of AUTH payload created from EAP MSK failed"); chunk_free(&auth_data); @@ -118,23 +106,18 @@ static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init, static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init, chunk_t other_nonce, auth_payload_t **auth_payload) { - chunk_t auth_data, secret; - identification_t *my_id = this->ike_sa->get_my_id(this->ike_sa); + identification_t *my_id; + chunk_t auth_data; + keymat_t *keymat; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + keymat = this->ike_sa->get_keymat(this->ike_sa); DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", my_id, auth_class_names, AUTH_CLASS_EAP); - - if (this->msk.len) - { /* use MSK if EAP method established one... */ - secret = this->msk; - } - else - { /* ... or use SKp if not */ - secret = this->ike_sa->get_skp_build(this->ike_sa); - } - auth_data = build_shared_key_signature(ike_sa_init, other_nonce, secret, - my_id, this->ike_sa->get_skp_build(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa)); + + auth_data = keymat->get_psk_sig(keymat, FALSE, ike_sa_init, other_nonce, + this->msk, my_id); *auth_payload = auth_payload_create(); (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c index 9094077e4..a3c84c491 100644 --- a/src/charon/sa/authenticators/psk_authenticator.c +++ b/src/charon/sa/authenticators/psk_authenticator.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -23,12 +23,6 @@ #include #include -/** - * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. - */ -#define IKEV2_KEY_PAD "Key Pad for IKEv2" -#define IKEV2_KEY_PAD_LENGTH 17 - typedef struct private_psk_authenticator_t private_psk_authenticator_t; @@ -48,55 +42,6 @@ struct private_psk_authenticator_t { ike_sa_t *ike_sa; }; -/** - * Builds the octets to be signed as described in section 2.15 of RFC 4306 - */ -chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, - identification_t *id, prf_t *prf) -{ - u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00}; - chunk_t id_header = chunk_from_buf(id_header_buf); - chunk_t id_with_header, id_prfd, id_encoding; - - id_header_buf[0] = id->get_type(id); - id_encoding = id->get_encoding(id); - - id_with_header = chunk_cat("cc", id_header, id_encoding); - prf->allocate_bytes(prf, id_with_header, &id_prfd); - chunk_free(&id_with_header); - - return chunk_cat("ccm", ike_sa_init, nonce, id_prfd); -} - -/** - * Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE. - */ -chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, - chunk_t secret, identification_t *id, - chunk_t skp, prf_t *prf) -{ - chunk_t key_pad, key, auth_data, octets; - - prf->set_key(prf, skp); - octets = build_tbs_octets(ike_sa_init, nonce, id, prf); - /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), ) */ - key_pad.ptr = IKEV2_KEY_PAD; - key_pad.len = IKEV2_KEY_PAD_LENGTH; - prf->set_key(prf, secret); - prf->allocate_bytes(prf, key_pad, &key); - prf->set_key(prf, key); - prf->allocate_bytes(prf, octets, &auth_data); - DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets); - DBG3(DBG_IKE, "secret %B", &secret); - DBG3(DBG_IKE, "keypad %B", &key_pad); - DBG3(DBG_IKE, "prf(secret, keypad) %B", &key); - DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &auth_data); - chunk_free(&octets); - chunk_free(&key); - - return auth_data; -} - /** * Implementation of authenticator_t.verify. */ @@ -105,25 +50,25 @@ static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, { chunk_t auth_data, recv_auth_data; identification_t *my_id, *other_id; - shared_key_t *shared_key; + shared_key_t *key; enumerator_t *enumerator; bool authenticated = FALSE; int keys_found = 0; + keymat_t *keymat; + keymat = this->ike_sa->get_keymat(this->ike_sa); + recv_auth_data = auth_payload->get_data(auth_payload); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); enumerator = charon->credentials->create_shared_enumerator( charon->credentials, SHARED_IKE, my_id, other_id); - while (!authenticated && enumerator->enumerate(enumerator, &shared_key, NULL, NULL)) + while (!authenticated && enumerator->enumerate(enumerator, &key, NULL, NULL)) { keys_found++; - auth_data = build_shared_key_signature(ike_sa_init, my_nonce, - shared_key->get_key(shared_key), other_id, - this->ike_sa->get_skp_verify(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa)); - recv_auth_data = auth_payload->get_data(auth_payload); - if (auth_data.len == recv_auth_data.len && - memeq(auth_data.ptr, recv_auth_data.ptr, auth_data.len)) + + auth_data = keymat->get_psk_sig(keymat, TRUE, ike_sa_init, my_nonce, + key->get_key(key), other_id); + if (auth_data.len && chunk_equals(auth_data, recv_auth_data)) { DBG1(DBG_IKE, "authentication of '%D' with %N successful", other_id, auth_method_names, AUTH_PSK); @@ -153,26 +98,26 @@ static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init, chunk_t other_nonce, auth_payload_t **auth_payload) { - shared_key_t *shared_key; - chunk_t auth_data; identification_t *my_id, *other_id; + shared_key_t *key; + chunk_t auth_data; + keymat_t *keymat; + keymat = this->ike_sa->get_keymat(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", my_id, auth_method_names, AUTH_PSK); - shared_key = charon->credentials->get_shared(charon->credentials, SHARED_IKE, - my_id, other_id); - if (shared_key == NULL) + key = charon->credentials->get_shared(charon->credentials, SHARED_IKE, + my_id, other_id); + if (key == NULL) { DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); return NOT_FOUND; } - auth_data = build_shared_key_signature(ike_sa_init, other_nonce, - shared_key->get_key(shared_key), my_id, - this->ike_sa->get_skp_build(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa)); - shared_key->destroy(shared_key); + auth_data = keymat->get_psk_sig(keymat, FALSE, ike_sa_init, other_nonce, + key->get_key(key), my_id); + key->destroy(key); DBG2(DBG_IKE, "successfully created shared key MAC"); *auth_payload = auth_payload_create(); (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); diff --git a/src/charon/sa/authenticators/pubkey_authenticator.c b/src/charon/sa/authenticators/pubkey_authenticator.c index 46e0be4c0..49a4283d2 100644 --- a/src/charon/sa/authenticators/pubkey_authenticator.c +++ b/src/charon/sa/authenticators/pubkey_authenticator.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Tobias Brunner - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -43,12 +43,6 @@ struct private_pubkey_authenticator_t { ike_sa_t *ike_sa; }; -/** - * Function implemented in psk_authenticator.c - */ -extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, - identification_t *id, prf_t *prf); - /** * Implementation of authenticator_t.verify. */ @@ -58,15 +52,15 @@ static status_t verify(private_pubkey_authenticator_t *this, chunk_t ike_sa_init public_key_t *public; auth_method_t auth_method; chunk_t auth_data, octets; - identification_t *other_id; - prf_t *prf; + identification_t *id; auth_info_t *auth, *current_auth; enumerator_t *enumerator; key_type_t key_type = KEY_ECDSA; signature_scheme_t scheme; status_t status = FAILED; + keymat_t *keymat; - other_id = this->ike_sa->get_other_id(this->ike_sa); + id = this->ike_sa->get_other_id(this->ike_sa); auth_method = auth_payload->get_auth_method(auth_payload); switch (auth_method) { @@ -89,19 +83,17 @@ static status_t verify(private_pubkey_authenticator_t *this, chunk_t ike_sa_init return INVALID_ARG; } auth_data = auth_payload->get_data(auth_payload); - prf = this->ike_sa->get_prf(this->ike_sa); - prf->set_key(prf, this->ike_sa->get_skp_verify(this->ike_sa)); - octets = build_tbs_octets(ike_sa_init, my_nonce, other_id, prf); - + keymat = this->ike_sa->get_keymat(this->ike_sa); + octets = keymat->get_auth_octets(keymat, TRUE, ike_sa_init, my_nonce, id); auth = this->ike_sa->get_other_auth(this->ike_sa); enumerator = charon->credentials->create_public_enumerator( - charon->credentials, key_type, other_id, auth); + charon->credentials, key_type, id, auth); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, scheme, octets, auth_data)) { DBG1(DBG_IKE, "authentication of '%D' with %N successful", - other_id, auth_method_names, auth_method); + id, auth_method_names, auth_method); status = SUCCESS; auth->merge(auth, current_auth); break; @@ -125,19 +117,19 @@ static status_t build(private_pubkey_authenticator_t *this, chunk_t ike_sa_init, chunk_t octets, auth_data; status_t status = FAILED; private_key_t *private; - identification_t *my_id; - prf_t *prf; + identification_t *id; auth_info_t *auth; auth_method_t auth_method; signature_scheme_t scheme; + keymat_t *keymat; - my_id = this->ike_sa->get_my_id(this->ike_sa); + id = this->ike_sa->get_my_id(this->ike_sa); auth = this->ike_sa->get_my_auth(this->ike_sa); private = charon->credentials->get_private(charon->credentials, KEY_ANY, - my_id, auth); + id, auth); if (private == NULL) { - DBG1(DBG_IKE, "no private key found for '%D'", my_id); + DBG1(DBG_IKE, "no private key found for '%D'", id); return NOT_FOUND; } @@ -176,9 +168,8 @@ static status_t build(private_pubkey_authenticator_t *this, chunk_t ike_sa_init, key_type_names, private->get_type(private)); return status; } - prf = this->ike_sa->get_prf(this->ike_sa); - prf->set_key(prf, this->ike_sa->get_skp_build(this->ike_sa)); - octets = build_tbs_octets(ike_sa_init, other_nonce, my_id, prf); + keymat = this->ike_sa->get_keymat(this->ike_sa); + octets = keymat->get_auth_octets(keymat, FALSE, ike_sa_init, other_nonce, id); if (private->sign(private, scheme, octets, &auth_data)) { @@ -189,9 +180,9 @@ static status_t build(private_pubkey_authenticator_t *this, chunk_t ike_sa_init, chunk_free(&auth_data); status = SUCCESS; } - DBG1(DBG_IKE, "authentication of '%D' (myself) with %N %s", my_id, - auth_method_names, auth_method, - (status == SUCCESS)? "successful":"failed"); + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N %s", id, + auth_method_names, auth_method, + (status == SUCCESS)? "successful":"failed"); chunk_free(&octets); private->destroy(private); diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index d4ee4bb35..5f02157a0 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -191,49 +191,9 @@ struct private_ike_sa_t { linked_list_t *child_sas; /** - * Selected IKE proposal + * keymat of this IKE_SA */ - proposal_t *selected_proposal; - - /** - * crypter for inbound traffic - */ - crypter_t *crypter_in; - - /** - * crypter for outbound traffic - */ - crypter_t *crypter_out; - - /** - * Signer for inbound traffic - */ - signer_t *signer_in; - - /** - * Signer for outbound traffic - */ - signer_t *signer_out; - - /** - * Multi purpose prf, set key, use it, forget it - */ - prf_t *prf; - - /** - * Prf function for derivating keymat child SAs - */ - prf_t *child_prf; - - /** - * Key to build outging authentication data (SKp) - */ - chunk_t skp_build; - - /** - * Key to verify incoming authentication data (SKp) - */ - chunk_t skp_verify; + keymat_t *keymat; /** * Virtual IP on local host, if any @@ -742,6 +702,14 @@ static void reset(private_ike_sa_t *this) this->task_manager->reset(this->task_manager); } +/** + * Implementation of ike_sa_t.get_keymat + */ +static keymat_t* get_keymat(private_ike_sa_t *this) +{ + return this->keymat; +} + /** * Implementation of ike_sa_t.set_virtual_ip */ @@ -912,7 +880,9 @@ static status_t generate_message(private_ike_sa_t *this, message_t *message, { this->stats[STAT_OUTBOUND] = time(NULL); message->set_ike_sa_id(message, this->ike_sa_id); - return message->generate(message, this->crypter_out, this->signer_out, packet); + return message->generate(message, + this->keymat->get_crypter(this->keymat, FALSE), + this->keymat->get_signer(this->keymat, FALSE), packet); } /** @@ -1335,7 +1305,9 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) is_request = message->get_request(message); - status = message->parse_body(message, this->crypter_in, this->signer_in); + status = message->parse_body(message, + this->keymat->get_crypter(this->keymat, TRUE), + this->keymat->get_signer(this->keymat, TRUE)); if (status != SUCCESS) { @@ -1472,38 +1444,6 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) } } -/** - * Implementation of ike_sa_t.get_prf. - */ -static prf_t *get_prf(private_ike_sa_t *this) -{ - return this->prf; -} - -/** - * Implementation of ike_sa_t.get_prf. - */ -static prf_t *get_child_prf(private_ike_sa_t *this) -{ - return this->child_prf; -} - -/** - * Implementation of ike_sa_t.get_skp_bild - */ -static chunk_t get_skp_build(private_ike_sa_t *this) -{ - return this->skp_build; -} - -/** - * Implementation of ike_sa_t.get_skp_verify - */ -static chunk_t get_skp_verify(private_ike_sa_t *this) -{ - return this->skp_verify; -} - /** * Implementation of ike_sa_t.get_id. */ @@ -1563,224 +1503,6 @@ static void set_eap_identity(private_ike_sa_t *this, identification_t *id) this->eap_identity = id; } -/** - * Implementation of ike_sa_t.derive_keys. - */ -static status_t derive_keys(private_ike_sa_t *this, - proposal_t *proposal, chunk_t secret, - chunk_t nonce_i, chunk_t nonce_r, - bool initiator, prf_t *child_prf, prf_t *old_prf) -{ - prf_plus_t *prf_plus; - chunk_t skeyseed, key, full_nonce, fixed_nonce, prf_plus_seed; - u_int16_t alg, key_size; - crypter_t *crypter_i, *crypter_r; - signer_t *signer_i, *signer_r; - u_int8_t spi_i_buf[sizeof(u_int64_t)], spi_r_buf[sizeof(u_int64_t)]; - chunk_t spi_i = chunk_from_buf(spi_i_buf); - chunk_t spi_r = chunk_from_buf(spi_r_buf); - - /* Create SAs general purpose PRF first, we may use it here */ - if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) - { - DBG1(DBG_IKE, "no %N selected", - transform_type_names, PSEUDO_RANDOM_FUNCTION); - return FAILED; - } - this->prf = lib->crypto->create_prf(lib->crypto, alg); - if (this->prf == NULL) - { - DBG1(DBG_IKE, "%N %N not supported!", - transform_type_names, PSEUDO_RANDOM_FUNCTION, - pseudo_random_function_names, alg); - return FAILED; - } - DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); - /* full nonce is used as seed for PRF+ ... */ - full_nonce = chunk_cat("cc", nonce_i, nonce_r); - /* but the PRF may need a fixed key which only uses the first bytes of - * the nonces. */ - switch (alg) - { - case PRF_AES128_XCBC: - /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does - * not and therefore fixed key semantics apply to XCBC for key - * derivation. */ - nonce_i.len = min(nonce_i.len, this->prf->get_key_size(this->prf)/2); - nonce_r.len = min(nonce_r.len, this->prf->get_key_size(this->prf)/2); - break; - default: - /* all other algorithms use variable key length, full nonce */ - break; - } - fixed_nonce = chunk_cat("cc", nonce_i, nonce_r); - *((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); - *((u_int64_t*)spi_r.ptr) = this->ike_sa_id->get_responder_spi(this->ike_sa_id); - prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r); - - /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) - * - * if we are rekeying, SKEYSEED is built on another way - */ - if (child_prf == NULL) /* not rekeying */ - { - /* SKEYSEED = prf(Ni | Nr, g^ir) */ - this->prf->set_key(this->prf, fixed_nonce); - this->prf->allocate_bytes(this->prf, secret, &skeyseed); - DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); - this->prf->set_key(this->prf, skeyseed); - chunk_clear(&skeyseed); - chunk_clear(&secret); - prf_plus = prf_plus_create(this->prf, prf_plus_seed); - } - else - { - /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) - * use OLD SAs PRF functions for both prf_plus and prf */ - secret = chunk_cat("mc", secret, full_nonce); - child_prf->allocate_bytes(child_prf, secret, &skeyseed); - DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); - old_prf->set_key(old_prf, skeyseed); - chunk_clear(&skeyseed); - chunk_clear(&secret); - prf_plus = prf_plus_create(old_prf, prf_plus_seed); - } - chunk_free(&full_nonce); - chunk_free(&fixed_nonce); - chunk_clear(&prf_plus_seed); - - /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */ - - /* SK_d is used for generating CHILD_SA key mat => child_prf */ - proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL); - this->child_prf = lib->crypto->create_prf(lib->crypto, alg); - key_size = this->child_prf->get_key_size(this->child_prf); - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_d secret %B", &key); - this->child_prf->set_key(this->child_prf, key); - chunk_clear(&key); - - /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ - if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) - { - DBG1(DBG_IKE, "no %N selected", - transform_type_names, INTEGRITY_ALGORITHM); - return FAILED; - } - signer_i = lib->crypto->create_signer(lib->crypto, alg); - signer_r = lib->crypto->create_signer(lib->crypto, alg); - if (signer_i == NULL || signer_r == NULL) - { - DBG1(DBG_IKE, "%N %N not supported!", - transform_type_names, INTEGRITY_ALGORITHM, - integrity_algorithm_names ,alg); - prf_plus->destroy(prf_plus); - return FAILED; - } - key_size = signer_i->get_key_size(signer_i); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_ai secret %B", &key); - signer_i->set_key(signer_i, key); - chunk_clear(&key); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_ar secret %B", &key); - signer_r->set_key(signer_r, key); - chunk_clear(&key); - - if (initiator) - { - this->signer_in = signer_r; - this->signer_out = signer_i; - } - else - { - this->signer_in = signer_i; - this->signer_out = signer_r; - } - - /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ - if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size)) - { - DBG1(DBG_IKE, "no %N selected", - transform_type_names, ENCRYPTION_ALGORITHM); - prf_plus->destroy(prf_plus); - return FAILED; - } - crypter_i = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); - crypter_r = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); - if (crypter_i == NULL || crypter_r == NULL) - { - DBG1(DBG_IKE, "%N %N (key size %d) not supported!", - transform_type_names, ENCRYPTION_ALGORITHM, - encryption_algorithm_names, alg, key_size); - prf_plus->destroy(prf_plus); - return FAILED; - } - key_size = crypter_i->get_key_size(crypter_i); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_ei secret %B", &key); - crypter_i->set_key(crypter_i, key); - chunk_clear(&key); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_er secret %B", &key); - crypter_r->set_key(crypter_r, key); - chunk_clear(&key); - - if (initiator) - { - this->crypter_in = crypter_r; - this->crypter_out = crypter_i; - } - else - { - this->crypter_in = crypter_i; - this->crypter_out = crypter_r; - } - - /* SK_pi/SK_pr used for authentication => stored for later */ - key_size = this->prf->get_key_size(this->prf); - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_pi secret %B", &key); - if (initiator) - { - this->skp_build = key; - } - else - { - this->skp_verify = key; - } - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_pr secret %B", &key); - if (initiator) - { - this->skp_verify = key; - } - else - { - this->skp_build = key; - } - - /* all done, prf_plus not needed anymore */ - prf_plus->destroy(prf_plus); - - /* save selected proposal */ - this->selected_proposal = proposal->clone(proposal); - - return SUCCESS; -} - -/** - * Implementation of ike_sa_t.get_proposal. - */ -static proposal_t* get_proposal(private_ike_sa_t *this) -{ - return this->selected_proposal; -} - /** * Implementation of ike_sa_t.add_child_sa. */ @@ -2452,16 +2174,7 @@ static void destroy(private_ike_sa_t *this) charon->bus->set_sa(charon->bus, NULL); this->task_manager->destroy(this->task_manager); - - DESTROY_IF(this->crypter_in); - DESTROY_IF(this->crypter_out); - DESTROY_IF(this->signer_in); - DESTROY_IF(this->signer_out); - DESTROY_IF(this->prf); - DESTROY_IF(this->child_prf); - chunk_free(&this->skp_verify); - chunk_free(&this->skp_build); - DESTROY_IF(this->selected_proposal); + this->keymat->destroy(this->keymat); if (this->my_virtual_ip) { @@ -2561,12 +2274,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.destroy = (void (*)(ike_sa_t*))destroy; this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive; - this->public.get_prf = (prf_t* (*)(ike_sa_t*)) get_prf; - this->public.get_child_prf = (prf_t* (*)(ike_sa_t *)) get_child_prf; - this->public.get_skp_verify = (chunk_t (*)(ike_sa_t *)) get_skp_verify; - this->public.get_skp_build = (chunk_t (*)(ike_sa_t *)) get_skp_build; - this->public.derive_keys = (status_t (*)(ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys; - this->public.get_proposal = (proposal_t* (*)(ike_sa_t*)) get_proposal; + this->public.get_keymat = (keymat_t*(*)(ike_sa_t*))get_keymat; this->public.add_child_sa = (void (*)(ike_sa_t*,child_sa_t*)) add_child_sa; this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator; @@ -2607,15 +2315,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->eap_identity = NULL; this->extensions = 0; this->conditions = 0; - this->selected_proposal = NULL; - this->crypter_in = NULL; - this->crypter_out = NULL; - this->signer_in = NULL; - this->signer_out = NULL; - this->prf = NULL; - this->skp_verify = chunk_empty; - this->skp_build = chunk_empty; - this->child_prf = NULL; + this->keymat = keymat_create(ike_sa_id->is_initiator(ike_sa_id)); this->state = IKE_CREATED; this->keepalive_interval = lib->settings->get_time(lib->settings, "charon.keep_alive", KEEPALIVE_INTERVAL); diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index a438e410c..354f02614 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -38,9 +38,7 @@ typedef struct ike_sa_t ike_sa_t; #include #include #include -#include -#include -#include +#include #include #include #include @@ -714,62 +712,13 @@ struct ike_sa_t { * was sent. */ void (*send_keepalive) (ike_sa_t *this); - + /** - * Derive all keys and create the transforms for IKE communication. + * Get the keying material of this IKE_SA. * - * Keys are derived using the diffie hellman secret, nonces and internal - * stored SPIs. - * Key derivation differs when an IKE_SA is set up to replace an - * existing IKE_SA (rekeying). The SK_d key from the old IKE_SA - * is included in the derivation process. - * - * @param proposal proposal which contains algorithms to use - * @param secret secret derived from DH exchange, gets freed - * @param nonce_i initiators nonce - * @param nonce_r responders nonce - * @param initiator TRUE if initiator, FALSE otherwise - * @param child_prf PRF with SK_d key when rekeying, NULL otherwise - * @param old_prf general purpose PRF of old SA when rekeying + * @return per IKE_SA keymat instance */ - status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret, - chunk_t nonce_i, chunk_t nonce_r, - bool initiator, prf_t *child_prf, prf_t *old_prf); - - /** - * Get the selected IKE proposal - * - * @return selected IKE proposal - */ - proposal_t* (*get_proposal)(ike_sa_t *this); - - /** - * Get a multi purpose prf for the negotiated PRF function. - * - * @return pointer to prf_t object - */ - prf_t *(*get_prf) (ike_sa_t *this); - - /** - * Get the prf-object, which is used to derive keys for child SAs. - * - * @return pointer to prf_t object - */ - prf_t *(*get_child_prf) (ike_sa_t *this); - - /** - * Get the key to build outgoing authentication data. - * - * @return pointer to prf_t object - */ - chunk_t (*get_skp_build) (ike_sa_t *this); - - /** - * Get the key to verify incoming authentication data. - * - * @return pointer to prf_t object - */ - chunk_t (*get_skp_verify) (ike_sa_t *this); + keymat_t* (*get_keymat)(ike_sa_t *this); /** * Associates a child SA to this IKE SA diff --git a/src/charon/sa/keymat.c b/src/charon/sa/keymat.c new file mode 100644 index 000000000..4af33dd08 --- /dev/null +++ b/src/charon/sa/keymat.c @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +#include "keymat.h" + +#include +#include + +typedef struct private_keymat_t private_keymat_t; + +/** + * Private data of an keymat_t object. + */ +struct private_keymat_t { + + /** + * Public keymat_t interface. + */ + keymat_t public; + + /** + * IKE_SA Role, initiator or responder + */ + bool initiator; + + /** + * diffie hellman key exchange + */ + diffie_hellman_t *dh; + + /** + * inbound signer (verify) + */ + signer_t *signer_in; + + /** + * outbound signer (sign) + */ + signer_t *signer_out; + + /** + * inbound crypter (decrypt) + */ + crypter_t *crypter_in; + + /** + * outbound crypter (encrypt) + */ + crypter_t *crypter_out; + + /** + * General purpose PRF + */ + prf_t *prf; + + /** + * PRF for CHILD_SA keymat + */ + prf_t *child_prf; + + /** + * Key to build outging authentication data (SKp) + */ + chunk_t skp_build; + + /** + * Key to verify incoming authentication data (SKp) + */ + chunk_t skp_verify; + + /** + * Negotiated IKE proposal + */ + proposal_t *proposal; +}; + +/** + * Implementation of keymat_t.set_dh_group + */ +static bool set_dh_group(private_keymat_t *this, diffie_hellman_group_t group) +{ + DESTROY_IF(this->dh); + this->dh = lib->crypto->create_dh(lib->crypto, group); + return this->dh != NULL; +} + +/** + * Implementation of keymat_t.get_dh + */ +static diffie_hellman_t* get_dh(private_keymat_t *this) +{ + return this->dh; +} + +/** + * Implementation of keymat_t.derive_keys + */ +static bool derive_keys(private_keymat_t *this, proposal_t *proposal, + chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, + private_keymat_t *rekey) +{ + chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed; + chunk_t spi_i, spi_r; + crypter_t *crypter_i, *crypter_r; + signer_t *signer_i, *signer_r; + prf_plus_t *prf_plus; + u_int16_t alg, key_size; + + spi_i = chunk_alloca(sizeof(u_int64_t)); + spi_r = chunk_alloca(sizeof(u_int64_t)); + + if (!this->dh || this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) + { + return FALSE; + } + + /* Create SAs general purpose PRF first, we may use it here */ + if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) + { + DBG1(DBG_IKE, "no %N selected", + transform_type_names, PSEUDO_RANDOM_FUNCTION); + return FALSE; + } + this->prf = lib->crypto->create_prf(lib->crypto, alg); + if (this->prf == NULL) + { + DBG1(DBG_IKE, "%N %N not supported!", + transform_type_names, PSEUDO_RANDOM_FUNCTION, + pseudo_random_function_names, alg); + return FALSE; + } + DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); + /* full nonce is used as seed for PRF+ ... */ + full_nonce = chunk_cat("cc", nonce_i, nonce_r); + /* but the PRF may need a fixed key which only uses the first bytes of + * the nonces. */ + switch (alg) + { + case PRF_AES128_XCBC: + /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does + * not and therefore fixed key semantics apply to XCBC for key + * derivation. */ + nonce_i.len = min(nonce_i.len, this->prf->get_key_size(this->prf)/2); + nonce_r.len = min(nonce_r.len, this->prf->get_key_size(this->prf)/2); + break; + default: + /* all other algorithms use variable key length, full nonce */ + break; + } + fixed_nonce = chunk_cat("cc", nonce_i, nonce_r); + *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id); + *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id); + prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r); + + /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) + * + * if we are rekeying, SKEYSEED is built on another way + */ + if (rekey == NULL) /* not rekeying */ + { + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + this->prf->set_key(this->prf, fixed_nonce); + this->prf->allocate_bytes(this->prf, secret, &skeyseed); + DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); + this->prf->set_key(this->prf, skeyseed); + chunk_clear(&skeyseed); + chunk_clear(&secret); + prf_plus = prf_plus_create(this->prf, prf_plus_seed); + } + else + { + /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) + * use OLD SAs PRF functions for both prf_plus and prf */ + secret = chunk_cat("mc", secret, full_nonce); + rekey->child_prf->allocate_bytes(rekey->child_prf, secret, &skeyseed); + DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); + rekey->prf->set_key(rekey->prf, skeyseed); + chunk_clear(&skeyseed); + chunk_clear(&secret); + prf_plus = prf_plus_create(rekey->prf, prf_plus_seed); + } + chunk_free(&full_nonce); + chunk_free(&fixed_nonce); + chunk_clear(&prf_plus_seed); + + /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */ + + /* SK_d is used for generating CHILD_SA key mat => child_prf */ + proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL); + this->child_prf = lib->crypto->create_prf(lib->crypto, alg); + key_size = this->child_prf->get_key_size(this->child_prf); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_d secret %B", &key); + this->child_prf->set_key(this->child_prf, key); + chunk_clear(&key); + + /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ + if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) + { + DBG1(DBG_IKE, "no %N selected", + transform_type_names, INTEGRITY_ALGORITHM); + return FALSE; + } + signer_i = lib->crypto->create_signer(lib->crypto, alg); + signer_r = lib->crypto->create_signer(lib->crypto, alg); + if (signer_i == NULL || signer_r == NULL) + { + DBG1(DBG_IKE, "%N %N not supported!", + transform_type_names, INTEGRITY_ALGORITHM, + integrity_algorithm_names ,alg); + prf_plus->destroy(prf_plus); + return FALSE; + } + key_size = signer_i->get_key_size(signer_i); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ai secret %B", &key); + signer_i->set_key(signer_i, key); + chunk_clear(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ar secret %B", &key); + signer_r->set_key(signer_r, key); + chunk_clear(&key); + + if (this->initiator) + { + this->signer_in = signer_r; + this->signer_out = signer_i; + } + else + { + this->signer_in = signer_i; + this->signer_out = signer_r; + } + + /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ + if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size)) + { + DBG1(DBG_IKE, "no %N selected", + transform_type_names, ENCRYPTION_ALGORITHM); + prf_plus->destroy(prf_plus); + return FALSE; + } + crypter_i = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); + crypter_r = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); + if (crypter_i == NULL || crypter_r == NULL) + { + DBG1(DBG_IKE, "%N %N (key size %d) not supported!", + transform_type_names, ENCRYPTION_ALGORITHM, + encryption_algorithm_names, alg, key_size); + prf_plus->destroy(prf_plus); + return FALSE; + } + key_size = crypter_i->get_key_size(crypter_i); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ei secret %B", &key); + crypter_i->set_key(crypter_i, key); + chunk_clear(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_er secret %B", &key); + crypter_r->set_key(crypter_r, key); + chunk_clear(&key); + + if (this->initiator) + { + this->crypter_in = crypter_r; + this->crypter_out = crypter_i; + } + else + { + this->crypter_in = crypter_i; + this->crypter_out = crypter_r; + } + + /* SK_pi/SK_pr used for authentication => stored for later */ + key_size = this->prf->get_key_size(this->prf); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_pi secret %B", &key); + if (this->initiator) + { + this->skp_build = key; + } + else + { + this->skp_verify = key; + } + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_pr secret %B", &key); + if (this->initiator) + { + this->skp_verify = key; + } + else + { + this->skp_build = key; + } + + /* all done, prf_plus not needed anymore */ + prf_plus->destroy(prf_plus); + + /* save selected proposal */ + this->proposal = proposal->clone(proposal); + + return TRUE; +} + +/** + * Implementation of keymat_t.get_proposal + */ +static proposal_t* get_proposal(private_keymat_t *this) +{ + return this->proposal; +} + +/** + * Implementation of keymat_t.get_signer + */ +static signer_t* get_signer(private_keymat_t *this, bool in) +{ + return in ? this->signer_in : this->signer_out; +} + +/** + * Implementation of keymat_t.get_crypter + */ +static crypter_t* get_crypter(private_keymat_t *this, bool in) +{ + return in ? this->crypter_in : this->crypter_out; +} + +/** + * Implementation of keymat_t.get_child_prf + */ +static prf_t* get_child_prf(private_keymat_t *this) +{ + return this->child_prf; +} + +/** + * Implementation of keymat_t.get_auth_octets + */ +static chunk_t get_auth_octets(private_keymat_t *this, bool verify, + chunk_t ike_sa_init, chunk_t nonce, + identification_t *id) +{ + chunk_t chunk, idx, octets; + chunk_t skp; + + skp = verify ? this->skp_verify : this->skp_build; + + chunk = chunk_alloca(4); + memset(chunk.ptr, 0, chunk.len); + chunk.ptr[0] = id->get_type(id); + idx = chunk_cata("cc", chunk, id->get_encoding(id)); + + DBG3(DBG_IKE, "IDx' %B", &idx); + DBG3(DBG_IKE, "SK_p %B", &skp); + this->prf->set_key(this->prf, skp); + this->prf->allocate_bytes(this->prf, idx, &chunk); + + octets = chunk_cat("ccm", ike_sa_init, nonce, chunk); + DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets); + return octets; +} + +/** + * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +#define IKEV2_KEY_PAD "Key Pad for IKEv2" +#define IKEV2_KEY_PAD_LENGTH 17 + +/** + * Implementation of keymat_t.get_psk_sig + */ +static chunk_t get_psk_sig(private_keymat_t *this, bool verify, + chunk_t ike_sa_init, chunk_t nonce, chunk_t secret, + identification_t *id) +{ + chunk_t key_pad, key, sig, octets; + + if (!secret.len) + { /* EAP uses SK_p if no MSK has been established */ + secret = verify ? this->skp_verify : this->skp_build; + } + octets = get_auth_octets(this, verify, ike_sa_init, nonce, id); + /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), ) */ + key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH); + this->prf->set_key(this->prf, secret); + this->prf->allocate_bytes(this->prf, key_pad, &key); + this->prf->set_key(this->prf, key); + this->prf->allocate_bytes(this->prf, octets, &sig); + DBG4(DBG_IKE, "secret %B", &secret); + DBG4(DBG_IKE, "prf(secret, keypad) %B", &key); + DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &sig); + chunk_free(&octets); + chunk_free(&key); + + return sig; +} + +/** + * Implementation of keymat_t.destroy. + */ +static void destroy(private_keymat_t *this) +{ + DESTROY_IF(this->dh); + DESTROY_IF(this->signer_in); + DESTROY_IF(this->signer_out); + DESTROY_IF(this->crypter_in); + DESTROY_IF(this->crypter_out); + DESTROY_IF(this->prf); + DESTROY_IF(this->child_prf); + DESTROY_IF(this->proposal); + chunk_clear(&this->skp_verify); + chunk_clear(&this->skp_build); + free(this); +} + +/** + * See header + */ +keymat_t *keymat_create(bool initiator) +{ + private_keymat_t *this = malloc_thing(private_keymat_t); + + this->public.set_dh_group = (bool(*)(keymat_t*, diffie_hellman_group_t group))set_dh_group; + this->public.get_dh = (diffie_hellman_t*(*)(keymat_t*))get_dh; + this->public.derive_keys = (bool(*)(keymat_t*, proposal_t *proposal, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, keymat_t *rekey))derive_keys; + this->public.get_proposal = (proposal_t*(*)(keymat_t*))get_proposal; + this->public.get_signer = (signer_t*(*)(keymat_t*, bool in))get_signer; + this->public.get_crypter = (crypter_t*(*)(keymat_t*, bool in))get_crypter; + this->public.get_child_prf = (prf_t*(*)(keymat_t*))get_child_prf; + this->public.get_auth_octets = (chunk_t(*)(keymat_t *, bool verify, chunk_t ike_sa_init, chunk_t nonce, identification_t *id))get_auth_octets; + this->public.get_psk_sig = (chunk_t(*)(keymat_t*, bool verify, chunk_t ike_sa_init, chunk_t nonce, chunk_t secret, identification_t *id))get_psk_sig; + this->public.destroy = (void(*)(keymat_t*))destroy; + + this->initiator = initiator; + + this->dh = NULL; + this->signer_in = NULL; + this->signer_out = NULL; + this->crypter_in = NULL; + this->crypter_out = NULL; + this->prf = NULL; + this->child_prf = NULL; + this->proposal = NULL; + this->skp_verify = chunk_empty; + this->skp_build = chunk_empty; + + return &this->public; +} + diff --git a/src/charon/sa/keymat.h b/src/charon/sa/keymat.h new file mode 100644 index 000000000..c41c02246 --- /dev/null +++ b/src/charon/sa/keymat.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +/** + * @defgroup keymat keymat + * @{ @ingroup sa + */ + +#ifndef KEYMAT_H_ +#define KEYMAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct keymat_t keymat_t; + +/** + * Derivation an management of sensitive keying material. + */ +struct keymat_t { + + /** + * Set the diffie hellman group to use. + * + * @param group diffie hellman group to use + * @return TRUE if group supported + */ + bool (*set_dh_group)(keymat_t *this, diffie_hellman_group_t group); + + /** + * Get the diffie hellman key agreement interface. + * + * Call set_dh_group() before acquiring this interface. + * + * @return key agreement interface + */ + diffie_hellman_t* (*get_dh)(keymat_t *this); + + /** + * Derive keys from the shared secret. + * + * @param proposal selected algorithms + * @param nonce_i initiators nonce value + * @param nonce_r responders nonce value + * @param id IKE_SA identifier + * @param rekey keymat of old SA if we are rekeying + * @return TRUE on success + */ + bool (*derive_keys)(keymat_t *this, proposal_t *proposal, chunk_t nonce_i, + chunk_t nonce_r, ike_sa_id_t *id, keymat_t *rekey); + /** + * Get a signer to sign/verify IKE messages. + * + * @param in TRUE for inbound (verify), FALSE for outbound (sign) + * @return signer + */ + signer_t* (*get_signer)(keymat_t *this, bool in); + + /* + * Get a crypter to en-/decrypt IKE messages. + * + * @param in TRUE for inbound (decrypt), FALSE for outbound (encrypt) + * @return crypter + */ + crypter_t* (*get_crypter)(keymat_t *this, bool in); + + /** + * Get a keyed PRF to derive keymat for children. + * + * @return PRF to derive CHILD_SA keymat from + */ + prf_t* (*get_child_prf)(keymat_t *this); + + /** + * Get the selected proposal passed to derive_keys(). + * + * @return selected proposal + */ + proposal_t* (*get_proposal)(keymat_t *this); + + /** + * Generate octets to use for authentication procedure (RFC4306 2.15). + * + * This method creates the plain octets and is usually signed by a private + * key. PSK and EAP authentication include a secret into the data, use + * the get_psk_sig() method instead. + * + * @param verify TRUE to create for verfification, FALSE to sign + * @param ike_sa_init encoded ike_sa_init message + * @param nonce nonce value + * @param id identity + * @return authentication octets + */ + chunk_t (*get_auth_octets)(keymat_t *this, bool verify, chunk_t ike_sa_init, + chunk_t nonce, identification_t *id); + /** + * Build the shared secret signature used for PSK and EAP authentication. + * + * This method wraps the get_auth_octets() method and additionally + * includes the secret into the signature. If no secret is given, SK_p is + * used as secret (used for EAP methods without MSK). + * + * @param verify TRUE to create for verfification, FALSE to sign + * @param ike_sa_init encoded ike_sa_init message + * @param nonce nonce value + * @param secret optional secret to include into signature + * @param id identity + * @return signature octets + */ + chunk_t (*get_psk_sig)(keymat_t *this, bool verify, chunk_t ike_sa_init, + chunk_t nonce, chunk_t secret, identification_t *id); + /** + * Destroy a keymat_t. + */ + void (*destroy)(keymat_t *this); +}; + +/** + * Create a keymat instance. + * + * @param initiator TRUE if we are the initiator + * @return keymat instance + */ +keymat_t *keymat_create(bool initiator); + +#endif /* KEYMAT_ @}*/ diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c index bf447d626..8f2329c3a 100644 --- a/src/charon/sa/tasks/child_create.c +++ b/src/charon/sa/tasks/child_create.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Tobias Brunner - * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -196,6 +196,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) chunk_t nonce_i, nonce_r, secret, seed; linked_list_t *my_ts, *other_ts; host_t *me, *other, *other_vip, *my_vip; + keymat_t *keymat; if (this->proposals == NULL) { @@ -351,7 +352,8 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) return NOT_FOUND; } - prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + keymat = this->ike_sa->get_keymat(this->ike_sa); + prf_plus = prf_plus_create(keymat->get_child_prf(keymat), seed); if (this->initiator) { status = this->child_sa->update(this->child_sa, this->proposal, diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c index c109bffb1..bc7d2dd70 100644 --- a/src/charon/sa/tasks/ike_init.c +++ b/src/charon/sa/tasks/ike_init.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Tobias Brunner - * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -64,10 +64,15 @@ struct private_ike_init_t { diffie_hellman_group_t dh_group; /** - * Diffie hellman object used to generate public DH value. + * diffie hellman key exchange */ diffie_hellman_t *dh; + /** + * Keymat derivation (from IKE_SA) + */ + keymat_t *keymat; + /** * nonce chosen by us */ @@ -192,7 +197,10 @@ static void process_payloads(private_ike_init_t *this, message_t *message) this->dh_group = ke_payload->get_dh_group_number(ke_payload); if (!this->initiator) { - this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); + if (this->keymat->set_dh_group(this->keymat, this->dh_group)) + { + this->dh = this->keymat->get_dh(this->keymat); + } } if (this->dh) { @@ -241,18 +249,18 @@ static status_t build_i(private_ike_init_t *this, message_t *message) DBG1(DBG_IKE, "giving up after %d retries", MAX_RETRIES); return FAILED; } - + /* if the DH group is set via use_dh_group(), we already have a DH object */ if (!this->dh) { this->dh_group = this->config->get_dh_group(this->config); - this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); - if (this->dh == NULL) + if (!this->keymat->set_dh_group(this->keymat, this->dh_group)) { DBG1(DBG_IKE, "configured DH group %N not supported", diffie_hellman_group_names, this->dh_group); return FAILED; } + this->dh = this->keymat->get_dh(this->keymat); } /* generate nonce only when we are trying the first time */ @@ -368,9 +376,9 @@ static status_t process_r(private_ike_init_t *this, message_t *message) */ static status_t build_r(private_ike_init_t *this, message_t *message) { - chunk_t secret; - status_t status; - + keymat_t *old_keymat = NULL; + ike_sa_id_t *id; + /* check if we have everything we need */ if (this->proposal == NULL || this->other_nonce.len == 0 || this->my_nonce.len == 0) @@ -381,8 +389,7 @@ static status_t build_r(private_ike_init_t *this, message_t *message) } if (this->dh == NULL || - !this->proposal->has_dh_group(this->proposal, this->dh_group) || - this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) + !this->proposal->has_dh_group(this->proposal, this->dh_group)) { u_int16_t group; @@ -404,30 +411,14 @@ static status_t build_r(private_ike_init_t *this, message_t *message) return FAILED; } + id = this->ike_sa->get_id(this->ike_sa); if (this->old_sa) - { - ike_sa_id_t *id; - prf_t *prf, *child_prf; - - /* Apply SPI if we are rekeying */ - id = this->ike_sa->get_id(this->ike_sa); + { /* rekeying: Apply SPI, include keymat from old SA in key derivation */ id->set_initiator_spi(id, this->proposal->get_spi(this->proposal)); - - /* setup crypto keys for the rekeyed SA */ - prf = this->old_sa->get_prf(this->old_sa); - child_prf = this->old_sa->get_child_prf(this->old_sa); - status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, - this->other_nonce, this->my_nonce, - FALSE, child_prf, prf); + old_keymat = this->old_sa->get_keymat(this->old_sa); } - else - { - /* setup crypto keys */ - status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, - this->other_nonce, this->my_nonce, - FALSE, NULL, NULL); - } - if (status != SUCCESS) + if (!this->keymat->derive_keys(this->keymat, this->proposal, this->other_nonce, + this->my_nonce, id, old_keymat)) { DBG1(DBG_IKE, "key derivation failed"); message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); @@ -443,8 +434,8 @@ static status_t build_r(private_ike_init_t *this, message_t *message) */ static status_t process_i(private_ike_init_t *this, message_t *message) { - chunk_t secret; - status_t status; + keymat_t *old_keymat = NULL; + ike_sa_id_t *id; iterator_t *iterator; payload_t *payload; @@ -521,42 +512,25 @@ static status_t process_i(private_ike_init_t *this, message_t *message) } if (this->dh == NULL || - !this->proposal->has_dh_group(this->proposal, this->dh_group) || - this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) + !this->proposal->has_dh_group(this->proposal, this->dh_group)) { DBG1(DBG_IKE, "peer DH group selection invalid"); return FAILED; } - /* Apply SPI if we are rekeying */ + id = this->ike_sa->get_id(this->ike_sa); if (this->old_sa) - { - ike_sa_id_t *id; - prf_t *prf, *child_prf; - - id = this->ike_sa->get_id(this->ike_sa); + { /* rekeying: Apply SPI, include keymat from old SA in key derivation */ id->set_responder_spi(id, this->proposal->get_spi(this->proposal)); - - /* setup crypto keys for the rekeyed SA */ - prf = this->old_sa->get_prf(this->old_sa); - child_prf = this->old_sa->get_child_prf(this->old_sa); - status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, - this->my_nonce, this->other_nonce, - TRUE, child_prf, prf); + old_keymat = this->old_sa->get_keymat(this->old_sa); } - else - { - /* setup crypto keys for a new SA */ - status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, - this->my_nonce, this->other_nonce, - TRUE, NULL, NULL); - } - if (status != SUCCESS) + if (!this->keymat->derive_keys(this->keymat, this->proposal, this->my_nonce, + this->other_nonce, id, old_keymat)) { DBG1(DBG_IKE, "key derivation failed"); return FAILED; } - + return SUCCESS; } @@ -590,12 +564,12 @@ static chunk_t get_lower_nonce(private_ike_init_t *this) static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) { DESTROY_IF(this->proposal); - DESTROY_IF(this->dh); chunk_free(&this->other_nonce); this->ike_sa = ike_sa; this->proposal = NULL; - this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); + this->keymat->set_dh_group(this->keymat, this->dh_group); + this->dh = this->keymat->get_dh(this->keymat); } /** @@ -604,7 +578,6 @@ static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) static void destroy(private_ike_init_t *this) { DESTROY_IF(this->proposal); - DESTROY_IF(this->dh); chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); chunk_free(&this->cookie); @@ -637,6 +610,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) this->initiator = initiator; this->dh_group = MODP_NONE; this->dh = NULL; + this->keymat = ike_sa->get_keymat(ike_sa); this->my_nonce = chunk_empty; this->other_nonce = chunk_empty; this->cookie = chunk_empty; diff --git a/src/charon/sa/tasks/ike_reauth.c b/src/charon/sa/tasks/ike_reauth.c index c6d74dc7e..d16a0b8fc 100644 --- a/src/charon/sa/tasks/ike_reauth.c +++ b/src/charon/sa/tasks/ike_reauth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006-2008 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c index 2dc444e56..242f2098d 100644 --- a/src/charon/sa/tasks/ike_rekey.c +++ b/src/charon/sa/tasks/ike_rekey.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil *