libtls: Implement TLS 1.3 handshake on client-side
authorbytinbit <bytinbit@users.noreply.github.com>
Thu, 21 May 2020 07:37:38 +0000 (09:37 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 12 Feb 2021 10:45:44 +0000 (11:45 +0100)
The code is a minimal handshake with the HelloRetryRequest message
implementation missing.
Can be tested with an OpenSSL server running TLS 1.3. The server must
be at least version 1.1.1 (September 2018).

Co-authored-by: ryru <pascal.knecht@hsr.ch>
scripts/tls_test.c
src/libtls/tls.c
src/libtls/tls.h
src/libtls/tls_alert.c
src/libtls/tls_alert.h
src/libtls/tls_crypto.c
src/libtls/tls_crypto.h
src/libtls/tls_fragmentation.c
src/libtls/tls_peer.c
src/libtls/tls_protection.c
src/libtls/tls_server.c

index 84a32f9..316c986 100644 (file)
@@ -92,6 +92,7 @@ static int run_client(host_t *host, identification_t *server,
 
        while (times == -1 || times-- > 0)
        {
+               /* Open IPv4 socket */
                fd = socket(AF_INET, SOCK_STREAM, 0);
                if (fd == -1)
                {
@@ -101,11 +102,21 @@ static int run_client(host_t *host, identification_t *server,
                if (connect(fd, host->get_sockaddr(host),
                                        *host->get_sockaddr_len(host)) == -1)
                {
-                       DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno));
-                       close(fd);
-                       return 1;
+                       /* Check if the socket should use IPv6 instead */
+                       fd = socket(AF_INET6, SOCK_STREAM, 0);
+                       {
+                               if (connect(fd, host->get_sockaddr(host),
+                                           *host->get_sockaddr_len(host)) == -1)
+                               {
+                                       DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno));
+                                       close(fd);
+                                       return 1;
+                               }
+                       }
+
                }
-               tls = tls_socket_create(FALSE, server, client, fd, cache, TLS_1_2, TRUE);
+               tls = tls_socket_create(FALSE, server, client, fd, cache,
+                                                           TLS_1_3, TRUE);
                if (!tls)
                {
                        close(fd);
@@ -337,11 +348,17 @@ int main(int argc, char *argv[])
                usage(stderr, argv[0]);
                return 1;
        }
-       host = host_create_from_dns(address, 0, port);
+       /* Try to connect with IPv4 first*/
+       host = host_create_from_dns(address, AF_INET, port);
        if (!host)
        {
-               DBG1(DBG_TLS, "resolving hostname %s failed", address);
-               return 1;
+               /* If it fails, try IPv6*/
+               host = host_create_from_dns(address, AF_INET6, port);
+               if (!host)
+               {
+                       DBG1(DBG_TLS, "resolving hostname %s failed", address);
+                       return 1;
+               }
        }
        server = identification_create_from_string(address);
        cache = tls_cache_create(100, 30);
index ea39f7f..4a461c2 100644 (file)
 
 ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0,
        "SSLv2");
-ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0,
+ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_3, SSL_2_0,
        "SSLv3",
        "TLS 1.0",
        "TLS 1.1",
-       "TLS 1.2");
-ENUM_END(tls_version_names, TLS_1_2);
+       "TLS 1.2",
+       "TLS 1.3");
+ENUM_END(tls_version_names, TLS_1_3);
 
 ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA,
        "ChangeCipherSpec",
@@ -40,12 +41,22 @@ ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA,
        "ApplicationData",
 );
 
-ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_SERVER_HELLO,
-       "HelloRequest",
+ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_HELLO_REQUEST,
+       "HelloRequest");
+ENUM_NEXT(tls_handshake_type_names,
+               TLS_CLIENT_HELLO, TLS_HELLO_RETRY_REQUEST, TLS_HELLO_REQUEST,
        "ClientHello",
-       "ServerHello");
+       "ServerHello",
+       "HelloVerifyRequest",
+       "NewSessionTicket",
+       "EndOfEarlyData",
+       "HelloRetryRequest");
+ENUM_NEXT(tls_handshake_type_names,
+               TLS_ENCRYPTED_EXTENSIONS, TLS_ENCRYPTED_EXTENSIONS,
+               TLS_HELLO_RETRY_REQUEST,
+       "EncryptedExtensions");
 ENUM_NEXT(tls_handshake_type_names,
-               TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_SERVER_HELLO,
+               TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_ENCRYPTED_EXTENSIONS,
        "Certificate",
        "ServerKeyExchange",
        "CertificateRequest",
@@ -53,9 +64,16 @@ ENUM_NEXT(tls_handshake_type_names,
        "CertificateVerify",
        "ClientKeyExchange");
 ENUM_NEXT(tls_handshake_type_names,
-               TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE,
-       "Finished");
-ENUM_END(tls_handshake_type_names, TLS_FINISHED);
+                 TLS_FINISHED, TLS_KEY_UPDATE, TLS_CLIENT_KEY_EXCHANGE,
+       "Finished",
+       "CertificateUrl",
+       "CertificateStatus",
+       "SupplementalData",
+       "KeyUpdate");
+ENUM_NEXT(tls_handshake_type_names,
+               TLS_MESSAGE_HASH, TLS_MESSAGE_HASH, TLS_KEY_UPDATE,
+       "MessageHash");
+ENUM_END(tls_handshake_type_names, TLS_MESSAGE_HASH);
 
 ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST,
        "server name",
@@ -65,17 +83,42 @@ ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST,
        "truncated hmac",
        "status request");
 ENUM_NEXT(tls_extension_names,
-               TLS_EXT_ELLIPTIC_CURVES, TLS_EXT_EC_POINT_FORMATS,
+               TLS_EXT_SUPPORTED_GROUPS, TLS_EXT_EC_POINT_FORMATS,
                TLS_EXT_STATUS_REQUEST,
-       "elliptic curves",
+       "supported groups",
        "ec point formats");
 ENUM_NEXT(tls_extension_names,
-               TLS_EXT_SIGNATURE_ALGORITHMS, TLS_EXT_SIGNATURE_ALGORITHMS,
+               TLS_EXT_SIGNATURE_ALGORITHMS,
+               TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION,
                TLS_EXT_EC_POINT_FORMATS,
-       "signature algorithms");
+       "signature algorithms",
+       "use rtp",
+       "heartbeat",
+       "application layer protocol negotiation");
+ENUM_NEXT(tls_extension_names,
+               TLS_CLIENT_CERTIFICATE_TYPE, TLS_SERVER_CERTIFICATE_TYPE,
+               TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION,
+       "client certificate type",
+       "server certificate type");
+ENUM_NEXT(tls_extension_names,
+               TLS_EXT_PRE_SHARED_KEY, TLS_EXT_PSK_KEY_EXCHANGE_MODES,
+               TLS_SERVER_CERTIFICATE_TYPE,
+       "pre-shared key",
+       "early data",
+       "supported versions",
+       "cookie",
+       "psk key exchange modes");
+ENUM_NEXT(tls_extension_names,
+               TLS_EXT_CERTIFICATE_AUTHORITIES, TLS_EXT_KEY_SHARE,
+               TLS_EXT_PSK_KEY_EXCHANGE_MODES,
+       "certificate authorities",
+       "oid filters",
+       "post-handshake auth",
+       "signature algorithms cert",
+       "key-share");
 ENUM_NEXT(tls_extension_names,
                TLS_EXT_RENEGOTIATION_INFO, TLS_EXT_RENEGOTIATION_INFO,
-               TLS_EXT_SIGNATURE_ALGORITHMS,
+               TLS_EXT_KEY_SHARE,
        "renegotiation info");
 ENUM_END(tls_extension_names, TLS_EXT_RENEGOTIATION_INFO);
 
@@ -107,9 +150,14 @@ struct private_tls_t {
        bool is_server;
 
        /**
-        * Negotiated TLS version
+        * Negotiated TLS version and maximum supported TLS version
         */
-       tls_version_t version;
+       tls_version_t version_max;
+
+       /**
+        * Minimal supported TLS version
+        */
+       tls_version_t version_min;
 
        /**
         * TLS stack purpose, as given to constructor
@@ -300,7 +348,14 @@ METHOD(tls_t, build, status_t,
                        {
                                case NEED_MORE:
                                        record.type = type;
-                                       htoun16(&record.version, this->version);
+                                       if (this->version_max < TLS_1_3)
+                                       {
+                                               htoun16(&record.version, this->version_max);
+                                       }
+                                       else
+                                       {
+                                               htoun16(&record.version, TLS_1_2);
+                                       }
                                        htoun16(&record.length, data.len);
                                        this->output = chunk_cat("mcm", this->output,
                                                                                         chunk_from_thing(record), data);
@@ -361,16 +416,22 @@ METHOD(tls_t, get_peer_id, identification_t*,
        return this->handshake->get_peer_id(this->handshake);
 }
 
-METHOD(tls_t, get_version, tls_version_t,
+METHOD(tls_t, get_version_max, tls_version_t,
+       private_tls_t *this)
+{
+       return this->version_max;
+}
+
+METHOD(tls_t, get_version_min, tls_version_t,
        private_tls_t *this)
 {
-       return this->version;
+       return this->version_min;
 }
 
 METHOD(tls_t, set_version, bool,
        private_tls_t *this, tls_version_t version)
 {
-       if (version > this->version)
+       if (version > this->version_max)
        {
                return FALSE;
        }
@@ -379,7 +440,8 @@ METHOD(tls_t, set_version, bool,
                case TLS_1_0:
                case TLS_1_1:
                case TLS_1_2:
-                       this->version = version;
+               case TLS_1_3:
+                       this->version_max = version;
                        this->protection->set_version(this->protection, version);
                        return TRUE;
                case SSL_2_0:
@@ -466,7 +528,8 @@ tls_t *tls_create(bool is_server, identification_t *server,
                        .is_server = _is_server,
                        .get_server_id = _get_server_id,
                        .get_peer_id = _get_peer_id,
-                       .get_version = _get_version,
+                       .get_version_max = _get_version_max,
+                       .get_version_min = _get_version_min,
                        .set_version = _set_version,
                        .get_purpose = _get_purpose,
                        .is_complete = _is_complete,
@@ -475,7 +538,8 @@ tls_t *tls_create(bool is_server, identification_t *server,
                        .destroy = _destroy,
                },
                .is_server = is_server,
-               .version = TLS_1_2,
+               .version_max = TLS_1_3,
+               .version_min = TLS_1_0,
                .application = application,
                .purpose = purpose,
        );
index f832ef5..4509e05 100644 (file)
@@ -52,6 +52,7 @@ enum tls_version_t {
        TLS_1_0 = 0x0301,
        TLS_1_1 = 0x0302,
        TLS_1_2 = 0x0303,
+       TLS_1_3 = 0x0304,
 };
 
 /**
@@ -81,6 +82,11 @@ enum tls_handshake_type_t {
        TLS_HELLO_REQUEST = 0,
        TLS_CLIENT_HELLO = 1,
        TLS_SERVER_HELLO = 2,
+       TLS_HELLO_VERIFY_REQUEST = 3,
+       TLS_NEW_SESSION_TICKET = 4,
+       TLS_END_OF_EARLY_DATA = 5,
+       TLS_HELLO_RETRY_REQUEST = 6,
+       TLS_ENCRYPTED_EXTENSIONS = 8,
        TLS_CERTIFICATE = 11,
        TLS_SERVER_KEY_EXCHANGE = 12,
        TLS_CERTIFICATE_REQUEST = 13,
@@ -88,6 +94,11 @@ enum tls_handshake_type_t {
        TLS_CERTIFICATE_VERIFY = 15,
        TLS_CLIENT_KEY_EXCHANGE = 16,
        TLS_FINISHED = 20,
+       TLS_CERTIFICATE_URL = 21,
+       TLS_CERTIFICATE_STATUS = 22,
+       TLS_SUPPLEMENTAL_DATA = 23,
+       TLS_KEY_UPDATE = 24,
+       TLS_MESSAGE_HASH = 254,
 };
 
 /**
@@ -114,7 +125,7 @@ enum tls_purpose_t {
 };
 
 /**
- * TLS Hello extension types.
+ * TLS Handshake extension types.
  */
 enum tls_extension_t {
        /** Server name the client wants to talk to */
@@ -129,12 +140,42 @@ enum tls_extension_t {
        TLS_EXT_TRUNCATED_HMAC = 4,
        /** list of OCSP responders the client trusts */
        TLS_EXT_STATUS_REQUEST = 5,
-       /** list of supported elliptic curves */
-       TLS_EXT_ELLIPTIC_CURVES = 10,
+       /** list of supported groups, in legacy tls: elliptic curves */
+       TLS_EXT_SUPPORTED_GROUPS = 10,
        /** supported point formats */
        TLS_EXT_EC_POINT_FORMATS = 11,
        /** list supported signature algorithms */
        TLS_EXT_SIGNATURE_ALGORITHMS = 13,
+       /** indicate usage of Datagram Transport Layer Security (DTLS) */
+       TLS_EXT_USE_SRTP = 14,
+       /** indicate usage of heartbeat */
+       TLS_EXT_HEARTBEAT = 15,
+       /** indicate usage of application-layer protocol negotiation */
+       TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16,
+       /** exchange raw public key, client side*/
+       TLS_CLIENT_CERTIFICATE_TYPE = 19,
+       /** exchange raw public key, server side*/
+       TLS_SERVER_CERTIFICATE_TYPE = 20,
+       /** negotiate identity of the psk **/
+       TLS_EXT_PRE_SHARED_KEY = 41,
+       /** send data in 0-RTT when psk is used and early data is allowed **/
+       TLS_EXT_EARLY_DATA = 42,
+       /** negotiate supported tls versions **/
+       TLS_EXT_SUPPORTED_VERSIONS = 43,
+       /** identify client **/
+       TLS_EXT_COOKIE = 44,
+       /** psk modes supported by the client **/
+       TLS_EXT_PSK_KEY_EXCHANGE_MODES = 45,
+       /** indicate supported ca's by endpoint **/
+       TLS_EXT_CERTIFICATE_AUTHORITIES = 47,
+       /** provide oid/value pairs to match client's certificate **/
+       TLS_EXT_OID_FILTERS = 48,
+       /** willing to perform post-handshake authentication **/
+       TLS_EXT_POST_HANDSHAKE_AUTH = 49,
+       /** list supported signature algorithms to verify certificates **/
+       TLS_EXT_SIGNATURE_ALGORITHMS_CERT = 50,
+       /** list endpoint's cryptographic parameters **/
+       TLS_EXT_KEY_SHARE = 51,
        /** cryptographic binding for RFC 5746 renegotiation indication */
        TLS_EXT_RENEGOTIATION_INFO = 65281,
 };
@@ -216,11 +257,18 @@ struct tls_t {
        identification_t* (*get_peer_id)(tls_t *this);
 
        /**
-        * Get the negotiated TLS/SSL version.
+        * Get the maximum and negotiated TLS/SSL version.
         *
-        * @return                      negotiated TLS version
+        * @return                      max and negotiated TLS version
         */
-       tls_version_t (*get_version)(tls_t *this);
+       tls_version_t (*get_version_max)(tls_t *this);
+
+       /**
+       * Get the minimum TLS/SSL version.
+       *
+       * @return                       min TLS version
+       */
+       tls_version_t (*get_version_min)(tls_t *this);
 
        /**
         * Set the negotiated TLS/SSL version.
index 69570e9..a9cd671 100644 (file)
@@ -63,19 +63,34 @@ ENUM_NEXT(tls_alert_desc_names, TLS_INTERNAL_ERROR, TLS_INTERNAL_ERROR,
                TLS_INSUFFICIENT_SECURITY,
        "internal error",
 );
+ENUM_NEXT(tls_alert_desc_names, TLS_INAPPROPRIATE_FALLBACK,
+               TLS_INAPPROPRIATE_FALLBACK, TLS_INTERNAL_ERROR,
+       "inappropriate fallback",
+);
 ENUM_NEXT(tls_alert_desc_names, TLS_USER_CANCELED, TLS_USER_CANCELED,
-               TLS_INTERNAL_ERROR,
+               TLS_INAPPROPRIATE_FALLBACK,
        "user canceled",
 );
 ENUM_NEXT(tls_alert_desc_names, TLS_NO_RENEGOTIATION, TLS_NO_RENEGOTIATION,
                TLS_USER_CANCELED,
        "no renegotiation",
 );
-ENUM_NEXT(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION, TLS_UNSUPPORTED_EXTENSION,
+ENUM_NEXT(tls_alert_desc_names, TLS_MISSING_EXTENSION, TLS_CERTIFICATE_REQUIRED,
                TLS_NO_RENEGOTIATION,
+       "missing extensions",
        "unsupported extension",
+       "certificate unobtainable",
+       "recognized name",
+       "bad certificate status response",
+       "bad certificate hash value",
+       "unknown psk identity",
+       "certificate required",
+);
+ENUM_NEXT(tls_alert_desc_names, TLS_NO_APPLICATION_PROTOCOL,
+               TLS_NO_APPLICATION_PROTOCOL, TLS_CERTIFICATE_REQUIRED,
+       "no application protocol"
 );
-ENUM_END(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION);
+ENUM_END(tls_alert_desc_names, TLS_NO_APPLICATION_PROTOCOL);
 
 
 typedef struct private_tls_alert_t private_tls_alert_t;
index 8ce50f8..0535013 100644 (file)
@@ -61,9 +61,18 @@ enum tls_alert_desc_t {
        TLS_PROTOCOL_VERSION = 70,
        TLS_INSUFFICIENT_SECURITY = 71,
        TLS_INTERNAL_ERROR = 80,
+       TLS_INAPPROPRIATE_FALLBACK = 86,
        TLS_USER_CANCELED = 90,
        TLS_NO_RENEGOTIATION = 100,
+       TLS_MISSING_EXTENSION = 109,
        TLS_UNSUPPORTED_EXTENSION = 110,
+       TLS_CERTIFICATE_UNOBTAINABLE = 111,
+       TLS_RECOGNIZED_NAME = 112,
+       TLS_BAD_CERTIFICATE_STATUS_RESPONSE = 113,
+       TLS_BAD_CERTIFICATE_HASH_VALUE = 114,
+       TLS_UNKNOWN_PSK_IDENTITY = 115,
+       TLS_CERTIFICATE_REQUIRED = 116,
+       TLS_NO_APPLICATION_PROTOCOL = 120,
 };
 
 /**
index ebadb91..6b46513 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "tls_crypto.h"
+#include "tls_hkdf.h"
 
 #include <utils/debug.h>
 #include <plugins/plugin_feature.h>
@@ -175,9 +176,17 @@ ENUM_NEXT(tls_cipher_suite_names, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
                                                                  TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
                                                                  TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256,
        "TLS_EMPTY_RENEGOTIATION_INFO_SCSV");
+ENUM_NEXT(tls_cipher_suite_names, TLS_AES_128_GCM_SHA256,
+                                                                 TLS_AES_128_CCM_8_SHA256,
+                                                                 TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
+       "TLS_AES_128_GCM_SHA256",
+       "TLS_AES_256_GCM_SHA384",
+       "TLS_CHACHA20_POLY1305_SHA256",
+       "TLS_AES_128_CCM_SHA256",
+       "TLS_AES_128_CCM_8_SHA256");
 ENUM_NEXT(tls_cipher_suite_names, TLS_ECDH_ECDSA_WITH_NULL_SHA,
                                                                  TLS_ECDHE_PSK_WITH_NULL_SHA384,
-                                                                 TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
+                                                                 TLS_AES_128_CCM_8_SHA256,
        "TLS_ECDH_ECDSA_WITH_NULL_SHA",
        "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
        "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
@@ -279,7 +288,7 @@ ENUM(tls_ecc_curve_type_names, TLS_ECC_EXPLICIT_PRIME, TLS_ECC_NAMED_CURVE,
        "NAMED_CURVE",
 );
 
-ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1,
+ENUM_BEGIN(tls_named_group_names, TLS_SECT163K1, TLS_SECP521R1,
        "SECT163K1",
        "SECT163R1",
        "SECT163R2",
@@ -306,6 +315,18 @@ ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1,
        "SECP384R1",
        "SECP521R1",
 );
+ENUM_NEXT(tls_named_group_names, TLS_CURVE25519, TLS_CURVE_448, TLS_SECP521R1,
+       "CURVE25519",
+       "CURVE448",
+);
+ENUM_NEXT(tls_named_group_names, TLS_FFDHE2048, TLS_FFDHE8192, TLS_CURVE_448,
+       "FFDHE2048",
+       "FFDHE3072",
+       "FFDHE4096",
+       "FFDHE6144",
+       "FFDHE8192",
+);
+ENUM_END(tls_named_group_names, TLS_FFDHE8192);
 
 ENUM(tls_ansi_point_format_names, TLS_ANSI_COMPRESSED, TLS_ANSI_HYBRID_Y,
        "compressed",
@@ -351,6 +372,11 @@ struct private_tls_crypto_t {
        int suite_count;
 
        /**
+        * HKDF for TLS 1.3
+        */
+       tls_hkdf_t *hkdf;
+
+       /**
         * Selected cipher suite
         */
        tls_cipher_suite_t suite;
@@ -366,6 +392,35 @@ struct private_tls_crypto_t {
        bool ecdsa;
 
        /**
+        * MD5 supported?
+        */
+       bool md5;
+
+       /**
+        * SHA1 supported?
+        */
+       bool sha1;
+       /**
+        * SHA224 supported?
+        */
+       bool sha224;
+
+       /*
+        * SHA256 supported?
+        */
+       bool sha256;
+
+       /**
+        * SHA384 supported?
+        */
+       bool sha384;
+
+       /**
+        * SHA512 supported?
+        */
+       bool sha512;
+
+       /**
         * TLS context
         */
        tls_t *tls;
@@ -415,216 +470,291 @@ typedef struct {
        integrity_algorithm_t mac;
        encryption_algorithm_t encr;
        size_t encr_size;
+       tls_version_t tls_version;
 } suite_algs_t;
 
 /**
  * Mapping suites to a set of algorithms
  */
 static suite_algs_t suite_algs[] = {
+       /* Cipher suites of TLS 1.3: key exchange and authentication
+        * delegated to extensions, therefore KEY_ANY, MODP_NONE, PRF_UNDEFINED */
+       { TLS_AES_128_GCM_SHA256,
+               KEY_ANY, MODP_NONE,
+               HASH_SHA256, PRF_UNDEFINED,
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_GCM_ICV16, 16,
+               TLS_1_3,
+       },
+       { TLS_AES_256_GCM_SHA384,
+               KEY_ANY, MODP_NONE,
+               HASH_SHA384, PRF_UNDEFINED,
+               AUTH_HMAC_SHA2_384_384, ENCR_AES_GCM_ICV16, 32,
+               TLS_1_3,
+       },
+       { TLS_CHACHA20_POLY1305_SHA256,
+               KEY_ANY, MODP_NONE,
+               HASH_SHA256, PRF_UNDEFINED,
+               AUTH_HMAC_SHA2_256_256, ENCR_CHACHA20_POLY1305, 16,
+               TLS_1_3,
+       },
+       { TLS_AES_128_CCM_SHA256,
+         KEY_ANY, MODP_NONE,
+         HASH_SHA256, PRF_UNDEFINED,
+         AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV16, 16,
+         TLS_1_3,
+       },
+       { TLS_AES_128_CCM_8_SHA256,
+         KEY_ANY, MODP_NONE,
+         HASH_SHA256, PRF_UNDEFINED,
+         AUTH_HMAC_SHA2_256_256, ENCR_AES_CCM_ICV8, 16,
+         TLS_1_3,
+       },
+       /* Legacy TLS cipher suites */
        { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                KEY_ECDSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
                KEY_ECDSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
                KEY_ECDSA, ECP_384_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
                KEY_ECDSA, ECP_384_BIT,
                HASH_SHA384, PRF_HMAC_SHA2_384,
-               AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { 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
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16,
+               TLS_1_2,
        },
        { 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
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32,
+               TLS_1_2,
        },
        { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                KEY_RSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
                KEY_RSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                KEY_RSA, ECP_384_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
                KEY_RSA, ECP_384_BIT,
                HASH_SHA384, PRF_HMAC_SHA2_384,
-               AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { 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
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16,
+               TLS_1_2,
        },
        { 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
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                KEY_RSA, MODP_2048_BIT,
                HASH_SHA256,PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
                KEY_RSA, MODP_3072_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
                KEY_RSA, MODP_3072_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
                KEY_RSA, MODP_4096_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { 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
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16,
+               TLS_1_2,
        },
        { 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
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
                KEY_RSA, MODP_2048_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16
+               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
                KEY_RSA, MODP_3072_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16
+               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
                KEY_RSA, MODP_3072_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32
+               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
                KEY_RSA, MODP_4096_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32
+               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32,
+               TLS_1_2,
        },
        { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
                KEY_RSA, MODP_2048_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_3DES, 0
+               AUTH_HMAC_SHA1_160, ENCR_3DES, 0,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_AES_128_CBC_SHA,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_AES_128_CBC_SHA256,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_AES_256_CBC_SHA,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_AES_256_CBC_SHA256,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32
+               AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_AES_128_GCM_SHA256,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 16,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_AES_256_GCM_SHA384,
                KEY_RSA, MODP_NONE,
                HASH_SHA384, PRF_HMAC_SHA2_384,
-               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32
+               AUTH_UNDEFINED, ENCR_AES_GCM_ICV16, 32,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16
+               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16
+               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32
+               AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32
+               AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32,
+               TLS_1_2,
        },
        { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
                KEY_ECDSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_3DES, 0
+               AUTH_HMAC_SHA1_160, ENCR_3DES, 0,
+               TLS_1_2,
        },
        { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
                KEY_RSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_3DES, 0
+               AUTH_HMAC_SHA1_160, ENCR_3DES, 0,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_3DES, 0
+               AUTH_HMAC_SHA1_160, ENCR_3DES, 0,
+               TLS_1_2,
        },
        { TLS_ECDHE_ECDSA_WITH_NULL_SHA,
                KEY_ECDSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_NULL, 0
+               AUTH_HMAC_SHA1_160, ENCR_NULL, 0,
+               TLS_1_2,
        },
        { TLS_ECDHE_RSA_WITH_NULL_SHA,
                KEY_ECDSA, ECP_256_BIT,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_NULL, 0
+               AUTH_HMAC_SHA1_160, ENCR_NULL, 0,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_NULL_SHA,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA1_160, ENCR_NULL, 0
+               AUTH_HMAC_SHA1_160, ENCR_NULL, 0,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_NULL_SHA256,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0
+               AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0,
+               TLS_1_2,
        },
        { TLS_RSA_WITH_NULL_MD5,
                KEY_RSA, MODP_NONE,
                HASH_SHA256, PRF_HMAC_SHA2_256,
-               AUTH_HMAC_MD5_128, ENCR_NULL, 0
+               AUTH_HMAC_MD5_128, ENCR_NULL, 0,
+               TLS_1_2,
        },
 };
 
@@ -978,14 +1108,17 @@ static void filter_specific_config_suites(private_tls_crypto_t *this,
 static void filter_unsupported_suites(suite_algs_t suites[], int *count)
 {
        /* filter suite list by each algorithm */
+       if (suites->tls_version < TLS_1_3)
+       {
+               filter_suite(suites, count, offsetof(suite_algs_t, encr),
+                                        lib->crypto->create_aead_enumerator);
+               filter_suite(suites, count, offsetof(suite_algs_t, prf),
+                                        lib->crypto->create_prf_enumerator);
+       }
        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),
@@ -1068,7 +1201,7 @@ static bool create_null(private_tls_crypto_t *this, suite_algs_t *algs)
  */
 static bool create_traditional(private_tls_crypto_t *this, suite_algs_t *algs)
 {
-       if (this->tls->get_version(this->tls) < TLS_1_1)
+       if (this->tls->get_version_max(this->tls) < TLS_1_1)
        {
                this->aead_in = tls_aead_create_implicit(algs->mac,
                                                                algs->encr, algs->encr_size);
@@ -1097,8 +1230,20 @@ static bool create_traditional(private_tls_crypto_t *this, suite_algs_t *algs)
  */
 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->tls->get_version_max(this->tls) < TLS_1_3)
+       {
+               this->aead_in = tls_aead_create_aead(algs->encr, algs->encr_size);
+               this->aead_out = tls_aead_create_aead(algs->encr, algs->encr_size);
+       }
+       else
+       {
+               this->aead_in = tls_aead_create_seq(algs->encr, algs->encr_size);
+               this->aead_out = tls_aead_create_seq(algs->encr, algs->encr_size);
+
+               this->protection->set_cipher(this->protection, TRUE, this->aead_in);
+               this->protection->set_cipher(this->protection, FALSE, this->aead_out);
+
+       }
        if (!this->aead_in || !this->aead_out)
        {
                DBG1(DBG_TLS, "selected TLS transforms %N-%u not supported",
@@ -1125,18 +1270,30 @@ 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)
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               this->prf = tls_prf_create_10();
+               if (this->tls->get_version_max(this->tls) < TLS_1_2)
+               {
+                       this->prf = tls_prf_create_10();
+               }
+               else
+               {
+                       this->prf = tls_prf_create_12(algs->prf);
+               }
+               if (!this->prf)
+               {
+                       DBG1(DBG_TLS, "selected TLS PRF not supported");
+                       return FALSE;
+               }
        }
        else
        {
-               this->prf = tls_prf_create_12(algs->prf);
-       }
-       if (!this->prf)
-       {
-               DBG1(DBG_TLS, "selected TLS PRF not supported");
-               return FALSE;
+               this->hkdf = tls_hkdf_create(algs->hash, chunk_empty);
+               if (!this->hkdf)
+               {
+                       DBG1(DBG_TLS, "TLS HKDF creation unsuccessful");
+                       return FALSE;
+               }
        }
        if (algs->encr == ENCR_NULL)
        {
@@ -1245,6 +1402,30 @@ METHOD(tls_crypto_t, get_signature_algorithms, void,
                {
                        continue;
                }
+               if (schemes[i].hash == TLS_HASH_MD5 && !this->md5)
+               {
+                       continue;
+               }
+               if (schemes[i].hash == TLS_HASH_SHA1 && !this->sha1)
+               {
+                       continue;
+               }
+               if (schemes[i].hash == TLS_HASH_SHA224 && !this->sha224)
+               {
+                       continue;
+               }
+               if (schemes[i].hash == TLS_HASH_SHA256 && !this->sha256)
+               {
+                       continue;
+               }
+               if (schemes[i].hash == TLS_HASH_SHA384 && !this->sha384)
+               {
+                       continue;
+               }
+               if (schemes[i].hash == TLS_HASH_SHA512 && !this->sha512)
+               {
+                       continue;
+               }
                if (!lib->plugins->has_feature(lib->plugins,
                                                PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].scheme)))
                {
@@ -1287,7 +1468,7 @@ static signature_scheme_t hashsig_to_scheme(key_type_t type,
  */
 static struct {
        diffie_hellman_group_t group;
-       tls_named_curve_t curve;
+       tls_named_group_t curve;
 } curves[] = {
        { ECP_256_BIT, TLS_SECP256R1},
        { ECP_384_BIT, TLS_SECP384R1},
@@ -1300,7 +1481,7 @@ CALLBACK(group_filter, bool,
        void *null, enumerator_t *orig, va_list args)
 {
        diffie_hellman_group_t group, *out;
-       tls_named_curve_t *curve;
+       tls_named_group_t *curve;
        char *plugin;
        int i;
 
@@ -1357,7 +1538,7 @@ METHOD(tls_crypto_t, append_handshake, void,
  */
 static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash)
 {
-       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
                hasher_t *hasher;
                suite_algs_t *alg;
@@ -1407,7 +1588,7 @@ 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)
 {
-       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
                const chunk_t hashsig_def = chunk_from_chars(
                                        TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA);
@@ -1490,7 +1671,64 @@ METHOD(tls_crypto_t, verify, bool,
        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)
+       if (this->tls->get_version_max(this->tls) == TLS_1_3)
+       {
+               signature_scheme_t scheme = SIGN_UNKNOWN;
+               uint8_t hash, alg;
+               chunk_t sig, transcript_hash, static_sig_data_all;
+
+               chunk_t static_sig_data = chunk_from_chars(
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+                       0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c,
+                       0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+                       0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+                       0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66,
+                       0x79, 0x00,
+               );
+
+               if (!reader->read_uint8(reader, &hash) ||
+                       !reader->read_uint8(reader, &alg) ||
+                       !reader->read_data16(reader, &sig))
+               {
+                       DBG1(DBG_TLS, "received invalid signature");
+                       return FALSE;
+               }
+
+               if (!hash_data(this, data, &transcript_hash))
+               {
+                       DBG1(DBG_TLS, "Unable to hash");
+                       return FALSE;
+               }
+
+               static_sig_data_all = chunk_cat("cc", static_sig_data, transcript_hash);
+               scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
+               if (scheme == SIGN_UNKNOWN)
+               {
+                       DBG1(DBG_TLS, "signature algorithms %N/%N not supported",
+                                tls_hash_algorithm_names, hash,
+                                tls_signature_algorithm_names, alg);
+                       return FALSE;
+               }
+               if (!key->verify(key, scheme, NULL, static_sig_data_all, sig))
+               {
+                       DBG1(DBG_TLS, "verification of signature failed");
+                       return FALSE;
+               }
+               DBG2(DBG_TLS,
+                        "verified signature with %N/%N",
+                        tls_hash_algorithm_names,
+                        hash,
+                        tls_signature_algorithm_names,
+                        alg);
+       }
+       else if (this->tls->get_version_max(this->tls) == TLS_1_2)
        {
                signature_scheme_t scheme = SIGN_UNKNOWN;
                uint8_t hash, alg;
@@ -1594,6 +1832,42 @@ METHOD(tls_crypto_t, calculate_finished, bool,
        return TRUE;
 }
 
+METHOD(tls_crypto_t, calculate_finished_tls13, bool,
+       private_tls_crypto_t *this, bool is_server, chunk_t *out)
+{
+       chunk_t finished_key, finished_hash;
+       prf_t *prf;
+
+       this->hkdf->derive_finished(this->hkdf, is_server, &finished_key);
+       if (!hash_data(this, this->handshake, &finished_hash))
+       {
+               DBG1(DBG_TLS, "creating hash of handshake failed");
+       }
+
+       if (this->suite == TLS_AES_256_GCM_SHA384)
+       {
+               prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_384);
+       }
+       else
+       {
+               prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_256);
+       }
+       if(!prf->set_key(prf, finished_key) ||
+          !prf->allocate_bytes(prf, finished_hash, out))
+       {
+               DBG1(DBG_TLS, "setting key or generating hash for HMAC failed");
+               chunk_clear(&finished_key);
+               chunk_clear(&finished_hash);
+               prf->destroy(prf);
+               return FALSE;
+       }
+
+       chunk_clear(&finished_key);
+       chunk_clear(&finished_hash);
+       prf->destroy(prf);
+       return TRUE;
+}
+
 /**
  * Derive master secret from premaster, optionally save session
  */
@@ -1710,6 +1984,150 @@ METHOD(tls_crypto_t, derive_secrets, bool,
                   expand_keys(this, client_random, server_random);
 }
 
+METHOD(tls_crypto_t, derive_handshake_secret, bool,
+       private_tls_crypto_t *this, chunk_t shared_secret)
+{
+       chunk_t c_key, c_iv, s_key, s_iv;
+
+       this->hkdf->set_shared_secret(this->hkdf, shared_secret);
+
+       /* client key material */
+       if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_C_HS_TRAFFIC,
+                                                                        this->handshake, NULL) ||
+               !this->hkdf->derive_key(this->hkdf, FALSE,
+                                                               this->aead_out->
+                                                               get_encr_key_size(this->aead_out), &c_key) ||
+               !this->hkdf->derive_iv(this->hkdf, FALSE,
+                                                          this->aead_out->
+                                                          get_iv_size(this->aead_out), &c_iv))
+       {
+               DBG1(DBG_TLS, "deriving client key material failed");
+               chunk_clear(&c_key);
+               chunk_clear(&c_iv);
+               return FALSE;
+       }
+
+       /* server key material */
+       if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_S_HS_TRAFFIC,
+          this->handshake, NULL) ||
+          !this->hkdf->derive_key(this->hkdf, TRUE, this->aead_in->
+          get_encr_key_size(this->aead_in), &s_key) ||
+          !this->hkdf->derive_iv(this->hkdf, TRUE, this->aead_in->
+          get_iv_size(this->aead_in), &s_iv))
+       {
+               DBG1(DBG_TLS, "deriving server key material failed");
+               chunk_clear(&c_key);
+               chunk_clear(&c_iv);
+               chunk_clear(&s_key);
+               chunk_clear(&s_iv);
+               return FALSE;
+       }
+
+       if (this->tls->is_server(this->tls))
+       {
+               if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) ||
+                       !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv))
+               {
+                       DBG1(DBG_TLS, "setting aead server key material failed");
+                       chunk_clear(&c_key);
+                       chunk_clear(&c_iv);
+                       chunk_clear(&s_key);
+                       chunk_clear(&s_iv);
+                       return FALSE;
+               }
+       }
+       else
+       {
+               if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) ||
+                       !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv))
+               {
+                       DBG1(DBG_TLS, "setting aead client key material failed");
+                       chunk_clear(&c_key);
+                       chunk_clear(&c_iv);
+                       chunk_clear(&s_key);
+                       chunk_clear(&s_iv);
+                       return FALSE;
+               }
+       }
+
+       chunk_clear(&c_key);
+       chunk_clear(&c_iv);
+       chunk_clear(&s_key);
+       chunk_clear(&s_iv);
+       return TRUE;
+}
+
+METHOD(tls_crypto_t, derive_app_secret, bool,
+       private_tls_crypto_t *this)
+{
+       chunk_t c_key, c_iv, s_key, s_iv;
+
+       /* client key material */
+       if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_C_AP_TRAFFIC,
+                                                                        this->handshake, NULL) ||
+               !this->hkdf->derive_key(this->hkdf, FALSE,
+                                                               this->aead_out->
+                                                               get_encr_key_size(this->aead_out), &c_key) ||
+               !this->hkdf->derive_iv(this->hkdf, FALSE,
+                                                          this->aead_out->
+                                                          get_iv_size(this->aead_out), &c_iv))
+       {
+               DBG1(DBG_TLS, "deriving client key material failed");
+               chunk_clear(&c_key);
+               chunk_clear(&c_iv);
+               return FALSE;
+       }
+
+       /* server key material */
+       if (!this->hkdf->generate_secret(this->hkdf, TLS_HKDF_S_AP_TRAFFIC,
+          this->handshake, NULL) ||
+          !this->hkdf->derive_key(this->hkdf, TRUE, this->aead_in->
+          get_encr_key_size(this->aead_in), &s_key) ||
+          !this->hkdf->derive_iv(this->hkdf, TRUE, this->aead_in->
+          get_iv_size(this->aead_in), &s_iv))
+       {
+               DBG1(DBG_TLS, "deriving server key material failed");
+               chunk_clear(&c_key);
+               chunk_clear(&c_iv);
+               chunk_clear(&s_key);
+               chunk_clear(&s_iv);
+               return FALSE;
+       }
+
+       if (this->tls->is_server(this->tls))
+       {
+               if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) ||
+                       !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv))
+               {
+                       DBG1(DBG_TLS, "setting aead server key material failed");
+                       chunk_clear(&c_key);
+                       chunk_clear(&c_iv);
+                       chunk_clear(&s_key);
+                       chunk_clear(&s_iv);
+                       return FALSE;
+               }
+       }
+       else
+       {
+               if (!this->aead_in->set_keys(this->aead_in, chunk_empty, s_key, s_iv) ||
+                       !this->aead_out->set_keys(this->aead_out, chunk_empty, c_key, c_iv))
+               {
+                       DBG1(DBG_TLS, "setting aead client key material failed");
+                       chunk_clear(&c_key);
+                       chunk_clear(&c_iv);
+                       chunk_clear(&s_key);
+                       chunk_clear(&s_iv);
+                       return FALSE;
+               }
+       }
+
+       chunk_clear(&c_key);
+       chunk_clear(&c_iv);
+       chunk_clear(&s_key);
+       chunk_clear(&s_iv);
+       return TRUE;
+}
+
 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)
@@ -1776,6 +2194,7 @@ METHOD(tls_crypto_t, destroy, void,
        free(this->handshake.ptr);
        free(this->msk.ptr);
        DESTROY_IF(this->prf);
+       DESTROY_IF(this->hkdf);
        free(this->suites);
        free(this);
 }
@@ -1789,6 +2208,8 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
        enumerator_t *enumerator;
        credential_type_t type;
        int subtype;
+       int hash_algorithm;
+       const char *plugin;
 
        INIT(this,
                .public = {
@@ -1804,7 +2225,10 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
                        .sign_handshake = _sign_handshake,
                        .verify_handshake = _verify_handshake,
                        .calculate_finished = _calculate_finished,
+                       .calculate_finished_tls13 = _calculate_finished_tls13,
                        .derive_secrets = _derive_secrets,
+                       .derive_handshake_secret = _derive_handshake_secret,
+                       .derive_app_secret = _derive_app_secret,
                        .resume_session = _resume_session,
                        .get_session = _get_session,
                        .change_cipher = _change_cipher,
@@ -1835,6 +2259,49 @@ tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache)
        }
        enumerator->destroy(enumerator);
 
+       enumerator = lib->crypto->create_hasher_enumerator(lib->crypto);
+       while (enumerator->enumerate(enumerator, &hash_algorithm, &plugin))
+       {
+               switch (hash_algorithm)
+               {
+                       case TLS_HASH_MD5:
+                               if (tls->get_version_max(tls) < TLS_1_3)
+                               {
+                                       this->md5 = TRUE;
+                               }
+                               else
+                               {
+                                       this->md5 = FALSE;
+                               }
+                               break;
+                       case TLS_HASH_SHA1:
+                               this->sha1 = TRUE;
+                               break;
+                       case TLS_HASH_SHA224:
+                               if (tls->get_version_max(tls) < TLS_1_3)
+                               {
+                                       this->sha224 = TRUE;
+                               }
+                               else
+                               {
+                                       this->sha224 = FALSE;
+                               }
+                               break;
+                       case TLS_HASH_SHA384:
+                               this->sha384 = TRUE;
+                               break;
+                       case TLS_HASH_SHA256:
+                               this->sha256 = TRUE;
+                               break;
+                       case TLS_HASH_SHA512:
+                               this->sha512 = TRUE;
+                               break;
+                       default:
+                               continue;
+               }
+       }
+       enumerator->destroy(enumerator);
+
        switch (tls->get_purpose(tls))
        {
                case TLS_PURPOSE_EAP_TLS:
index a42e07b..a1390e3 100644 (file)
@@ -27,7 +27,7 @@ typedef enum tls_hash_algorithm_t tls_hash_algorithm_t;
 typedef enum tls_signature_algorithm_t tls_signature_algorithm_t;
 typedef enum tls_client_certificate_type_t tls_client_certificate_type_t;
 typedef enum tls_ecc_curve_type_t tls_ecc_curve_type_t;
-typedef enum tls_named_curve_t tls_named_curve_t;
+typedef enum tls_named_group_t tls_named_group_t;
 typedef enum tls_ansi_point_format_t tls_ansi_point_format_t;
 typedef enum tls_ec_point_format_t tls_ec_point_format_t;
 
@@ -191,6 +191,12 @@ enum tls_cipher_suite_t {
 
        TLS_EMPTY_RENEGOTIATION_INFO_SCSV =                     0x00FF,
 
+       TLS_AES_128_GCM_SHA256 =                                        0x1301,
+       TLS_AES_256_GCM_SHA384 =                                        0x1302,
+       TLS_CHACHA20_POLY1305_SHA256 =                          0x1303,
+       TLS_AES_128_CCM_SHA256 =                                        0x1304,
+       TLS_AES_128_CCM_8_SHA256 =                                      0x1305,
+
        TLS_ECDH_ECDSA_WITH_NULL_SHA =                          0xC001,
        TLS_ECDH_ECDSA_WITH_RC4_128_SHA =                       0xC002,
        TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA =          0xC003,
@@ -327,7 +333,7 @@ extern enum_name_t *tls_ecc_curve_type_names;
 /**
  * TLS Named Curve identifiers
  */
-enum tls_named_curve_t {
+enum tls_named_group_t {
        TLS_SECT163K1 =          1,
        TLS_SECT163R1 =          2,
        TLS_SECT163R2 =          3,
@@ -353,12 +359,21 @@ enum tls_named_curve_t {
        TLS_SECP256R1 =         23,
        TLS_SECP384R1 =         24,
        TLS_SECP521R1 =         25,
+
+       /* TLS 1.3: new ecdhe, dhe groups */
+       TLS_CURVE25519 =    29,
+       TLS_CURVE_448  =    30,
+       TLS_FFDHE2048  =    256,
+       TLS_FFDHE3072  =    257,
+       TLS_FFDHE4096  =    258,
+       TLS_FFDHE6144  =    259,
+       TLS_FFDHE8192  =    260,
 };
 
 /**
- * Enum names for tls_named_curve_t
+ * Enum names for tls_named_group_t
  */
-extern enum_name_t *tls_named_curve_names;
+extern enum_name_t *tls_named_group_names;
 
 /**
  * EC Point format, ANSI X9.62.
@@ -432,7 +447,7 @@ struct tls_crypto_t {
        /**
         * Create an enumerator over supported ECDH groups.
         *
-        * Enumerates over (diffie_hellman_group_t, tls_named_curve_t)
+        * Enumerates over (diffie_hellman_group_t, tls_named_group_t)
         *
         * @return                              enumerator
         */
@@ -499,7 +514,7 @@ struct tls_crypto_t {
                                                         bio_reader_t *reader);
 
        /**
-        * Calculate the data of a TLS finished message.
+        * Calculate the data of a legacyTLS finished message.
         *
         * @param label                 ASCII label to use for calculation
         * @param out                   buffer to write finished data to
@@ -508,6 +523,15 @@ struct tls_crypto_t {
        bool (*calculate_finished)(tls_crypto_t *this, char *label, char out[12]);
 
        /**
+        * Calculate the data of a TLS finished message.
+        *
+        * @param out                   buffer to write finished data to
+        * @return                              TRUE if calculation successful
+        */
+       bool (*calculate_finished_tls13)(tls_crypto_t *this, bool is_server,
+                                                                        chunk_t *out);
+
+       /**
         * Derive the master secret, MAC and encryption keys.
         *
         * @param premaster             premaster secret
@@ -522,6 +546,21 @@ struct tls_crypto_t {
                                                   chunk_t client_random, chunk_t server_random);
 
        /**
+        * Derive the handshake keys.
+        *
+        * @param shared_secret         input key material
+        * @return                                      TRUE if secret derived successfully
+        */
+       bool (*derive_handshake_secret)(tls_crypto_t *this, chunk_t shared_secret);
+
+       /**
+        * Derive the application keys.
+        *
+        * @return                                      TRUE if secret derived successfully
+        */
+       bool (*derive_app_secret)(tls_crypto_t *this);
+
+       /**
         * Try to resume a TLS session, derive key material.
         *
         * @param session               session identifier
index 7a96302..f74cf59 100644 (file)
@@ -292,7 +292,11 @@ METHOD(tls_fragmentation_t, process, status_t,
                        break;
                case TLS_HANDSHAKE:
                        status = process_handshake(this, reader);
-                       break;
+                       if (!this->handshake->finished(this->handshake))
+                       {
+                               break;
+                       }
+                       /* fall */
                case TLS_APPLICATION_DATA:
                        status = process_application(this, reader);
                        break;
@@ -353,7 +357,8 @@ static status_t build_handshake(private_tls_fragmentation_t *this)
                                msg->write_data24(msg, hs->get_buf(hs));
                                DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)",
                                         tls_handshake_type_names, type, hs->get_buf(hs).len);
-                               if (!this->handshake->cipherspec_changed(this->handshake, FALSE))
+                               if (type != TLS_FINISHED &&
+                                       !this->handshake->cipherspec_changed(this->handshake, FALSE))
                                {
                                        hs->destroy(hs);
                                        continue;
index 1f2439c..471ba49 100644 (file)
@@ -37,6 +37,12 @@ typedef enum {
        STATE_FINISHED_SENT,
        STATE_CIPHERSPEC_CHANGED_IN,
        STATE_FINISHED_RECEIVED,
+       /* new states in TLS 1.3 */
+       STATE_HELLORETRYREQ_RECEIVED,
+       STATE_ENCRYPTED_EXTENSIONS_RECEIVED,
+       STATE_CERT_VERIFY_RECEIVED,
+       STATE_FINISHED_SENT_KEY_SWITCHED,
+
 } peer_state_t;
 
 /**
@@ -143,8 +149,11 @@ static status_t process_server_hello(private_tls_peer_t *this,
 {
        uint8_t compression;
        uint16_t version, cipher;
-       chunk_t random, session, ext = chunk_empty;
+       chunk_t random, session, ext = chunk_empty, ext_key_share = chunk_empty;
        tls_cipher_suite_t suite = 0;
+       int offset = 0;
+       uint16_t extension_type, extension_length;
+       uint16_t key_type, key_length;
 
        this->crypto->append_handshake(this->crypto,
                                                                   TLS_SERVER_HELLO, reader->peek(reader));
@@ -163,6 +172,51 @@ static status_t process_server_hello(private_tls_peer_t *this,
 
        memcpy(this->server_random, random.ptr, sizeof(this->server_random));
 
+       bio_reader_t *extension_reader = bio_reader_create(ext);
+
+       /* parse extension to decide which tls version of the state machine we
+        * want to go
+        */
+       while (offset < ext.len)
+       {
+               chunk_t extension_payload = chunk_empty;
+
+               extension_reader->read_uint16(extension_reader, &extension_type);
+               extension_reader->read_uint16(extension_reader, &extension_length);
+               offset += extension_length + 4;
+
+               if (!extension_reader->read_data(extension_reader,
+                                                                                extension_length,
+                                                                                &extension_payload))
+               {
+                       DBG2(DBG_TLS, "unable to read extension payload data");
+               }
+
+               bio_reader_t *ext_payload_reader = bio_reader_create(extension_payload);
+
+               switch (extension_type)
+               {
+                       case TLS_EXT_SUPPORTED_VERSIONS:
+                               ext_payload_reader->read_uint16(ext_payload_reader, &version);
+                               break;
+
+                       case TLS_EXT_KEY_SHARE:
+                               ext_payload_reader->read_uint16(ext_payload_reader, &key_type);
+                               ext_payload_reader->read_uint16(ext_payload_reader, &key_length);
+                               if (!ext_payload_reader->read_data(ext_payload_reader,
+                                                                                                  key_length,
+                                                                                                  &ext_key_share))
+                               {
+                                       DBG2(DBG_TLS, "no valid key share found in extension");
+                               }
+                               break;
+                       default:
+                               continue;
+               }
+               ext_payload_reader->destroy(ext_payload_reader);
+       }
+       extension_reader->destroy(extension_reader);
+
        if (!this->tls->set_version(this->tls, version))
        {
                DBG1(DBG_TLS, "negotiated version %N not supported",
@@ -171,17 +225,24 @@ static status_t process_server_hello(private_tls_peer_t *this,
                return NEED_MORE;
        }
 
-       if (chunk_equals(this->session, session))
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               suite = this->crypto->resume_session(this->crypto, session, this->server,
-                                                                               chunk_from_thing(this->client_random),
-                                                                               chunk_from_thing(this->server_random));
-               if (suite)
+               if (chunk_equals(this->session, session))
                {
-                       DBG1(DBG_TLS, "resumed %N using suite %N",
-                                tls_version_names, version, tls_cipher_suite_names, suite);
-                       this->resume = TRUE;
+                       suite = this->crypto->resume_session(this->crypto, session,
+                                                                                                this->server, chunk_from_thing
+                                                                                                (this->client_random),
+                                                                                                chunk_from_thing
+                                                                                                (this->server_random));
+                       if (suite)
+                       {
+                               DBG1(DBG_TLS, "resumed %N using suite %N",
+                                        tls_version_names, version, tls_cipher_suite_names, suite);
+                               this->resume = TRUE;
+                       }
                }
+               DESTROY_IF(this->dh);
+               this->dh = NULL;
        }
        if (!suite)
        {
@@ -198,11 +259,102 @@ static status_t process_server_hello(private_tls_peer_t *this,
                free(this->session.ptr);
                this->session = chunk_clone(session);
        }
+
+       if (this->tls->get_version_max(this->tls) == TLS_1_3)
+       {
+               chunk_t shared_secret;
+
+               if (key_type != CURVE_25519 &&
+                       !this->dh->set_other_public_value(this->dh, ext_key_share))
+               {
+                       DBG2(DBG_TLS, "server key share unable to save");
+               }
+               if (!this->dh->get_shared_secret(this->dh, &shared_secret))
+               {
+                       DBG2(DBG_TLS, "No shared secret key found");
+               }
+
+               if (!this->crypto->derive_handshake_secret(this->crypto, shared_secret))
+               {
+                       DBG2(DBG_TLS, "derive handshake traffic secret failed");
+               }
+       }
+
        this->state = STATE_HELLO_RECEIVED;
        return NEED_MORE;
 }
 
 /**
+* Process a server encrypted extensions message
+*/
+static status_t process_encrypted_extensions(private_tls_peer_t *this,
+                                                                                        bio_reader_t *reader)
+{
+       uint16_t length;
+       chunk_t ext = chunk_empty;
+       int offset = 0;
+       uint16_t extension_type, extension_length;
+
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_ENCRYPTED_EXTENSIONS, reader->peek(reader));
+
+
+       if (!reader->read_uint16(reader, &length) ||
+               (reader->remaining(reader) && !reader->read_data16(reader, &ext)))
+       {
+               DBG1(DBG_TLS, "received invalid EncryptedExtensions");
+               this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
+               return NEED_MORE;
+       }
+       if (ext.len == 0)
+       {
+               this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED;
+               return NEED_MORE;
+       }
+       else
+       {
+               bio_reader_t *extension_reader = bio_reader_create(ext);
+
+               while (offset < ext.len)
+               {
+                       chunk_t extension_payload = chunk_empty;
+                       extension_reader->read_uint16(extension_reader, &extension_type);
+                       extension_reader->read_uint16(extension_reader, &extension_length);
+                       offset += extension_length + 4;
+
+                       if (!extension_reader->read_data(extension_reader,
+                                                                                        extension_length,
+                                                                                        &extension_payload))
+                       {
+                               DBG2(DBG_TLS, "unable to read extension payload data");
+                       }
+                       switch (extension_type)
+                       {
+                               /* fall through because not supported so far */
+                               case TLS_EXT_SERVER_NAME:
+                               case TLS_EXT_MAX_FRAGMENT_LENGTH:
+                               case TLS_EXT_SUPPORTED_GROUPS:
+                               case TLS_EXT_USE_SRTP:
+                               case TLS_EXT_HEARTBEAT:
+                               case TLS_EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION:
+                               case TLS_SERVER_CERTIFICATE_TYPE:
+                                       this->state = STATE_ENCRYPTED_EXTENSIONS_RECEIVED;
+                                       extension_reader->destroy(extension_reader);
+                                       break;
+                               default:
+                                       DBG1(DBG_TLS, "received forbidden EncryptedExtensions");
+                                       this->alert->add(this->alert, TLS_FATAL,
+                                                                        TLS_ILLEGAL_PARAMETER);
+                                       extension_reader->destroy(extension_reader);
+                                       return NEED_MORE;
+                       }
+               }
+
+       }
+       return NEED_MORE;
+}
+
+/**
  * Check if a server certificate is acceptable for the given server identity
  */
 static bool check_certificate(private_tls_peer_t *this, certificate_t *cert)
@@ -252,6 +404,20 @@ static status_t process_certificate(private_tls_peer_t *this,
        this->crypto->append_handshake(this->crypto,
                                                                   TLS_CERTIFICATE, reader->peek(reader));
 
+       if (this->tls->get_version_max(this->tls) > TLS_1_2)
+       {
+               if (!reader->read_data8(reader, &data))
+               {
+                       DBG1(DBG_TLS, "certificate request context invalid");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
+                       return NEED_MORE;
+               }
+               if (data.len > 0)
+               {
+                       DBG1(DBG_TLS, "certificate request context available,"
+                                "but CertificateRequest not received");
+               }
+       }
        if (!reader->read_data24(reader, &data))
        {
                DBG1(DBG_TLS, "certificate message header invalid");
@@ -300,6 +466,17 @@ static status_t process_certificate(private_tls_peer_t *this,
                        DBG1(DBG_TLS, "parsing TLS certificate failed, skipped");
                        this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE);
                }
+               if (this->tls->get_version_max(this->tls) > TLS_1_2)
+               {
+                       if (!certs->read_data16(certs, &data))
+                       {
+                               DBG1(DBG_TLS, "reading extension field of certificate failed",
+                                        &data);
+                               this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
+                               return NEED_MORE;
+                       }
+                       break;
+               }
        }
        certs->destroy(certs);
        this->state = STATE_CERT_RECEIVED;
@@ -307,6 +484,52 @@ static status_t process_certificate(private_tls_peer_t *this,
 }
 
 /**
+ *  Process Certificate verify
+ */
+static status_t process_cert_verify(private_tls_peer_t *this,
+                                                                       bio_reader_t *reader)
+{
+       enumerator_t *enumerator;
+       public_key_t *public;
+       auth_cfg_t *auth;
+       bio_reader_t *sig;
+       bool verified = FALSE;
+
+       enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
+                                                                                                               KEY_ANY, this->server,
+                                                                                                               this->server_auth, TRUE);
+       while (enumerator->enumerate(enumerator, &public, &auth))
+       {
+               sig = bio_reader_create(reader->peek(reader));
+               verified = this->crypto->verify_handshake(this->crypto, public, sig);
+               sig->destroy(sig);
+               if (verified)
+               {
+                       this->server_auth->merge(this->server_auth, auth, FALSE);
+                       break;
+               }
+               DBG1(DBG_TLS, "signature verification failed, trying another key");
+       }
+       enumerator->destroy(enumerator);
+
+       if (!verified)
+       {
+               DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer",
+                        this->server);
+               this->server->destroy(this->server);
+               this->peer = NULL;
+               this->state = STATE_KEY_EXCHANGE_RECEIVED;
+       }
+       else
+       {
+               this->state = STATE_CERT_VERIFY_RECEIVED;
+       }
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_CERTIFICATE_VERIFY, reader->peek(reader));
+       return NEED_MORE;
+}
+
+/**
  * Find a trusted public key to encrypt/verify key exchange data
  */
 static public_key_t *find_public_key(private_tls_peer_t *this)
@@ -407,10 +630,10 @@ static status_t process_modp_key_exchange(private_tls_peer_t *this,
  * Get the EC group for a TLS named curve
  */
 static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this,
-                                                                                               tls_named_curve_t curve)
+                                                                                               tls_named_group_t curve)
 {
        diffie_hellman_group_t group;
-       tls_named_curve_t current;
+       tls_named_group_t current;
        enumerator_t *enumerator;
 
        enumerator = this->crypto->create_ec_enumerator(this->crypto);
@@ -464,7 +687,7 @@ static status_t process_ec_key_exchange(private_tls_peer_t *this,
        if (!group)
        {
                DBG1(DBG_TLS, "ECDH curve %N not supported",
-                        tls_named_curve_names, curve);
+                        tls_named_group_names, curve);
                this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
                return NEED_MORE;
        }
@@ -569,7 +792,7 @@ static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader)
                return NEED_MORE;
        }
        this->cert_types = chunk_clone(types);
-       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
                if (!reader->read_data16(reader, &hashsig))
                {
@@ -634,26 +857,54 @@ static status_t process_hello_done(private_tls_peer_t *this,
  */
 static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader)
 {
-       chunk_t received;
+       chunk_t received, verify_data;
        char buf[12];
+       uint32_t hash_length;
 
-       if (!reader->read_data(reader, sizeof(buf), &received))
-       {
-               DBG1(DBG_TLS, "received server finished too short");
-               this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
-               return NEED_MORE;
-       }
-       if (!this->crypto->calculate_finished(this->crypto, "server finished", buf))
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               DBG1(DBG_TLS, "calculating server finished failed");
-               this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
-               return NEED_MORE;
+               if (!reader->read_data(reader, sizeof(buf), &received))
+               {
+                       DBG1(DBG_TLS, "received server finished too short");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
+                       return NEED_MORE;
+               }
+               if (!this->crypto->calculate_finished(this->crypto, "server finished",
+                                                                                         buf))
+               {
+                       DBG1(DBG_TLS, "calculating server finished failed");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
+                       return NEED_MORE;
+               }
+               if (!chunk_equals_const(received, chunk_from_thing(buf)))
+               {
+                       DBG1(DBG_TLS, "received server finished invalid");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
+                       return NEED_MORE;
+               }
        }
-       if (!chunk_equals_const(received, chunk_from_thing(buf)))
+       else
        {
-               DBG1(DBG_TLS, "received server finished invalid");
-               this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
-               return NEED_MORE;
+               hash_length = reader->remaining(reader);
+               if (!reader->read_data(reader, hash_length, &received))
+               {
+                       DBG1(DBG_TLS, "received server finished too short");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
+                       return NEED_MORE;
+               }
+               if (!this->crypto->calculate_finished_tls13(this->crypto, true,
+                       &verify_data))
+               {
+                       DBG1(DBG_TLS, "calculating server finished failed");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
+                       return NEED_MORE;
+               }
+               if (!chunk_equals(received, verify_data))
+               {
+                       DBG1(DBG_TLS, "received server finished invalid");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR);
+                       return NEED_MORE;
+               }
        }
        this->state = STATE_FINISHED_RECEIVED;
        this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
@@ -661,61 +912,142 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader)
        return NEED_MORE;
 }
 
+/**
+ * Process New Session Ticket message
+ */
+static status_t process_new_session_ticket(private_tls_peer_t *this,
+                                                                                  bio_reader_t *reader)
+{
+       uint32_t ticket_lifetime, ticket_age_add;
+       chunk_t ticket_nonce, ticket, extensions;
+
+       if (!reader->read_uint32(reader, &ticket_lifetime) ||
+               !reader->read_uint32(reader, &ticket_age_add) ||
+               !reader->read_data8(reader, &ticket_nonce) ||
+               !reader->read_data16(reader, &ticket) ||
+               !reader->read_data16(reader, &extensions))
+       {
+               DBG1(DBG_TLS, "received invalid NewSessionTicket");
+               this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
+               return NEED_MORE;
+       }
+       return NEED_MORE;
+}
+
 METHOD(tls_handshake_t, process, status_t,
        private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader)
 {
        tls_handshake_type_t expected;
 
-       switch (this->state)
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               case STATE_HELLO_SENT:
-                       if (type == TLS_SERVER_HELLO)
-                       {
-                               return process_server_hello(this, reader);
-                       }
-                       expected = TLS_SERVER_HELLO;
-                       break;
-               case STATE_HELLO_RECEIVED:
-                       if (type == TLS_CERTIFICATE)
-                       {
-                               return process_certificate(this, reader);
-                       }
-                       expected = TLS_CERTIFICATE;
-                       break;
-               case STATE_CERT_RECEIVED:
-                       if (type == TLS_SERVER_KEY_EXCHANGE)
-                       {
-                               return process_key_exchange(this, reader);
-                       }
-                       /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */
-               case STATE_KEY_EXCHANGE_RECEIVED:
-                       if (type == TLS_CERTIFICATE_REQUEST)
-                       {
-                               return process_certreq(this, reader);
-                       }
-                       /* no cert request, server does not want to authenticate us */
-                       DESTROY_IF(this->peer);
-                       this->peer = NULL;
-                       /* fall through since TLS_CERTIFICATE_REQUEST is optional */
-               case STATE_CERTREQ_RECEIVED:
-                       if (type == TLS_SERVER_HELLO_DONE)
-                       {
-                               return process_hello_done(this, reader);
-                       }
-                       expected = TLS_SERVER_HELLO_DONE;
-                       break;
-               case STATE_CIPHERSPEC_CHANGED_IN:
-                       if (type == TLS_FINISHED)
-                       {
-                               return process_finished(this, reader);
-                       }
-                       expected = TLS_FINISHED;
-                       break;
-               default:
-                       DBG1(DBG_TLS, "TLS %N not expected in current state",
-                                tls_handshake_type_names, type);
-                       this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
-                       return NEED_MORE;
+               switch (this->state)
+               {
+                       case STATE_HELLO_SENT:
+                               if (type == TLS_SERVER_HELLO)
+                               {
+                                       return process_server_hello(this, reader);
+                               }
+                               expected = TLS_SERVER_HELLO;
+                               break;
+                       case STATE_HELLO_RECEIVED:
+                               if (type == TLS_CERTIFICATE)
+                               {
+                                       return process_certificate(this, reader);
+                               }
+                               expected = TLS_CERTIFICATE;
+                               break;
+                       case STATE_CERT_RECEIVED:
+                               if (type == TLS_SERVER_KEY_EXCHANGE)
+                               {
+                                       return process_key_exchange(this, reader);
+                               }
+                               /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */
+                       case STATE_KEY_EXCHANGE_RECEIVED:
+                               if (type == TLS_CERTIFICATE_REQUEST)
+                               {
+                                       return process_certreq(this, reader);
+                               }
+                               /* no cert request, server does not want to authenticate us */
+                               DESTROY_IF(this->peer);
+                               this->peer = NULL;
+                               /* fall through since TLS_CERTIFICATE_REQUEST is optional */
+                       case STATE_CERTREQ_RECEIVED:
+                               if (type == TLS_SERVER_HELLO_DONE)
+                               {
+                                       return process_hello_done(this, reader);
+                               }
+                               expected = TLS_SERVER_HELLO_DONE;
+                               break;
+                       case STATE_CIPHERSPEC_CHANGED_IN:
+                               if (type == TLS_FINISHED)
+                               {
+                                       return process_finished(this, reader);
+                               }
+                               expected = TLS_FINISHED;
+                               break;
+                       default:
+                               DBG1(DBG_TLS, "TLS %N not expected in current state",
+                                        tls_handshake_type_names, type);
+                               this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
+                               return NEED_MORE;
+               }
+       }
+       else
+       {
+               switch (this->state)
+               {
+                       case STATE_HELLO_SENT:
+                               if (type == TLS_SERVER_HELLO)
+                               {
+                                       return process_server_hello(this, reader);
+                               }
+                               expected = TLS_SERVER_HELLO;
+                               break;
+                       case STATE_CIPHERSPEC_CHANGED_IN:
+                       case STATE_HELLO_RECEIVED:
+                               if (type == TLS_ENCRYPTED_EXTENSIONS)
+                               {
+                                       return process_encrypted_extensions(this, reader);
+                               }
+                               expected = TLS_ENCRYPTED_EXTENSIONS;
+                               break;
+                       case STATE_ENCRYPTED_EXTENSIONS_RECEIVED:
+                               if (type == TLS_CERTIFICATE)
+                               {
+                                       return process_certificate(this, reader);
+                               }
+                               expected = TLS_CERTIFICATE;
+                               break;
+                       case STATE_CERT_RECEIVED:
+                               if (type == TLS_CERTIFICATE_VERIFY)
+                               {
+                                       return process_cert_verify(this, reader);
+                               }
+                               expected = TLS_CERTIFICATE_VERIFY;
+                               break;
+                       case STATE_CERT_VERIFY_RECEIVED:
+                               if (type == TLS_FINISHED)
+                               {
+                                       return process_finished(this, reader);
+                               }
+                               expected = TLS_FINISHED;
+                               break;
+                       case STATE_FINISHED_RECEIVED:
+                               return NEED_MORE;
+                       case STATE_FINISHED_SENT_KEY_SWITCHED:
+                               if (type == TLS_NEW_SESSION_TICKET)
+                               {
+                                       return process_new_session_ticket(this, reader);
+                               }
+                               expected = TLS_NEW_SESSION_TICKET;
+                               break;
+                       default:
+                               DBG1(DBG_TLS, "TLS %N not expected in current state",
+                                        tls_handshake_type_names, type);
+                               this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE);
+                               return NEED_MORE;
+               }
        }
        DBG1(DBG_TLS, "TLS %N expected, but received %N",
                 tls_handshake_type_names, expected, tls_handshake_type_names, type);
@@ -731,11 +1063,13 @@ static status_t send_client_hello(private_tls_peer_t *this,
 {
        tls_cipher_suite_t *suites;
        bio_writer_t *extensions, *curves = NULL;
-       tls_version_t version;
-       tls_named_curve_t curve;
+       tls_version_t version_max, version_min;
+       tls_named_group_t curve;
        enumerator_t *enumerator;
-       int count, i;
+       int count, i, v;
        rng_t *rng;
+       chunk_t pub;
+       uint8_t nof_tls_versions;
 
        htoun32(&this->client_random, time(NULL));
        rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
@@ -750,10 +1084,21 @@ static status_t send_client_hello(private_tls_peer_t *this,
        }
        rng->destroy(rng);
 
-       /* TLS version */
-       version = this->tls->get_version(this->tls);
-       this->hello_version = version;
-       writer->write_uint16(writer, version);
+       /* client key generation */
+       this->dh = lib->crypto->create_dh(lib->crypto, CURVE_25519);
+
+       /* TLS version_max in handshake protocol */
+       version_max = this->tls->get_version_max(this->tls);
+       version_min = this->tls->get_version_min(this->tls);
+       if (version_max < TLS_1_3)
+       {
+               this->hello_version = version_max;
+       }
+       else
+       {
+               this->hello_version = TLS_1_2;
+       }
+       writer->write_uint16(writer, this->hello_version);
        writer->write_data(writer, chunk_from_thing(this->client_random));
 
        /* session identifier */
@@ -774,16 +1119,29 @@ static status_t send_client_hello(private_tls_peer_t *this,
 
        extensions = bio_writer_create(32);
 
-       extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS);
-       this->crypto->get_signature_algorithms(this->crypto, extensions);
+       if (this->server->get_type(this->server) == ID_FQDN)
+       {
+               bio_writer_t *names;
 
-       /* add supported Elliptic Curves, if any */
+               DBG2(DBG_TLS, "sending extension: Server Name Indication for '%Y'",
+                        this->server);
+               names = bio_writer_create(8);
+               names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME);
+               names->write_data16(names, this->server->get_encoding(this->server));
+               names->wrap16(names);
+               extensions->write_uint16(extensions, TLS_EXT_SERVER_NAME);
+               extensions->write_data16(extensions, names->get_buf(names));
+               names->destroy(names);
+       }
+
+       DBG2(DBG_TLS, "sending extension: %N",
+                tls_extension_names, TLS_EXT_SUPPORTED_GROUPS);
        enumerator = this->crypto->create_ec_enumerator(this->crypto);
        while (enumerator->enumerate(enumerator, NULL, &curve))
        {
                if (!curves)
                {
-                       extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES);
+                       extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_GROUPS);
                        curves = bio_writer_create(16);
                }
                curves->write_uint16(curves, curve);
@@ -791,6 +1149,10 @@ static status_t send_client_hello(private_tls_peer_t *this,
        enumerator->destroy(enumerator);
        if (curves)
        {
+               if (version_max == TLS_1_3)
+               {
+                       curves->write_uint16(curves, TLS_CURVE25519);
+               }
                curves->wrap16(curves);
                extensions->write_data16(extensions, curves->get_buf(curves));
                curves->destroy(curves);
@@ -801,20 +1163,38 @@ static status_t send_client_hello(private_tls_peer_t *this,
                extensions->write_uint8(extensions, 1);
                extensions->write_uint8(extensions, TLS_EC_POINT_UNCOMPRESSED);
        }
-       if (this->server->get_type(this->server) == ID_FQDN)
+
+       DBG2(DBG_TLS, "sending extension: %N",
+                tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS);
+       nof_tls_versions = (version_max - version_min + 1)*2;
+       extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS);
+       extensions->write_uint16(extensions, nof_tls_versions+1);
+       extensions->write_uint8(extensions, nof_tls_versions);
+       for (v = version_max; v >= version_min; v--)
        {
-               bio_writer_t *names;
+               extensions->write_uint16(extensions, v);
+               nof_tls_versions += 2;
+       }
 
-               DBG2(DBG_TLS, "sending Server Name Indication for '%Y'", this->server);
+       DBG2(DBG_TLS, "sending extension: %N",
+                tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS);
+       extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS);
+       this->crypto->get_signature_algorithms(this->crypto, extensions);
 
-               names = bio_writer_create(8);
-               names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME);
-               names->write_data16(names, this->server->get_encoding(this->server));
-               names->wrap16(names);
-               extensions->write_uint16(extensions, TLS_EXT_SERVER_NAME);
-               extensions->write_data16(extensions, names->get_buf(names));
-               names->destroy(names);
+       DBG2(DBG_TLS, "sending extension: %N",
+                tls_extension_names, TLS_EXT_KEY_SHARE);
+       if (!this->dh->get_my_public_value(this->dh, &pub))
+       {
+               this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
+               return NEED_MORE;
        }
+       extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE);
+       extensions->write_uint16(extensions, pub.len+6);
+       extensions->write_uint16(extensions, pub.len+4);
+       extensions->write_uint16(extensions, TLS_CURVE25519);
+       extensions->write_uint16(extensions, pub.len);
+       extensions->write_data(extensions, pub);
+       free(pub.ptr);
 
        writer->write_data16(writer, extensions->get_buf(extensions));
        extensions->destroy(extensions);
@@ -1071,16 +1451,33 @@ static status_t send_certificate_verify(private_tls_peer_t *this,
 static status_t send_finished(private_tls_peer_t *this,
                                                          tls_handshake_type_t *type, bio_writer_t *writer)
 {
-       char buf[12];
+       chunk_t verify_data;
 
-       if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               DBG1(DBG_TLS, "calculating client finished data failed");
-               this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
-               return NEED_MORE;
+               char buf[12];
+
+               if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
+               {
+                       DBG1(DBG_TLS, "calculating client finished data failed");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
+                       return NEED_MORE;
+               }
+
+               writer->write_data(writer, chunk_from_thing(buf));
        }
+       else
+       {
+               if (!this->crypto->calculate_finished_tls13(this->crypto, false,
+                  &verify_data))
+               {
+                       DBG1(DBG_TLS, "calculating client finished data failed");
+                       this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR);
+                       return NEED_MORE;
+               }
 
-       writer->write_data(writer, chunk_from_thing(buf));
+               writer->write_data(writer, verify_data);
+       }
 
        *type = TLS_FINISHED;
        this->state = STATE_FINISHED_SENT;
@@ -1091,63 +1488,112 @@ static status_t send_finished(private_tls_peer_t *this,
 METHOD(tls_handshake_t, build, status_t,
        private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer)
 {
-       switch (this->state)
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               case STATE_INIT:
-                       return send_client_hello(this, type, writer);
-               case STATE_HELLO_DONE:
-                       if (this->peer)
-                       {
-                               return send_certificate(this, type, writer);
-                       }
-                       /* otherwise fall through to next state */
-               case STATE_CERT_SENT:
-                       return send_key_exchange(this, type, writer);
-               case STATE_KEY_EXCHANGE_SENT:
-                       if (this->peer)
-                       {
-                               return send_certificate_verify(this, type, writer);
-                       }
-                       else
-                       {
+               switch (this->state)
+               {
+                       case STATE_INIT:
+                               return send_client_hello(this, type, writer);
+                       case STATE_HELLO_DONE:
+                               if (this->peer)
+                               {
+                                       return send_certificate(this, type, writer);
+                               }
+                               /* otherwise fall through to next state */
+                       case STATE_CERT_SENT:
+                               return send_key_exchange(this, type, writer);
+                       case STATE_KEY_EXCHANGE_SENT:
+                               if (this->peer)
+                               {
+                                       return send_certificate_verify(this, type, writer);
+                               }
+                               else
+                               {
+                                       return INVALID_STATE;
+                               }
+                       case STATE_CIPHERSPEC_CHANGED_OUT:
+                               return send_finished(this, type, writer);
+                       default:
                                return INVALID_STATE;
-                       }
-               case STATE_CIPHERSPEC_CHANGED_OUT:
-                       return send_finished(this, type, writer);
-               default:
-                       return INVALID_STATE;
+               }
        }
+       else
+       {
+               switch (this->state)
+               {
+                       case STATE_INIT:
+                               return send_client_hello(this, type, writer);
+                       case STATE_HELLO_DONE:
+                               /* otherwise fall through to next state */
+                       case STATE_CIPHERSPEC_CHANGED_OUT:
+                       case STATE_FINISHED_RECEIVED:
+                               /* fall through since legacy TLS and TLS 1.3
+                               * expect the same message */
+                               return send_finished(this, type, writer);
+                       case STATE_FINISHED_SENT:
+                               this->crypto->derive_app_secret(this->crypto);
+                               this->crypto->change_cipher(this->crypto, TRUE);
+                               this->crypto->change_cipher(this->crypto, FALSE);
+                               this->state = STATE_FINISHED_SENT_KEY_SWITCHED;
+                               return SUCCESS;
+                       case STATE_FINISHED_SENT_KEY_SWITCHED:
+                               return SUCCESS;
+                       default:
+                               return INVALID_STATE;
+               }
+       }
+
 }
 
 METHOD(tls_handshake_t, cipherspec_changed, bool,
        private_tls_peer_t *this, bool inbound)
 {
-       if (inbound)
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
-               if (this->resume)
+               if (inbound)
                {
-                       return this->state == STATE_HELLO_RECEIVED;
+                       if (this->resume)
+                       {
+                               return this->state == STATE_HELLO_RECEIVED;
+                       }
+                       return this->state == STATE_FINISHED_SENT;
+               }
+               else
+               {
+                       if (this->resume)
+                       {
+                               return this->state == STATE_FINISHED_RECEIVED;
+                       }
+                       if (this->peer)
+                       {
+                               return this->state == STATE_VERIFY_SENT;
+                       }
+                       return this->state == STATE_KEY_EXCHANGE_SENT;
+
                }
-               return this->state == STATE_FINISHED_SENT;
        }
        else
        {
-               if (this->resume)
+               if (inbound)
                {
-                       return this->state == STATE_FINISHED_RECEIVED;
+                       return this->state == STATE_HELLO_RECEIVED;
                }
-               if (this->peer)
+               else
                {
-                       return this->state == STATE_VERIFY_SENT;
+                       return FALSE;
                }
-               return this->state == STATE_KEY_EXCHANGE_SENT;
        }
+
 }
 
 METHOD(tls_handshake_t, change_cipherspec, void,
        private_tls_peer_t *this, bool inbound)
 {
-       this->crypto->change_cipher(this->crypto, inbound);
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
+       {
+               this->crypto->change_cipher(this->crypto, inbound);
+       }
+
        if (inbound)
        {
                this->state = STATE_CIPHERSPEC_CHANGED_IN;
@@ -1161,11 +1607,19 @@ METHOD(tls_handshake_t, change_cipherspec, void,
 METHOD(tls_handshake_t, finished, bool,
        private_tls_peer_t *this)
 {
-       if (this->resume)
+       if (this->tls->get_version_max(this->tls) < TLS_1_3)
        {
+               if (this->resume)
+               {
                return this->state == STATE_FINISHED_SENT;
+               }
+
+               return this->state == STATE_FINISHED_RECEIVED;
+       }
+       else
+       {
+               return this->state == STATE_FINISHED_SENT_KEY_SWITCHED;
        }
-       return this->state == STATE_FINISHED_RECEIVED;
 }
 
 METHOD(tls_handshake_t, get_peer_id, identification_t*,
index 1666d66..d286e17 100644 (file)
@@ -73,23 +73,19 @@ METHOD(tls_protection_t, process, status_t,
                return NEED_MORE;
        }
 
-       if (this->aead_in)
+       if (this-> version < TLS_1_3 ||
+               type == TLS_APPLICATION_DATA)
        {
-               if (!this->aead_in->decrypt(this->aead_in, this->version,
-                                                                       &type, this->seq_in, &data))
+               if (this->aead_in)
                {
-                       DBG1(DBG_TLS, "TLS record decryption failed");
-                       this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
-                       return NEED_MORE;
+                       if (!this->aead_in->decrypt(this->aead_in, this->version,
+                                                                               &type, this->seq_in, &data))
+                       {
+                               DBG1(DBG_TLS, "TLS record decryption failed");
+                               this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
+                               return NEED_MORE;
+                       }
                }
-       }
-
-       if (type == TLS_CHANGE_CIPHER_SPEC)
-       {
-               this->seq_in = 0;
-       }
-       else
-       {
                this->seq_in++;
        }
        return this->compression->process(this->compression, type, data);
@@ -103,9 +99,8 @@ METHOD(tls_protection_t, build, status_t,
        status = this->compression->build(this->compression, type, data);
        if (status == NEED_MORE)
        {
-               if (*type == TLS_CHANGE_CIPHER_SPEC)
+               if (this-> version < TLS_1_3 && *type == TLS_CHANGE_CIPHER_SPEC)
                {
-                       this->seq_out = 0;
                        return status;
                }
                if (this->aead_out)
@@ -129,10 +124,12 @@ METHOD(tls_protection_t, set_cipher, void,
        if (inbound)
        {
                this->aead_in = aead;
+               this->seq_in = 0;
        }
        else
        {
                this->aead_out = aead;
+               this->seq_out = 0;
        }
 }
 
index 70d17f2..9714df0 100644 (file)
@@ -256,7 +256,7 @@ static status_t process_client_hello(private_tls_server_t *this,
                                case TLS_EXT_SIGNATURE_ALGORITHMS:
                                        this->hashsig = chunk_clone(ext);
                                        break;
-                               case TLS_EXT_ELLIPTIC_CURVES:
+                               case TLS_EXT_SUPPORTED_GROUPS:
                                        this->curves_received = TRUE;
                                        this->curves = chunk_clone(ext);
                                        break;
@@ -299,7 +299,7 @@ static status_t process_client_hello(private_tls_server_t *this,
                this->session = chunk_clone(session);
                this->resume = TRUE;
                DBG1(DBG_TLS, "resumed %N using suite %N",
-                        tls_version_names, this->tls->get_version(this->tls),
+                        tls_version_names, this->tls->get_version_max(this->tls),
                         tls_cipher_suite_names, this->suite);
        }
        else
@@ -324,7 +324,7 @@ static status_t process_client_hello(private_tls_server_t *this,
                }
                DESTROY_IF(rng);
                DBG1(DBG_TLS, "negotiated %N using suite %N",
-                        tls_version_names, this->tls->get_version(this->tls),
+                        tls_version_names, this->tls->get_version_max(this->tls),
                         tls_cipher_suite_names, this->suite);
        }
        this->state = STATE_HELLO_RECEIVED;
@@ -688,7 +688,7 @@ static status_t send_server_hello(private_tls_server_t *this,
                                                        tls_handshake_type_t *type, bio_writer_t *writer)
 {
        /* TLS version */
-       writer->write_uint16(writer, this->tls->get_version(this->tls));
+       writer->write_uint16(writer, this->tls->get_version_max(this->tls));
        writer->write_data(writer, chunk_from_thing(this->server_random));
 
        /* session identifier if we have one */
@@ -774,7 +774,7 @@ static status_t send_certificate_request(private_tls_server_t *this,
        supported->write_uint8(supported, TLS_ECDSA_SIGN);
        writer->write_data8(writer, supported->get_buf(supported));
        supported->destroy(supported);
-       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
                this->crypto->get_signature_algorithms(this->crypto, writer);
        }
@@ -805,11 +805,11 @@ static status_t send_certificate_request(private_tls_server_t *this,
 /**
  * Get the TLS curve of a given EC DH group
  */
-static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this,
-                                                                                  diffie_hellman_group_t group)
+static tls_named_group_t ec_group_to_curve(private_tls_server_t *this,
+                                           diffie_hellman_group_t group)
 {
        diffie_hellman_group_t current;
-       tls_named_curve_t curve;
+       tls_named_group_t curve;
        enumerator_t *enumerator;
 
        enumerator = this->crypto->create_ec_enumerator(this->crypto);
@@ -828,7 +828,7 @@ static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this,
 /**
  * Check if the peer supports a given TLS curve
  */
-bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve)
+bool peer_supports_curve(private_tls_server_t *this, tls_named_group_t curve)
 {
        bio_reader_t *reader;
        uint16_t current;
@@ -854,9 +854,9 @@ bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve)
  * Try to find a curve supported by both, client and server
  */
 static bool find_supported_curve(private_tls_server_t *this,
-                                                                tls_named_curve_t *curve)
+                                 tls_named_group_t *curve)
 {
-       tls_named_curve_t current;
+       tls_named_group_t current;
        enumerator_t *enumerator;
 
        enumerator = this->crypto->create_ec_enumerator(this->crypto);
@@ -881,7 +881,7 @@ static status_t send_server_key_exchange(private_tls_server_t *this,
                                                        diffie_hellman_group_t group)
 {
        diffie_hellman_params_t *params = NULL;
-       tls_named_curve_t curve;
+       tls_named_group_t curve;
        chunk_t chunk;
 
        if (diffie_hellman_group_is_ec(group))
@@ -894,7 +894,7 @@ static status_t send_server_key_exchange(private_tls_server_t *this,
                        this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE);
                        return NEED_MORE;
                }
-               DBG2(DBG_TLS, "selected ECDH group %N", tls_named_curve_names, curve);
+               DBG2(DBG_TLS, "selected ECDH group %N", tls_named_group_names, curve);
                writer->write_uint8(writer, TLS_ECC_NAMED_CURVE);
                writer->write_uint16(writer, curve);
        }