]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
keymat_v2: Support key derivation with multiple key exchanges
authorTobias Brunner <tobias@strongswan.org>
Thu, 9 Apr 2020 09:37:52 +0000 (11:37 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 7 Aug 2024 14:20:18 +0000 (16:20 +0200)
src/charon-tkm/src/tkm/tkm_keymat.c
src/charon-tkm/tests/keymat_tests.c
src/libcharon/plugins/ha/ha_dispatcher.c
src/libcharon/sa/ikev2/keymat_v2.c
src/libcharon/sa/ikev2/keymat_v2.h
src/libcharon/sa/ikev2/tasks/child_create.c
src/libcharon/sa/ikev2/tasks/ike_init.c

index 3d181ca6d15d72a4cf942994fa1b2e8c4876f528..d8b800f05a7fbe7bf0c510398b3c8c4cc3470d06 100644 (file)
@@ -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);
        }
index e5d6280e7495fb1b5de85657506b46bd9d8ce68a..56c8b6809dda098a9e7e3b5e8cede00a801e3b20 100644 (file)
@@ -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");
index 08a348b6133da96f6f45bcac592a831de62a8892..b0ec3d9cf0276fd45a0b287530f2157eecc1298c 100644 (file)
@@ -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)
        {
index b6807e9115da982695042e41fa302a5f4dde4fdb..08fcdedc7f7a2286db52ddb3286c11c811222294 100644 (file)
@@ -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);
index 353afb86c1e68f4e7cb3d0273e1196dff41cc38e..4fcc20d5893aaf53ba57039b093a01cc2c8721a2 100644 (file)
@@ -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);
index 3e80a7d2deee768e936d2216a8772d1344df7de6..39632e78698c42b421f061c9278135641a7f10e2 100644 (file)
@@ -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)
        {
index b84c2832a799777b534a2aac6e547231f5be0f9b..2ef721ddc518241808b52f35131f7ad9d8d549fa 100644 (file)
@@ -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;