--- embedaddon/strongswan/src/libtls/tls_server.c 2020/06/03 09:46:45 1.1.1.1 +++ embedaddon/strongswan/src/libtls/tls_server.c 2021/03/17 00:20:09 1.1.1.2 @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020-2021 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -19,6 +22,7 @@ #include #include +#include typedef struct private_tls_server_t private_tls_server_t; @@ -42,6 +46,12 @@ typedef enum { STATE_FINISHED_RECEIVED, STATE_CIPHERSPEC_CHANGED_OUT, STATE_FINISHED_SENT, + /* new states in TLS 1.3 */ + STATE_ENCRYPTED_EXTENSIONS_SENT, + STATE_CERT_VERIFY_SENT, + STATE_KEY_UPDATE_REQUESTED, + STATE_KEY_UPDATE_SENT, + STATE_FINISHED_SENT_KEY_SWITCHED, } server_state_t; /** @@ -80,11 +90,6 @@ struct private_tls_server_t { identification_t *peer; /** - * Is it acceptable if we couldn't verify the peer certificate? - */ - bool peer_auth_optional; - - /** * State we are in */ server_state_t state; @@ -120,6 +125,11 @@ struct private_tls_server_t { diffie_hellman_t *dh; /** + * Requested DH group + */ + tls_named_group_t requested_curve; + + /** * Selected TLS cipher suite */ tls_cipher_suite_t suite; @@ -153,77 +163,191 @@ struct private_tls_server_t { * Did we receive the curves from the client? */ bool curves_received; + + /** + * Whether to include CAs in CertificateRequest messages + */ + bool send_certreq_authorities; }; /** + * Find a trusted public key to encrypt/verify key exchange data + */ +public_key_t *tls_find_public_key(auth_cfg_t *peer_auth) +{ + public_key_t *public = NULL, *current; + certificate_t *cert, *found; + enumerator_t *enumerator; + auth_cfg_t *auth; + + cert = peer_auth->get(peer_auth, AUTH_HELPER_SUBJECT_CERT); + if (cert) + { + enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, + KEY_ANY, cert->get_subject(cert), + peer_auth, TRUE); + while (enumerator->enumerate(enumerator, ¤t, &auth)) + { + found = auth->get(auth, AUTH_RULE_SUBJECT_CERT); + if (found && cert->equals(cert, found)) + { + public = current->get_ref(current); + peer_auth->merge(peer_auth, auth, FALSE); + break; + } + } + enumerator->destroy(enumerator); + } + return public; +} + +/** * Find a cipher suite and a server key */ static bool select_suite_and_key(private_tls_server_t *this, tls_cipher_suite_t *suites, int count) { + tls_version_t version_min, version_max; private_key_t *key; - key_type_t type; + auth_cfg_t *auth; + enumerator_t *enumerator; - key = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->server, - this->server_auth); - if (!key) + version_min = this->tls->get_version_min(this->tls); + version_max = this->tls->get_version_max(this->tls); + enumerator = tls_create_private_key_enumerator(version_min, version_max, + this->hashsig, this->server); + if (!enumerator) { + DBG1(DBG_TLS, "no common signature algorithms found"); + return FALSE; + } + if (!enumerator->enumerate(enumerator, &key, &auth)) + { DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", this->server); + enumerator->destroy(enumerator); return FALSE; } - this->suite = this->crypto->select_cipher_suite(this->crypto, - suites, count, key->get_type(key)); + + if (version_max >= TLS_1_3) + { + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, KEY_ANY); + } + else + { + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, key->get_type(key)); + while (!this->suite && + enumerator->enumerate(enumerator, &key, &auth)) + { /* find a key and cipher suite for one of the remaining key types */ + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, + key->get_type(key)); + } + } if (!this->suite) - { /* no match for this key, try to find another type */ - if (key->get_type(key) == KEY_ECDSA) + { + DBG1(DBG_TLS, "received cipher suites or signature schemes unacceptable"); + enumerator->destroy(enumerator); + return FALSE; + } + DBG1(DBG_TLS, "using key of type %N", key_type_names, key->get_type(key)); + DESTROY_IF(this->private); + this->private = key->get_ref(key); + this->server_auth->purge(this->server_auth, FALSE); + this->server_auth->merge(this->server_auth, auth, FALSE); + enumerator->destroy(enumerator); + return TRUE; +} + +/** + * Check if the peer supports a given TLS curve + */ +static bool peer_supports_curve(private_tls_server_t *this, + tls_named_group_t curve) +{ + bio_reader_t *reader; + uint16_t current; + + if (!this->curves_received) + { /* none received, assume yes */ + return TRUE; + } + reader = bio_reader_create(this->curves); + while (reader->remaining(reader) && reader->read_uint16(reader, ¤t)) + { + if (current == curve) { - type = KEY_RSA; + reader->destroy(reader); + return TRUE; } - else - { - type = KEY_ECDSA; - } - key->destroy(key); + } + reader->destroy(reader); + return FALSE; +} - this->suite = this->crypto->select_cipher_suite(this->crypto, - suites, count, type); - if (!this->suite) +/** + * TLS 1.3 key exchange key share + */ +typedef struct { + uint16_t curve; + chunk_t key_share; +} key_share_t; + +/** + * Check if peer sent a key share of a given TLS named DH group + */ +static bool peer_offered_curve(array_t *key_shares, tls_named_group_t curve, + key_share_t *out) +{ + key_share_t peer; + int i; + + for (i = 0; i < array_count(key_shares); i++) + { + array_get(key_shares, i, &peer); + if (curve == peer.curve) { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; + if (out) + { + *out = peer; + } + return TRUE; } - this->server_auth->destroy(this->server_auth); - this->server_auth = auth_cfg_create(); - key = lib->credmgr->get_private(lib->credmgr, type, this->server, - this->server_auth); - if (!key) - { - DBG1(DBG_TLS, "received cipher suites unacceptable"); - return FALSE; - } } - this->private = key; - return TRUE; + return FALSE; } /** + * Check if client is currently retrying to connect to the server. + */ +static bool retrying(private_tls_server_t *this) +{ + return this->state == STATE_INIT && this->requested_curve; +} + +/** * Process client hello message */ static status_t process_client_hello(private_tls_server_t *this, bio_reader_t *reader) { - uint16_t version, extension; - chunk_t random, session, ciphers, compression, ext = chunk_empty; - bio_reader_t *extensions; + uint16_t legacy_version = 0, version = 0, extension_type = 0; + chunk_t random, session, ciphers, versions = chunk_empty, compression; + chunk_t ext = chunk_empty, key_shares = chunk_empty; + key_share_t peer = {0}; + chunk_t extension_data = chunk_empty; + bio_reader_t *extensions, *extension; tls_cipher_suite_t *suites; + tls_version_t original_version_max; int count, i; rng_t *rng; this->crypto->append_handshake(this->crypto, TLS_CLIENT_HELLO, reader->peek(reader)); - if (!reader->read_uint16(reader, &version) || + if (!reader->read_uint16(reader, &legacy_version) || !reader->read_data(reader, sizeof(this->client_random), &random) || !reader->read_data8(reader, &session) || !reader->read_data16(reader, &ciphers) || @@ -235,38 +359,91 @@ static status_t process_client_hello(private_tls_serve return NEED_MORE; } - if (ext.len) + /* before we do anything version-related, determine our supported suites + * as that might change the min./max. versions */ + this->crypto->get_cipher_suites(this->crypto, NULL); + + extensions = bio_reader_create(ext); + while (extensions->remaining(extensions)) { - extensions = bio_reader_create(ext); - while (extensions->remaining(extensions)) + if (!extensions->read_uint16(extensions, &extension_type) || + !extensions->read_data16(extensions, &extension_data)) { - if (!extensions->read_uint16(extensions, &extension) || - !extensions->read_data16(extensions, &ext)) - { - DBG1(DBG_TLS, "received invalid ClientHello Extensions"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - extensions->destroy(extensions); - return NEED_MORE; - } - DBG2(DBG_TLS, "received TLS '%N' extension", - tls_extension_names, extension); - DBG3(DBG_TLS, "%B", &ext); - switch (extension) - { - case TLS_EXT_SIGNATURE_ALGORITHMS: - this->hashsig = chunk_clone(ext); - break; - case TLS_EXT_ELLIPTIC_CURVES: - this->curves_received = TRUE; - this->curves = chunk_clone(ext); - break; - default: - break; - } + DBG1(DBG_TLS, "received invalid ClientHello Extensions"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + return NEED_MORE; } - extensions->destroy(extensions); + extension = bio_reader_create(extension_data); + DBG2(DBG_TLS, "received TLS '%N' extension", + tls_extension_names, extension_type); + DBG3(DBG_TLS, "%B", &extension_data); + switch (extension_type) + { + case TLS_EXT_SIGNATURE_ALGORITHMS: + if (!extension->read_data16(extension, &extension_data)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + chunk_free(&this->hashsig); + this->hashsig = chunk_clone(extension_data); + break; + case TLS_EXT_SUPPORTED_GROUPS: + if (!extension->read_data16(extension, &extension_data)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + chunk_free(&this->curves); + this->curves_received = TRUE; + this->curves = chunk_clone(extension_data); + break; + case TLS_EXT_SUPPORTED_VERSIONS: + if (!extension->read_data8(extension, &versions)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + break; + case TLS_EXT_KEY_SHARE: + if (!extension->read_data16(extension, &key_shares)) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + extension->destroy(extension); + return NEED_MORE; + } + break; + default: + break; + } + extension->destroy(extension); } + extensions->destroy(extensions); + if (this->tls->get_version_max(this->tls) >= TLS_1_3 && !this->hashsig.len) + { + DBG1(DBG_TLS, "no %N extension received", tls_extension_names, + TLS_MISSING_EXTENSION); + this->alert->add(this->alert, TLS_FATAL, TLS_MISSING_EXTENSION); + return NEED_MORE; + } + memcpy(this->client_random, random.ptr, sizeof(this->client_random)); htoun32(&this->server_random, time(NULL)); @@ -282,28 +459,77 @@ static status_t process_client_hello(private_tls_serve } rng->destroy(rng); - if (!this->tls->set_version(this->tls, version)) + original_version_max = this->tls->get_version_max(this->tls); + + if (versions.len) { - DBG1(DBG_TLS, "negotiated version %N not supported", - tls_version_names, version); + bio_reader_t *client_versions; + + client_versions = bio_reader_create(versions); + while (client_versions->remaining(client_versions)) + { + if (client_versions->read_uint16(client_versions, &version)) + { + if (this->tls->set_version(this->tls, version, version)) + { + this->client_version = version; + break; + } + } + } + client_versions->destroy(client_versions); + } + else + { + version = legacy_version; + if (this->tls->set_version(this->tls, version, version)) + { + this->client_version = version; + } + } + + /* downgrade protection (see RFC 8446, section 4.1.3) */ + if ((original_version_max == TLS_1_3 && version < TLS_1_3) || + (original_version_max == TLS_1_2 && version < TLS_1_2)) + { + chunk_t downgrade_protection = tls_downgrade_protection_tls11; + + if (version == TLS_1_2) + { + downgrade_protection = tls_downgrade_protection_tls12; + } + memcpy(&this->server_random[24], downgrade_protection.ptr, + downgrade_protection.len); + } + + if (!this->client_version) + { + DBG1(DBG_TLS, "proposed version %N not supported", tls_version_names, + version); this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); return NEED_MORE; } - this->client_version = version; - this->suite = this->crypto->resume_session(this->crypto, session, this->peer, - chunk_from_thing(this->client_random), - chunk_from_thing(this->server_random)); - if (this->suite) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { + this->suite = this->crypto->resume_session(this->crypto, session, + this->peer, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + } + + if (this->suite && !retrying(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 { + tls_cipher_suite_t original_suite = this->suite; + count = ciphers.len / sizeof(uint16_t); suites = alloca(count * sizeof(tls_cipher_suite_t)); DBG2(DBG_TLS, "received %d TLS cipher suites:", count); @@ -317,16 +543,131 @@ static status_t process_client_hello(private_tls_serve this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; } - rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); - if (!rng || !rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session)) + if (retrying(this) && original_suite != this->suite) { - DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + DBG1(DBG_TLS, "selected %N instead of %N during retry", + tls_cipher_suite_names, this->suite, tls_cipher_suite_names, + original_suite); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; } - DESTROY_IF(rng); + if (this->tls->get_version_max(this->tls) < TLS_1_3) + { + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (!rng || + !rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session)) + { + DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + } + DESTROY_IF(rng); + } + else + { + chunk_free(&this->session); + this->session = chunk_clone(session); + } 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); } + + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + diffie_hellman_group_t group; + tls_named_group_t curve, requesting_curve = 0; + enumerator_t *enumerator; + array_t *peer_key_shares; + + peer_key_shares = array_create(sizeof(key_share_t), 1); + extension = bio_reader_create(key_shares); + while (extension->remaining(extension)) + { + if (!extension->read_uint16(extension, &peer.curve) || + !extension->read_data16(extension, &peer.key_share) || + !peer.key_share.len) + { + DBG1(DBG_TLS, "invalid %N extension", + tls_extension_names, extension_type); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extension->destroy(extension); + array_destroy(peer_key_shares); + return NEED_MORE; + } + array_insert(peer_key_shares, ARRAY_TAIL, &peer); + } + extension->destroy(extension); + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, &group, &curve)) + { + if (!requesting_curve && + peer_supports_curve(this, curve) && + !peer_offered_curve(peer_key_shares, curve, NULL)) + { + requesting_curve = curve; + } + if (peer_supports_curve(this, curve) && + peer_offered_curve(peer_key_shares, curve, &peer)) + { + DBG1(DBG_TLS, "using key exchange %N", + tls_named_group_names, curve); + this->dh = lib->crypto->create_dh(lib->crypto, group); + break; + } + } + enumerator->destroy(enumerator); + array_destroy(peer_key_shares); + + if (!this->dh) + { + if (retrying(this)) + { + DBG1(DBG_TLS, "already replied with a hello retry request"); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + + if (!requesting_curve) + { + DBG1(DBG_TLS, "no mutual supported group in client hello"); + this->alert->add(this->alert, TLS_FATAL, TLS_ILLEGAL_PARAMETER); + return NEED_MORE; + } + this->requested_curve = requesting_curve; + + if (!this->crypto->hash_handshake(this->crypto, NULL)) + { + DBG1(DBG_TLS, "failed to hash handshake messages"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + } + else + { + if (peer.key_share.len && + peer.curve != TLS_CURVE25519 && + peer.curve != TLS_CURVE448) + { /* classic format (see RFC 8446, section 4.2.8.2) */ + if (peer.key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, peer.key_share.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + peer.key_share = chunk_skip(peer.key_share, 1); + } + if (!peer.key_share.len || + !this->dh->set_other_public_value(this->dh, peer.key_share)) + { + DBG1(DBG_TLS, "DH key derivation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + this->requested_curve = 0; + } + } + this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -345,6 +686,16 @@ static status_t process_certificate(private_tls_server 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 (!reader->read_data24(reader, &data)) { DBG1(DBG_TLS, "certificate message header invalid"); @@ -352,6 +703,21 @@ static status_t process_certificate(private_tls_server return NEED_MORE; } certs = bio_reader_create(data); + if (!certs->remaining(certs)) + { + if (this->tls->get_flags(this->tls) & TLS_FLAG_CLIENT_AUTH_OPTIONAL) + { + /* client authentication is not required so we clear the identity */ + DESTROY_IF(this->peer); + this->peer = NULL; + } + else + { + DBG1(DBG_TLS, "no certificate sent by peer"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + } while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) @@ -372,11 +738,11 @@ static status_t process_certificate(private_tls_server DBG1(DBG_TLS, "received TLS peer certificate '%Y'", cert->get_subject(cert)); first = FALSE; - if (this->peer == NULL) - { /* apply identity to authenticate */ + if (this->peer && this->peer->get_type(this->peer) == ID_ANY) + { + this->peer->destroy(this->peer); this->peer = cert->get_subject(cert); this->peer = this->peer->clone(this->peer); - this->peer_auth_optional = TRUE; } } else @@ -391,6 +757,15 @@ static status_t process_certificate(private_tls_server 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, "failed to read extensions of CertificateEntry"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + } } certs->destroy(certs); this->state = STATE_CERT_RECEIVED; @@ -469,12 +844,14 @@ static status_t process_key_exchange_dhe(private_tls_s bio_reader_t *reader) { chunk_t premaster, pub; + diffie_hellman_group_t group; bool ec; this->crypto->append_handshake(this->crypto, TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader)); - ec = diffie_hellman_group_is_ec(this->dh->get_dh_group(this->dh)); + group = this->dh->get_dh_group(this->dh); + ec = diffie_hellman_group_is_ec(group); if ((ec && !reader->read_data8(reader, &pub)) || (!ec && (!reader->read_data16(reader, &pub) || pub.len == 0))) { @@ -483,7 +860,9 @@ static status_t process_key_exchange_dhe(private_tls_s return NEED_MORE; } - if (ec) + if (ec && + group != CURVE_25519 && + group != CURVE_448) { if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) { @@ -541,48 +920,29 @@ static status_t process_key_exchange(private_tls_serve static status_t process_cert_verify(private_tls_server_t *this, bio_reader_t *reader) { - bool verified = FALSE; - enumerator_t *enumerator; public_key_t *public; - auth_cfg_t *auth; - bio_reader_t *sig; + chunk_t msg; - enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - KEY_ANY, this->peer, this->peer_auth, TRUE); - while (enumerator->enumerate(enumerator, &public, &auth)) + public = tls_find_public_key(this->peer_auth); + if (!public) { - sig = bio_reader_create(reader->peek(reader)); - verified = this->crypto->verify_handshake(this->crypto, public, sig); - sig->destroy(sig); - if (verified) - { - this->peer_auth->merge(this->peer_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->peer); - if (!this->peer_auth_optional) - { /* client authentication is required */ - this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); - return NEED_MORE; - } - /* reset peer identity, we couldn't authenticate it */ - this->peer->destroy(this->peer); - this->peer = NULL; - this->state = STATE_KEY_EXCHANGE_RECEIVED; + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; } - else + + msg = reader->peek(reader); + if (!this->crypto->verify_handshake(this->crypto, public, reader)) { - this->state = STATE_CERT_VERIFY_RECEIVED; + public->destroy(public); + DBG1(DBG_TLS, "signature verification failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; } - this->crypto->append_handshake(this->crypto, - TLS_CERTIFICATE_VERIFY, reader->peek(reader)); + public->destroy(public); + this->state = STATE_CERT_VERIFY_RECEIVED; + this->crypto->append_handshake(this->crypto, TLS_CERTIFICATE_VERIFY, msg); return NEED_MORE; } @@ -592,89 +952,199 @@ static status_t process_cert_verify(private_tls_server static status_t process_finished(private_tls_server_t *this, bio_reader_t *reader) { - chunk_t received; - char buf[12]; + chunk_t received, verify_data; + u_char buf[12]; - if (!reader->read_data(reader, sizeof(buf), &received)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "received client finished too short"); - this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); - return NEED_MORE; + if (!reader->read_data(reader, sizeof(buf), &received)) + { + DBG1(DBG_TLS, "received client finished too short"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (!this->crypto->calculate_finished_legacy(this->crypto, + "client finished", buf)) + { + DBG1(DBG_TLS, "calculating client finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + verify_data = chunk_from_thing(buf); } - if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + else { - DBG1(DBG_TLS, "calculating client finished failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return NEED_MORE; + received = reader->peek(reader); + if (!this->crypto->calculate_finished(this->crypto, FALSE, &verify_data)) + { + DBG1(DBG_TLS, "calculating client finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, TRUE); } - if (!chunk_equals_const(received, chunk_from_thing(buf))) + + if (!chunk_equals_const(received, verify_data)) { DBG1(DBG_TLS, "received client finished invalid"); this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); return NEED_MORE; } + if (verify_data.ptr != buf) + { + chunk_free(&verify_data); + } + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); this->state = STATE_FINISHED_RECEIVED; return NEED_MORE; } +/** + * Process KeyUpdate message + */ +static status_t process_key_update(private_tls_server_t *this, + bio_reader_t *reader) +{ + uint8_t update_requested; + + if (!reader->read_uint8(reader, &update_requested) || + update_requested > 1) + { + DBG1(DBG_TLS, "received invalid KeyUpdate"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + if (!this->crypto->update_app_keys(this->crypto, TRUE)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, TRUE); + + if (update_requested) + { + DBG1(DBG_TLS, "client requested KeyUpdate"); + this->state = STATE_KEY_UPDATE_REQUESTED; + } + return NEED_MORE; +} + METHOD(tls_handshake_t, process, status_t, private_tls_server_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_INIT: - if (type == TLS_CLIENT_HELLO) - { - return process_client_hello(this, reader); - } - expected = TLS_CLIENT_HELLO; - break; - case STATE_HELLO_DONE: - if (type == TLS_CERTIFICATE) - { - return process_certificate(this, reader); - } - if (this->peer) - { - expected = TLS_CERTIFICATE; + switch (this->state) + { + case STATE_INIT: + if (type == TLS_CLIENT_HELLO) + { + return process_client_hello(this, reader); + } + expected = TLS_CLIENT_HELLO; break; - } - /* otherwise fall through to next state */ - case STATE_CERT_RECEIVED: - if (type == TLS_CLIENT_KEY_EXCHANGE) - { - return process_key_exchange(this, reader); - } - expected = TLS_CLIENT_KEY_EXCHANGE; - break; - case STATE_KEY_EXCHANGE_RECEIVED: - if (type == TLS_CERTIFICATE_VERIFY) - { - return process_cert_verify(this, reader); - } - if (this->peer) - { - expected = TLS_CERTIFICATE_VERIFY; + case STATE_HELLO_DONE: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_RECEIVED: + if (type == TLS_CLIENT_KEY_EXCHANGE) + { + return process_key_exchange(this, reader); + } + expected = TLS_CLIENT_KEY_EXCHANGE; break; - } - return INVALID_STATE; - 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; + case STATE_KEY_EXCHANGE_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE_VERIFY; + break; + } + return INVALID_STATE; + 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_INIT: + if (type == TLS_CLIENT_HELLO) + { + return process_client_hello(this, reader); + } + expected = TLS_CLIENT_HELLO; + break; + case STATE_CIPHERSPEC_CHANGED_IN: + case STATE_FINISHED_SENT: + case STATE_FINISHED_SENT_KEY_SWITCHED: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE_VERIFY; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_VERIFY_RECEIVED: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + return NEED_MORE; + case STATE_FINISHED_RECEIVED: + if (type == TLS_KEY_UPDATE) + { + return process_key_update(this, reader); + } + return INVALID_STATE; + 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); this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); @@ -682,15 +1152,63 @@ METHOD(tls_handshake_t, process, status_t, } /** + * Write public key into key share extension + */ +bool tls_write_key_share(bio_writer_t **key_share, diffie_hellman_t *dh) +{ + bio_writer_t *writer; + tls_named_group_t curve; + chunk_t pub; + + if (!dh) + { + return FALSE; + } + curve = tls_ec_group_to_curve(dh->get_dh_group(dh)); + if (!curve || !dh->get_my_public_value(dh, &pub)) + { + return FALSE; + } + *key_share = writer = bio_writer_create(pub.len + 7); + writer->write_uint16(writer, curve); + if (curve == TLS_CURVE25519 || + curve == TLS_CURVE448) + { + writer->write_data16(writer, pub); + } + else + { /* classic format (see RFC 8446, section 4.2.8.2) */ + writer->write_uint16(writer, pub.len + 1); + writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); + writer->write_data(writer, pub); + } + free(pub.ptr); + return TRUE; +} + +/** * Send ServerHello message */ 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_data(writer, chunk_from_thing(this->server_random)); + bio_writer_t *key_share, *extensions; + tls_version_t version; + version = this->tls->get_version_max(this->tls); + + /* cap legacy version at TLS 1.2 for middlebox compatibility */ + writer->write_uint16(writer, min(TLS_1_2, version)); + + if (this->requested_curve) + { + writer->write_data(writer, tls_hello_retry_request_magic); + } + else + { + writer->write_data(writer, chunk_from_thing(this->server_random)); + } + /* session identifier if we have one */ writer->write_data8(writer, this->session); @@ -700,13 +1218,80 @@ static status_t send_server_hello(private_tls_server_t /* NULL compression only */ writer->write_uint8(writer, 0); + if (version >= TLS_1_3) + { + extensions = bio_writer_create(32); + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SUPPORTED_VERSIONS); + extensions->write_uint16(extensions, TLS_EXT_SUPPORTED_VERSIONS); + extensions->write_uint16(extensions, 2); + extensions->write_uint16(extensions, version); + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_KEY_SHARE); + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + if (this->requested_curve) + { + DBG1(DBG_TLS, "requesting key exchange with %N", + tls_named_group_names, this->requested_curve); + extensions->write_uint16(extensions, 2); + extensions->write_uint16(extensions, this->requested_curve); + } + else + { + if (!tls_write_key_share(&key_share, this->dh)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + extensions->write_data16(extensions, key_share->get_buf(key_share)); + key_share->destroy(key_share); + } + + writer->write_data16(writer, extensions->get_buf(extensions)); + extensions->destroy(extensions); + } + *type = TLS_SERVER_HELLO; - this->state = STATE_HELLO_SENT; + this->state = this->requested_curve ? STATE_INIT : STATE_HELLO_SENT; this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); return NEED_MORE; } /** + * Send encrypted extensions message + */ +static status_t send_encrypted_extensions(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) +{ + chunk_t shared_secret = chunk_empty; + + if (!this->dh->get_shared_secret(this->dh, &shared_secret) || + !this->crypto->derive_handshake_keys(this->crypto, shared_secret)) + { + DBG1(DBG_TLS, "DH key derivation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + chunk_clear(&shared_secret); + return NEED_MORE; + } + chunk_clear(&shared_secret); + + this->crypto->change_cipher(this->crypto, TRUE); + this->crypto->change_cipher(this->crypto, FALSE); + + /* currently no extensions are supported */ + writer->write_uint16(writer, 0); + + *type = TLS_ENCRYPTED_EXTENSIONS; + this->state = STATE_ENCRYPTED_EXTENSIONS_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** * Send Certificate */ static status_t send_certificate(private_tls_server_t *this, @@ -718,6 +1303,12 @@ static status_t send_certificate(private_tls_server_t bio_writer_t *certs; chunk_t data; + /* certificate request context as described in RFC 8446, section 4.4.2 */ + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + writer->write_uint8(writer, 0); + } + /* generate certificate payload */ certs = bio_writer_create(256); cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT); @@ -730,6 +1321,11 @@ static status_t send_certificate(private_tls_server_t certs->write_data24(certs, data); free(data.ptr); } + /* extensions see RFC 8446, section 4.4.2 */ + if (this->tls->get_version_max(this->tls) > TLS_1_2) + { + certs->write_uint16(certs, 0); + } } enumerator = this->server_auth->create_enumerator(this->server_auth); while (enumerator->enumerate(enumerator, &rule, &cert)) @@ -757,31 +1353,40 @@ static status_t send_certificate(private_tls_server_t } /** - * Send Certificate Request + * Send Certificate Verify */ -static status_t send_certificate_request(private_tls_server_t *this, - tls_handshake_type_t *type, bio_writer_t *writer) +static status_t send_certificate_verify(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) { - bio_writer_t *authorities, *supported; + if (!this->crypto->sign_handshake(this->crypto, this->private, writer, + this->hashsig)) + { + DBG1(DBG_TLS, "signature generation failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + *type = TLS_CERTIFICATE_VERIFY; + this->state = STATE_CERT_VERIFY_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/* + * Write all available certificate authorities to output writer + */ +static void write_certificate_authorities(bio_writer_t *writer) +{ + bio_writer_t *authorities; enumerator_t *enumerator; certificate_t *cert; x509_t *x509; identification_t *id; - supported = bio_writer_create(4); - /* we propose both RSA and ECDSA */ - supported->write_uint8(supported, TLS_RSA_SIGN); - 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) - { - this->crypto->get_signature_algorithms(this->crypto, writer); - } - authorities = bio_writer_create(64); - enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, - CERT_X509, KEY_RSA, NULL, TRUE); + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509, + KEY_RSA, NULL, TRUE); while (enumerator->enumerate(enumerator, &cert)) { x509 = (x509_t*)cert; @@ -795,68 +1400,81 @@ static status_t send_certificate_request(private_tls_s enumerator->destroy(enumerator); writer->write_data16(writer, authorities->get_buf(authorities)); authorities->destroy(authorities); - - *type = TLS_CERTIFICATE_REQUEST; - this->state = STATE_CERTREQ_SENT; - this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); - return NEED_MORE; } /** - * Get the TLS curve of a given EC DH group + * Send Certificate Request */ -static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this, - diffie_hellman_group_t group) +static status_t send_certificate_request(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) { - diffie_hellman_group_t current; - tls_named_curve_t curve; - enumerator_t *enumerator; + bio_writer_t *authorities, *supported, *extensions; - enumerator = this->crypto->create_ec_enumerator(this->crypto); - while (enumerator->enumerate(enumerator, ¤t, &curve)) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - if (current == group) + supported = bio_writer_create(4); + /* we propose both RSA and ECDSA */ + supported->write_uint8(supported, TLS_RSA_SIGN); + supported->write_uint8(supported, TLS_ECDSA_SIGN); + writer->write_data8(writer, supported->get_buf(supported)); + supported->destroy(supported); + if (this->tls->get_version_max(this->tls) >= TLS_1_2) { - enumerator->destroy(enumerator); - return curve; + this->crypto->get_signature_algorithms(this->crypto, writer, TRUE); } - } - enumerator->destroy(enumerator); - return 0; -} -/** - * Check if the peer supports a given TLS curve - */ -bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve) -{ - bio_reader_t *reader; - uint16_t current; - - if (!this->curves_received) - { /* none received, assume yes */ - return TRUE; + if (this->send_certreq_authorities) + { + write_certificate_authorities(writer); + } + else + { + writer->write_data16(writer, chunk_empty); + } } - reader = bio_reader_create(this->curves); - while (reader->remaining(reader) && reader->read_uint16(reader, ¤t)) + else { - if (current == curve) + /* certificate request context as described in RFC 8446, section 4.3.2 */ + writer->write_uint8(writer, 0); + + extensions = bio_writer_create(32); + + if (this->send_certreq_authorities) { - reader->destroy(reader); - return TRUE; + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_CERTIFICATE_AUTHORITIES); + authorities = bio_writer_create(64); + write_certificate_authorities(authorities); + extensions->write_uint16(extensions, TLS_EXT_CERTIFICATE_AUTHORITIES); + extensions->write_data16(extensions, authorities->get_buf(authorities)); + authorities->destroy(authorities); } + + DBG2(DBG_TLS, "sending extension: %N", + tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); + extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); + supported = bio_writer_create(32); + this->crypto->get_signature_algorithms(this->crypto, supported, TRUE); + extensions->write_data16(extensions, supported->get_buf(supported)); + supported->destroy(supported); + writer->write_data16(writer, extensions->get_buf(extensions)); + extensions->destroy(extensions); } - reader->destroy(reader); - return FALSE; + + *type = TLS_CERTIFICATE_REQUEST; + this->state = STATE_CERTREQ_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; } /** * 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,12 +1499,12 @@ static status_t send_server_key_exchange(private_tls_s 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)) { - curve = ec_group_to_curve(this, group); + curve = tls_ec_group_to_curve(group); if (!curve || (!peer_supports_curve(this, curve) && !find_supported_curve(this, &curve))) { @@ -894,7 +1512,7 @@ static status_t send_server_key_exchange(private_tls_s 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); } @@ -929,12 +1547,17 @@ static status_t send_server_key_exchange(private_tls_s { writer->write_data16(writer, chunk); } - else + else if (group != CURVE_25519 && + group != CURVE_448) { /* ECP uses 8bit length header only, but a point format */ writer->write_uint8(writer, chunk.len + 1); writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); writer->write_data(writer, chunk); } + else + { /* ECPoint uses an 8-bit length header only */ + writer->write_data8(writer, chunk); + } free(chunk.ptr); chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), @@ -972,17 +1595,35 @@ static status_t send_hello_done(private_tls_server_t * static status_t send_finished(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - char buf[12]; - - 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 data failed"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return FAILED; + char buf[12]; + + if (!this->crypto->calculate_finished_legacy(this->crypto, + "server finished", buf)) + { + DBG1(DBG_TLS, "calculating server finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return FAILED; + } + + writer->write_data(writer, chunk_from_thing(buf)); } + else + { + chunk_t verify_data; - writer->write_data(writer, chunk_from_thing(buf)); + if (!this->crypto->calculate_finished(this->crypto, TRUE, &verify_data)) + { + DBG1(DBG_TLS, "calculating server finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + writer->write_data(writer, verify_data); + chunk_free(&verify_data); + } + *type = TLS_FINISHED; this->state = STATE_FINISHED_SENT; this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); @@ -990,67 +1631,160 @@ static status_t send_finished(private_tls_server_t *th return NEED_MORE; } +/** + * Send KeyUpdate message + */ +static status_t send_key_update(private_tls_server_t *this, + tls_handshake_type_t *type, bio_writer_t *writer) +{ + *type = TLS_KEY_UPDATE; + + /* we currently only send this as reply, so we never request an update */ + writer->write_uint8(writer, 0); + + this->state = STATE_KEY_UPDATE_SENT; + return NEED_MORE; +} + METHOD(tls_handshake_t, build, status_t, private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { diffie_hellman_group_t group; - switch (this->state) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { - case STATE_HELLO_RECEIVED: - return send_server_hello(this, type, writer); - case STATE_HELLO_SENT: - return send_certificate(this, type, writer); - case STATE_CERT_SENT: - group = this->crypto->get_dh_group(this->crypto); - if (group) - { - return send_server_key_exchange(this, type, writer, group); - } - /* otherwise fall through to next state */ - case STATE_KEY_EXCHANGE_SENT: - return send_certificate_request(this, type, writer); - case STATE_CERTREQ_SENT: - return send_hello_done(this, type, writer); - case STATE_CIPHERSPEC_CHANGED_OUT: - return send_finished(this, type, writer); - case STATE_FINISHED_SENT: - return INVALID_STATE; - default: - return INVALID_STATE; + switch (this->state) + { + case STATE_HELLO_RECEIVED: + return send_server_hello(this, type, writer); + case STATE_HELLO_SENT: + return send_certificate(this, type, writer); + case STATE_CERT_SENT: + group = this->crypto->get_dh_group(this->crypto); + if (group) + { + return send_server_key_exchange(this, type, writer, group); + } + /* otherwise fall through to next state */ + case STATE_KEY_EXCHANGE_SENT: + if (this->peer) + { + return send_certificate_request(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERTREQ_SENT: + return send_hello_done(this, type, writer); + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + return INVALID_STATE; + default: + return INVALID_STATE; + } } + else + { + switch (this->state) + { + case STATE_HELLO_RECEIVED: + return send_server_hello(this, type, writer); + case STATE_HELLO_SENT: + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_encrypted_extensions(this, type, writer); + case STATE_ENCRYPTED_EXTENSIONS_SENT: + if (this->peer) + { + return send_certificate_request(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERTREQ_SENT: + return send_certificate(this, type, writer); + case STATE_CERT_SENT: + return send_certificate_verify(this, type, writer); + case STATE_CERT_VERIFY_SENT: + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + if (!this->crypto->derive_app_keys(this->crypto)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + /* inbound key switches after process client finished message */ + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_FINISHED_SENT_KEY_SWITCHED; + return INVALID_STATE; + case STATE_KEY_UPDATE_REQUESTED: + return send_key_update(this, type, writer); + case STATE_KEY_UPDATE_SENT: + if (!this->crypto->update_app_keys(this->crypto, FALSE)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_FINISHED_RECEIVED; + default: + return INVALID_STATE; + } + } } METHOD(tls_handshake_t, cipherspec_changed, bool, private_tls_server_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_FINISHED_SENT; + if (this->resume) + { + return this->state == STATE_FINISHED_SENT; + } + if (this->peer) + { + return this->state == STATE_CERT_VERIFY_RECEIVED; + } + return this->state == STATE_KEY_EXCHANGE_RECEIVED; } - if (this->peer) + else { - return this->state == STATE_CERT_VERIFY_RECEIVED; + if (this->resume) + { + return this->state == STATE_HELLO_SENT; + } + return this->state == STATE_FINISHED_RECEIVED; } - return this->state == STATE_KEY_EXCHANGE_RECEIVED; + return FALSE; } else { - if (this->resume) + if (inbound) + { /* accept ChangeCipherSpec after ServerFinish or HelloRetryRequest */ + return this->state == STATE_FINISHED_SENT || + this->state == STATE_FINISHED_SENT_KEY_SWITCHED || + retrying(this); + } + else { return this->state == STATE_HELLO_SENT; } - return this->state == STATE_FINISHED_RECEIVED; } - return FALSE; } METHOD(tls_handshake_t, change_cipherspec, void, private_tls_server_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 (retrying(this)) + { /* client might send a ChangeCipherSpec after a HelloRetryRequest and + * before a new ClientHello which should not cause any state changes */ + return; + } + if (inbound) { this->state = STATE_CIPHERSPEC_CHANGED_IN; @@ -1064,11 +1798,18 @@ METHOD(tls_handshake_t, change_cipherspec, void, METHOD(tls_handshake_t, finished, bool, private_tls_server_t *this) { - if (this->resume) + if (this->tls->get_version_max(this->tls) < TLS_1_3) { + if (this->resume) + { + return this->state == STATE_FINISHED_RECEIVED; + } + return this->state == STATE_FINISHED_SENT; + } + else + { return this->state == STATE_FINISHED_RECEIVED; } - return this->state == STATE_FINISHED_SENT; } METHOD(tls_handshake_t, get_peer_id, identification_t*, @@ -1135,6 +1876,9 @@ tls_server_t *tls_server_create(tls_t *tls, .state = STATE_INIT, .peer_auth = auth_cfg_create(), .server_auth = auth_cfg_create(), + .send_certreq_authorities = lib->settings->get_bool(lib->settings, + "%s.tls.send_certreq_authorities", + TRUE, lib->ns), ); return &this->public;