diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2018-02-19 18:17:21 +0100 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2018-02-19 18:17:21 +0100 |
commit | 7793611ee71b576dd9c66dee327349fa64e38740 (patch) | |
tree | f1379ec1aed52a3c772874d4ed690b90975b9623 /src/libstrongswan/crypto/proposal | |
parent | e1d78dc2faaa06e7c3f71ef674a71e4de2f0758e (diff) | |
download | vyos-strongswan-7793611ee71b576dd9c66dee327349fa64e38740.tar.gz vyos-strongswan-7793611ee71b576dd9c66dee327349fa64e38740.zip |
New upstream version 5.6.2
Diffstat (limited to 'src/libstrongswan/crypto/proposal')
-rw-r--r-- | src/libstrongswan/crypto/proposal/proposal.c | 1134 | ||||
-rw-r--r-- | src/libstrongswan/crypto/proposal/proposal.h | 246 | ||||
-rw-r--r-- | src/libstrongswan/crypto/proposal/proposal_keywords.h | 2 |
3 files changed, 1381 insertions, 1 deletions
diff --git a/src/libstrongswan/crypto/proposal/proposal.c b/src/libstrongswan/crypto/proposal/proposal.c new file mode 100644 index 000000000..bb0a02b59 --- /dev/null +++ b/src/libstrongswan/crypto/proposal/proposal.c @@ -0,0 +1,1134 @@ +/* + * Copyright (C) 2008-2018 Tobias Brunner + * Copyright (C) 2006-2010 Martin Willi + * Copyright (C) 2013-2015 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <string.h> + +#include "proposal.h" + +#include <collections/array.h> +#include <utils/identification.h> + +#include <crypto/transform.h> +#include <crypto/prfs/prf.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> + +ENUM(protocol_id_names, PROTO_NONE, PROTO_IPCOMP, + "PROTO_NONE", + "IKE", + "AH", + "ESP", + "IPCOMP", +); + +typedef struct private_proposal_t private_proposal_t; + +/** + * Private data of an proposal_t object + */ +struct private_proposal_t { + + /** + * Public part + */ + proposal_t public; + + /** + * protocol (ESP or AH) + */ + protocol_id_t protocol; + + /** + * Priority ordered list of transforms, as entry_t + */ + array_t *transforms; + + /** + * senders SPI + */ + uint64_t spi; + + /** + * Proposal number + */ + u_int number; +}; + +/** + * Struct used to store different kinds of algorithms. + */ +typedef struct { + /** Type of the transform */ + transform_type_t type; + /** algorithm identifier */ + uint16_t alg; + /** key size in bits, or zero if not needed */ + uint16_t key_size; +} entry_t; + +METHOD(proposal_t, add_algorithm, void, + private_proposal_t *this, transform_type_t type, + uint16_t alg, uint16_t key_size) +{ + entry_t entry = { + .type = type, + .alg = alg, + .key_size = key_size, + }; + + array_insert(this->transforms, ARRAY_TAIL, &entry); +} + +CALLBACK(alg_filter, bool, + uintptr_t type, enumerator_t *orig, va_list args) +{ + entry_t *entry; + uint16_t *alg, *key_size; + + VA_ARGS_VGET(args, alg, key_size); + + while (orig->enumerate(orig, &entry)) + { + if (entry->type != type) + { + continue; + } + if (alg) + { + *alg = entry->alg; + } + if (key_size) + { + *key_size = entry->key_size; + } + return TRUE; + } + return FALSE; +} + +METHOD(proposal_t, create_enumerator, enumerator_t*, + private_proposal_t *this, transform_type_t type) +{ + return enumerator_create_filter( + array_create_enumerator(this->transforms), + alg_filter, (void*)(uintptr_t)type, NULL); +} + +METHOD(proposal_t, get_algorithm, bool, + private_proposal_t *this, transform_type_t type, + uint16_t *alg, uint16_t *key_size) +{ + enumerator_t *enumerator; + bool found = FALSE; + + enumerator = create_enumerator(this, type); + if (enumerator->enumerate(enumerator, alg, key_size)) + { + found = TRUE; + } + enumerator->destroy(enumerator); + + return found; +} + +METHOD(proposal_t, has_dh_group, bool, + private_proposal_t *this, diffie_hellman_group_t group) +{ + bool found = FALSE, any = FALSE; + enumerator_t *enumerator; + uint16_t current; + + enumerator = create_enumerator(this, DIFFIE_HELLMAN_GROUP); + while (enumerator->enumerate(enumerator, ¤t, NULL)) + { + any = TRUE; + if (current == group) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!any && group == MODP_NONE) + { + found = TRUE; + } + return found; +} + +METHOD(proposal_t, promote_dh_group, bool, + private_proposal_t *this, diffie_hellman_group_t group) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = array_create_enumerator(this->transforms); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->type == DIFFIE_HELLMAN_GROUP && + entry->alg == group) + { + array_remove_at(this->transforms, enumerator); + found = TRUE; + } + } + enumerator->destroy(enumerator); + + if (found) + { + entry_t entry = { + .type = DIFFIE_HELLMAN_GROUP, + .alg = group, + }; + array_insert(this->transforms, ARRAY_HEAD, &entry); + } + return found; +} + +METHOD(proposal_t, strip_dh, void, + private_proposal_t *this, diffie_hellman_group_t keep) +{ + enumerator_t *enumerator; + entry_t *entry; + + enumerator = array_create_enumerator(this->transforms); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->type == DIFFIE_HELLMAN_GROUP && + entry->alg != keep) + { + array_remove_at(this->transforms, enumerator); + } + } + enumerator->destroy(enumerator); +} + +/** + * Select a matching proposal from this and other, insert into selected. + */ +static bool select_algo(private_proposal_t *this, proposal_t *other, + proposal_t *selected, transform_type_t type, bool priv) +{ + enumerator_t *e1, *e2; + uint16_t alg1, alg2, ks1, ks2; + bool found = FALSE, optional = FALSE; + + if (type == INTEGRITY_ALGORITHM && + selected->get_algorithm(selected, ENCRYPTION_ALGORITHM, &alg1, NULL) && + encryption_algorithm_is_aead(alg1)) + { + /* no integrity algorithm required, we have an AEAD */ + return TRUE; + } + if (type == DIFFIE_HELLMAN_GROUP) + { + optional = this->protocol == PROTO_ESP || this->protocol == PROTO_AH; + } + + e1 = create_enumerator(this, type); + e2 = other->create_enumerator(other, type); + if (!e1->enumerate(e1, &alg1, NULL)) + { + if (!e2->enumerate(e2, &alg2, NULL)) + { + found = TRUE; + } + else if (optional) + { + do + { /* if NONE is proposed, we accept the proposal */ + found = !alg2; + } + while (!found && e2->enumerate(e2, &alg2, NULL)); + } + } + else if (!e2->enumerate(e2, NULL, NULL)) + { + if (optional) + { + do + { /* if NONE is proposed, we accept the proposal */ + found = !alg1; + } + while (!found && e1->enumerate(e1, &alg1, NULL)); + } + } + + e1->destroy(e1); + e1 = create_enumerator(this, type); + /* compare algs, order of algs in "first" is preferred */ + while (!found && e1->enumerate(e1, &alg1, &ks1)) + { + e2->destroy(e2); + e2 = other->create_enumerator(other, type); + while (e2->enumerate(e2, &alg2, &ks2)) + { + if (alg1 == alg2 && ks1 == ks2) + { + if (!priv && alg1 >= 1024) + { + /* accept private use algorithms only if requested */ + DBG1(DBG_CFG, "an algorithm from private space would match, " + "but peer implementation is unknown, skipped"); + continue; + } + selected->add_algorithm(selected, type, alg1, ks1); + found = TRUE; + break; + } + } + } + /* no match in all comparisons */ + e1->destroy(e1); + e2->destroy(e2); + + if (!found) + { + DBG2(DBG_CFG, " no acceptable %N found", transform_type_names, type); + } + return found; +} + +METHOD(proposal_t, select_proposal, proposal_t*, + private_proposal_t *this, proposal_t *other, bool other_remote, + bool private) +{ + proposal_t *selected; + + DBG2(DBG_CFG, "selecting proposal:"); + + if (this->protocol != other->get_protocol(other)) + { + DBG2(DBG_CFG, " protocol mismatch, skipping"); + return NULL; + } + + if (other_remote) + { + selected = proposal_create(this->protocol, other->get_number(other)); + selected->set_spi(selected, other->get_spi(other)); + } + else + { + selected = proposal_create(this->protocol, this->number); + selected->set_spi(selected, this->spi); + + } + + if (!select_algo(this, other, selected, ENCRYPTION_ALGORITHM, private) || + !select_algo(this, other, selected, PSEUDO_RANDOM_FUNCTION, private) || + !select_algo(this, other, selected, INTEGRITY_ALGORITHM, private) || + !select_algo(this, other, selected, DIFFIE_HELLMAN_GROUP, private) || + !select_algo(this, other, selected, EXTENDED_SEQUENCE_NUMBERS, private)) + { + selected->destroy(selected); + return NULL; + } + + DBG2(DBG_CFG, " proposal matches"); + return selected; +} + +METHOD(proposal_t, get_protocol, protocol_id_t, + private_proposal_t *this) +{ + return this->protocol; +} + +METHOD(proposal_t, set_spi, void, + private_proposal_t *this, uint64_t spi) +{ + this->spi = spi; +} + +METHOD(proposal_t, get_spi, uint64_t, + private_proposal_t *this) +{ + return this->spi; +} + +/** + * Check if two proposals have the same algorithms for a given transform type + */ +static bool algo_list_equals(private_proposal_t *this, proposal_t *other, + transform_type_t type) +{ + enumerator_t *e1, *e2; + uint16_t alg1, alg2, ks1, ks2; + bool equals = TRUE; + + e1 = create_enumerator(this, type); + e2 = other->create_enumerator(other, type); + while (e1->enumerate(e1, &alg1, &ks1)) + { + if (!e2->enumerate(e2, &alg2, &ks2)) + { + /* this has more algs */ + equals = FALSE; + break; + } + if (alg1 != alg2 || ks1 != ks2) + { + equals = FALSE; + break; + } + } + if (e2->enumerate(e2, &alg2, &ks2)) + { + /* other has more algs */ + equals = FALSE; + } + e1->destroy(e1); + e2->destroy(e2); + + return equals; +} + +METHOD(proposal_t, get_number, u_int, + private_proposal_t *this) +{ + return this->number; +} + +METHOD(proposal_t, equals, bool, + private_proposal_t *this, proposal_t *other) +{ + if (&this->public == other) + { + return TRUE; + } + return ( + algo_list_equals(this, other, ENCRYPTION_ALGORITHM) && + algo_list_equals(this, other, INTEGRITY_ALGORITHM) && + algo_list_equals(this, other, PSEUDO_RANDOM_FUNCTION) && + algo_list_equals(this, other, DIFFIE_HELLMAN_GROUP) && + algo_list_equals(this, other, EXTENDED_SEQUENCE_NUMBERS)); +} + +METHOD(proposal_t, clone_, proposal_t*, + private_proposal_t *this) +{ + private_proposal_t *clone; + enumerator_t *enumerator; + entry_t *entry; + + clone = (private_proposal_t*)proposal_create(this->protocol, 0); + + enumerator = array_create_enumerator(this->transforms); + while (enumerator->enumerate(enumerator, &entry)) + { + array_insert(clone->transforms, ARRAY_TAIL, entry); + } + enumerator->destroy(enumerator); + + clone->spi = this->spi; + clone->number = this->number; + + return &clone->public; +} + +/** + * Map integrity algorithms to the PRF functions using the same algorithm. + */ +static const struct { + integrity_algorithm_t integ; + pseudo_random_function_t prf; +} integ_prf_map[] = { + {AUTH_HMAC_SHA1_96, PRF_HMAC_SHA1 }, + {AUTH_HMAC_SHA1_160, PRF_HMAC_SHA1 }, + {AUTH_HMAC_SHA2_256_128, PRF_HMAC_SHA2_256 }, + {AUTH_HMAC_SHA2_384_192, PRF_HMAC_SHA2_384 }, + {AUTH_HMAC_SHA2_512_256, PRF_HMAC_SHA2_512 }, + {AUTH_HMAC_MD5_96, PRF_HMAC_MD5 }, + {AUTH_HMAC_MD5_128, PRF_HMAC_MD5 }, + {AUTH_AES_XCBC_96, PRF_AES128_XCBC }, + {AUTH_CAMELLIA_XCBC_96, PRF_CAMELLIA128_XCBC }, + {AUTH_AES_CMAC_96, PRF_AES128_CMAC }, +}; + +/** + * Remove all entries of the given transform type + */ +static void remove_transform(private_proposal_t *this, transform_type_t type) +{ + enumerator_t *e; + entry_t *entry; + + e = array_create_enumerator(this->transforms); + while (e->enumerate(e, &entry)) + { + if (entry->type == type) + { + array_remove_at(this->transforms, e); + } + } + e->destroy(e); +} + +/** + * Checks the proposal read from a string. + */ +static bool check_proposal(private_proposal_t *this) +{ + enumerator_t *e; + entry_t *entry; + uint16_t alg, ks; + bool all_aead = TRUE, any_aead = FALSE, any_enc = FALSE; + int i; + + if (this->protocol == PROTO_IKE) + { + if (!get_algorithm(this, PSEUDO_RANDOM_FUNCTION, NULL, NULL)) + { /* No explicit PRF found. We assume the same algorithm as used + * for integrity checking. */ + e = create_enumerator(this, INTEGRITY_ALGORITHM); + while (e->enumerate(e, &alg, &ks)) + { + for (i = 0; i < countof(integ_prf_map); i++) + { + if (alg == integ_prf_map[i].integ) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, + integ_prf_map[i].prf, 0); + break; + } + } + } + e->destroy(e); + } + if (!get_algorithm(this, PSEUDO_RANDOM_FUNCTION, NULL, NULL)) + { + DBG1(DBG_CFG, "a PRF algorithm is mandatory in IKE proposals"); + return FALSE; + } + /* remove MODP_NONE from IKE proposal */ + e = array_create_enumerator(this->transforms); + while (e->enumerate(e, &entry)) + { + if (entry->type == DIFFIE_HELLMAN_GROUP && !entry->alg) + { + array_remove_at(this->transforms, e); + } + } + e->destroy(e); + if (!get_algorithm(this, DIFFIE_HELLMAN_GROUP, NULL, NULL)) + { + DBG1(DBG_CFG, "a DH group is mandatory in IKE proposals"); + return FALSE; + } + } + else + { /* remove PRFs from ESP/AH proposals */ + remove_transform(this, PSEUDO_RANDOM_FUNCTION); + } + + if (this->protocol == PROTO_IKE || this->protocol == PROTO_ESP) + { + e = create_enumerator(this, ENCRYPTION_ALGORITHM); + while (e->enumerate(e, &alg, &ks)) + { + any_enc = TRUE; + if (encryption_algorithm_is_aead(alg)) + { + any_aead = TRUE; + continue; + } + all_aead = FALSE; + } + e->destroy(e); + + if (!any_enc) + { + DBG1(DBG_CFG, "an encryption algorithm is mandatory in %N proposals", + protocol_id_names, this->protocol); + return FALSE; + } + else if (any_aead && !all_aead) + { + DBG1(DBG_CFG, "classic and combined-mode (AEAD) encryption " + "algorithms can't be contained in the same %N proposal", + protocol_id_names, this->protocol); + return FALSE; + } + else if (all_aead) + { /* if all encryption algorithms in the proposal are AEADs, + * we MUST NOT propose any integrity algorithms */ + remove_transform(this, INTEGRITY_ALGORITHM); + } + } + else + { /* AES-GMAC is parsed as encryption algorithm, so we map that to the + * proper integrity algorithm */ + e = array_create_enumerator(this->transforms); + while (e->enumerate(e, &entry)) + { + if (entry->type == ENCRYPTION_ALGORITHM) + { + if (entry->alg == ENCR_NULL_AUTH_AES_GMAC) + { + entry->type = INTEGRITY_ALGORITHM; + ks = entry->key_size; + entry->key_size = 0; + switch (ks) + { + case 128: + entry->alg = AUTH_AES_128_GMAC; + continue; + case 192: + entry->alg = AUTH_AES_192_GMAC; + continue; + case 256: + entry->alg = AUTH_AES_256_GMAC; + continue; + default: + break; + } + } + /* remove all other encryption algorithms */ + array_remove_at(this->transforms, e); + } + } + e->destroy(e); + + if (!get_algorithm(this, INTEGRITY_ALGORITHM, NULL, NULL)) + { + DBG1(DBG_CFG, "an integrity algorithm is mandatory in AH " + "proposals"); + return FALSE; + } + } + + if (this->protocol == PROTO_AH || this->protocol == PROTO_ESP) + { + if (!get_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NULL, NULL)) + { /* ESN not specified, assume not supported */ + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + } + } + + array_compress(this->transforms); + return TRUE; +} + +/** + * add a algorithm identified by a string to the proposal. + */ +static bool add_string_algo(private_proposal_t *this, const char *alg) +{ + const proposal_token_t *token; + + token = lib->proposal->get_token(lib->proposal, alg); + if (token == NULL) + { + DBG1(DBG_CFG, "algorithm '%s' not recognized", alg); + return FALSE; + } + + add_algorithm(this, token->type, token->algorithm, token->keysize); + + return TRUE; +} + +/** + * print all algorithms of a kind to buffer + */ +static int print_alg(private_proposal_t *this, printf_hook_data_t *data, + u_int kind, void *names, bool *first) +{ + enumerator_t *enumerator; + size_t written = 0; + uint16_t alg, size; + + enumerator = create_enumerator(this, kind); + while (enumerator->enumerate(enumerator, &alg, &size)) + { + if (*first) + { + written += print_in_hook(data, "%N", names, alg); + *first = FALSE; + } + else + { + written += print_in_hook(data, "/%N", names, alg); + } + if (size) + { + written += print_in_hook(data, "_%u", size); + } + } + enumerator->destroy(enumerator); + return written; +} + +/** + * Described in header. + */ +int proposal_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args) +{ + private_proposal_t *this = *((private_proposal_t**)(args[0])); + linked_list_t *list = *((linked_list_t**)(args[0])); + enumerator_t *enumerator; + size_t written = 0; + bool first = TRUE; + + if (this == NULL) + { + return print_in_hook(data, "(null)"); + } + + if (spec->hash) + { + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &this)) + { /* call recursively */ + if (first) + { + written += print_in_hook(data, "%P", this); + first = FALSE; + } + else + { + written += print_in_hook(data, ", %P", this); + } + } + enumerator->destroy(enumerator); + return written; + } + + written = print_in_hook(data, "%N:", protocol_id_names, this->protocol); + written += print_alg(this, data, ENCRYPTION_ALGORITHM, + encryption_algorithm_names, &first); + written += print_alg(this, data, INTEGRITY_ALGORITHM, + integrity_algorithm_names, &first); + written += print_alg(this, data, PSEUDO_RANDOM_FUNCTION, + pseudo_random_function_names, &first); + written += print_alg(this, data, DIFFIE_HELLMAN_GROUP, + diffie_hellman_group_names, &first); + written += print_alg(this, data, EXTENDED_SEQUENCE_NUMBERS, + extended_sequence_numbers_names, &first); + return written; +} + +METHOD(proposal_t, destroy, void, + private_proposal_t *this) +{ + array_destroy(this->transforms); + free(this); +} + +/* + * Described in header + */ +proposal_t *proposal_create(protocol_id_t protocol, u_int number) +{ + private_proposal_t *this; + + INIT(this, + .public = { + .add_algorithm = _add_algorithm, + .create_enumerator = _create_enumerator, + .get_algorithm = _get_algorithm, + .has_dh_group = _has_dh_group, + .promote_dh_group = _promote_dh_group, + .strip_dh = _strip_dh, + .select = _select_proposal, + .get_protocol = _get_protocol, + .set_spi = _set_spi, + .get_spi = _get_spi, + .get_number = _get_number, + .equals = _equals, + .clone = _clone_, + .destroy = _destroy, + }, + .protocol = protocol, + .number = number, + .transforms = array_create(sizeof(entry_t), 0), + ); + + return &this->public; +} + +/** + * Add supported IKE algorithms to proposal + */ +static bool proposal_add_supported_ike(private_proposal_t *this, bool aead) +{ + enumerator_t *enumerator; + encryption_algorithm_t encryption; + integrity_algorithm_t integrity; + pseudo_random_function_t prf; + diffie_hellman_group_t group; + const char *plugin_name; + + if (aead) + { + /* Round 1 adds algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_aead_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + switch (encryption) + { + case ENCR_AES_GCM_ICV16: + case ENCR_AES_CCM_ICV16: + case ENCR_CAMELLIA_CCM_ICV16: + /* we assume that we support all AES/Camellia sizes */ + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); + break; + case ENCR_CHACHA20_POLY1305: + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_aead_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + switch (encryption) + { + case ENCR_AES_GCM_ICV12: + case ENCR_AES_GCM_ICV8: + case ENCR_AES_CCM_ICV12: + case ENCR_AES_CCM_ICV8: + case ENCR_CAMELLIA_CCM_ICV12: + case ENCR_CAMELLIA_CCM_ICV8: + /* we assume that we support all AES/Camellia sizes */ + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + if (!array_count(this->transforms)) + { + return FALSE; + } + } + else + { + /* Round 1 adds algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_crypter_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + switch (encryption) + { + case ENCR_AES_CBC: + case ENCR_AES_CTR: + case ENCR_CAMELLIA_CBC: + case ENCR_CAMELLIA_CTR: + /* we assume that we support all AES/Camellia sizes */ + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 256); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_crypter_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &encryption, &plugin_name)) + { + switch (encryption) + { + case ENCR_3DES: + add_algorithm(this, ENCRYPTION_ALGORITHM, encryption, 0); + break; + case ENCR_DES: + /* no, thanks */ + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + if (!array_count(this->transforms)) + { + return FALSE; + } + + /* Round 1 adds algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_signer_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &integrity, &plugin_name)) + { + switch (integrity) + { + case AUTH_HMAC_SHA2_256_128: + case AUTH_HMAC_SHA2_384_192: + case AUTH_HMAC_SHA2_512_256: + add_algorithm(this, INTEGRITY_ALGORITHM, integrity, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_signer_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &integrity, &plugin_name)) + { + switch (integrity) + { + case AUTH_AES_XCBC_96: + case AUTH_AES_CMAC_96: + case AUTH_HMAC_SHA1_96: + add_algorithm(this, INTEGRITY_ALGORITHM, integrity, 0); + break; + case AUTH_HMAC_MD5_96: + /* no, thanks */ + default: + break; + } + } + enumerator->destroy(enumerator); + } + + /* Round 1 adds algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_prf_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &prf, &plugin_name)) + { + switch (prf) + { + case PRF_HMAC_SHA2_256: + case PRF_HMAC_SHA2_384: + case PRF_HMAC_SHA2_512: + case PRF_AES128_XCBC: + case PRF_AES128_CMAC: + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, prf, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_prf_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &prf, &plugin_name)) + { + switch (prf) + { + case PRF_HMAC_SHA1: + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, prf, 0); + break; + case PRF_HMAC_MD5: + /* no, thanks */ + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 1 adds ECC and NTRU algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &group, &plugin_name)) + { + switch (group) + { + case ECP_256_BIT: + case ECP_384_BIT: + case ECP_521_BIT: + case ECP_256_BP: + case ECP_384_BP: + case ECP_512_BP: + case CURVE_25519: + case CURVE_448: + case NTRU_128_BIT: + case NTRU_192_BIT: + case NTRU_256_BIT: + case NH_128_BIT: + add_algorithm(this, DIFFIE_HELLMAN_GROUP, group, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 2 adds other algorithms with at least 128 bit security strength */ + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &group, &plugin_name)) + { + switch (group) + { + case MODP_3072_BIT: + case MODP_4096_BIT: + case MODP_6144_BIT: + case MODP_8192_BIT: + add_algorithm(this, DIFFIE_HELLMAN_GROUP, group, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + /* Round 3 adds algorithms with less than 128 bit security strength */ + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &group, &plugin_name)) + { + switch (group) + { + case MODP_NULL: + /* only for testing purposes */ + break; + case MODP_768_BIT: + case MODP_1024_BIT: + case MODP_1536_BIT: + /* weak */ + break; + case MODP_1024_160: + case MODP_2048_224: + case MODP_2048_256: + /* RFC 5114 primes are of questionable source */ + break; + case ECP_224_BIT: + case ECP_224_BP: + case ECP_192_BIT: + case NTRU_112_BIT: + /* rarely used */ + break; + case MODP_2048_BIT: + add_algorithm(this, DIFFIE_HELLMAN_GROUP, group, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + return TRUE; +} + +/* + * Described in header + */ +proposal_t *proposal_create_default(protocol_id_t protocol) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol, 0); + + switch (protocol) + { + case PROTO_IKE: + if (!proposal_add_supported_ike(this, FALSE)) + { + destroy(this); + return NULL; + } + break; + case PROTO_ESP: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + case PROTO_AH: + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + default: + break; + } + return &this->public; +} + +/* + * Described in header + */ +proposal_t *proposal_create_default_aead(protocol_id_t protocol) +{ + private_proposal_t *this; + + switch (protocol) + { + case PROTO_IKE: + this = (private_proposal_t*)proposal_create(protocol, 0); + if (!proposal_add_supported_ike(this, TRUE)) + { + destroy(this); + return NULL; + } + return &this->public; + case PROTO_ESP: + /* we currently don't include any AEAD proposal for ESP, as we + * don't know if our kernel backend actually supports it. */ + return NULL; + case PROTO_AH: + default: + return NULL; + } +} + +/* + * Described in header + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs) +{ + private_proposal_t *this; + enumerator_t *enumerator; + bool failed = TRUE; + char *alg; + + this = (private_proposal_t*)proposal_create(protocol, 0); + + /* get all tokens, separated by '-' */ + enumerator = enumerator_create_token(algs, "-", " "); + while (enumerator->enumerate(enumerator, &alg)) + { + if (!add_string_algo(this, alg)) + { + failed = TRUE; + break; + } + failed = FALSE; + } + enumerator->destroy(enumerator); + + if (failed || !check_proposal(this)) + { + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libstrongswan/crypto/proposal/proposal.h b/src/libstrongswan/crypto/proposal/proposal.h new file mode 100644 index 000000000..0052674b9 --- /dev/null +++ b/src/libstrongswan/crypto/proposal/proposal.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2009-2018 Tobias Brunner + * Copyright (C) 2006 Martin Willi + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup proposal proposal + * @{ @ingroup crypto + */ + +#ifndef PROPOSAL_H_ +#define PROPOSAL_H_ + +typedef enum protocol_id_t protocol_id_t; +typedef enum extended_sequence_numbers_t extended_sequence_numbers_t; +typedef struct proposal_t proposal_t; + +#include <library.h> +#include <utils/identification.h> +#include <collections/linked_list.h> +#include <networking/host.h> +#include <crypto/transform.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <crypto/diffie_hellman.h> +#include <selectors/traffic_selector.h> + +/** + * Protocol ID of a proposal. + */ +enum protocol_id_t { + PROTO_NONE = 0, + PROTO_IKE = 1, + PROTO_AH = 2, + PROTO_ESP = 3, + PROTO_IPCOMP = 4, /* IKEv1 only */ +}; + +/** + * enum names for protocol_id_t + */ +extern enum_name_t *protocol_id_names; + +/** + * Stores a set of algorithms used for an SA. + * + * A proposal stores algorithms for a specific + * protocol. It can store algorithms for one protocol. + * Proposals with multiple protocols are not supported, + * as it's not specified in RFC4301 anymore. + */ +struct proposal_t { + + /** + * Add an algorithm to the proposal. + * + * The algorithms are stored by priority, first added + * is the most preferred. + * Key size is only needed for encryption algorithms + * with variable key size (such as AES). Must be set + * to zero if key size is not specified. + * The alg parameter accepts encryption_algorithm_t, + * integrity_algorithm_t, dh_group_number_t and + * extended_sequence_numbers_t. + * + * @param type kind of algorithm + * @param alg identifier for algorithm + * @param key_size key size to use + */ + void (*add_algorithm) (proposal_t *this, transform_type_t type, + uint16_t alg, uint16_t key_size); + + /** + * Get an enumerator over algorithms for a specific algo type. + * + * @param type kind of algorithm + * @return enumerator over uint16_t alg, uint16_t key_size + */ + enumerator_t *(*create_enumerator) (proposal_t *this, transform_type_t type); + + /** + * Get the algorithm for a type to use. + * + * If there are multiple algorithms, only the first is returned. + * + * @param type kind of algorithm + * @param alg pointer which receives algorithm + * @param key_size pointer which receives the key size + * @return TRUE if algorithm of this kind available + */ + bool (*get_algorithm) (proposal_t *this, transform_type_t type, + uint16_t *alg, uint16_t *key_size); + + /** + * Check if the proposal has a specific DH group. + * + * @param group group to check for + * @return TRUE if algorithm included + */ + bool (*has_dh_group)(proposal_t *this, diffie_hellman_group_t group); + + /** + * Move the given DH group to the front of the list if it was contained in + * the proposal. + * + * @param group group to promote + * @return TRUE if algorithm included + */ + bool (*promote_dh_group)(proposal_t *this, diffie_hellman_group_t group); + + /** + * Strip DH groups from proposal to use it without PFS. + * + * @param keep group to keep (MODP_NONE to remove all) + */ + void (*strip_dh)(proposal_t *this, diffie_hellman_group_t keep); + + /** + * Compare two proposal, and select a matching subset. + * + * If the proposals are for the same protocols (AH/ESP), they are + * compared. If they have at least one algorithm of each type + * in common, a resulting proposal of this kind is created. + * + * @param other proposal to compare against + * @param other_remote whether other is the remote proposal from which to + * copy SPI and proposal number to the result, + * otherwise copy from this proposal + * @param private accepts algorithms allocated in a private range + * @return selected proposal, NULL if proposals don't match + */ + proposal_t *(*select)(proposal_t *this, proposal_t *other, + bool other_remote, bool private); + + /** + * Get the protocol ID of the proposal. + * + * @return protocol of the proposal + */ + protocol_id_t (*get_protocol) (proposal_t *this); + + /** + * Get the SPI of the proposal. + * + * @return spi for proto + */ + uint64_t (*get_spi) (proposal_t *this); + + /** + * Set the SPI of the proposal. + * + * @param spi spi to set for proto + */ + void (*set_spi) (proposal_t *this, uint64_t spi); + + /** + * Get the proposal number, as encoded in SA payload + * + * @return proposal number + */ + u_int (*get_number)(proposal_t *this); + + /** + * Check for the eqality of two proposals. + * + * @param other other proposal to check for equality + * @return TRUE if other equal to this + */ + bool (*equals)(proposal_t *this, proposal_t *other); + + /** + * Clone a proposal. + * + * @return clone of proposal + */ + proposal_t *(*clone) (proposal_t *this); + + /** + * Destroys the proposal object. + */ + void (*destroy) (proposal_t *this); +}; + +/** + * Create a child proposal for AH, ESP or IKE. + * + * @param protocol protocol, such as PROTO_ESP + * @param number proposal number, as encoded in SA payload + * @return proposal_t object + */ +proposal_t *proposal_create(protocol_id_t protocol, u_int number); + +/** + * Create a default proposal if nothing further specified. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + */ +proposal_t *proposal_create_default(protocol_id_t protocol); + +/** + * Create a default proposal for supported AEAD algorithms + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object, NULL if none supported + */ +proposal_t *proposal_create_default_aead(protocol_id_t protocol); + +/** + * Create a proposal from a string identifying the algorithms. + * + * The string is in the same form as a in the ipsec.conf file. + * E.g.: aes128-sha2_256-modp2048 + * 3des-md5 + * An additional '!' at the end of the string forces this proposal, + * without it the peer may choose another algorithm we support. + * + * @param protocol protocol, such as PROTO_ESP + * @param algs algorithms as string + * @return proposal_t object + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs); + +/** + * printf hook function for proposal_t. + * + * Arguments are: + * proposal_t *proposal + * With the #-specifier, arguments are: + * linked_list_t *list containing proposal_t* + */ +int proposal_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +#endif /** PROPOSAL_H_ @}*/ diff --git a/src/libstrongswan/crypto/proposal/proposal_keywords.h b/src/libstrongswan/crypto/proposal/proposal_keywords.h index 856abdce6..b062221e5 100644 --- a/src/libstrongswan/crypto/proposal/proposal_keywords.h +++ b/src/libstrongswan/crypto/proposal/proposal_keywords.h @@ -37,7 +37,7 @@ /** * @defgroup proposal_keywords proposal_keywords - * @{ @ingroup crypto + * @{ @ingroup proposal */ #ifndef PROPOSAL_KEYWORDS_H_ |