From 3f730ec1cd930dc8ceafea3465a32c6104982e26 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 16 May 2008 13:27:21 +0000 Subject: [PATCH] Added support for AES-CCM and AES-GCM (authenticated encryption algorithms) in charon. --- src/charon/config/proposal.c | 123 +++++++++++++++-- .../payloads/transform_substructure.c | 28 +++- src/charon/kernel/kernel_interface.c | 125 +++++++++++++----- src/libstrongswan/crypto/crypters/crypter.c | 13 +- src/libstrongswan/crypto/crypters/crypter.h | 8 +- 5 files changed, 242 insertions(+), 55 deletions(-) diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c index 53f58f243..4e05bdc70 100644 --- a/src/charon/config/proposal.c +++ b/src/charon/config/proposal.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2006 Martin Willi * 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 */ @@ -343,18 +362,21 @@ static proposal_t *select_proposal(private_proposal_t *this, private_proposal_t return NULL; } /* 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 */ 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; } +/** + * 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. * 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); } + 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) { 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; } + check_proposal(this); + if (protocol == PROTO_AH || protocol == PROTO_ESP) { add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); diff --git a/src/charon/encoding/payloads/transform_substructure.c b/src/charon/encoding/payloads/transform_substructure.c index 6706cf802..27e921a90 100644 --- a/src/charon/encoding/payloads/transform_substructure.c +++ b/src/charon/encoding/payloads/transform_substructure.c @@ -390,14 +390,28 @@ transform_substructure_t *transform_substructure_create_type(transform_type_t tr transform->set_transform_id(transform,transform_id); /* a keylength attribute is only created for variable length algos */ - if (transform_type == ENCRYPTION_ALGORITHM && - (transform_id == ENCR_AES_CBC || - transform_id == ENCR_IDEA || - transform_id == ENCR_CAST || - transform_id == ENCR_BLOWFISH)) + if (transform_type == ENCRYPTION_ALGORITHM) { - transform_attribute_t *attribute = transform_attribute_create_key_length(key_length); - transform->add_transform_attribute(transform,attribute); + switch(transform_id) + { + 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; diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index 43837a29c..4d47907bf 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -112,18 +112,24 @@ struct kernel_algorithm_t { * Algorithms for encryption */ static kernel_algorithm_t encryption_algs[] = { -/* {ENCR_DES_IV64, "***", 0}, */ - {ENCR_DES, "des", 64}, - {ENCR_3DES, "des3_ede", 192}, -/* {ENCR_RC5, "***", 0}, */ -/* {ENCR_IDEA, "***", 0}, */ - {ENCR_CAST, "cast128", 0}, - {ENCR_BLOWFISH, "blowfish", 0}, -/* {ENCR_3IDEA, "***", 0}, */ -/* {ENCR_DES_IV32, "***", 0}, */ - {ENCR_NULL, "cipher_null", 0}, - {ENCR_AES_CBC, "aes", 0}, -/* {ENCR_AES_CTR, "***", 0}, */ +/* {ENCR_DES_IV64, "***", 0}, */ + {ENCR_DES, "des", 64}, + {ENCR_3DES, "des3_ede", 192}, +/* {ENCR_RC5, "***", 0}, */ +/* {ENCR_IDEA, "***", 0}, */ + {ENCR_CAST, "cast128", 0}, + {ENCR_BLOWFISH, "blowfish", 0}, +/* {ENCR_3IDEA, "***", 0}, */ +/* {ENCR_DES_IV32, "***", 0}, */ + {ENCR_NULL, "cipher_null", 0}, + {ENCR_AES_CBC, "aes", 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}, }; @@ -1966,6 +1972,7 @@ static status_t add_sa(private_kernel_interface_t *this, { unsigned char request[BUFFER_SIZE]; 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 xfrm_usersa_info *sa; @@ -2000,34 +2007,82 @@ static status_t add_sa(private_kernel_interface_t *this, struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); - if (enc_alg != ENCR_UNDEFINED) + switch (enc_alg) { - rthdr->rta_type = XFRMA_ALG_CRYPT; - alg_name = lookup_algorithm(encryption_algs, enc_alg, &enc_size); - if (alg_name == NULL) + case ENCR_UNDEFINED: + /* no encryption */ + 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!", - encryption_algorithm_names, enc_alg); - return FAILED; + u_int16_t icv_size = 0; + rthdr->rta_type = XFRMA_ALG_AEAD; + alg_name = lookup_algorithm(encryption_algs, enc_alg, &icv_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); + + /* additional KEYMAT required */ + enc_size += add_keymat; + + 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; } - 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); - hdr->nlmsg_len += rthdr->rta_len; - if (hdr->nlmsg_len > sizeof(request)) + 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) { rthdr->rta_type = XFRMA_ALG_AUTH; @@ -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", 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; if (hdr->nlmsg_len > sizeof(request)) { diff --git a/src/libstrongswan/crypto/crypters/crypter.c b/src/libstrongswan/crypto/crypters/crypter.c index 69bab02fb..879cf6049 100644 --- a/src/libstrongswan/crypto/crypters/crypter.c +++ b/src/libstrongswan/crypto/crypters/crypter.c @@ -30,9 +30,16 @@ ENUM_NEXT(encryption_algorithm_names, ENCR_DES_IV64, ENCR_DES_IV32, ENCR_UNDEFIN "BLOWFISH", "3IDEA", "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", "AES_CBC", - "AES_CTR"); -ENUM_END(encryption_algorithm_names, ENCR_AES_CTR); + "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); diff --git a/src/libstrongswan/crypto/crypters/crypter.h b/src/libstrongswan/crypto/crypters/crypter.h index 5a64b1cf7..345d7abe3 100644 --- a/src/libstrongswan/crypto/crypters/crypter.h +++ b/src/libstrongswan/crypto/crypters/crypter.h @@ -45,7 +45,13 @@ enum encryption_algorithm_t { ENCR_DES_IV32 = 9, ENCR_NULL = 11, 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 }; /**