diff --git a/src/libtls/tests/suites/test_socket.c b/src/libtls/tests/suites/test_socket.c index a5f80e59e..5682dad88 100644 --- a/src/libtls/tests/suites/test_socket.c +++ b/src/libtls/tests/suites/test_socket.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -441,6 +444,18 @@ static void test_tls(tls_version_t version, uint16_t port, bool cauth, u_int i) free(config); } +START_TEST(test_tls13) +{ + test_tls(TLS_1_3, 5669, FALSE, _i); +} +END_TEST + +START_TEST(test_tls13_mutual) +{ + test_tls(TLS_1_3, 5670, TRUE, _i); +} +END_TEST + START_TEST(test_tls12) { test_tls(TLS_1_2, 5671, FALSE, _i); @@ -488,6 +503,16 @@ Suite *socket_suite_create() s = suite_create("socket"); + tc = tcase_create("TLS 1.3/anon"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_test(test_tls13, TLS_1_3); + suite_add_tcase(s, tc); + + tc = tcase_create("TLS 1.3/mutl"); + tcase_add_checked_fixture(tc, setup_creds, teardown_creds); + add_tls_test(test_tls13_mutual, TLS_1_3); + suite_add_tcase(s, tc); + tc = tcase_create("TLS 1.2/anon"); tcase_add_checked_fixture(tc, setup_creds, teardown_creds); add_tls_test(test_tls12, TLS_1_2); diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index eb5f3b96b..52140f672 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -1487,6 +1487,16 @@ static signature_params_t *params_for_scheme(tls_signature_scheme_t sig) for (i = 0; i < countof(schemes); i++) { + /* strongSwan supports only RSA_PSS_RSAE schemes for signing but can + * verify public keys in rsaEncryption as well as rsassaPss encoding. + * Current implementation does not distinguish between signing and + * verifying. */ + if (sig == TLS_SIG_RSA_PSS_PSS_SHA256 || + sig == TLS_SIG_RSA_PSS_PSS_SHA384 || + sig == TLS_SIG_RSA_PSS_PSS_SHA512) + { + continue; + } if (schemes[i].sig == sig) { return &schemes[i].params; @@ -1677,6 +1687,18 @@ METHOD(tls_crypto_t, sign, bool, chunk_t sig; bool done = FALSE; + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + chunk_t transcript_hash; + + if (!hash_data(this, data, &transcript_hash)) + { + DBG1(DBG_TLS, "unable to create transcript hash"); + return FALSE; + } + data = chunk_cata("cm", tls13_sig_data_server, transcript_hash); + } + if (!hashsig.len) { /* fallback if none given */ hashsig = hashsig_def; diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 990365ff1..c85825208 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2020 Pascal Knecht + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -42,6 +45,9 @@ 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, } server_state_t; /** @@ -119,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 */ @@ -172,50 +183,96 @@ static bool select_suite_and_key(private_tls_server_t *this, this->server); return FALSE; } - 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) - { - type = KEY_RSA; - } - else - { - type = KEY_ECDSA; - } - key->destroy(key); - this->suite = this->crypto->select_cipher_suite(this->crypto, - suites, count, type); + if (this->tls->get_version_max(this->tls) >= TLS_1_3) + { + /* currently no support to derive key based on client supported + * signature schemes */ + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, KEY_ANY); if (!this->suite) { DBG1(DBG_TLS, "received cipher suites unacceptable"); return FALSE; } - 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; + } + else + { + 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) + { + type = KEY_RSA; + } + else + { + type = KEY_ECDSA; + } + key->destroy(key); + + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, + count, type); + if (!this->suite) + { + DBG1(DBG_TLS, "received cipher suites unacceptable"); + return FALSE; + } + 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; } +/** + * 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) + { + reader->destroy(reader); + return TRUE; + } + } + reader->destroy(reader); + return FALSE; +} + /** * 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, key_share_length, key_type = 0; + uint16_t extension_type = 0; + chunk_t random, session, ciphers, versions = chunk_empty, compression; + chunk_t ext = chunk_empty, key_share = chunk_empty; + chunk_t extension_data = chunk_empty; + bio_reader_t *extensions, *extension; tls_cipher_suite_t *suites; int count, i; rng_t *rng; @@ -223,7 +280,7 @@ static status_t process_client_hello(private_tls_server_t *this, 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) || @@ -239,37 +296,63 @@ static status_t process_client_hello(private_tls_server_t *this, * as that might change the min./max. versions */ this->crypto->get_cipher_suites(this->crypto, NULL); - if (ext.len) + 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_SUPPORTED_GROUPS: - 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: + this->hashsig = chunk_clone(extension_data); + break; + case TLS_EXT_SUPPORTED_GROUPS: + 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_uint16(extension, &key_share_length) || + !extension->read_uint16(extension, &key_type) || + !extension->read_data16(extension, &key_share) || + !key_share.len) + { + 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); memcpy(this->client_random, random.ptr, sizeof(this->client_random)); @@ -286,18 +369,48 @@ static status_t process_client_hello(private_tls_server_t *this, } rng->destroy(rng); - if (!this->tls->set_version(this->tls, version, version)) + 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; + } + } + 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->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) { this->session = chunk_clone(session); @@ -321,16 +434,74 @@ static status_t process_client_hello(private_tls_server_t *this, 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 (this->tls->get_version_max(this->tls) < TLS_1_3) { - DBG1(DBG_TLS, "generating TLS session identifier failed, skipped"); + 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 + { + this->session = chunk_clone(session); } - DESTROY_IF(rng); DBG1(DBG_TLS, "negotiated %N using suite %N", 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; + enumerator_t *enumerator; + chunk_t shared_secret = chunk_empty; + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, &group, &curve)) + { + if (curve == key_type && peer_supports_curve(this, curve)) + { + this->dh = lib->crypto->create_dh(lib->crypto, group); + break; + } + } + enumerator->destroy(enumerator); + if (!this->dh) + { /* simply fail since HelloRetryRequest is not currently implemented */ + DBG1(DBG_TLS, "key_type is not within supported group"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + + if (key_share.len && + key_type != TLS_CURVE25519 && + key_type != TLS_CURVE448) + { /* classic format (see RFC 8446, section 4.2.8.2) */ + if (key_share.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, key_share.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + key_share = chunk_skip(key_share, 1); + } + if (!key_share.len || + !this->dh->set_other_public_value(this->dh, key_share)) + { + 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->requested_curve = key_type; + } + this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -596,29 +767,57 @@ static status_t process_cert_verify(private_tls_server_t *this, 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_legacy(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; + } + + if (!this->crypto->derive_app_keys(this->crypto)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->change_cipher(this->crypto, TRUE); + this->crypto->change_cipher(this->crypto, FALSE); } - 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; @@ -629,56 +828,86 @@ METHOD(tls_handshake_t, process, status_t, { 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: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + return NEED_MORE; + case STATE_FINISHED_RECEIVED: + 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); @@ -692,8 +921,12 @@ METHOD(tls_handshake_t, process, status_t, 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_max(this->tls)); + bio_writer_t *extensions, *key_share; + tls_version_t version = this->tls->get_version_max(this->tls); + chunk_t pub; + + /* cap legacy version at TLS 1.2 for middlebox compatibility */ + writer->write_uint16(writer, min(TLS_1_2, version)); writer->write_data(writer, chunk_from_thing(this->server_random)); /* session identifier if we have one */ @@ -705,12 +938,88 @@ static status_t send_server_hello(private_tls_server_t *this, /* 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); + + if (this->dh) + { + tls_named_group_t selected_curve = this->requested_curve; + + 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); + extensions->destroy(extensions); + return NEED_MORE; + } + extensions->write_uint16(extensions, TLS_EXT_KEY_SHARE); + key_share = bio_writer_create(pub.len + 6); + key_share->write_uint16(key_share, selected_curve); + if (selected_curve == TLS_CURVE25519 || + selected_curve == TLS_CURVE448) + { + key_share->write_data16(key_share, pub); + } + else + { /* classic format (see RFC 8446, section 4.2.8.2) */ + key_share->write_uint16(key_share, pub.len + 1); + key_share->write_uint8(key_share, TLS_ANSI_UNCOMPRESSED); + key_share->write_data(key_share, pub); + } + extensions->write_data16(extensions, key_share->get_buf(key_share)); + key_share->destroy(key_share); + free(pub.ptr); + } + + writer->write_data16(writer, extensions->get_buf(extensions)); + extensions->destroy(extensions); + } + *type = TLS_SERVER_HELLO; this->state = 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 */ @@ -723,6 +1032,12 @@ static status_t send_certificate(private_tls_server_t *this, 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); @@ -735,6 +1050,11 @@ static status_t send_certificate(private_tls_server_t *this, 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)) @@ -761,6 +1081,27 @@ static status_t send_certificate(private_tls_server_t *this, return NEED_MORE; } +/** + * Send Certificate Verify + */ +static status_t send_certificate_verify(private_tls_server_t *this, + tls_handshake_type_t *type, + bio_writer_t *writer) +{ + 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; +} + /** * Send Certificate Request */ @@ -830,36 +1171,11 @@ static tls_named_group_t ec_group_to_curve(private_tls_server_t *this, return 0; } -/** - * Check if the peer supports a given TLS curve - */ -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) - { - reader->destroy(reader); - return TRUE; - } - } - reader->destroy(reader); - return FALSE; -} - /** * Try to find a curve supported by both, client and server */ static bool find_supported_curve(private_tls_server_t *this, - tls_named_group_t *curve) + tls_named_group_t *curve) { tls_named_group_t current; enumerator_t *enumerator; @@ -977,17 +1293,34 @@ static status_t send_hello_done(private_tls_server_t *this, 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_legacy(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]; - writer->write_data(writer, chunk_from_thing(buf)); + 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; + + 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; @@ -1001,62 +1334,104 @@ METHOD(tls_handshake_t, build, status_t, { 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: + 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; + } + } + 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: + 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: + return INVALID_STATE; + 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 */ + return this->state == STATE_FINISHED_SENT; + } + 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 (inbound) { this->state = STATE_CIPHERSPEC_CHANGED_IN; @@ -1070,11 +1445,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*,