From 7a2b02667c30cb3d3dbffc57ebca6466608492cd Mon Sep 17 00:00:00 2001 From: bytinbit Date: Thu, 21 May 2020 09:37:38 +0200 Subject: [PATCH] libtls: Implement TLS 1.3 handshake on client-side 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 --- scripts/tls_test.c | 31 +- src/libtls/tls.c | 112 +++-- src/libtls/tls.h | 60 ++- src/libtls/tls_alert.c | 21 +- src/libtls/tls_alert.h | 9 + src/libtls/tls_crypto.c | 593 +++++++++++++++++++++++--- src/libtls/tls_crypto.h | 51 ++- src/libtls/tls_fragmentation.c | 9 +- src/libtls/tls_peer.c | 732 ++++++++++++++++++++++++++------- src/libtls/tls_protection.c | 29 +- src/libtls/tls_server.c | 26 +- 11 files changed, 1394 insertions(+), 279 deletions(-) diff --git a/scripts/tls_test.c b/scripts/tls_test.c index 84a32f96f..316c98625 100644 --- a/scripts/tls_test.c +++ b/scripts/tls_test.c @@ -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); diff --git a/src/libtls/tls.c b/src/libtls/tls.c index ea39f7fd1..4a461c2cc 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -26,12 +26,13 @@ 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, ); diff --git a/src/libtls/tls.h b/src/libtls/tls.h index f832ef50e..4509e05a3 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -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. diff --git a/src/libtls/tls_alert.c b/src/libtls/tls_alert.c index 69570e9c9..a9cd67165 100644 --- a/src/libtls/tls_alert.c +++ b/src/libtls/tls_alert.c @@ -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; diff --git a/src/libtls/tls_alert.h b/src/libtls/tls_alert.h index 8ce50f83d..053501357 100644 --- a/src/libtls/tls_alert.h +++ b/src/libtls/tls_alert.h @@ -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, }; /** diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index ebadb91c8..6b4651393 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -14,6 +14,7 @@ */ #include "tls_crypto.h" +#include "tls_hkdf.h" #include #include @@ -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", @@ -350,6 +371,11 @@ struct private_tls_crypto_t { */ int suite_count; + /** + * HKDF for TLS 1.3 + */ + tls_hkdf_t *hkdf; + /** * Selected cipher suite */ @@ -365,6 +391,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 */ @@ -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: diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index a42e07bb3..a1390e349 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -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 @@ -507,6 +522,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. * @@ -521,6 +545,21 @@ struct tls_crypto_t { chunk_t session, identification_t *id, 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. * diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index 7a963029d..f74cf59bf 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -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; diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 1f2439ca1..471ba49f7 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -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,10 +259,101 @@ 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 */ @@ -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,12 +466,69 @@ 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; return NEED_MORE; } +/** + * 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 */ @@ -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*, diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index 1666d664a..d286e179a 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -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; } } diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 70d17f22c..9714df0f6 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -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); } -- 2.39.2