summaryrefslogtreecommitdiff
path: root/src/libstrongswan/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/crypto')
-rw-r--r--src/libstrongswan/crypto/proposal/proposal.c1134
-rw-r--r--src/libstrongswan/crypto/proposal/proposal.h246
-rw-r--r--src/libstrongswan/crypto/proposal/proposal_keywords.h2
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, &current, 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_