aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2014-02-03 18:03:41 +0100
committerMartin Willi <martin@revosec.ch>2014-03-31 15:56:12 +0200
commitf0f301170b0a0fa3fa872f6c00650f2b39d66bf3 (patch)
treec8171f9a6fddef1f515054e4875d4a49b2d4f679
parentd3204677bad04eef716ff22dafb65b643e7564f8 (diff)
downloadstrongswan-f0f301170b0a0fa3fa872f6c00650f2b39d66bf3.tar.bz2
strongswan-f0f301170b0a0fa3fa872f6c00650f2b39d66bf3.tar.xz
tls: Implement the TLS AEAD abstraction for real AEAD modes
-rw-r--r--src/libtls/Makefile.am2
-rw-r--r--src/libtls/tls_aead.c218
-rw-r--r--src/libtls/tls_aead.h9
-rw-r--r--src/libtls/tls_crypto.c41
4 files changed, 262 insertions, 8 deletions
diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am
index 403a6868b..e5e3b8be9 100644
--- a/src/libtls/Makefile.am
+++ b/src/libtls/Makefile.am
@@ -8,7 +8,7 @@ ipseclib_LTLIBRARIES = libtls.la
libtls_la_SOURCES = \
tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \
tls_crypto.c tls_prf.c tls_socket.c tls_eap.c tls_cache.c tls_peer.c \
- tls_aead_expl.c tls_aead_impl.c tls_aead_null.c \
+ tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead.c \
tls_server.c tls.c
libtls_la_LIBADD = \
diff --git a/src/libtls/tls_aead.c b/src/libtls/tls_aead.c
new file mode 100644
index 000000000..be44cc098
--- /dev/null
+++ b/src/libtls/tls_aead.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * 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 "tls_aead.h"
+
+#include <crypto/iv/iv_gen_rand.h>
+
+typedef struct private_tls_aead_t private_tls_aead_t;
+
+/**
+ * Private data of an tls_aead_t object.
+ */
+struct private_tls_aead_t {
+
+ /**
+ * Public tls_aead_t interface.
+ */
+ tls_aead_t public;
+
+ /**
+ * AEAD transform
+ */
+ aead_t *aead;
+
+ /**
+ * Size of salt, the implicit nonce
+ */
+ size_t salt;
+};
+
+/**
+ * Associated header data to create signature over
+ */
+typedef struct __attribute__((__packed__)) {
+ u_int64_t seq;
+ u_int8_t type;
+ u_int16_t version;
+ u_int16_t length;
+} sigheader_t;
+
+METHOD(tls_aead_t, encrypt, bool,
+ private_tls_aead_t *this, tls_version_t version, tls_content_type_t type,
+ u_int64_t seq, chunk_t *data)
+{
+ chunk_t assoc, encrypted, iv, plain;
+ u_int8_t icvlen;
+ sigheader_t hdr;
+ iv_gen_t *gen;
+
+ gen = this->aead->get_iv_gen(this->aead);
+ iv.len = this->aead->get_iv_size(this->aead);
+ icvlen = this->aead->get_icv_size(this->aead);
+
+ encrypted = chunk_alloc(iv.len + data->len + icvlen);
+ iv.ptr = encrypted.ptr;
+ if (!gen->get_iv(gen, seq, iv.len, iv.ptr))
+ {
+ chunk_free(&encrypted);
+ return FALSE;
+ }
+ memcpy(encrypted.ptr + iv.len, data->ptr, data->len);
+ plain = chunk_skip(encrypted, iv.len);
+ plain.len -= icvlen;
+
+ hdr.type = type;
+ htoun64(&hdr.seq, seq);
+ htoun16(&hdr.version, version);
+ htoun16(&hdr.length, plain.len);
+
+ assoc = chunk_from_thing(hdr);
+ if (!this->aead->encrypt(this->aead, plain, assoc, iv, NULL))
+ {
+ return FALSE;
+ }
+ chunk_free(data);
+ *data = encrypted;
+ return TRUE;
+}
+
+METHOD(tls_aead_t, decrypt, bool,
+ private_tls_aead_t *this, tls_version_t version, tls_content_type_t type,
+ u_int64_t seq, chunk_t *data)
+{
+ chunk_t assoc, iv;
+ u_int8_t icvlen;
+ sigheader_t hdr;
+
+ iv.len = this->aead->get_iv_size(this->aead);
+ if (data->len < iv.len)
+ {
+ return FALSE;
+ }
+ iv.ptr = data->ptr;
+ *data = chunk_skip(*data, iv.len);
+ icvlen = this->aead->get_icv_size(this->aead);
+ if (data->len < icvlen)
+ {
+ return FALSE;
+ }
+
+ hdr.type = type;
+ htoun64(&hdr.seq, seq);
+ htoun16(&hdr.version, version);
+ htoun16(&hdr.length, data->len - icvlen);
+
+ assoc = chunk_from_thing(hdr);
+ if (!this->aead->decrypt(this->aead, *data, assoc, iv, NULL))
+ {
+ return FALSE;
+ }
+ data->len -= icvlen;
+ return TRUE;
+}
+
+METHOD(tls_aead_t, get_mac_key_size, size_t,
+ private_tls_aead_t *this)
+{
+ return 0;
+}
+
+METHOD(tls_aead_t, get_encr_key_size, size_t,
+ private_tls_aead_t *this)
+{
+ return this->aead->get_key_size(this->aead) - this->salt;
+}
+
+METHOD(tls_aead_t, get_iv_size, size_t,
+ private_tls_aead_t *this)
+{
+ return this->salt;
+}
+
+METHOD(tls_aead_t, set_keys, bool,
+ private_tls_aead_t *this, chunk_t mac, chunk_t encr, chunk_t iv)
+{
+ chunk_t key;
+
+ if (mac.len)
+ {
+ return FALSE;
+ }
+ key = chunk_cata("cc", encr, iv);
+ return this->aead->set_key(this->aead, key);
+}
+
+METHOD(tls_aead_t, destroy, void,
+ private_tls_aead_t *this)
+{
+ this->aead->destroy(this->aead);
+ free(this);
+}
+
+/**
+ * See header
+ */
+tls_aead_t *tls_aead_create_aead(encryption_algorithm_t encr, size_t encr_size)
+{
+ private_tls_aead_t *this;
+ size_t salt;
+
+ switch (encr)
+ {
+ case ENCR_AES_GCM_ICV8:
+ case ENCR_AES_GCM_ICV12:
+ case ENCR_AES_GCM_ICV16:
+ case ENCR_AES_CCM_ICV8:
+ case ENCR_AES_CCM_ICV12:
+ case ENCR_AES_CCM_ICV16:
+ case ENCR_CAMELLIA_CCM_ICV8:
+ case ENCR_CAMELLIA_CCM_ICV12:
+ case ENCR_CAMELLIA_CCM_ICV16:
+ salt = 4;
+ break;
+ default:
+ return NULL;
+ }
+
+ INIT(this,
+ .public = {
+ .encrypt = _encrypt,
+ .decrypt = _decrypt,
+ .get_iv_size = _get_iv_size,
+ .get_mac_key_size = _get_mac_key_size,
+ .get_encr_key_size = _get_encr_key_size,
+ .get_iv_size = _get_iv_size,
+ .set_keys = _set_keys,
+ .destroy = _destroy,
+ },
+ .aead = lib->crypto->create_aead(lib->crypto, encr, encr_size, salt),
+ .salt = salt,
+ );
+
+ if (!this->aead)
+ {
+ free(this);
+ return NULL;
+ }
+
+ if (this->aead->get_block_size(this->aead) != 1)
+ { /* TLS does not define any padding scheme for AEAD */
+ destroy(this);
+ return NULL;
+ }
+
+ return &this->public;
+}
diff --git a/src/libtls/tls_aead.h b/src/libtls/tls_aead.h
index 48b16ccdb..1d5ba92b5 100644
--- a/src/libtls/tls_aead.h
+++ b/src/libtls/tls_aead.h
@@ -144,4 +144,13 @@ tls_aead_t *tls_aead_create_implicit(integrity_algorithm_t mac,
*/
tls_aead_t *tls_aead_create_null(integrity_algorithm_t mac);
+/**
+ * Create a tls_aead instance using real a AEAD cipher.
+ *
+ * @param encr AEAD encryption algorithm
+ * @param encr_size encryption key size, in bytes
+ * @return TLS AEAD transform
+ */
+tls_aead_t *tls_aead_create_aead(encryption_algorithm_t encr, size_t encr_size);
+
#endif /** TLS_AEAD_H_ @}*/
diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c
index 03d19fa00..82b4b4cc7 100644
--- a/src/libtls/tls_crypto.c
+++ b/src/libtls/tls_crypto.c
@@ -607,7 +607,7 @@ static suite_algs_t *find_suite(tls_cipher_suite_t suite)
/**
* Filter a suite list using a transform enumerator
*/
-static void filter_suite(private_tls_crypto_t *this,
+static void filter_suite(private_tls_crypto_t *this, bool aead,
suite_algs_t suites[], int *count, int offset,
enumerator_t*(*create_enumerator)(crypto_factory_t*))
{
@@ -625,8 +625,10 @@ static void filter_suite(private_tls_crypto_t *this,
while (enumerator->enumerate(enumerator, current_alg, &plugin_name))
{
if ((suites[i].encr == ENCR_NULL ||
+ aead != encryption_algorithm_is_aead(suites[i].encr) ||
!current.encr || current.encr == suites[i].encr) &&
- (!current.mac || current.mac == suites[i].mac) &&
+ (suites[i].mac == AUTH_UNDEFINED ||
+ !current.mac || current.mac == suites[i].mac) &&
(!current.prf || current.prf == suites[i].prf) &&
(!current.hash || current.hash == suites[i].hash) &&
(suites[i].dh == MODP_NONE ||
@@ -912,15 +914,17 @@ static void build_cipher_suite_list(private_tls_crypto_t *this,
}
/* filter suite list by each algorithm */
- filter_suite(this, suites, &count, offsetof(suite_algs_t, encr),
+ filter_suite(this, FALSE, suites, &count, offsetof(suite_algs_t, encr),
lib->crypto->create_crypter_enumerator);
- filter_suite(this, suites, &count, offsetof(suite_algs_t, mac),
+ filter_suite(this, TRUE, suites, &count, offsetof(suite_algs_t, encr),
+ lib->crypto->create_aead_enumerator);
+ filter_suite(this, FALSE, suites, &count, offsetof(suite_algs_t, mac),
lib->crypto->create_signer_enumerator);
- filter_suite(this, suites, &count, offsetof(suite_algs_t, prf),
+ filter_suite(this, FALSE, suites, &count, offsetof(suite_algs_t, prf),
lib->crypto->create_prf_enumerator);
- filter_suite(this, suites, &count, offsetof(suite_algs_t, hash),
+ filter_suite(this, FALSE, suites, &count, offsetof(suite_algs_t, hash),
lib->crypto->create_hasher_enumerator);
- filter_suite(this, suites, &count, offsetof(suite_algs_t, dh),
+ filter_suite(this, FALSE, suites, &count, offsetof(suite_algs_t, dh),
lib->crypto->create_dh_enumerator);
/* filter suites with strongswan.conf options */
@@ -994,6 +998,22 @@ static bool create_traditional(private_tls_crypto_t *this, suite_algs_t *algs)
}
/**
+ * Create AEAD transforms
+ */
+static bool create_aead(private_tls_crypto_t *this, suite_algs_t *algs)
+{
+ this->aead_in = tls_aead_create_aead(algs->encr, algs->encr_size);
+ this->aead_out = tls_aead_create_aead(algs->encr, algs->encr_size);
+ if (!this->aead_in || !this->aead_out)
+ {
+ DBG1(DBG_TLS, "selected TLS transforms %N-%u not supported",
+ encryption_algorithm_names, algs->encr, algs->encr_size * 8);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
* Clean up and unset AEAD transforms
*/
static void destroy_aeads(private_tls_crypto_t *this)
@@ -1030,6 +1050,13 @@ static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs)
return TRUE;
}
}
+ else if (encryption_algorithm_is_aead(algs->encr))
+ {
+ if (create_aead(this, algs))
+ {
+ return TRUE;
+ }
+ }
else
{
if (create_traditional(this, algs))