Added IV generation to keymat_v1_t.
This commit is contained in:
@@ -16,9 +16,27 @@
|
|||||||
#include "keymat_v1.h"
|
#include "keymat_v1.h"
|
||||||
|
|
||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
|
#include <utils/linked_list.h>
|
||||||
|
|
||||||
typedef struct private_keymat_v1_t private_keymat_v1_t;
|
typedef struct private_keymat_v1_t private_keymat_v1_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max. number of IVs to track.
|
||||||
|
*/
|
||||||
|
#define MAX_IV 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data stored for IVs
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/** message ID */
|
||||||
|
u_int32_t mid;
|
||||||
|
/** current IV */
|
||||||
|
chunk_t iv;
|
||||||
|
/** last block of encrypted message */
|
||||||
|
chunk_t last_block;
|
||||||
|
} iv_data_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private data of an keymat_t object.
|
* Private data of an keymat_t object.
|
||||||
*/
|
*/
|
||||||
@@ -49,6 +67,11 @@ struct private_keymat_v1_t {
|
|||||||
*/
|
*/
|
||||||
aead_t *aead;
|
aead_t *aead;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hasher used for IV generation
|
||||||
|
*/
|
||||||
|
hasher_t *hasher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key used for authentication during main mode
|
* Key used for authentication during main mode
|
||||||
*/
|
*/
|
||||||
@@ -63,8 +86,30 @@ struct private_keymat_v1_t {
|
|||||||
* Key used for authentication after main mode
|
* Key used for authentication after main mode
|
||||||
*/
|
*/
|
||||||
chunk_t skeyid_a;
|
chunk_t skeyid_a;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase 1 IV
|
||||||
|
*/
|
||||||
|
iv_data_t phase1_iv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep track of IVs for exchanges after phase 1. We store only a limited
|
||||||
|
* number of IVs in an MRU sort of way. Stores iv_data_t objects.
|
||||||
|
*/
|
||||||
|
linked_list_t *ivs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy an iv_data_t object.
|
||||||
|
*/
|
||||||
|
static void iv_data_destroy(iv_data_t *this)
|
||||||
|
{
|
||||||
|
chunk_free(&this->last_block);
|
||||||
|
chunk_free(&this->iv);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants used in key derivation.
|
* Constants used in key derivation.
|
||||||
*/
|
*/
|
||||||
@@ -239,6 +284,28 @@ static u_int16_t auth_to_prf(u_int16_t alg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts integrity algorithm to hash algorithm
|
||||||
|
*/
|
||||||
|
static u_int16_t auth_to_hash(u_int16_t alg)
|
||||||
|
{
|
||||||
|
switch (alg)
|
||||||
|
{
|
||||||
|
case AUTH_HMAC_SHA1_96:
|
||||||
|
return HASH_SHA1;
|
||||||
|
case AUTH_HMAC_SHA2_256_128:
|
||||||
|
return HASH_SHA256;
|
||||||
|
case AUTH_HMAC_SHA2_384_192:
|
||||||
|
return HASH_SHA384;
|
||||||
|
case AUTH_HMAC_SHA2_512_256:
|
||||||
|
return HASH_SHA512;
|
||||||
|
case AUTH_HMAC_MD5_96:
|
||||||
|
return HASH_MD5;
|
||||||
|
default:
|
||||||
|
return HASH_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust the key length for PRF algorithms that expect a fixed key length.
|
* Adjust the key length for PRF algorithms that expect a fixed key length.
|
||||||
*/
|
*/
|
||||||
@@ -367,9 +434,132 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL) ||
|
||||||
|
(alg = auth_to_hash(alg)) == HASH_UNKNOWN)
|
||||||
|
{
|
||||||
|
DBG1(DBG_IKE, "no %N selected", transform_type_names, HASH_ALGORITHM);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
this->hasher = lib->crypto->create_hasher(lib->crypto, alg);
|
||||||
|
if (!this->hasher)
|
||||||
|
{
|
||||||
|
DBG1(DBG_IKE, "%N %N not supported!",
|
||||||
|
transform_type_names, HASH_ALGORITHM,
|
||||||
|
hash_algorithm_names, alg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dh->get_my_public_value(dh, &dh_me);
|
||||||
|
g_xi = this->initiator ? dh_me : dh_other;
|
||||||
|
g_xr = this->initiator ? dh_other : dh_me;
|
||||||
|
|
||||||
|
/* initial IV = hash(g^xi | g^xr) */
|
||||||
|
data = chunk_cata("cc", g_xi, g_xr);
|
||||||
|
this->hasher->allocate_hash(this->hasher, data, &this->phase1_iv.iv);
|
||||||
|
if (this->phase1_iv.iv.len > this->aead->get_block_size(this->aead))
|
||||||
|
{
|
||||||
|
this->phase1_iv.iv.len = this->aead->get_block_size(this->aead);
|
||||||
|
}
|
||||||
|
chunk_free(&dh_me);
|
||||||
|
DBG4(DBG_IKE, "initial IV %B", &this->phase1_iv.iv);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an IV
|
||||||
|
*/
|
||||||
|
static void generate_iv(private_keymat_v1_t *this, iv_data_t *iv)
|
||||||
|
{
|
||||||
|
if (iv->mid == 0 || iv->iv.ptr)
|
||||||
|
{ /* use last block of previous encrypted message */
|
||||||
|
chunk_free(&iv->iv);
|
||||||
|
iv->iv = iv->last_block;
|
||||||
|
iv->last_block = chunk_empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* initial phase 2 IV = hash(last_phase1_block | mid) */
|
||||||
|
u_int32_t net = htonl(iv->mid);
|
||||||
|
chunk_t data = chunk_cata("cc", this->phase1_iv.iv,
|
||||||
|
chunk_from_thing(net));
|
||||||
|
this->hasher->allocate_hash(this->hasher, data, &iv->iv);
|
||||||
|
if (iv->iv.len > this->aead->get_block_size(this->aead))
|
||||||
|
{
|
||||||
|
iv->iv.len = this->aead->get_block_size(this->aead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG4(DBG_IKE, "next IV for MID %u %B", iv->mid, &iv->iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to find an IV for the given message ID, if not found, generate it.
|
||||||
|
*/
|
||||||
|
static iv_data_t *lookup_iv(private_keymat_v1_t *this, u_int32_t mid)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
iv_data_t *iv, *found = NULL;
|
||||||
|
|
||||||
|
if (mid == 0)
|
||||||
|
{
|
||||||
|
return &this->phase1_iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
enumerator = this->ivs->create_enumerator(this->ivs);
|
||||||
|
while (enumerator->enumerate(enumerator, &iv))
|
||||||
|
{
|
||||||
|
if (iv->mid == mid)
|
||||||
|
{ /* IV gets moved to the front of the list */
|
||||||
|
this->ivs->remove_at(this->ivs, enumerator);
|
||||||
|
found = iv;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
INIT(found,
|
||||||
|
.mid = mid,
|
||||||
|
);
|
||||||
|
generate_iv(this, found);
|
||||||
|
}
|
||||||
|
this->ivs->insert_first(this->ivs, found);
|
||||||
|
/* remove least recently used IV if maximum reached */
|
||||||
|
if (this->ivs->get_count(this->ivs) > MAX_IV &&
|
||||||
|
this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS)
|
||||||
|
{
|
||||||
|
iv_data_destroy(iv);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(keymat_v1_t, get_iv, chunk_t,
|
||||||
|
private_keymat_v1_t *this, u_int32_t mid)
|
||||||
|
{
|
||||||
|
return chunk_clone(lookup_iv(this, mid)->iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(keymat_v1_t, update_iv, void,
|
||||||
|
private_keymat_v1_t *this, u_int32_t mid, chunk_t last_block)
|
||||||
|
{
|
||||||
|
iv_data_t *iv = lookup_iv(this, mid);
|
||||||
|
if (iv)
|
||||||
|
{ /* update last block */
|
||||||
|
chunk_free(&iv->last_block);
|
||||||
|
iv->last_block = chunk_clone(last_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(keymat_v1_t, confirm_iv, void,
|
||||||
|
private_keymat_v1_t *this, u_int32_t mid)
|
||||||
|
{
|
||||||
|
iv_data_t *iv = lookup_iv(this, mid);
|
||||||
|
if (iv)
|
||||||
|
{
|
||||||
|
generate_iv(this, iv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(keymat_t, create_dh, diffie_hellman_t*,
|
METHOD(keymat_t, create_dh, diffie_hellman_t*,
|
||||||
private_keymat_v1_t *this, diffie_hellman_group_t group)
|
private_keymat_v1_t *this, diffie_hellman_group_t group)
|
||||||
{
|
{
|
||||||
@@ -387,9 +577,13 @@ METHOD(keymat_t, destroy, void,
|
|||||||
{
|
{
|
||||||
DESTROY_IF(this->prf);
|
DESTROY_IF(this->prf);
|
||||||
DESTROY_IF(this->aead);
|
DESTROY_IF(this->aead);
|
||||||
|
DESTROY_IF(this->hasher);
|
||||||
chunk_clear(&this->skeyid);
|
chunk_clear(&this->skeyid);
|
||||||
chunk_clear(&this->skeyid_d);
|
chunk_clear(&this->skeyid_d);
|
||||||
chunk_clear(&this->skeyid_a);
|
chunk_clear(&this->skeyid_a);
|
||||||
|
chunk_free(&this->phase1_iv.iv);
|
||||||
|
chunk_free(&this->phase1_iv.last_block);
|
||||||
|
this->ivs->destroy_function(this->ivs, (void*)iv_data_destroy);
|
||||||
free(this);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +602,11 @@ keymat_v1_t *keymat_v1_create(bool initiator)
|
|||||||
.destroy = _destroy,
|
.destroy = _destroy,
|
||||||
},
|
},
|
||||||
.derive_ike_keys = _derive_ike_keys,
|
.derive_ike_keys = _derive_ike_keys,
|
||||||
|
.get_iv = _get_iv,
|
||||||
|
.update_iv = _update_iv,
|
||||||
|
.confirm_iv = _confirm_iv,
|
||||||
},
|
},
|
||||||
|
.ivs = linked_list_create(),
|
||||||
.initiator = initiator,
|
.initiator = initiator,
|
||||||
.prf_alg = PRF_UNDEFINED,
|
.prf_alg = PRF_UNDEFINED,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -56,6 +56,37 @@ struct keymat_v1_t {
|
|||||||
chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
|
chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
|
||||||
auth_class_t auth, shared_key_t *shared_key);
|
auth_class_t auth, shared_key_t *shared_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the IV for a message with the given message ID.
|
||||||
|
*
|
||||||
|
* @param mid message ID
|
||||||
|
* @return IV (needs to be freed)
|
||||||
|
*/
|
||||||
|
chunk_t (*get_iv)(keymat_v1_t *this, u_int32_t mid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the IV for the next message with the given message ID.
|
||||||
|
*
|
||||||
|
* A call of confirm_iv() is required in order to actually make the IV
|
||||||
|
* available. This is needed for the inbound case where we store the last
|
||||||
|
* block of the encrypted message but want to update the IV only after
|
||||||
|
* verification of the decrypted message.
|
||||||
|
*
|
||||||
|
* @param mid message ID
|
||||||
|
* @param last_block last block of encrypted message (gets cloned)
|
||||||
|
*/
|
||||||
|
void (*update_iv)(keymat_v1_t *this, u_int32_t mid, chunk_t last_block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirms the updated IV for the given message ID.
|
||||||
|
*
|
||||||
|
* To actually make the new IV available via get_iv this method has to
|
||||||
|
* be called after update_iv.
|
||||||
|
*
|
||||||
|
* @param mid message ID
|
||||||
|
*/
|
||||||
|
void (*confirm_iv)(keymat_v1_t *this, u_int32_t mid);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user