child-create: Add support for multiple key exchanges
It also changes that payloads are built before installing the CHILD_SA on the responder, that is, the KE payload is generated before keys are derived, so that key_exchange_t::get_public_key() is called before get_shared_secret(), or its internal equivalent, which could be relevant for KE implementations that want to ensure that the key can't be accessed again after the key derivation.
This commit is contained in:
@@ -107,29 +107,27 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
|
|||||||
}
|
}
|
||||||
esa = *(esa_info_t *)(data->enc_key.ptr);
|
esa = *(esa_info_t *)(data->enc_key.ptr);
|
||||||
|
|
||||||
/* only handle the case where we have both distinct ESP spi's available */
|
/* only handle the case where we have both distinct ESP SPIs available,
|
||||||
if (esa.spi_r == id->spi)
|
* which is always the outbound SA */
|
||||||
|
if (esa.spi_l == id->spi)
|
||||||
{
|
{
|
||||||
chunk_free(&esa.nonce_i);
|
chunk_free(&esa.nonce_i);
|
||||||
chunk_free(&esa.nonce_r);
|
chunk_free(&esa.nonce_r);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spi_loc = esa.spi_l;
|
||||||
|
spi_rem = id->spi;
|
||||||
|
local = id->src;
|
||||||
|
peer = id->dst;
|
||||||
|
|
||||||
if (data->initiator)
|
if (data->initiator)
|
||||||
{
|
{
|
||||||
spi_loc = id->spi;
|
|
||||||
spi_rem = esa.spi_r;
|
|
||||||
local = id->dst;
|
|
||||||
peer = id->src;
|
|
||||||
nonce_loc = &esa.nonce_i;
|
nonce_loc = &esa.nonce_i;
|
||||||
nonce_rem = &esa.nonce_r;
|
nonce_rem = &esa.nonce_r;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spi_loc = esa.spi_r;
|
|
||||||
spi_rem = id->spi;
|
|
||||||
local = id->src;
|
|
||||||
peer = id->dst;
|
|
||||||
nonce_loc = &esa.nonce_r;
|
nonce_loc = &esa.nonce_r;
|
||||||
nonce_rem = &esa.nonce_i;
|
nonce_rem = &esa.nonce_i;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
|
|||||||
|
|
||||||
INIT(esa_info_i,
|
INIT(esa_info_i,
|
||||||
.isa_id = this->isa_ctx_id,
|
.isa_id = this->isa_ctx_id,
|
||||||
.spi_r = proposal->get_spi(proposal),
|
.spi_l = proposal->get_spi(proposal),
|
||||||
.nonce_i = chunk_clone(nonce_i),
|
.nonce_i = chunk_clone(nonce_i),
|
||||||
.nonce_r = chunk_clone(nonce_r),
|
.nonce_r = chunk_clone(nonce_r),
|
||||||
.is_encr_r = FALSE,
|
.is_encr_r = FALSE,
|
||||||
@@ -230,15 +230,15 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
|
|||||||
|
|
||||||
INIT(esa_info_r,
|
INIT(esa_info_r,
|
||||||
.isa_id = this->isa_ctx_id,
|
.isa_id = this->isa_ctx_id,
|
||||||
.spi_r = proposal->get_spi(proposal),
|
.spi_l = proposal->get_spi(proposal),
|
||||||
.nonce_i = chunk_clone(nonce_i),
|
.nonce_i = chunk_clone(nonce_i),
|
||||||
.nonce_r = chunk_clone(nonce_r),
|
.nonce_r = chunk_clone(nonce_r),
|
||||||
.is_encr_r = TRUE,
|
.is_encr_r = TRUE,
|
||||||
.dh_id = dh_id,
|
.dh_id = dh_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
DBG1(DBG_CHD, "passing on esa info (isa: %llu, spi_r: %x, dh_id: %llu)",
|
DBG1(DBG_CHD, "passing on esa info (isa: %llu, spi_l: %x, dh_id: %llu)",
|
||||||
esa_info_i->isa_id, ntohl(esa_info_i->spi_r), esa_info_i->dh_id);
|
esa_info_i->isa_id, ntohl(esa_info_i->spi_l), esa_info_i->dh_id);
|
||||||
|
|
||||||
/* store ESA info in encr_i/r, which is passed to add_sa */
|
/* store ESA info in encr_i/r, which is passed to add_sa */
|
||||||
*encr_i = chunk_create((u_char *)esa_info_i, sizeof(esa_info_t));
|
*encr_i = chunk_create((u_char *)esa_info_i, sizeof(esa_info_t));
|
||||||
@@ -296,6 +296,12 @@ METHOD(keymat_v2_t, get_skd, pseudo_random_function_t,
|
|||||||
{
|
{
|
||||||
isa_info_t *isa_info;
|
isa_info_t *isa_info;
|
||||||
|
|
||||||
|
if (!this->ae_ctx_id)
|
||||||
|
{
|
||||||
|
*skd = chunk_empty;
|
||||||
|
return PRF_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
INIT(isa_info,
|
INIT(isa_info,
|
||||||
.parent_isa_id = this->isa_ctx_id,
|
.parent_isa_id = this->isa_ctx_id,
|
||||||
.ae_id = this->ae_ctx_id,
|
.ae_id = this->ae_ctx_id,
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ struct esa_info_t {
|
|||||||
isa_id_type isa_id;
|
isa_id_type isa_id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responder SPI of child SA.
|
* Local SPI of child SA.
|
||||||
*/
|
*/
|
||||||
esp_spi_type spi_r;
|
esp_spi_type spi_l;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiator nonce.
|
* Initiator nonce.
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ START_TEST(test_derive_child_keys)
|
|||||||
fail_if(!info, "encr_i does not contain esa information");
|
fail_if(!info, "encr_i does not contain esa information");
|
||||||
fail_if(info->isa_id != keymat->get_isa_id(keymat),
|
fail_if(info->isa_id != keymat->get_isa_id(keymat),
|
||||||
"Isa context id mismatch (encr_i)");
|
"Isa context id mismatch (encr_i)");
|
||||||
fail_if(info->spi_r != 42,
|
fail_if(info->spi_l != 42,
|
||||||
"SPI mismatch (encr_i)");
|
"SPI mismatch (encr_i)");
|
||||||
fail_unless(chunk_equals(info->nonce_i, nonce),
|
fail_unless(chunk_equals(info->nonce_i, nonce),
|
||||||
"nonce_i mismatch (encr_i)");
|
"nonce_i mismatch (encr_i)");
|
||||||
@@ -124,7 +124,7 @@ START_TEST(test_derive_child_keys)
|
|||||||
fail_if(!info, "encr_r does not contain esa information");
|
fail_if(!info, "encr_r does not contain esa information");
|
||||||
fail_if(info->isa_id != keymat->get_isa_id(keymat),
|
fail_if(info->isa_id != keymat->get_isa_id(keymat),
|
||||||
"Isa context id mismatch (encr_r)");
|
"Isa context id mismatch (encr_r)");
|
||||||
fail_if(info->spi_r != 42,
|
fail_if(info->spi_l != 42,
|
||||||
"SPI mismatch (encr_r)");
|
"SPI mismatch (encr_r)");
|
||||||
fail_unless(chunk_equals(info->nonce_i, nonce),
|
fail_unless(chunk_equals(info->nonce_i, nonce),
|
||||||
"nonce_i mismatch (encr_r)");
|
"nonce_i mismatch (encr_r)");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -81,13 +81,13 @@ struct child_create_t {
|
|||||||
void (*use_label)(child_create_t *this, sec_label_t *label);
|
void (*use_label)(child_create_t *this, sec_label_t *label);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initially propose a specific DH group to override configuration.
|
* Initially propose a specific KE method to override configuration.
|
||||||
*
|
*
|
||||||
* This is used during rekeying to prefer the previously negotiated group.
|
* This is used during rekeying to prefer the previously negotiated method.
|
||||||
*
|
*
|
||||||
* @param dh_group DH group to use
|
* @param ke_method KE method to use
|
||||||
*/
|
*/
|
||||||
void (*use_dh_group)(child_create_t *this, key_exchange_method_t dh_group);
|
void (*use_ke_method)(child_create_t *this, key_exchange_method_t ke_method);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the lower of the two nonces, used for rekey collisions.
|
* Get the lower of the two nonces, used for rekey collisions.
|
||||||
|
|||||||
@@ -201,16 +201,16 @@ METHOD(task_t, build_i, status_t,
|
|||||||
if (!this->child_create)
|
if (!this->child_create)
|
||||||
{
|
{
|
||||||
proposal_t *proposal;
|
proposal_t *proposal;
|
||||||
uint16_t dh_group;
|
uint16_t ke_method;
|
||||||
|
|
||||||
this->child_create = child_create_create(this->ike_sa,
|
this->child_create = child_create_create(this->ike_sa,
|
||||||
config->get_ref(config), TRUE, NULL, NULL);
|
config->get_ref(config), TRUE, NULL, NULL);
|
||||||
|
|
||||||
proposal = this->child_sa->get_proposal(this->child_sa);
|
proposal = this->child_sa->get_proposal(this->child_sa);
|
||||||
if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD,
|
if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD,
|
||||||
&dh_group, NULL))
|
&ke_method, NULL))
|
||||||
{ /* reuse the DH group negotiated previously */
|
{ /* reuse the KE method negotiated previously */
|
||||||
this->child_create->use_dh_group(this->child_create, dh_group);
|
this->child_create->use_ke_method(this->child_create, ke_method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reqid = this->child_sa->get_reqid_ref(this->child_sa);
|
reqid = this->child_sa->get_reqid_ref(this->child_sa);
|
||||||
|
|||||||
Reference in New Issue
Block a user