kernel-netlink: Check return value of both halfs when installing default route in...
[strongswan.git] / src / libtls / tls_crypto.c
index d8930ac..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",
@@ -375,7 +376,7 @@ struct private_tls_crypto_t {
        tls_cache_t *cache;
 
        /**
-        * All handshake data concatentated
+        * All handshake data concatenated
         */
        chunk_t handshake;
 
@@ -385,34 +386,14 @@ struct private_tls_crypto_t {
        tls_prf_t *prf;
 
        /**
-        * Signer instance for inbound traffic
+        * AEAD transform for inbound traffic
         */
-       signer_t *signer_in;
+       tls_aead_t *aead_in;
 
        /**
-        * Signer instance for outbound traffic
+        * AEAD transform for outbound traffic
         */
-       signer_t *signer_out;
-
-       /**
-        * Crypter instance for inbound traffic
-        */
-       crypter_t *crypter_in;
-
-       /**
-        * Crypter instance for outbound traffic
-        */
-       crypter_t *crypter_out;
-
-       /**
-        * IV for input decryption, if < TLSv1.2
-        */
-       chunk_t iv_in;
-
-       /**
-        * IV for output decryption, if < TLSv1.2
-        */
-       chunk_t iv_out;
+       tls_aead_t *aead_out;
 
        /**
         * EAP-[T]TLS MSK
@@ -460,6 +441,16 @@ 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_SHA256, PRF_HMAC_SHA2_256,
@@ -480,6 +471,16 @@ 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_SHA256,PRF_HMAC_SHA2_256,
@@ -500,6 +501,16 @@ 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_SHA256, PRF_HMAC_SHA2_256,
@@ -545,6 +556,16 @@ 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_SHA256, PRF_HMAC_SHA2_256,
@@ -627,8 +648,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,
-                                                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;
@@ -641,21 +661,56 @@ static void filter_suite(private_tls_crypto_t *this,
 
        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, 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) &&
-                               (suites[i].dh == MODP_NONE ||
-                                !current.dh   || current.dh   == suites[i].dh))
+                       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);
        }
@@ -665,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;
 
@@ -711,7 +765,8 @@ static void filter_key_exchange_config_suites(private_tls_crypto_t *this,
        int i, remaining = 0;
        char *token, *config;
 
-       config = lib->settings->get_str(lib->settings, "libtls.key_exchange", NULL);
+       config = lib->settings->get_str(lib->settings, "%s.tls.key_exchange", NULL,
+                                                                       lib->ns);
        if (config)
        {
                for (i = 0; i < *count; i++)
@@ -765,7 +820,8 @@ static void filter_cipher_config_suites(private_tls_crypto_t *this,
        int i, remaining = 0;
        char *token, *config;
 
-       config = lib->settings->get_str(lib->settings, "libtls.cipher", NULL);
+       config = lib->settings->get_str(lib->settings, "%s.tls.cipher", NULL,
+                                                                       lib->ns);
        if (config)
        {
                for (i = 0; i < *count; i++)
@@ -787,6 +843,20 @@ static void filter_cipher_config_suites(private_tls_crypto_t *this,
                                        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)
@@ -830,7 +900,8 @@ static void filter_mac_config_suites(private_tls_crypto_t *this,
        int i, remaining = 0;
        char *token, *config;
 
-       config = lib->settings->get_str(lib->settings, "libtls.mac", NULL);
+       config = lib->settings->get_str(lib->settings, "%s.tls.mac", NULL,
+                                                                       lib->ns);
        if (config)
        {
                for (i = 0; i < *count; i++)
@@ -879,7 +950,8 @@ static void filter_specific_config_suites(private_tls_crypto_t *this,
        int i, remaining = 0, suite;
        char *token, *config;
 
-       config = lib->settings->get_str(lib->settings, "libtls.suites", NULL);
+       config = lib->settings->get_str(lib->settings, "%s.tls.suites", NULL,
+                                                                       lib->ns);
        if (config)
        {
                for (i = 0; i < *count; i++)
@@ -887,8 +959,8 @@ static void filter_specific_config_suites(private_tls_crypto_t *this,
                        enumerator = enumerator_create_token(config, ",", " ");
                        while (enumerator->enumerate(enumerator, &token))
                        {
-                               suite = enum_from_name(tls_cipher_suite_names, token);
-                               if (suite == suites[i].suite)
+                               if (enum_from_name(tls_cipher_suite_names, token, &suite) &&
+                                       suite == suites[i].suite)
                                {
                                        suites[remaining++] = suites[i];
                                        break;
@@ -901,6 +973,26 @@ static void filter_specific_config_suites(private_tls_crypto_t *this,
 }
 
 /**
+ * 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,
@@ -914,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)
        {
@@ -927,17 +1020,7 @@ 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_suite(this, suites, &count, offsetof(suite_algs_t, dh),
-                                lib->crypto->create_dh_enumerator);
+       filter_unsupported_suites(suites, &count);
 
        /* filter suites with strongswan.conf options */
        filter_key_exchange_config_suites(this, suites, &count);
@@ -965,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)
        {
@@ -983,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,
@@ -1061,60 +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, bio_writer_t *writer)
 {
        bio_writer_t *supported;
-       enumerator_t *enumerator;
-       hash_algorithm_t alg;
-       tls_hash_algorithm_t hash;
-       const char *plugin_name;
+       int i;
 
        supported = bio_writer_create(32);
-       enumerator = lib->crypto->create_hasher_enumerator(lib->crypto);
-       while (enumerator->enumerate(enumerator, &alg, &plugin_name))
+
+       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 {
@@ -1128,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)
-                       {
-                               *out = curves[i].group;
-                       }
-                       if (curve)
+                       if (curves[i].group == group)
                        {
-                               *curve = curves[i].curve;
+                               if (out)
+                               {
+                                       *out = curves[i].group;
+                               }
+                               if (curve)
+                               {
+                                       *curve = curves[i].curve;
+                               }
+                               return TRUE;
                        }
-                       return TRUE;
                }
        }
        return FALSE;
@@ -1159,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,
@@ -1172,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));
@@ -1196,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
@@ -1210,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));
@@ -1231,59 +1403,6 @@ 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, bio_writer_t *writer,
        chunk_t data, chunk_t hashsig)
@@ -1292,7 +1411,7 @@ METHOD(tls_crypto_t, sign, bool,
        {
                signature_scheme_t scheme;
                bio_reader_t *reader;
-               u_int8_t hash, alg;
+               uint8_t hash, alg;
                chunk_t sig;
                bool done = FALSE;
 
@@ -1309,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;
@@ -1341,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)
                                {
@@ -1350,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;
                                }
@@ -1372,7 +1492,7 @@ METHOD(tls_crypto_t, verify, bool,
        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) ||
@@ -1390,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;
                }
@@ -1414,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)
                                {
@@ -1423,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;
                                }
@@ -1462,7 +1584,11 @@ 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;
 }
@@ -1470,7 +1596,7 @@ METHOD(tls_crypto_t, calculate_finished, bool,
 /**
  * Derive master secret from premaster, optionally save session
  */
-static void derive_master(private_tls_crypto_t *this, chunk_t premaster,
+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)
 {
@@ -1479,93 +1605,84 @@ static void derive_master(private_tls_crypto_t *this, chunk_t premaster,
 
        /* 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));
+       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)))
+       {
+               return FALSE;
+       }
+
        if (this->cache && session.len)
        {
                this->cache->create(this->cache, session, id, chunk_from_thing(master),
                                                        this->suite);
        }
        memwipe(master, sizeof(master));
+       return TRUE;
 }
 
 /**
  * Expand key material from master secret
  */
-static void expand_keys(private_tls_crypto_t *this,
+static bool expand_keys(private_tls_crypto_t *this,
                                                chunk_t client_random, chunk_t server_random)
 {
-       chunk_t seed, block, client_write, server_write;
-       int mks, eks = 0, ivs = 0;
+       chunk_t seed, block;
+       chunk_t cw_mac, cw, cw_iv;
+       chunk_t sw_mac, sw, sw_iv;
+       int mklen, eklen, ivlen;
 
-       /* derive key block for key expansion */
-       mks = this->signer_out->get_key_size(this->signer_out);
-       if (this->crypter_out)
+       if (!this->aead_in || !this->aead_out)
        {
-               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;
        }
+
+       /* 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((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))
+       block = chunk_alloca((mklen + eklen + ivlen) * 2);
+       if (!this->prf->get_bytes(this->prf, "key expansion", seed,
+                                                         block.len, block.ptr))
        {
-               this->signer_in->set_key(this->signer_in, client_write);
-               this->signer_out->set_key(this->signer_out, server_write);
-       }
-       else
-       {
-               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)
-       {
-               client_write = chunk_create(block.ptr, eks);
-               block = chunk_skip(block, eks);
-               server_write = chunk_create(block.ptr, eks);
-               block = chunk_skip(block, eks);
+       /* 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);
 
-               if (this->tls->is_server(this->tls))
-               {
-                       this->crypter_in->set_key(this->crypter_in, client_write);
-                       this->crypter_out->set_key(this->crypter_out, server_write);
-               }
-               else
+       /* 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->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_out->set_key(this->crypter_out, client_write);
-                       this->crypter_in->set_key(this->crypter_in, server_write);
+                       return FALSE;
                }
-               if (ivs)
+       }
+       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))
                {
-                       client_write = chunk_create(block.ptr, ivs);
-                       block = chunk_skip(block, ivs);
-                       server_write = chunk_create(block.ptr, ivs);
-                       block = chunk_skip(block, ivs);
-
-                       if (this->tls->is_server(this->tls))
-                       {
-                               this->iv_in = chunk_clone(client_write);
-                               this->iv_out = chunk_clone(server_write);
-                       }
-                       else
-                       {
-                               this->iv_out = chunk_clone(client_write);
-                               this->iv_in = chunk_clone(server_write);
-                       }
+                       return FALSE;
                }
        }
 
@@ -1574,17 +1691,22 @@ static void expand_keys(private_tls_crypto_t *this,
        {
                seed = chunk_cata("cc", client_random, server_random);
                this->msk = chunk_alloc(64);
-               this->prf->get_bytes(this->prf, this->msk_label, seed,
-                                                        this->msk.len, this->msk.ptr);
+               if (!this->prf->get_bytes(this->prf, this->msk_label, seed,
+                                                                 this->msk.len, this->msk.ptr))
+               {
+                       return FALSE;
+               }
        }
+       return TRUE;
 }
 
-METHOD(tls_crypto_t, derive_secrets, void,
+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)
 {
-       derive_master(this, premaster, session, id, client_random, server_random);
-       expand_keys(this, client_random, 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,
@@ -1598,16 +1720,20 @@ METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t,
                this->suite = this->cache->lookup(this->cache, session, id, &master);
                if (this->suite)
                {
-                       select_cipher_suite(this, &this->suite, 1, KEY_ANY);
+                       this->suite = select_cipher_suite(this, &this->suite, 1, KEY_ANY);
                        if (this->suite)
                        {
-                               this->prf->set_key(this->prf, master);
-                               expand_keys(this, client_random, server_random);
+                               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 this->suite;
+       return 0;
 }
 
 METHOD(tls_crypto_t, get_session, chunk_t,
@@ -1627,13 +1753,11 @@ 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);
                }
        }
 }
@@ -1647,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);
@@ -1718,11 +1837,14 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
        switch (tls->get_purpose(tls))
        {
                case TLS_PURPOSE_EAP_TLS:
-               case TLS_PURPOSE_EAP_PEAP:
                        /* MSK PRF ASCII constant label according to EAP-TLS RFC 5216 */
                        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";
@@ -1731,8 +1853,43 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
                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;
+}