kernel-netlink: Check return value of both halfs when installing default route in...
[strongswan.git] / src / libtls / tls_crypto.c
index 2fb5a1f..0ec2f5c 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2010 Martin Willi
- * Copyright (C) 2010 revosec AG
+ * Copyright (C) 2010-2014 Martin Willi
+ * Copyright (C) 2010-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
@@ -15,7 +15,8 @@
 
 #include "tls_crypto.h"
 
-#include <debug.h>
+#include <utils/debug.h>
+#include <plugins/plugin_feature.h>
 
 ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL,
                                                                   TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
@@ -80,7 +81,7 @@ ENUM_NEXT(tls_cipher_suite_names, TLS_KRB5_WITH_DES_CBC_SHA,
        "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
        "TLS_DH_anon_WITH_AES_256_CBC_SHA",
        "TLS_RSA_WITH_NULL_SHA256",
-       "TLS_RSA_WITH_AES_128_CBC_SHA256 ",
+       "TLS_RSA_WITH_AES_128_CBC_SHA256",
        "TLS_RSA_WITH_AES_256_CBC_SHA256",
        "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
        "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
@@ -111,13 +112,13 @@ ENUM_NEXT(tls_cipher_suite_names, TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
        "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
        "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
        "TLS_PSK_WITH_RC4_128_SHA",
-       "TLS_PSK_WITH_3DES_EDE_CBC_SHA2",
+       "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
        "TLS_PSK_WITH_AES_128_CBC_SHA",
        "TLS_PSK_WITH_AES_256_CBC_SHA",
        "TLS_DHE_PSK_WITH_RC4_128_SHA",
        "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
        "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
-       "TLS_DHE_PSK_WITH_AES_256_CBC_SHA2",
+       "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
        "TLS_RSA_PSK_WITH_RC4_128_SHA",
        "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
        "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
@@ -306,6 +307,21 @@ ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1,
        "SECP521R1",
 );
 
+ENUM(tls_ansi_point_format_names, TLS_ANSI_COMPRESSED, TLS_ANSI_HYBRID_Y,
+       "compressed",
+       "compressed y",
+       "uncompressed",
+       "uncompressed y",
+       "hybrid",
+       "hybrid y",
+);
+
+ENUM(tls_ec_point_format_names,
+        TLS_EC_POINT_UNCOMPRESSED, TLS_EC_POINT_ANSIX962_COMPRESSED_CHAR2,
+       "uncompressed",
+       "ansiX962 compressed prime",
+       "ansiX962 compressed char2",
+);
 
 typedef struct private_tls_crypto_t private_tls_crypto_t;
 
@@ -355,44 +371,29 @@ struct private_tls_crypto_t {
        tls_t *tls;
 
        /**
-        * All handshake data concatentated
-        */
-       chunk_t handshake;
-
-       /**
-        * Connection state TLS PRF
-        */
-       tls_prf_t *prf;
-
-       /**
-        * Signer instance for inbound traffic
-        */
-       signer_t *signer_in;
-
-       /**
-        * Signer instance for outbound traffic
+        * TLS session cache
         */
-       signer_t *signer_out;
+       tls_cache_t *cache;
 
        /**
-        * Crypter instance for inbound traffic
+        * All handshake data concatenated
         */
-       crypter_t *crypter_in;
+       chunk_t handshake;
 
        /**
-        * Crypter instance for outbound traffic
+        * Connection state TLS PRF
         */
-       crypter_t *crypter_out;
+       tls_prf_t *prf;
 
        /**
-        * IV for input decryption, if < TLSv1.2
+        * AEAD transform for inbound traffic
         */
-       chunk_t iv_in;
+       tls_aead_t *aead_in;
 
        /**
-        * IV for output decryption, if < TLSv1.2
+        * AEAD transform for outbound traffic
         */
-       chunk_t iv_out;
+       tls_aead_t *aead_out;
 
        /**
         * EAP-[T]TLS MSK
@@ -422,7 +423,7 @@ typedef struct {
 static suite_algs_t suite_algs[] = {
        { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                KEY_ECDSA, ECP_256_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
        },
        { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
@@ -432,7 +433,7 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
                KEY_ECDSA, ECP_384_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
        },
        { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
@@ -440,9 +441,19 @@ static suite_algs_t suite_algs[] = {
                HASH_SHA384, PRF_HMAC_SHA2_384,
                AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32
        },
+       { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+               KEY_ECDSA, ECP_256_BIT,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16
+       },
+       { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+               KEY_ECDSA, ECP_384_BIT,
+               HASH_SHA384, PRF_HMAC_SHA2_384,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32
+       },
        { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                KEY_RSA, ECP_256_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
        },
        { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
@@ -452,7 +463,7 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                KEY_RSA, ECP_384_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
        },
        { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
@@ -460,9 +471,19 @@ static suite_algs_t suite_algs[] = {
                HASH_SHA384, PRF_HMAC_SHA2_384,
                AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32
        },
+       { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+               KEY_RSA, ECP_256_BIT,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16
+       },
+       { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               KEY_RSA, ECP_384_BIT,
+               HASH_SHA384, PRF_HMAC_SHA2_384,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32
+       },
        { TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                KEY_RSA, MODP_2048_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256,PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
        },
        { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
@@ -472,7 +493,7 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
                KEY_RSA, MODP_3072_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
        },
        { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
@@ -480,9 +501,19 @@ static suite_algs_t suite_algs[] = {
                HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32
        },
+       { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+               KEY_RSA, MODP_3072_BIT,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16
+       },
+       { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+               KEY_RSA, MODP_4096_BIT,
+               HASH_SHA384, PRF_HMAC_SHA2_384,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32
+       },
        { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
                KEY_RSA, MODP_2048_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
@@ -492,7 +523,7 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
                KEY_RSA, MODP_3072_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
@@ -502,12 +533,12 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
                KEY_RSA, MODP_2048_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_3DES, 0
        },
        { TLS_RSA_WITH_AES_128_CBC_SHA,
                KEY_RSA, MODP_NONE,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
        },
        { TLS_RSA_WITH_AES_128_CBC_SHA256,
@@ -517,7 +548,7 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_RSA_WITH_AES_256_CBC_SHA,
                KEY_RSA, MODP_NONE,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
        },
        { TLS_RSA_WITH_AES_256_CBC_SHA256,
@@ -525,9 +556,19 @@ static suite_algs_t suite_algs[] = {
                HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32
        },
+       { TLS_RSA_WITH_AES_128_GCM_SHA256,
+               KEY_RSA, MODP_NONE,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16
+       },
+       { TLS_RSA_WITH_AES_256_GCM_SHA384,
+               KEY_RSA, MODP_NONE,
+               HASH_SHA384, PRF_HMAC_SHA2_384,
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32
+       },
        { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
                KEY_RSA, MODP_NONE,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16
        },
        { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
@@ -537,7 +578,7 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
                KEY_RSA, MODP_NONE,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32
        },
        { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
@@ -547,32 +588,32 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
                KEY_ECDSA, ECP_256_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_3DES, 0
        },
        { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
-               KEY_ECDSA, ECP_256_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               KEY_RSA, ECP_256_BIT,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_3DES, 0
        },
        { TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                KEY_RSA, MODP_NONE,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_3DES, 0
        },
        { TLS_ECDHE_ECDSA_WITH_NULL_SHA,
                KEY_ECDSA, ECP_256_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_NULL, 0
        },
        { TLS_ECDHE_RSA_WITH_NULL_SHA,
                KEY_ECDSA, ECP_256_BIT,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_NULL, 0
        },
        { TLS_RSA_WITH_NULL_SHA,
                KEY_RSA, MODP_NONE,
-               HASH_SHA1, PRF_HMAC_SHA1,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_SHA1_160, ENCR_NULL, 0
        },
        { TLS_RSA_WITH_NULL_SHA256,
@@ -582,13 +623,13 @@ static suite_algs_t suite_algs[] = {
        },
        { TLS_RSA_WITH_NULL_MD5,
                KEY_RSA, MODP_NONE,
-               HASH_MD5, PRF_HMAC_MD5,
+               HASH_SHA256, PRF_HMAC_SHA2_256,
                AUTH_HMAC_MD5_128, ENCR_NULL, 0
        },
 };
 
 /**
- * Look up algoritms by a suite
+ * Look up algorithms by a suite
  */
 static suite_algs_t *find_suite(tls_cipher_suite_t suite)
 {
@@ -607,30 +648,69 @@ 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,
-                                                suite_algs_t suites[], int *count, int offset,
+static void filter_suite(suite_algs_t suites[], int *count, int offset,
                                                 enumerator_t*(*create_enumerator)(crypto_factory_t*))
 {
+       const char *plugin_name;
        suite_algs_t current;
-       int i, remaining = 0;
+       int *current_alg, i, remaining = 0;
        enumerator_t *enumerator;
 
        memset(&current, 0, sizeof(current));
+       current_alg = (int*)((char*)&current + offset);
+
        for (i = 0; i < *count; i++)
        {
+               if (create_enumerator == lib->crypto->create_crypter_enumerator &&
+                       encryption_algorithm_is_aead(suites[i].encr))
+               {       /* filtering crypters, but current suite uses an AEAD, apply */
+                       suites[remaining] = suites[i];
+                       remaining++;
+                       continue;
+               }
+               if (create_enumerator == lib->crypto->create_aead_enumerator &&
+                       !encryption_algorithm_is_aead(suites[i].encr))
+               {       /* filtering AEADs, but current suite doesn't use one, apply */
+                       suites[remaining] = suites[i];
+                       remaining++;
+                       continue;
+               }
                enumerator = create_enumerator(lib->crypto);
-               while (enumerator->enumerate(enumerator, ((char*)&current) + offset))
+               while (enumerator->enumerate(enumerator, current_alg, &plugin_name))
                {
-                       if ((suites[i].encr == ENCR_NULL ||
-                                !current.encr || current.encr == suites[i].encr) &&
-                               (!current.mac  || current.mac  == suites[i].mac) &&
-                               (!current.prf  || current.prf  == suites[i].prf) &&
-                               (!current.hash || current.hash == suites[i].hash))
+                       if (current.encr && current.encr != suites[i].encr)
                        {
-                               suites[remaining] = suites[i];
-                               remaining++;
-                               break;
+                               if (suites[i].encr != ENCR_NULL)
+                               {       /* skip, ENCR does not match nor is NULL */
+                                       continue;
+                               }
+                       }
+                       if (current.mac && current.mac != suites[i].mac)
+                       {
+                               if (suites[i].mac != AUTH_UNDEFINED)
+                               {       /* skip, MAC does not match nor is it undefined */
+                                       continue;
+                               }
+                       }
+                       if (current.prf && current.prf != suites[i].prf)
+                       {       /* skip, PRF does not match */
+                               continue;
+                       }
+                       if (current.hash && current.hash != suites[i].hash)
+                       {       /* skip, hash does not match */
+                               continue;
+                       }
+                       if (current.dh && current.dh != suites[i].dh)
+                       {
+                               if (suites[i].dh != MODP_NONE)
+                               {       /* skip DH group, does not match nor NONE */
+                                       continue;
+                               }
                        }
+                       /* suite supported, apply */
+                       suites[remaining] = suites[i];
+                       remaining++;
+                       break;
                }
                enumerator->destroy(enumerator);
        }
@@ -640,8 +720,7 @@ static void filter_suite(private_tls_crypto_t *this,
 /**
  * Purge NULL encryption cipher suites from list
  */
-static void filter_null_suites(private_tls_crypto_t *this,
-                                                          suite_algs_t suites[], int *count)
+static void filter_null_suites(suite_algs_t suites[], int *count)
 {
        int i, remaining = 0;
 
@@ -677,6 +756,243 @@ static void filter_key_suites(private_tls_crypto_t *this,
 }
 
 /**
+ * Filter suites by key exchange user config
+ */
+static void filter_key_exchange_config_suites(private_tls_crypto_t *this,
+                                                                                         suite_algs_t suites[], int *count)
+{
+       enumerator_t *enumerator;
+       int i, remaining = 0;
+       char *token, *config;
+
+       config = lib->settings->get_str(lib->settings, "%s.tls.key_exchange", NULL,
+                                                                       lib->ns);
+       if (config)
+       {
+               for (i = 0; i < *count; i++)
+               {
+                       enumerator = enumerator_create_token(config, ",", " ");
+                       while (enumerator->enumerate(enumerator, &token))
+                       {
+                               if (strcaseeq(token, "ecdhe-ecdsa") &&
+                                       diffie_hellman_group_is_ec(suites[i].dh) &&
+                                       suites[i].key == KEY_ECDSA)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "ecdhe-rsa") &&
+                                       diffie_hellman_group_is_ec(suites[i].dh) &&
+                                       suites[i].key == KEY_RSA)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "dhe-rsa") &&
+                                       !diffie_hellman_group_is_ec(suites[i].dh) &&
+                                       suites[i].dh != MODP_NONE &&
+                                       suites[i].key == KEY_RSA)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "rsa") &&
+                                       suites[i].dh == MODP_NONE &&
+                                       suites[i].key == KEY_RSA)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               *count = remaining;
+       }
+}
+
+/**
+ * Filter suites by cipher user config
+ */
+static void filter_cipher_config_suites(private_tls_crypto_t *this,
+                                                                               suite_algs_t suites[], int *count)
+{
+       enumerator_t *enumerator;
+       int i, remaining = 0;
+       char *token, *config;
+
+       config = lib->settings->get_str(lib->settings, "%s.tls.cipher", NULL,
+                                                                       lib->ns);
+       if (config)
+       {
+               for (i = 0; i < *count; i++)
+               {
+                       enumerator = enumerator_create_token(config, ",", " ");
+                       while (enumerator->enumerate(enumerator, &token))
+                       {
+                               if (strcaseeq(token, "aes128") &&
+                                       suites[i].encr == ENCR_AES_CBC &&
+                                       suites[i].encr_size == 16)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "aes256") &&
+                                       suites[i].encr == ENCR_AES_CBC &&
+                                       suites[i].encr_size == 32)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "aes128gcm") &&
+                                       suites[i].encr == ENCR_AES_GCM_ICV16 &&
+                                       suites[i].encr_size == 16)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "aes256gcm") &&
+                                       suites[i].encr == ENCR_AES_GCM_ICV16 &&
+                                       suites[i].encr_size == 32)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "camellia128") &&
+                                       suites[i].encr == ENCR_CAMELLIA_CBC &&
+                                       suites[i].encr_size == 16)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "camellia256") &&
+                                       suites[i].encr == ENCR_CAMELLIA_CBC &&
+                                       suites[i].encr_size == 32)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "3des") &&
+                                       suites[i].encr == ENCR_3DES)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "null") &&
+                                       suites[i].encr == ENCR_NULL)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               *count = remaining;
+       }
+}
+
+/**
+ * Filter suites by mac user config
+ */
+static void filter_mac_config_suites(private_tls_crypto_t *this,
+                                                                        suite_algs_t suites[], int *count)
+{
+       enumerator_t *enumerator;
+       int i, remaining = 0;
+       char *token, *config;
+
+       config = lib->settings->get_str(lib->settings, "%s.tls.mac", NULL,
+                                                                       lib->ns);
+       if (config)
+       {
+               for (i = 0; i < *count; i++)
+               {
+                       enumerator = enumerator_create_token(config, ",", " ");
+                       while (enumerator->enumerate(enumerator, &token))
+                       {
+                               if (strcaseeq(token, "md5") &&
+                                       suites[i].mac == AUTH_HMAC_MD5_128)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "sha1") &&
+                                       suites[i].mac == AUTH_HMAC_SHA1_160)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "sha256") &&
+                                       suites[i].mac == AUTH_HMAC_SHA2_256_256)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                               if (strcaseeq(token, "sha384") &&
+                                       suites[i].mac == AUTH_HMAC_SHA2_384_384)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               *count = remaining;
+       }
+}
+
+/**
+ * Filter for specific suites specified in strongswan.conf
+ */
+static void filter_specific_config_suites(private_tls_crypto_t *this,
+                                                                                 suite_algs_t suites[], int *count)
+{
+       enumerator_t *enumerator;
+       int i, remaining = 0, suite;
+       char *token, *config;
+
+       config = lib->settings->get_str(lib->settings, "%s.tls.suites", NULL,
+                                                                       lib->ns);
+       if (config)
+       {
+               for (i = 0; i < *count; i++)
+               {
+                       enumerator = enumerator_create_token(config, ",", " ");
+                       while (enumerator->enumerate(enumerator, &token))
+                       {
+                               if (enum_from_name(tls_cipher_suite_names, token, &suite) &&
+                                       suite == suites[i].suite)
+                               {
+                                       suites[remaining++] = suites[i];
+                                       break;
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               *count = remaining;
+       }
+}
+
+/**
+ * Filter out unsupported suites on given suite array
+ */
+static void filter_unsupported_suites(suite_algs_t suites[], int *count)
+{
+       /* filter suite list by each algorithm */
+       filter_suite(suites, count, offsetof(suite_algs_t, encr),
+                                lib->crypto->create_crypter_enumerator);
+       filter_suite(suites, count, offsetof(suite_algs_t, encr),
+                                lib->crypto->create_aead_enumerator);
+       filter_suite(suites, count, offsetof(suite_algs_t, mac),
+                                lib->crypto->create_signer_enumerator);
+       filter_suite(suites, count, offsetof(suite_algs_t, prf),
+                                lib->crypto->create_prf_enumerator);
+       filter_suite(suites, count, offsetof(suite_algs_t, hash),
+                                lib->crypto->create_hasher_enumerator);
+       filter_suite(suites, count, offsetof(suite_algs_t, dh),
+                                lib->crypto->create_dh_enumerator);
+}
+
+/**
  * Initialize the cipher suite list
  */
 static void build_cipher_suite_list(private_tls_crypto_t *this,
@@ -690,9 +1006,10 @@ static void build_cipher_suite_list(private_tls_crypto_t *this,
        {
                suites[i] = suite_algs[i];
        }
+
        if (require_encryption)
        {
-               filter_null_suites(this, suites, &count);
+               filter_null_suites(suites, &count);
        }
        if (!this->rsa)
        {
@@ -703,15 +1020,13 @@ static void build_cipher_suite_list(private_tls_crypto_t *this,
                filter_key_suites(this, suites, &count, KEY_ECDSA);
        }
 
-       /* filter suite list by each algorithm */
-       filter_suite(this, suites, &count, offsetof(suite_algs_t, encr),
-                                lib->crypto->create_crypter_enumerator);
-       filter_suite(this, suites, &count, offsetof(suite_algs_t, mac),
-                                lib->crypto->create_signer_enumerator);
-       filter_suite(this, suites, &count, offsetof(suite_algs_t, prf),
-                                lib->crypto->create_prf_enumerator);
-       filter_suite(this, suites, &count, offsetof(suite_algs_t, hash),
-                                lib->crypto->create_hasher_enumerator);
+       filter_unsupported_suites(suites, &count);
+
+       /* filter suites with strongswan.conf options */
+       filter_key_exchange_config_suites(this, suites, &count);
+       filter_cipher_config_suites(this, suites, &count);
+       filter_mac_config_suites(this, suites, &count);
+       filter_specific_config_suites(this, suites, &count);
 
        free(this->suites);
        this->suite_count = count;
@@ -733,10 +1048,82 @@ METHOD(tls_crypto_t, get_cipher_suites, int,
 }
 
 /**
+ * Create NULL encryption transforms
+ */
+static bool create_null(private_tls_crypto_t *this, suite_algs_t *algs)
+{
+       this->aead_in = tls_aead_create_null(algs->mac);
+       this->aead_out = tls_aead_create_null(algs->mac);
+       if (!this->aead_in || !this->aead_out)
+       {
+               DBG1(DBG_TLS, "selected TLS MAC %N not supported",
+                        integrity_algorithm_names, algs->mac);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Create traditional transforms
+ */
+static bool create_traditional(private_tls_crypto_t *this, suite_algs_t *algs)
+{
+       if (this->tls->get_version(this->tls) < TLS_1_1)
+       {
+               this->aead_in = tls_aead_create_implicit(algs->mac,
+                                                               algs->encr, algs->encr_size);
+               this->aead_out = tls_aead_create_implicit(algs->mac,
+                                                               algs->encr, algs->encr_size);
+       }
+       else
+       {
+               this->aead_in = tls_aead_create_explicit(algs->mac,
+                                                               algs->encr, algs->encr_size);
+               this->aead_out = tls_aead_create_explicit(algs->mac,
+                                                               algs->encr, algs->encr_size);
+       }
+       if (!this->aead_in || !this->aead_out)
+       {
+               DBG1(DBG_TLS, "selected TLS transforms %N-%u-%N not supported",
+                        encryption_algorithm_names, algs->encr, algs->encr_size * 8,
+                        integrity_algorithm_names, algs->mac);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * 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)
+{
+       DESTROY_IF(this->aead_in);
+       DESTROY_IF(this->aead_out);
+       this->aead_in = this->aead_out = NULL;
+}
+
+/**
  * Create crypto primitives
  */
 static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs)
 {
+       destroy_aeads(this);
        DESTROY_IF(this->prf);
        if (this->tls->get_version(this->tls) < TLS_1_2)
        {
@@ -751,38 +1138,29 @@ static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs)
                DBG1(DBG_TLS, "selected TLS PRF not supported");
                return FALSE;
        }
-
-       DESTROY_IF(this->signer_in);
-       DESTROY_IF(this->signer_out);
-       this->signer_in = lib->crypto->create_signer(lib->crypto, algs->mac);
-       this->signer_out = lib->crypto->create_signer(lib->crypto, algs->mac);
-       if (!this->signer_in || !this->signer_out)
+       if (algs->encr == ENCR_NULL)
        {
-               DBG1(DBG_TLS, "selected TLS MAC %N not supported",
-                        integrity_algorithm_names, algs->mac);
-               return FALSE;
+               if (create_null(this, algs))
+               {
+                       return TRUE;
+               }
        }
-
-       DESTROY_IF(this->crypter_in);
-       DESTROY_IF(this->crypter_out);
-       if (algs->encr == ENCR_NULL)
+       else if (encryption_algorithm_is_aead(algs->encr))
        {
-               this->crypter_in = this->crypter_out = NULL;
+               if (create_aead(this, algs))
+               {
+                       return TRUE;
+               }
        }
        else
        {
-               this->crypter_in = lib->crypto->create_crypter(lib->crypto,
-                                                                                               algs->encr, algs->encr_size);
-               this->crypter_out = lib->crypto->create_crypter(lib->crypto,
-                                                                                               algs->encr, algs->encr_size);
-               if (!this->crypter_in || !this->crypter_out)
+               if (create_traditional(this, algs))
                {
-                       DBG1(DBG_TLS, "selected TLS crypter %N not supported",
-                                encryption_algorithm_names, algs->encr);
-                       return FALSE;
+                       return TRUE;
                }
        }
-       return TRUE;
+       destroy_aeads(this);
+       return FALSE;
 }
 
 METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t,
@@ -829,59 +1207,82 @@ METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t,
        return MODP_NONE;
 }
 
+/**
+ * Map signature schemes to TLS key types and hashes, ordered by preference
+ */
+static struct {
+       tls_signature_algorithm_t sig;
+       tls_hash_algorithm_t hash;
+       signature_scheme_t scheme;
+} schemes[] = {
+       { TLS_SIG_ECDSA,        TLS_HASH_SHA256,        SIGN_ECDSA_WITH_SHA256_DER   },
+       { TLS_SIG_ECDSA,        TLS_HASH_SHA384,        SIGN_ECDSA_WITH_SHA384_DER   },
+       { TLS_SIG_ECDSA,        TLS_HASH_SHA512,        SIGN_ECDSA_WITH_SHA512_DER   },
+       { TLS_SIG_ECDSA,        TLS_HASH_SHA1,          SIGN_ECDSA_WITH_SHA1_DER     },
+       { TLS_SIG_RSA,          TLS_HASH_SHA256,        SIGN_RSA_EMSA_PKCS1_SHA2_256 },
+       { TLS_SIG_RSA,          TLS_HASH_SHA384,        SIGN_RSA_EMSA_PKCS1_SHA2_384 },
+       { TLS_SIG_RSA,          TLS_HASH_SHA512,        SIGN_RSA_EMSA_PKCS1_SHA2_512 },
+       { TLS_SIG_RSA,          TLS_HASH_SHA224,        SIGN_RSA_EMSA_PKCS1_SHA2_224 },
+       { TLS_SIG_RSA,          TLS_HASH_SHA1,          SIGN_RSA_EMSA_PKCS1_SHA1     },
+       { TLS_SIG_RSA,          TLS_HASH_MD5,           SIGN_RSA_EMSA_PKCS1_MD5      },
+};
+
 METHOD(tls_crypto_t, get_signature_algorithms, void,
-       private_tls_crypto_t *this, tls_writer_t *writer)
+       private_tls_crypto_t *this, bio_writer_t *writer)
 {
-       tls_writer_t *supported;
-       enumerator_t *enumerator;
-       hash_algorithm_t alg;
-       tls_hash_algorithm_t hash;
+       bio_writer_t *supported;
+       int i;
 
-       supported = tls_writer_create(32);
-       enumerator = lib->crypto->create_hasher_enumerator(lib->crypto);
-       while (enumerator->enumerate(enumerator, &alg))
+       supported = bio_writer_create(32);
+
+       for (i = 0; i < countof(schemes); i++)
        {
-               switch (alg)
+               if (schemes[i].sig == TLS_SIG_RSA && !this->rsa)
                {
-                       case HASH_MD5:
-                               hash = TLS_HASH_MD5;
-                               break;
-                       case HASH_SHA1:
-                               hash = TLS_HASH_SHA1;
-                               break;
-                       case HASH_SHA224:
-                               hash = TLS_HASH_SHA224;
-                               break;
-                       case HASH_SHA256:
-                               hash = TLS_HASH_SHA256;
-                               break;
-                       case HASH_SHA384:
-                               hash = TLS_HASH_SHA384;
-                               break;
-                       case HASH_SHA512:
-                               hash = TLS_HASH_SHA512;
-                               break;
-                       default:
-                               continue;
+                       continue;
                }
-               if (this->rsa)
+               if (schemes[i].sig == TLS_SIG_ECDSA && !this->ecdsa)
                {
-                       supported->write_uint8(supported, hash);
-                       supported->write_uint8(supported, TLS_SIG_RSA);
+                       continue;
                }
-               if (this->ecdsa && alg != HASH_MD5 && alg != HASH_SHA224)
-               {       /* currently we have no signature scheme for MD5/SHA224 */
-                       supported->write_uint8(supported, hash);
-                       supported->write_uint8(supported, TLS_SIG_ECDSA);
+               if (!lib->plugins->has_feature(lib->plugins,
+                                               PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].scheme)))
+               {
+                       continue;
                }
+               supported->write_uint8(supported, schemes[i].hash);
+               supported->write_uint8(supported, schemes[i].sig);
        }
-       enumerator->destroy(enumerator);
 
+       supported->wrap16(supported);
        writer->write_data16(writer, supported->get_buf(supported));
        supported->destroy(supported);
 }
 
 /**
+ * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair
+ */
+static signature_scheme_t hashsig_to_scheme(key_type_t type,
+                                                                                       tls_hash_algorithm_t hash,
+                                                                                       tls_signature_algorithm_t sig)
+{
+       int i;
+
+       if ((sig == TLS_SIG_RSA && type == KEY_RSA) ||
+               (sig == TLS_SIG_ECDSA && type == KEY_ECDSA))
+       {
+               for (i = 0; i < countof(schemes); i++)
+               {
+                       if (schemes[i].sig == sig && schemes[i].hash == hash)
+                       {
+                               return schemes[i].scheme;
+                       }
+               }
+       }
+       return SIGN_UNKNOWN;
+}
+
+/**
  * Mapping groups to TLS named curves
  */
 static struct {
@@ -895,28 +1296,32 @@ static struct {
        { ECP_192_BIT, TLS_SECP192R1},
 };
 
-/**
- * Filter EC groups, add TLS curve
- */
-static bool group_filter(void *null,
-                                               diffie_hellman_group_t *in, diffie_hellman_group_t *out,
-                                               void* dummy1, tls_named_curve_t *curve)
+CALLBACK(group_filter, bool,
+       void *null, enumerator_t *orig, va_list args)
 {
+       diffie_hellman_group_t group, *out;
+       tls_named_curve_t *curve;
+       char *plugin;
        int i;
 
-       for (i = 0; i < countof(curves); i++)
+       VA_ARGS_VGET(args, out, curve);
+
+       while (orig->enumerate(orig, &group, &plugin))
        {
-               if (curves[i].group == *in)
+               for (i = 0; i < countof(curves); i++)
                {
-                       if (out)
+                       if (curves[i].group == group)
                        {
-                               *out = curves[i].group;
-                       }
-                       if (curve)
-                       {
-                               *curve = curves[i].curve;
+                               if (out)
+                               {
+                                       *out = curves[i].group;
+                               }
+                               if (curve)
+                               {
+                                       *curve = curves[i].curve;
+                               }
+                               return TRUE;
                        }
-                       return TRUE;
                }
        }
        return FALSE;
@@ -926,8 +1331,8 @@ METHOD(tls_crypto_t, create_ec_enumerator, enumerator_t*,
        private_tls_crypto_t *this)
 {
        return enumerator_create_filter(
-                                       lib->crypto->create_dh_enumerator(lib->crypto),
-                                       (void*)group_filter, NULL, NULL);
+                                                       lib->crypto->create_dh_enumerator(lib->crypto),
+                                                       group_filter, NULL, NULL);
 }
 
 METHOD(tls_crypto_t, set_protection, void,
@@ -939,7 +1344,7 @@ METHOD(tls_crypto_t, set_protection, void,
 METHOD(tls_crypto_t, append_handshake, void,
        private_tls_crypto_t *this, tls_handshake_type_t type, chunk_t data)
 {
-       u_int32_t header;
+       uint32_t header;
 
        /* reconstruct handshake header */
        header = htonl(data.len | (type << 24));
@@ -963,12 +1368,12 @@ static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash)
                        return FALSE;
                }
                hasher = lib->crypto->create_hasher(lib->crypto, alg->hash);
-               if (!hasher)
+               if (!hasher || !hasher->allocate_hash(hasher, data, hash))
                {
                        DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, alg->hash);
+                       DESTROY_IF(hasher);
                        return FALSE;
                }
-               hasher->allocate_hash(hasher, data, hash);
                hasher->destroy(hasher);
        }
        else
@@ -977,20 +1382,20 @@ static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash)
                char buf[HASH_SIZE_MD5 + HASH_SIZE_SHA1];
 
                md5 = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
-               if (!md5)
+               if (!md5 || !md5->get_hash(md5, data, buf))
                {
                        DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, HASH_MD5);
+                       DESTROY_IF(md5);
                        return FALSE;
                }
-               md5->get_hash(md5, data, buf);
                md5->destroy(md5);
                sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
-               if (!sha1)
+               if (!sha1 || !sha1->get_hash(sha1, data, buf + HASH_SIZE_MD5))
                {
                        DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, HASH_SHA1);
+                       DESTROY_IF(sha1);
                        return FALSE;
                }
-               sha1->get_hash(sha1, data, buf + HASH_SIZE_MD5);
                sha1->destroy(sha1);
 
                *hash = chunk_clone(chunk_from_thing(buf));
@@ -998,68 +1403,15 @@ static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash)
        return TRUE;
 }
 
-/**
- * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair
- */
-static signature_scheme_t hashsig_to_scheme(key_type_t type,
-                                       tls_hash_algorithm_t hash, tls_signature_algorithm_t sig)
-{
-       switch (sig)
-       {
-               case TLS_SIG_RSA:
-                       if (type != KEY_RSA)
-                       {
-                               return SIGN_UNKNOWN;
-                       }
-                       switch (hash)
-                       {
-                               case TLS_HASH_MD5:
-                                       return SIGN_RSA_EMSA_PKCS1_MD5;
-                               case TLS_HASH_SHA1:
-                                       return SIGN_RSA_EMSA_PKCS1_SHA1;
-                               case TLS_HASH_SHA224:
-                                       return SIGN_RSA_EMSA_PKCS1_SHA224;
-                               case TLS_HASH_SHA256:
-                                       return SIGN_RSA_EMSA_PKCS1_SHA256;
-                               case TLS_HASH_SHA384:
-                                       return SIGN_RSA_EMSA_PKCS1_SHA384;
-                               case TLS_HASH_SHA512:
-                                       return SIGN_RSA_EMSA_PKCS1_SHA512;
-                               default:
-                                       return SIGN_UNKNOWN;
-                       }
-               case TLS_SIG_ECDSA:
-                       if (type != KEY_ECDSA)
-                       {
-                               return SIGN_UNKNOWN;
-                       }
-                       switch (hash)
-                       {
-                               case TLS_HASH_SHA224:
-                                       return SIGN_ECDSA_WITH_SHA1_DER;
-                               case TLS_HASH_SHA256:
-                                       return SIGN_ECDSA_WITH_SHA256_DER;
-                               case TLS_HASH_SHA384:
-                                       return SIGN_ECDSA_WITH_SHA384_DER;
-                               case TLS_HASH_SHA512:
-                                       return SIGN_ECDSA_WITH_SHA512_DER;
-                               default:
-                                       return SIGN_UNKNOWN;
-                       }
-               default:
-                       return SIGN_UNKNOWN;
-       }
-}
-
 METHOD(tls_crypto_t, sign, bool,
-       private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer,
+       private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer,
        chunk_t data, chunk_t hashsig)
 {
        if (this->tls->get_version(this->tls) >= TLS_1_2)
        {
                signature_scheme_t scheme;
-               tls_reader_t *reader;
-               u_int8_t hash, alg;
+               bio_reader_t *reader;
+               uint8_t hash, alg;
                chunk_t sig;
                bool done = FALSE;
 
@@ -1068,7 +1420,7 @@ METHOD(tls_crypto_t, sign, bool,
                        hashsig = chunk_from_chars(
                                TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA);
                }
-               reader = tls_reader_create(hashsig);
+               reader = bio_reader_create(hashsig);
                while (reader->remaining(reader) >= 2)
                {
                        if (reader->read_uint8(reader, &hash) &&
@@ -1076,7 +1428,7 @@ METHOD(tls_crypto_t, sign, bool,
                        {
                                scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
                                if (scheme != SIGN_UNKNOWN &&
-                                       key->sign(key, scheme, data, &sig))
+                                       key->sign(key, scheme, NULL, data, &sig))
                                {
                                        done = TRUE;
                                        break;
@@ -1108,7 +1460,8 @@ METHOD(tls_crypto_t, sign, bool,
                                {
                                        return FALSE;
                                }
-                               done = key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig);
+                               done = key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, NULL, hash,
+                                                                &sig);
                                free(hash.ptr);
                                if (!done)
                                {
@@ -1117,7 +1470,7 @@ METHOD(tls_crypto_t, sign, bool,
                                DBG2(DBG_TLS, "created signature with MD5+SHA1/RSA");
                                break;
                        case KEY_ECDSA:
-                               if (!key->sign(key, SIGN_ECDSA_WITH_SHA1_DER, data, &sig))
+                               if (!key->sign(key, SIGN_ECDSA_WITH_SHA1_DER, NULL, data, &sig))
                                {
                                        return FALSE;
                                }
@@ -1133,13 +1486,13 @@ METHOD(tls_crypto_t, sign, bool,
 }
 
 METHOD(tls_crypto_t, verify, bool,
-       private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader,
+       private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader,
        chunk_t data)
 {
        if (this->tls->get_version(this->tls) >= TLS_1_2)
        {
                signature_scheme_t scheme = SIGN_UNKNOWN;
-               u_int8_t hash, alg;
+               uint8_t hash, alg;
                chunk_t sig;
 
                if (!reader->read_uint8(reader, &hash) ||
@@ -1157,7 +1510,7 @@ METHOD(tls_crypto_t, verify, bool,
                                 tls_signature_algorithm_names, alg);
                        return FALSE;
                }
-               if (!key->verify(key, scheme, data, sig))
+               if (!key->verify(key, scheme, NULL, data, sig))
                {
                        return FALSE;
                }
@@ -1181,7 +1534,8 @@ METHOD(tls_crypto_t, verify, bool,
                                {
                                        return FALSE;
                                }
-                               done = key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig);
+                               done = key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, NULL, hash,
+                                                                  sig);
                                free(hash.ptr);
                                if (!done)
                                {
@@ -1190,7 +1544,8 @@ METHOD(tls_crypto_t, verify, bool,
                                DBG2(DBG_TLS, "verified signature data with MD5+SHA1/RSA");
                                break;
                        case KEY_ECDSA:
-                               if (!key->verify(key, SIGN_ECDSA_WITH_SHA1_DER, data, sig))
+                               if (!key->verify(key, SIGN_ECDSA_WITH_SHA1_DER, NULL, data,
+                                                                sig))
                                {
                                        return FALSE;
                                }
@@ -1204,14 +1559,14 @@ METHOD(tls_crypto_t, verify, bool,
 }
 
 METHOD(tls_crypto_t, sign_handshake, bool,
-       private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer,
+       private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer,
        chunk_t hashsig)
 {
        return sign(this, key, writer, this->handshake, hashsig);
 }
 
 METHOD(tls_crypto_t, verify_handshake, bool,
-       private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader)
+       private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader)
 {
        return verify(this, key, reader, this->handshake);
 }
@@ -1229,95 +1584,166 @@ METHOD(tls_crypto_t, calculate_finished, bool,
        {
                return FALSE;
        }
-       this->prf->get_bytes(this->prf, label, seed, 12, out);
+       if (!this->prf->get_bytes(this->prf, label, seed, 12, out))
+       {
+               free(seed.ptr);
+               return FALSE;
+       }
        free(seed.ptr);
        return TRUE;
 }
 
-METHOD(tls_crypto_t, derive_secrets, void,
-       private_tls_crypto_t *this, chunk_t premaster,
-       chunk_t client_random, chunk_t server_random)
+/**
+ * Derive master secret from premaster, optionally save session
+ */
+static bool derive_master(private_tls_crypto_t *this, chunk_t premaster,
+                                                 chunk_t session, identification_t *id,
+                                                 chunk_t client_random, chunk_t server_random)
 {
        char master[48];
-       chunk_t seed, block, client_write, server_write;
-       int mks, eks = 0, ivs = 0;
+       chunk_t seed;
 
        /* derive master secret */
        seed = chunk_cata("cc", client_random, server_random);
-       this->prf->set_key(this->prf, premaster);
-       this->prf->get_bytes(this->prf, "master secret", seed,
-                                                sizeof(master), master);
 
-       this->prf->set_key(this->prf, chunk_from_thing(master));
-       memset(master, 0, sizeof(master));
-
-       /* derive key block for key expansion */
-       mks = this->signer_out->get_key_size(this->signer_out);
-       if (this->crypter_out)
+       if (!this->prf->set_key(this->prf, premaster) ||
+               !this->prf->get_bytes(this->prf, "master secret", seed,
+                                                         sizeof(master), master) ||
+               !this->prf->set_key(this->prf, chunk_from_thing(master)))
        {
-               eks = this->crypter_out->get_key_size(this->crypter_out);
-               if (this->tls->get_version(this->tls) < TLS_1_1)
-               {
-                       ivs = this->crypter_out->get_iv_size(this->crypter_out);
-               }
+               return FALSE;
        }
-       seed = chunk_cata("cc", server_random, client_random);
-       block = chunk_alloca((mks + eks + ivs) * 2);
-       this->prf->get_bytes(this->prf, "key expansion", seed, block.len, block.ptr);
-
-       /* signer keys */
-       client_write = chunk_create(block.ptr, mks);
-       block = chunk_skip(block, mks);
-       server_write = chunk_create(block.ptr, mks);
-       block = chunk_skip(block, mks);
-       if (this->tls->is_server(this->tls))
+
+       if (this->cache && session.len)
        {
-               this->signer_in->set_key(this->signer_in, client_write);
-               this->signer_out->set_key(this->signer_out, server_write);
+               this->cache->create(this->cache, session, id, chunk_from_thing(master),
+                                                       this->suite);
        }
-       else
+       memwipe(master, sizeof(master));
+       return TRUE;
+}
+
+/**
+ * Expand key material from master secret
+ */
+static bool expand_keys(private_tls_crypto_t *this,
+                                               chunk_t client_random, chunk_t server_random)
+{
+       chunk_t seed, block;
+       chunk_t cw_mac, cw, cw_iv;
+       chunk_t sw_mac, sw, sw_iv;
+       int mklen, eklen, ivlen;
+
+       if (!this->aead_in || !this->aead_out)
        {
-               this->signer_out->set_key(this->signer_out, client_write);
-               this->signer_in->set_key(this->signer_in, server_write);
+               return FALSE;
        }
 
-       /* crypter keys, and IVs if < TLSv1.2 */
-       if (this->crypter_out && this->crypter_in)
+       /* derive key block for key expansion */
+       mklen = this->aead_in->get_mac_key_size(this->aead_in);
+       eklen = this->aead_in->get_encr_key_size(this->aead_in);
+       ivlen = this->aead_in->get_iv_size(this->aead_in);
+       seed = chunk_cata("cc", server_random, client_random);
+       block = chunk_alloca((mklen + eklen + ivlen) * 2);
+       if (!this->prf->get_bytes(this->prf, "key expansion", seed,
+                                                         block.len, block.ptr))
        {
-               client_write = chunk_create(block.ptr, eks);
-               block = chunk_skip(block, eks);
-               server_write = chunk_create(block.ptr, eks);
-               block = chunk_skip(block, eks);
+               return FALSE;
+       }
+
+       /* client/server write signer keys */
+       cw_mac = chunk_create(block.ptr, mklen);
+       block = chunk_skip(block, mklen);
+       sw_mac = chunk_create(block.ptr, mklen);
+       block = chunk_skip(block, mklen);
+
+       /* client/server write encryption keys */
+       cw = chunk_create(block.ptr, eklen);
+       block = chunk_skip(block, eklen);
+       sw = chunk_create(block.ptr, eklen);
+       block = chunk_skip(block, eklen);
+
+       /* client/server write IV; TLS 1.0 implicit IVs or AEAD salt, if any */
+       cw_iv = chunk_create(block.ptr, ivlen);
+       block = chunk_skip(block, ivlen);
+       sw_iv = chunk_create(block.ptr, ivlen);
+       block = chunk_skip(block, ivlen);
 
-               if (this->tls->is_server(this->tls))
+       if (this->tls->is_server(this->tls))
+       {
+               if (!this->aead_in->set_keys(this->aead_in, cw_mac, cw, cw_iv) ||
+                       !this->aead_out->set_keys(this->aead_out, sw_mac, sw, sw_iv))
                {
-                       this->crypter_in->set_key(this->crypter_in, client_write);
-                       this->crypter_out->set_key(this->crypter_out, server_write);
+                       return FALSE;
                }
-               else
+       }
+       else
+       {
+               if (!this->aead_out->set_keys(this->aead_out, cw_mac, cw, cw_iv) ||
+                       !this->aead_in->set_keys(this->aead_in, sw_mac, sw, sw_iv))
                {
-                       this->crypter_out->set_key(this->crypter_out, client_write);
-                       this->crypter_in->set_key(this->crypter_in, server_write);
+                       return FALSE;
                }
-               if (ivs)
+       }
+
+       /* EAP-MSK */
+       if (this->msk_label)
+       {
+               seed = chunk_cata("cc", client_random, server_random);
+               this->msk = chunk_alloc(64);
+               if (!this->prf->get_bytes(this->prf, this->msk_label, seed,
+                                                                 this->msk.len, this->msk.ptr))
                {
-                       client_write = chunk_create(block.ptr, ivs);
-                       block = chunk_skip(block, ivs);
-                       server_write = chunk_create(block.ptr, ivs);
-                       block = chunk_skip(block, ivs);
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
 
-                       if (this->tls->is_server(this->tls))
-                       {
-                               this->iv_in = chunk_clone(client_write);
-                               this->iv_out = chunk_clone(server_write);
-                       }
-                       else
+METHOD(tls_crypto_t, derive_secrets, bool,
+       private_tls_crypto_t *this, chunk_t premaster, chunk_t session,
+       identification_t *id, chunk_t client_random, chunk_t server_random)
+{
+       return derive_master(this, premaster, session, id,
+                                                client_random, server_random) &&
+                  expand_keys(this, client_random, server_random);
+}
+
+METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t,
+       private_tls_crypto_t *this, chunk_t session, identification_t *id,
+       chunk_t client_random, chunk_t server_random)
+{
+       chunk_t master;
+
+       if (this->cache && session.len)
+       {
+               this->suite = this->cache->lookup(this->cache, session, id, &master);
+               if (this->suite)
+               {
+                       this->suite = select_cipher_suite(this, &this->suite, 1, KEY_ANY);
+                       if (this->suite)
                        {
-                               this->iv_out = chunk_clone(client_write);
-                               this->iv_in = chunk_clone(server_write);
+                               if (!this->prf->set_key(this->prf, master) ||
+                                       !expand_keys(this, client_random, server_random))
+                               {
+                                       this->suite = 0;
+                               }
                        }
+                       chunk_clear(&master);
                }
+               return this->suite;
        }
+       return 0;
+}
+
+METHOD(tls_crypto_t, get_session, chunk_t,
+       private_tls_crypto_t *this, identification_t *server)
+{
+       if (this->cache)
+       {
+               return this->cache->check(this->cache, server);
+       }
+       return chunk_empty;
 }
 
 METHOD(tls_crypto_t, change_cipher, void,
@@ -1327,32 +1753,15 @@ METHOD(tls_crypto_t, change_cipher, void,
        {
                if (inbound)
                {
-                       this->protection->set_cipher(this->protection, TRUE,
-                                                       this->signer_in, this->crypter_in, this->iv_in);
+                       this->protection->set_cipher(this->protection, TRUE, this->aead_in);
                }
                else
                {
-                       this->protection->set_cipher(this->protection, FALSE,
-                                                       this->signer_out, this->crypter_out, this->iv_out);
+                       this->protection->set_cipher(this->protection, FALSE, this->aead_out);
                }
        }
 }
 
-METHOD(tls_crypto_t, derive_eap_msk, void,
-       private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random)
-{
-       if (this->msk_label)
-       {
-               chunk_t seed;
-
-               seed = chunk_cata("cc", client_random, server_random);
-               free(this->msk.ptr);
-               this->msk = chunk_alloc(64);
-               this->prf->get_bytes(this->prf, this->msk_label, seed,
-                                                        this->msk.len, this->msk.ptr);
-       }
-}
-
 METHOD(tls_crypto_t, get_eap_msk, chunk_t,
        private_tls_crypto_t *this)
 {
@@ -1362,12 +1771,7 @@ METHOD(tls_crypto_t, get_eap_msk, chunk_t,
 METHOD(tls_crypto_t, destroy, void,
        private_tls_crypto_t *this)
 {
-       DESTROY_IF(this->signer_in);
-       DESTROY_IF(this->signer_out);
-       DESTROY_IF(this->crypter_in);
-       DESTROY_IF(this->crypter_out);
-       free(this->iv_in.ptr);
-       free(this->iv_out.ptr);
+       destroy_aeads(this);
        free(this->handshake.ptr);
        free(this->msk.ptr);
        DESTROY_IF(this->prf);
@@ -1378,7 +1782,7 @@ METHOD(tls_crypto_t, destroy, void,
 /**
  * See header
  */
-tls_crypto_t *tls_crypto_create(tls_t *tls)
+tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
 {
        private_tls_crypto_t *this;
        enumerator_t *enumerator;
@@ -1400,12 +1804,14 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                        .verify_handshake = _verify_handshake,
                        .calculate_finished = _calculate_finished,
                        .derive_secrets = _derive_secrets,
+                       .resume_session = _resume_session,
+                       .get_session = _get_session,
                        .change_cipher = _change_cipher,
-                       .derive_eap_msk = _derive_eap_msk,
                        .get_eap_msk = _get_eap_msk,
                        .destroy = _destroy,
                },
                .tls = tls,
+               .cache = cache,
        );
 
        enumerator = lib->creds->create_builder_enumerator(lib->creds);
@@ -1435,6 +1841,10 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                        this->msk_label = "client EAP encryption";
                        build_cipher_suite_list(this, FALSE);
                        break;
+               case TLS_PURPOSE_EAP_PEAP:
+                       this->msk_label = "client EAP encryption";
+                       build_cipher_suite_list(this, TRUE);
+                       break;
                case TLS_PURPOSE_EAP_TTLS:
                        /* MSK PRF ASCII constant label according to EAP-TTLS RFC 5281 */
                        this->msk_label = "ttls keying material";
@@ -1443,6 +1853,43 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                case TLS_PURPOSE_GENERIC:
                        build_cipher_suite_list(this, TRUE);
                        break;
+               case TLS_PURPOSE_GENERIC_NULLOK:
+                       build_cipher_suite_list(this, FALSE);
+                       break;
+               default:
+                       break;
        }
        return &this->public;
 }
+
+/**
+ * See header.
+ */
+int tls_crypto_get_supported_suites(bool null, tls_cipher_suite_t **out)
+{
+       suite_algs_t suites[countof(suite_algs)];
+       int count = countof(suite_algs), i;
+
+       /* initialize copy of suite list */
+       for (i = 0; i < count; i++)
+       {
+               suites[i] = suite_algs[i];
+       }
+
+       filter_unsupported_suites(suites, &count);
+
+       if (!null)
+       {
+               filter_null_suites(suites, &count);
+       }
+
+       if (out)
+       {
+               *out = calloc(count, sizeof(tls_cipher_suite_t));
+               for (i = 0; i < count; i++)
+               {
+                       (*out)[i] = suites[i].suite;
+               }
+       }
+       return count;
+}