From: Tobias Brunner Date: Thu, 9 Apr 2020 09:37:52 +0000 (+0200) Subject: keymat_v2: Support key derivation with multiple key exchanges X-Git-Tag: 6.0.0rc1~56^2~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c14e4ab2a8b4b114ded9de9b4c62e02d045b00e0;p=thirdparty%2Fstrongswan.git keymat_v2: Support key derivation with multiple key exchanges --- diff --git a/src/charon-tkm/src/tkm/tkm_keymat.c b/src/charon-tkm/src/tkm/tkm_keymat.c index 3d181ca6d1..d8b800f05a 100644 --- a/src/charon-tkm/src/tkm/tkm_keymat.c +++ b/src/charon-tkm/src/tkm/tkm_keymat.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 Tobias Brunner + * Copyright (C) 2015-2020 Tobias Brunner * Copyright (C) 2012 Reto Buerki * Copyright (C) 2012 Adrian-Ken Rueegsegger * @@ -95,13 +95,14 @@ METHOD(keymat_t, create_nonce_gen, nonce_gen_t*, } METHOD(keymat_v2_t, derive_ike_keys, bool, - private_tkm_keymat_t *this, proposal_t *proposal, key_exchange_t *ke, + private_tkm_keymat_t *this, proposal_t *proposal, array_t *kes, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, pseudo_random_function_t rekey_function, chunk_t rekey_skd) { uint64_t nc_id, spi_loc, spi_rem; chunk_t *nonce; tkm_diffie_hellman_t *tkm_dh; + key_exchange_t *ke; dh_id_type dh_id; nonce_type nonce_rem; result_type res; @@ -109,6 +110,12 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, icv_len_type icv_len; iv_len_type iv_len; + if (array_count(kes) != 1) + { + DBG1(DBG_IKE, "the TKM currently only supports a single key exchange"); + return FALSE; + } + /* Acquire nonce context id */ nonce = this->initiator ? &nonce_i : &nonce_r; nc_id = tkm->chunk_map->get_id(tkm->chunk_map, nonce); @@ -119,6 +126,7 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, } /* Get DH context id */ + array_get(kes, ARRAY_HEAD, &ke); tkm_dh = (tkm_diffie_hellman_t *)ke; dh_id = tkm_dh->get_id(tkm_dh); @@ -198,14 +206,15 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, } METHOD(keymat_v2_t, derive_child_keys, bool, - private_tkm_keymat_t *this, proposal_t *proposal, key_exchange_t *ke, + private_tkm_keymat_t *this, proposal_t *proposal, array_t *kes, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r) { esa_info_t *esa_info_i, *esa_info_r; dh_id_type dh_id = 0; + key_exchange_t *ke; - if (ke) + if (kes && array_get(kes, ARRAY_HEAD, &ke)) { dh_id = ((tkm_diffie_hellman_t *)ke)->get_id((tkm_diffie_hellman_t *)ke); } diff --git a/src/charon-tkm/tests/keymat_tests.c b/src/charon-tkm/tests/keymat_tests.c index e5d6280e74..56c8b6809d 100644 --- a/src/charon-tkm/tests/keymat_tests.c +++ b/src/charon-tkm/tests/keymat_tests.c @@ -55,9 +55,12 @@ START_TEST(test_derive_ike_keys) ck_assert(dh->ke.get_public_key(&dh->ke, &pubvalue)); ck_assert(dh->ke.set_public_key(&dh->ke, pubvalue)); + array_t *kes = NULL; + array_insert_create(&kes, ARRAY_TAIL, dh); fail_unless(keymat->keymat_v2.derive_ike_keys(&keymat->keymat_v2, proposal, - &dh->ke, nonce, nonce, ike_sa_id, PRF_UNDEFINED, chunk_empty), + kes, nonce, nonce, ike_sa_id, PRF_UNDEFINED, chunk_empty), "Key derivation failed"); + array_destroy(kes); chunk_free(&nonce); aead_t * const aead = keymat->keymat_v2.keymat.get_aead(&keymat->keymat_v2.keymat, TRUE); @@ -92,11 +95,13 @@ START_TEST(test_derive_child_keys) chunk_t encr_i, encr_r, integ_i, integ_r; chunk_t nonce = chunk_from_chars("test chunk"); + array_t *kes = NULL; + array_insert_create(&kes, ARRAY_TAIL, dh); fail_unless(keymat->keymat_v2.derive_child_keys(&keymat->keymat_v2, proposal, - &dh->ke, - nonce, nonce, &encr_i, + kes, nonce, nonce, &encr_i, &integ_i, &encr_r, &integ_r), "Child key derivation failed"); + array_destroy(kes); esa_info_t *info = (esa_info_t *)encr_i.ptr; fail_if(!info, "encr_i does not contain esa information"); diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 08a348b613..b0ec3d9cf0 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -234,9 +234,12 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message if (ike_sa->get_version(ike_sa) == IKEV2) { keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); + array_t *kes = NULL; - ok = keymat_v2->derive_ike_keys(keymat_v2, proposal, dh, nonce_i, + array_insert_create(&kes, ARRAY_HEAD, dh); + ok = keymat_v2->derive_ike_keys(keymat_v2, proposal, kes, nonce_i, nonce_r, ike_sa->get_id(ike_sa), old_prf, old_skd); + array_destroy(kes); } if (ike_sa->get_version(ike_sa) == IKEV1) { @@ -662,6 +665,7 @@ static void process_child_add(private_ha_dispatcher_t *this, chunk_t encr_i, integ_i, encr_r, integ_r; linked_list_t *local_ts, *remote_ts; key_exchange_t *dh = NULL; + array_t *kes = NULL; enumerator = message->create_attribute_enumerator(message); while (enumerator->enumerate(enumerator, &attribute, &value)) @@ -767,12 +771,13 @@ static void process_child_add(private_ha_dispatcher_t *this, if (secret.len) { dh = ha_diffie_hellman_create(secret, chunk_empty); + array_insert_create(&kes, ARRAY_HEAD, dh); } if (ike_sa->get_version(ike_sa) == IKEV2) { keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); - ok = keymat_v2->derive_child_keys(keymat_v2, proposal, dh, + ok = keymat_v2->derive_child_keys(keymat_v2, proposal, kes, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r); } if (ike_sa->get_version(ike_sa) == IKEV1) @@ -786,6 +791,7 @@ static void process_child_add(private_ha_dispatcher_t *this, ok = keymat_v1->derive_child_keys(keymat_v1, proposal, dh, spi_i, spi_r, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r); } + array_destroy(kes); DESTROY_IF(dh); if (!ok) { diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index b6807e9115..08fcdedc7f 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 Tobias Brunner + * Copyright (C) 2015-2020 Tobias Brunner * Copyright (C) 2008 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -237,13 +237,13 @@ static bool set_aead_keys(private_keymat_v2_t *this, uint16_t enc_alg, } METHOD(keymat_v2_t, derive_ike_keys, bool, - private_keymat_v2_t *this, proposal_t *proposal, key_exchange_t *dh, + private_keymat_v2_t *this, proposal_t *proposal, array_t *kes, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, pseudo_random_function_t rekey_function, chunk_t rekey_skd) { - chunk_t skeyseed = chunk_empty, secret, full_nonce, fixed_nonce; - chunk_t prf_plus_seed, spi_i, spi_r, keymat = chunk_empty; - chunk_t sk_ei = chunk_empty, sk_er = chunk_empty; + chunk_t skeyseed = chunk_empty, secret, add_secret = chunk_empty; + chunk_t full_nonce, fixed_nonce, prf_plus_seed, spi_i, spi_r; + chunk_t keymat = chunk_empty, sk_ei = chunk_empty, sk_er = chunk_empty; chunk_t sk_ai = chunk_empty, sk_ar = chunk_empty, sk_pi, sk_pr; kdf_t *prf = NULL, *prf_plus = NULL; uint16_t prf_alg, key_size, enc_alg, enc_size, int_alg; @@ -302,13 +302,15 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, return FALSE; } - if (!dh->get_shared_secret(dh, &secret)) + if (!key_exchange_concat_secrets(kes, &secret, &add_secret)) { return FALSE; } - DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); + DBG4(DBG_IKE, "key exchange secret %B", &secret); + DBG4(DBG_IKE, "additional key exchange secret %B", &add_secret); /* full nonce is used as seed for PRF+ ... */ full_nonce = chunk_cat("cc", nonce_i, nonce_r); + DBG4(DBG_IKE, "nonces %B", &full_nonce); /* but the PRF may need a fixed key which only uses the first bytes of * the nonces. */ switch (prf_alg) @@ -342,6 +344,7 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, key_derivation_function_names, KDF_PRF, pseudo_random_function_names, this->prf_alg); chunk_clear(&secret); + chunk_clear(&add_secret); chunk_free(&full_nonce); chunk_free(&fixed_nonce); return FALSE; @@ -365,11 +368,12 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, key_derivation_function_names, KDF_PRF, pseudo_random_function_names, rekey_function); chunk_clear(&secret); + chunk_clear(&add_secret); chunk_free(&full_nonce); chunk_free(&fixed_nonce); return FALSE; } - secret = chunk_cat("sc", secret, full_nonce); + secret = chunk_cat("scc", secret, full_nonce, add_secret); if (prf->set_param(prf, KDF_PARAM_KEY, secret) && prf->set_param(prf, KDF_PARAM_SALT, rekey_skd) && prf->allocate_bytes(prf, 0, &skeyseed)) @@ -380,6 +384,7 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, } DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); chunk_clear(&secret); + chunk_clear(&add_secret); chunk_free(&fixed_nonce); DESTROY_IF(prf); @@ -529,12 +534,13 @@ METHOD(keymat_v2_t, derive_ike_keys_ppk, bool, } METHOD(keymat_v2_t, derive_child_keys, bool, - private_keymat_v2_t *this, proposal_t *proposal, key_exchange_t *dh, + private_keymat_v2_t *this, proposal_t *proposal, array_t *kes, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r) { uint16_t enc_alg, int_alg, enc_size = 0, int_size = 0; - chunk_t seed, secret = chunk_empty, keymat = chunk_empty; + chunk_t seed, secret = chunk_empty, add_secret = chunk_empty; + chunk_t keymat = chunk_empty; kdf_t *prf_plus; if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, @@ -601,15 +607,16 @@ METHOD(keymat_v2_t, derive_child_keys, bool, int_size /= 8; } - if (dh) + if (kes) { - if (!dh->get_shared_secret(dh, &secret)) + if (!key_exchange_concat_secrets(kes, &secret, &add_secret)) { return FALSE; } - DBG4(DBG_CHD, "DH secret %B", &secret); + DBG4(DBG_CHD, "key exchange secret %B", &secret); + DBG4(DBG_CHD, "additional key exchange secret %B", &add_secret); } - seed = chunk_cata("scc", secret, nonce_i, nonce_r); + seed = chunk_cata("sccs", secret, nonce_i, nonce_r, add_secret); DBG4(DBG_CHD, "seed %B", &seed); prf_plus = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS, this->prf_alg); diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h index 353afb86c1..4fcc20d589 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.h +++ b/src/libcharon/sa/ikev2/keymat_v2.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2019 Tobias Brunner + * Copyright (C) 2011-2020 Tobias Brunner * * Copyright (C) secunet Security Networks AG * @@ -44,7 +44,7 @@ struct keymat_v2_t { * crypters and authentication functions. * * @param proposal selected algorithms - * @param dh diffie hellman key allocated by create_ke() + * @param kes array of key_exchange_t* created by create_ke() * @param nonce_i initiators nonce value * @param nonce_r responders nonce value * @param id IKE_SA identifier @@ -53,7 +53,7 @@ struct keymat_v2_t { * @return TRUE on success */ bool (*derive_ike_keys)(keymat_v2_t *this, proposal_t *proposal, - key_exchange_t *dh, chunk_t nonce_i, + array_t *kes, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, pseudo_random_function_t rekey_function, chunk_t rekey_skd); @@ -77,7 +77,7 @@ struct keymat_v2_t { * If no PFS is used for the CHILD_SA, dh can be NULL. * * @param proposal selected algorithms - * @param dh diffie hellman key allocated by create_ke(), or NULL + * @param kes array of key_exchange_t* created by create_ke(), or NULL * @param nonce_i initiators nonce value * @param nonce_r responders nonce value * @param encr_i chunk to write initiators encryption key to @@ -87,7 +87,7 @@ struct keymat_v2_t { * @return TRUE on success */ bool (*derive_child_keys)(keymat_v2_t *this, - proposal_t *proposal, key_exchange_t *dh, + proposal_t *proposal, array_t *kes, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 3e80a7d2de..39632e7869 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -500,6 +500,7 @@ static status_t select_and_install(private_child_create_t *this, chunk_t integ_i = chunk_empty, integ_r = chunk_empty; linked_list_t *my_ts, *other_ts; host_t *me, *other; + array_t *kes = NULL; proposal_selection_flag_t flags = 0; if (this->proposals == NULL) @@ -690,8 +691,12 @@ static status_t select_and_install(private_child_create_t *this, this->ipcomp = IPCOMP_NONE; } status_i = status_o = FAILED; + if (this->dh) + { + array_insert_create(&kes, ARRAY_HEAD, this->dh); + } if (this->keymat->derive_child_keys(this->keymat, this->proposal, - this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) + kes, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) { if (this->initiator) { @@ -769,6 +774,7 @@ static status_t select_and_install(private_child_create_t *this, chunk_clear(&integ_r); chunk_clear(&encr_i); chunk_clear(&encr_r); + array_destroy(kes); if (status != SUCCESS) { diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index b84c2832a7..2ef721ddc5 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -802,6 +802,7 @@ static bool derive_keys_internal(private_ike_init_t *this, chunk_t nonce_i, pseudo_random_function_t prf_alg = PRF_UNDEFINED; chunk_t skd = chunk_empty; ike_sa_id_t *id; + array_t *kes; id = this->ike_sa->get_id(this->ike_sa); if (this->old_sa) @@ -810,11 +811,14 @@ static bool derive_keys_internal(private_ike_init_t *this, chunk_t nonce_i, old_keymat = (keymat_v2_t*)this->old_sa->get_keymat(this->old_sa); prf_alg = old_keymat->get_skd(old_keymat, &skd); } - if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh, + array_insert_create(&kes, ARRAY_HEAD, this->dh); + if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, kes, nonce_i, nonce_r, id, prf_alg, skd)) { + array_destroy(kes); return FALSE; } + array_destroy(kes); charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty, nonce_i, nonce_r, this->old_sa, NULL, AUTH_NONE); return TRUE;