Added support for AES-CCM and AES-GCM (authenticated encryption algorithms) in charon.

This commit is contained in:
Tobias Brunner
2008-05-16 13:27:21 +00:00
parent 5a9f62a754
commit 3f730ec1cd
5 changed files with 242 additions and 55 deletions
+114 -9
View File
@@ -1,4 +1,5 @@
/* /*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2006 Martin Willi * Copyright (C) 2006 Martin Willi
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
@@ -263,6 +264,24 @@ static void strip_dh(private_proposal_t *this)
} }
} }
/**
* Returns true if the given alg is an authenticated encryption algorithm
*/
static bool is_authenticated_encryption(u_int16_t alg)
{
switch(alg)
{
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
return TRUE;
}
return FALSE;
}
/** /**
* Find a matching alg/keysize in two linked lists * Find a matching alg/keysize in two linked lists
*/ */
@@ -343,18 +362,21 @@ static proposal_t *select_proposal(private_proposal_t *this, private_proposal_t
return NULL; return NULL;
} }
/* select integrity algorithm */ /* select integrity algorithm */
if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size)) if (!is_authenticated_encryption(algo))
{ {
if (add) if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size))
{ {
selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size); if (add)
{
selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size);
}
}
else
{
selected->destroy(selected);
DBG2(DBG_CFG, " no acceptable INTEGRITY_ALGORITHM found, skipping");
return NULL;
} }
}
else
{
selected->destroy(selected);
DBG2(DBG_CFG, " no acceptable INTEGRITY_ALGORITHM found, skipping");
return NULL;
} }
/* select prf algorithm */ /* select prf algorithm */
if (select_algo(this->prf_algos, other->prf_algos, &add, &algo, &key_size)) if (select_algo(this->prf_algos, other->prf_algos, &add, &algo, &key_size))
@@ -518,6 +540,37 @@ static proposal_t *clone_(private_proposal_t *this)
return &clone->public; return &clone->public;
} }
/**
* Checks the proposal read from a string.
*/
static void check_proposal(private_proposal_t *this)
{
enumerator_t *e;
algorithm_t *alg;
bool all_aead = TRUE;
e = this->encryption_algos->create_enumerator(this->encryption_algos);
while (e->enumerate(e, &alg))
{
if (!is_authenticated_encryption(alg->algorithm))
{
all_aead = FALSE;
break;
}
}
e->destroy(e);
if (all_aead)
{
/* if all encryption algorithms in the proposal are authenticated encryption
* algorithms we MUST NOT propose any integrity algorithms */
while(this->integrity_algos->remove_last(this->integrity_algos, (void**)&alg) == SUCCESS)
{
free(alg);
}
}
}
/** /**
* add a algorithm identified by a string to the proposal. * add a algorithm identified by a string to the proposal.
* TODO: we could use gperf here. * TODO: we could use gperf here.
@@ -540,6 +593,56 @@ static status_t add_string_algo(private_proposal_t *this, chunk_t alg)
{ {
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256);
} }
else if (strstr(alg.ptr, "ccm"))
{
u_int16_t key_size, icv_size;
if (sscanf(alg.ptr, "aes%huccm%hu", &key_size, &icv_size) == 2)
{
if (key_size == 128 || key_size == 192 || key_size == 256)
{
switch(icv_size)
{
case 8:
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV8, key_size);
break;
case 12:
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV12, key_size);
break;
case 16:
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CCM_ICV16, key_size);
break;
default:
/* invalid ICV size */
break;
}
}
}
}
else if (strstr(alg.ptr, "gcm"))
{
u_int16_t key_size, icv_size;
if (sscanf(alg.ptr, "aes%hugcm%hu", &key_size, &icv_size) == 2)
{
if (key_size == 128 || key_size == 192 || key_size == 256)
{
switch(icv_size)
{
case 8:
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV8, key_size);
break;
case 12:
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV12, key_size);
break;
case 16:
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_GCM_ICV16, key_size);
break;
default:
/* invalid ICV size */
break;
}
}
}
}
else if (strncmp(alg.ptr, "3des", alg.len) == 0) else if (strncmp(alg.ptr, "3des", alg.len) == 0)
{ {
add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0);
@@ -774,6 +877,8 @@ proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs
return NULL; return NULL;
} }
check_proposal(this);
if (protocol == PROTO_AH || protocol == PROTO_ESP) if (protocol == PROTO_AH || protocol == PROTO_ESP)
{ {
add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0);
@@ -390,14 +390,28 @@ transform_substructure_t *transform_substructure_create_type(transform_type_t tr
transform->set_transform_id(transform,transform_id); transform->set_transform_id(transform,transform_id);
/* a keylength attribute is only created for variable length algos */ /* a keylength attribute is only created for variable length algos */
if (transform_type == ENCRYPTION_ALGORITHM && if (transform_type == ENCRYPTION_ALGORITHM)
(transform_id == ENCR_AES_CBC ||
transform_id == ENCR_IDEA ||
transform_id == ENCR_CAST ||
transform_id == ENCR_BLOWFISH))
{ {
transform_attribute_t *attribute = transform_attribute_create_key_length(key_length); switch(transform_id)
transform->add_transform_attribute(transform,attribute); {
case ENCR_AES_CBC:
case ENCR_IDEA:
case ENCR_CAST:
case ENCR_BLOWFISH:
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
{
transform_attribute_t *attribute = transform_attribute_create_key_length(key_length);
transform->add_transform_attribute(transform,attribute);
break;
}
default:
break;
}
} }
return transform; return transform;
+89 -34
View File
@@ -112,18 +112,24 @@ struct kernel_algorithm_t {
* Algorithms for encryption * Algorithms for encryption
*/ */
static kernel_algorithm_t encryption_algs[] = { static kernel_algorithm_t encryption_algs[] = {
/* {ENCR_DES_IV64, "***", 0}, */ /* {ENCR_DES_IV64, "***", 0}, */
{ENCR_DES, "des", 64}, {ENCR_DES, "des", 64},
{ENCR_3DES, "des3_ede", 192}, {ENCR_3DES, "des3_ede", 192},
/* {ENCR_RC5, "***", 0}, */ /* {ENCR_RC5, "***", 0}, */
/* {ENCR_IDEA, "***", 0}, */ /* {ENCR_IDEA, "***", 0}, */
{ENCR_CAST, "cast128", 0}, {ENCR_CAST, "cast128", 0},
{ENCR_BLOWFISH, "blowfish", 0}, {ENCR_BLOWFISH, "blowfish", 0},
/* {ENCR_3IDEA, "***", 0}, */ /* {ENCR_3IDEA, "***", 0}, */
/* {ENCR_DES_IV32, "***", 0}, */ /* {ENCR_DES_IV32, "***", 0}, */
{ENCR_NULL, "cipher_null", 0}, {ENCR_NULL, "cipher_null", 0},
{ENCR_AES_CBC, "aes", 0}, {ENCR_AES_CBC, "aes", 0},
/* {ENCR_AES_CTR, "***", 0}, */ /* {ENCR_AES_CTR, "***", 0}, */
{ENCR_AES_CCM_ICV8, "rfc4309(ccm(aes))", 64}, /* key_size = ICV size */
{ENCR_AES_CCM_ICV12, "rfc4309(ccm(aes))", 96}, /* key_size = ICV size */
{ENCR_AES_CCM_ICV16, "rfc4309(ccm(aes))", 128}, /* key_size = ICV size */
{ENCR_AES_GCM_ICV8, "rfc4106(gcm(aes))", 64}, /* key_size = ICV size */
{ENCR_AES_GCM_ICV12, "rfc4106(gcm(aes))", 96}, /* key_size = ICV size */
{ENCR_AES_GCM_ICV16, "rfc4106(gcm(aes))", 128}, /* key_size = ICV size */
{END_OF_LIST, NULL, 0}, {END_OF_LIST, NULL, 0},
}; };
@@ -1966,6 +1972,7 @@ static status_t add_sa(private_kernel_interface_t *this,
{ {
unsigned char request[BUFFER_SIZE]; unsigned char request[BUFFER_SIZE];
char *alg_name; char *alg_name;
u_int16_t add_keymat = 32; /* additional 4 octets KEYMAT required for AES-GCM as of RFC4106 8.1. */
struct nlmsghdr *hdr; struct nlmsghdr *hdr;
struct xfrm_usersa_info *sa; struct xfrm_usersa_info *sa;
@@ -2000,32 +2007,80 @@ static status_t add_sa(private_kernel_interface_t *this,
struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info);
if (enc_alg != ENCR_UNDEFINED) switch (enc_alg)
{ {
rthdr->rta_type = XFRMA_ALG_CRYPT; case ENCR_UNDEFINED:
alg_name = lookup_algorithm(encryption_algs, enc_alg, &enc_size); /* no encryption */
if (alg_name == NULL) break;
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
/* AES-CCM needs only 3 additional octets KEYMAT as of RFC 4309 7.1. */
add_keymat = 24;
/* fall-through */
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
{ {
DBG1(DBG_KNL, "algorithm %N not supported by kernel!", u_int16_t icv_size = 0;
encryption_algorithm_names, enc_alg); rthdr->rta_type = XFRMA_ALG_AEAD;
return FAILED; alg_name = lookup_algorithm(encryption_algs, enc_alg, &icv_size);
} if (alg_name == NULL)
DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", {
encryption_algorithm_names, enc_alg, enc_size); DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
encryption_algorithm_names, enc_alg);
return FAILED;
}
DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
encryption_algorithm_names, enc_alg, enc_size);
rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_size); /* additional KEYMAT required */
hdr->nlmsg_len += rthdr->rta_len; enc_size += add_keymat;
if (hdr->nlmsg_len > sizeof(request))
rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_aead) + enc_size / 8);
hdr->nlmsg_len += rthdr->rta_len;
if (hdr->nlmsg_len > sizeof(request))
{
return FAILED;
}
struct xfrm_algo_aead* algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr);
algo->alg_key_len = enc_size;
algo->alg_icv_len = icv_size;
strcpy(algo->alg_name, alg_name);
prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key);
rthdr = XFRM_RTA_NEXT(rthdr);
break;
}
default:
{ {
return FAILED; rthdr->rta_type = XFRMA_ALG_CRYPT;
alg_name = lookup_algorithm(encryption_algs, enc_alg, &enc_size);
if (alg_name == NULL)
{
DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
encryption_algorithm_names, enc_alg);
return FAILED;
}
DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
encryption_algorithm_names, enc_alg, enc_size);
rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_size / 8);
hdr->nlmsg_len += rthdr->rta_len;
if (hdr->nlmsg_len > sizeof(request))
{
return FAILED;
}
struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr);
algo->alg_key_len = enc_size;
strcpy(algo->alg_name, alg_name);
prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key);
rthdr = XFRM_RTA_NEXT(rthdr);
break;
} }
struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr);
algo->alg_key_len = enc_size;
strcpy(algo->alg_name, alg_name);
prf_plus->get_bytes(prf_plus, enc_size / 8, algo->alg_key);
rthdr = XFRM_RTA_NEXT(rthdr);
} }
if (int_alg != AUTH_UNDEFINED) if (int_alg != AUTH_UNDEFINED)
@@ -2041,7 +2096,7 @@ static status_t add_sa(private_kernel_interface_t *this,
DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", DBG2(DBG_KNL, " using integrity algorithm %N with key size %d",
integrity_algorithm_names, int_alg, int_size); integrity_algorithm_names, int_alg, int_size);
rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_size); rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_size / 8);
hdr->nlmsg_len += rthdr->rta_len; hdr->nlmsg_len += rthdr->rta_len;
if (hdr->nlmsg_len > sizeof(request)) if (hdr->nlmsg_len > sizeof(request))
{ {
+10 -3
View File
@@ -30,9 +30,16 @@ ENUM_NEXT(encryption_algorithm_names, ENCR_DES_IV64, ENCR_DES_IV32, ENCR_UNDEFIN
"BLOWFISH", "BLOWFISH",
"3IDEA", "3IDEA",
"DES_IV32"); "DES_IV32");
ENUM_NEXT(encryption_algorithm_names, ENCR_NULL, ENCR_AES_CTR, ENCR_DES_IV32, ENUM_NEXT(encryption_algorithm_names, ENCR_NULL, ENCR_AES_CCM_ICV16, ENCR_DES_IV32,
"NULL", "NULL",
"AES_CBC", "AES_CBC",
"AES_CTR"); "AES_CTR",
ENUM_END(encryption_algorithm_names, ENCR_AES_CTR); "AES_CCM_8",
"AES_CCM_12",
"AES_CCM_16");
ENUM_NEXT(encryption_algorithm_names, ENCR_AES_GCM_ICV8, ENCR_AES_GCM_ICV16, ENCR_AES_CCM_ICV16,
"AES_GCM_8",
"AES_GCM_12",
"AES_GCM_16");
ENUM_END(encryption_algorithm_names, ENCR_AES_GCM_ICV16);
+7 -1
View File
@@ -45,7 +45,13 @@ enum encryption_algorithm_t {
ENCR_DES_IV32 = 9, ENCR_DES_IV32 = 9,
ENCR_NULL = 11, ENCR_NULL = 11,
ENCR_AES_CBC = 12, ENCR_AES_CBC = 12,
ENCR_AES_CTR = 13 ENCR_AES_CTR = 13,
ENCR_AES_CCM_ICV8 = 14,
ENCR_AES_CCM_ICV12 = 15,
ENCR_AES_CCM_ICV16 = 16,
ENCR_AES_GCM_ICV8 = 18,
ENCR_AES_GCM_ICV12 = 19,
ENCR_AES_GCM_ICV16 = 20
}; };
/** /**