while (times == -1 || times-- > 0)
{
+ /* Open IPv4 socket */
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
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);
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);
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",
"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",
"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",
"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);
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
{
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);
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;
}
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:
.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,
.destroy = _destroy,
},
.is_server = is_server,
- .version = TLS_1_2,
+ .version_max = TLS_1_3,
+ .version_min = TLS_1_0,
.application = application,
.purpose = purpose,
);
TLS_1_0 = 0x0301,
TLS_1_1 = 0x0302,
TLS_1_2 = 0x0303,
+ TLS_1_3 = 0x0304,
};
/**
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,
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,
};
/**
};
/**
- * TLS Hello extension types.
+ * TLS Handshake extension types.
*/
enum tls_extension_t {
/** Server name the client wants to talk to */
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,
};
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.
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;
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,
};
/**
*/
#include "tls_crypto.h"
+#include "tls_hkdf.h"
#include <utils/debug.h>
#include <plugins/plugin_feature.h>
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",
"NAMED_CURVE",
);
-ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1,
+ENUM_BEGIN(tls_named_group_names, TLS_SECT163K1, TLS_SECP521R1,
"SECT163K1",
"SECT163R1",
"SECT163R2",
"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",
*/
int suite_count;
+ /**
+ * HKDF for TLS 1.3
+ */
+ tls_hkdf_t *hkdf;
+
/**
* Selected cipher suite
*/
*/
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
*/
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,
},
};
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),
*/
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);
*/
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",
{
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)
{
{
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)))
{
*/
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},
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;
*/
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;
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);
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;
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
*/
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)
free(this->handshake.ptr);
free(this->msk.ptr);
DESTROY_IF(this->prf);
+ DESTROY_IF(this->hkdf);
free(this->suites);
free(this);
}
enumerator_t *enumerator;
credential_type_t type;
int subtype;
+ int hash_algorithm;
+ const char *plugin;
INIT(this,
.public = {
.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,
}
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:
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;
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,
/**
* TLS Named Curve identifiers
*/
-enum tls_named_curve_t {
+enum tls_named_group_t {
TLS_SECT163K1 = 1,
TLS_SECT163R1 = 2,
TLS_SECT163R2 = 3,
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.
/**
* 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
*/
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
*/
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.
*
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.
*
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;
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;
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;
/**
{
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));
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",
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)
{
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
*/
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");
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
*/
* 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);
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;
}
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))
{
*/
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);
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);
{
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);
}
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 */
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);
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);
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);
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;
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;
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*,
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);
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)
if (inbound)
{
this->aead_in = aead;
+ this->seq_in = 0;
}
else
{
this->aead_out = aead;
+ this->seq_out = 0;
}
}
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;
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
}
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;
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 */
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);
}
/**
* 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);
/**
* 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;
* 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);
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))
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);
}