From 87761ea610ba49c01ae552e46ae998c7fde002fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 1 Apr 2022 17:05:46 +0200 Subject: [PATCH] KEMTLS as spoken by thomwigger/kemtls-experiment Co-Authored-By: Hannes Rantzsch --- src/lib/pubkey/pk_algs.cpp | 15 ++++ src/lib/tls/msg_certificate_13.cpp | 32 ++++++--- src/lib/tls/msg_server_hello.cpp | 5 ++ src/lib/tls/tls12/tls_handshake_state.cpp | 3 + src/lib/tls/tls13/tls_cipher_state.cpp | 70 +++++++++++++++++- src/lib/tls/tls13/tls_cipher_state.h | 11 +++ src/lib/tls/tls13/tls_client_impl_13.cpp | 56 ++++++++++++--- src/lib/tls/tls13/tls_handshake_state_13.cpp | 8 +++ src/lib/tls/tls13/tls_handshake_state_13.h | 12 ++++ src/lib/tls/tls_algos.cpp | 14 ++++ src/lib/tls/tls_algos.h | 5 ++ src/lib/tls/tls_extensions.h | 2 + src/lib/tls/tls_extensions_key_share.cpp | 76 +++++++++++++++++++- src/lib/tls/tls_handshake_transitions.cpp | 6 +- src/lib/tls/tls_magic.h | 3 + src/lib/tls/tls_messages.h | 36 ++++++++++ src/tests/data/tls-policy/default_tls13.txt | 2 +- 17 files changed, 333 insertions(+), 23 deletions(-) diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp index b52ca97e0ff..ffdb90f892a 100644 --- a/src/lib/pubkey/pk_algs.cpp +++ b/src/lib/pubkey/pk_algs.cpp @@ -72,6 +72,10 @@ #include #endif +#if defined(BOTAN_HAS_KYBER) + #include +#endif + namespace Botan { std::unique_ptr @@ -152,6 +156,17 @@ load_public_key(const AlgorithmIdentifier& alg_id, return std::make_unique(key_bits); #endif +#if defined(BOTAN_HAS_KYBER) + // TODO: those OIDs are taken from thomwiggers/mk-cert for interoperability + // https://github.com/thomwiggers/mk-cert/blob/dd845f3825d8dc5fd69e0e234f6a62266ee4dd4a/algorithms.py#L84 + if(alg_name == "1.3.6.1.4.1.44363.46.52") + return std::make_unique(key_bits, KyberMode::Kyber512, KyberKeyEncoding::Raw); + if(alg_name == "1.3.6.1.4.1.44363.46.53") + return std::make_unique(key_bits, KyberMode::Kyber768, KyberKeyEncoding::Raw); + if(alg_name == "1.3.6.1.4.1.44363.46.54") + return std::make_unique(key_bits, KyberMode::Kyber1024, KyberKeyEncoding::Raw); +#endif + throw Decoding_Error("Unknown or unavailable public key algorithm " + alg_name); } diff --git a/src/lib/tls/msg_certificate_13.cpp b/src/lib/tls/msg_certificate_13.cpp index a75df2613e6..b2792b19da9 100644 --- a/src/lib/tls/msg_certificate_13.cpp +++ b/src/lib/tls/msg_certificate_13.cpp @@ -20,6 +20,10 @@ #include #include +#if defined(BOTAN_HAS_KEMTLS) && defined(BOTAN_HAS_KYBER) + #include +#endif + namespace Botan::TLS { namespace { @@ -90,6 +94,24 @@ void Certificate_13::verify(Callbacks& callbacks, callbacks.tls_verify_cert_chain(certs, ocsp_responses, trusted_CAs, usage, hostname, policy); } +std::unique_ptr Certificate_13::public_key() const + { + BOTAN_STATE_CHECK(!m_entries.empty()); + auto key = m_entries.front().certificate.load_subject_public_key(); + +#if defined(BOTAN_HAS_KEMTLS) && defined(BOTAN_HAS_KYBER) + // TODO: Is there a better way to detect a kyber public key? + // Maybe key->algorithm_identifier() + if(key->algo_name().find("Kyber") == 0) + { + dynamic_cast(*key) + .set_binary_encoding(KyberKeyEncoding::Raw); + } +#endif + + return key; + } + /** * Deserialize a Certificate message */ @@ -179,15 +201,7 @@ Certificate_13::Certificate_13(const std::vector& buf, else { /* validation of provided certificate public key */ - auto key = m_entries.front().certificate.load_subject_public_key(); - - policy.check_peer_key_acceptable(*key); - - if(!policy.allowed_signature_method(key->algo_name())) - { - throw TLS_Exception(Alert::HANDSHAKE_FAILURE, - "Rejecting " + key->algo_name() + " signature"); - } + policy.check_peer_key_acceptable(*public_key()); } } diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index 7d3b0aa570c..1f6ccea6367 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -659,6 +659,11 @@ Protocol_Version Server_Hello_13::selected_version() const return versions.front(); } +bool Server_Hello_13::requests_kemtls() const + { + return extensions().has() && extensions().get()->requests_kemtls(); + } + Hello_Retry_Request::Hello_Retry_Request(std::unique_ptr data) : Server_Hello_13(std::move(data), Server_Hello_13::as_hello_retry_request) {} diff --git a/src/lib/tls/tls12/tls_handshake_state.cpp b/src/lib/tls/tls12/tls_handshake_state.cpp index 3f100bc18b0..7852db6eb11 100644 --- a/src/lib/tls/tls12/tls_handshake_state.cpp +++ b/src/lib/tls/tls12/tls_handshake_state.cpp @@ -80,6 +80,9 @@ const char* handshake_type_to_string(Handshake_Type type) case KEY_UPDATE: return "key_update"; + case KEM_ENCAPSULATION: + return "kem_encapsulation"; + case HANDSHAKE_NONE: return "invalid"; } diff --git a/src/lib/tls/tls13/tls_cipher_state.cpp b/src/lib/tls/tls13/tls_cipher_state.cpp index 5e41102bf5e..eab51362734 100644 --- a/src/lib/tls/tls13/tls_cipher_state.cpp +++ b/src/lib/tls/tls13/tls_cipher_state.cpp @@ -166,6 +166,70 @@ void Cipher_State::advance_with_client_finished(const Transcript_Hash& transcrip m_state = State::Completed; } +#if defined(BOTAN_HAS_KEMTLS) +void Cipher_State::advance_with_client_kem_encapsulation( + secure_vector shared_secret, + const Transcript_Hash& transcript_hash) + { + BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); + + const auto kem_secret = hkdf_extract(std::move(shared_secret)); + + const auto client_handshake_traffic_secret = derive_secret(kem_secret, "c ahs traffic", transcript_hash); + const auto server_handshake_traffic_secret = derive_secret(kem_secret, "s ahs traffic", transcript_hash); + + m_salt = derive_secret(kem_secret, "derived", empty_hash()); // dAHS + const auto master_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); + + // Note: these labels differ from the research paper to comply with thomwiggers rustls implementation. + // https://thomwiggers.nl/publication/kemtls/kemtls.pdf + auto client_finished_key = hkdf_expand_label(master_secret, "client", {}, m_hash->output_length()); + auto server_finished_key = hkdf_expand_label(master_secret, "server", {}, m_hash->output_length()); + + if(m_connection_side == Connection_Side::SERVER) + { + derive_read_traffic_key(client_handshake_traffic_secret, false); + derive_write_traffic_key(server_handshake_traffic_secret, false); + m_finished_key = std::move(server_finished_key); + m_peer_finished_key = std::move(client_finished_key); + } + else + { + derive_read_traffic_key(server_handshake_traffic_secret, false); + derive_write_traffic_key(client_handshake_traffic_secret, false); + m_finished_key = std::move(client_finished_key); + m_peer_finished_key = std::move(server_finished_key); + } + + m_state = State::AuthenticatedHandshakeTraffic; + } + +void Cipher_State::advance_with_client_finished_kemtls( + const Transcript_Hash& transcript_hash /* CH..CF */) + { + BOTAN_ASSERT_NOMSG(m_state == State::AuthenticatedHandshakeTraffic); + + const auto master_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); + auto client_application_traffic_secret = derive_secret(master_secret, "c ap traffic", transcript_hash); + derive_write_traffic_key(client_application_traffic_secret); + } + +void Cipher_State::advance_with_server_finished_kemtls( + const Transcript_Hash& transcript_hash /* CH..SF */) + { + BOTAN_ASSERT_NOMSG(m_state == State::AuthenticatedHandshakeTraffic); + + const auto master_secret = hkdf_extract(secure_vector(m_hash->output_length(), 0x00)); + auto server_application_traffic_secret = derive_secret(master_secret, "s ap traffic", transcript_hash); + derive_read_traffic_key(server_application_traffic_secret); + + // This was the final state change; the salt is no longer needed. + zap(m_salt); + + m_state = State::Completed; + } +#endif + std::vector Cipher_State::current_nonce(const uint64_t seq_no, const secure_vector& iv) const { // RFC 8446 5.3 @@ -224,7 +288,8 @@ size_t Cipher_State::minimum_decryption_input_length() const std::vector Cipher_State::finished_mac(const Transcript_Hash& transcript_hash) const { - BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); + BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic || + m_state == State::AuthenticatedHandshakeTraffic); auto hmac = HMAC(m_hash->new_object()); hmac.set_key(m_finished_key); @@ -235,7 +300,8 @@ std::vector Cipher_State::finished_mac(const Transcript_Hash& transcrip bool Cipher_State::verify_peer_finished_mac(const Transcript_Hash& transcript_hash, const std::vector& peer_mac) const { - BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic); + BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic || + m_state == State::AuthenticatedHandshakeTraffic); auto hmac = HMAC(m_hash->new_object()); hmac.set_key(m_peer_finished_key); diff --git a/src/lib/tls/tls13/tls_cipher_state.h b/src/lib/tls/tls13/tls_cipher_state.h index ab0c7686165..391d24853e5 100644 --- a/src/lib/tls/tls13/tls_cipher_state.h +++ b/src/lib/tls/tls13/tls_cipher_state.h @@ -81,6 +81,15 @@ class BOTAN_TEST_API Cipher_State */ void advance_with_client_finished(const Transcript_Hash& transcript_hash); +#if defined(BOTAN_HAS_KEMTLS) + void advance_with_client_kem_encapsulation(secure_vector shared_secret, + const Transcript_Hash& transcript_hash); + + void advance_with_client_finished_kemtls(const Transcript_Hash& transcript_hash); + + void advance_with_server_finished_kemtls(const Transcript_Hash& transcript_hash); +#endif + /** * Encrypt a TLS record fragment (RFC 8446 5.2 -- TLSInnerPlaintext) using the * currently available traffic secret keys and the current sequence number. @@ -167,6 +176,7 @@ class BOTAN_TEST_API Cipher_State bool can_encrypt_application_traffic() const { return m_state != State::Uninitialized && m_state != State::HandshakeTraffic + && m_state != State::AuthenticatedHandshakeTraffic && !m_write_key.empty() && !m_write_iv.empty(); } @@ -248,6 +258,7 @@ class BOTAN_TEST_API Cipher_State Uninitialized, EarlyTraffic, HandshakeTraffic, + AuthenticatedHandshakeTraffic, // KEMTLS only ApplicationTraffic, Completed }; diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp index 90fb701466a..bc5b70b3b67 100644 --- a/src/lib/tls/tls13/tls_client_impl_13.cpp +++ b/src/lib/tls/tls13/tls_client_impl_13.cpp @@ -368,7 +368,36 @@ void Client_Impl_13::handle(const Certificate_13& certificate_msg) m_info.hostname(), m_handshake_state.client_hello().extensions().has()); - m_transitions.set_expected_next(CERTIFICATE_VERIFY); + const auto pubkey = certificate_msg.public_key(); + +#if defined(BOTAN_HAS_KEMTLS) + if(m_handshake_state.server_hello().requests_kemtls()) + { + send_handshake_message( + m_handshake_state.sent(KEM_Encapsulation(*pubkey, rng()))); + + m_cipher_state->advance_with_client_kem_encapsulation( + m_handshake_state.client_kem_encapsulation().secret(), + m_transcript_hash.current()); + + send_handshake_message(m_handshake_state.sent(Finished_13(m_cipher_state.get(), + m_transcript_hash.current()))); + + m_cipher_state->advance_with_client_finished_kemtls(m_transcript_hash.current()); + + m_transitions.set_expected_next(FINISHED); + } + else +#endif + { + if(!policy().allowed_signature_method(pubkey->algo_name())) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Rejecting " + pubkey->algo_name() + " signature"); + } + + m_transitions.set_expected_next(CERTIFICATE_VERIFY); + } } void Client_Impl_13::handle(const Certificate_Verify_13& certificate_verify_msg) @@ -395,14 +424,23 @@ void Client_Impl_13::handle(const Finished_13& finished_msg) m_transcript_hash.previous())) { throw TLS_Exception(Alert::DECRYPT_ERROR, "Finished message didn't verify"); } - // send client finished handshake message (still using handshake traffic secrets) - send_handshake_message(m_handshake_state.sent(Finished_13(m_cipher_state.get(), - m_transcript_hash.current()))); - - // derives the application traffic secrets and _replaces_ the handshake traffic secrets - // Note: this MUST happen AFTER the client finished message was sent! - m_cipher_state->advance_with_server_finished(m_transcript_hash.previous()); - m_cipher_state->advance_with_client_finished(m_transcript_hash.current()); +#if defined(BOTAN_HAS_KEMTLS) + if(m_handshake_state.server_hello().requests_kemtls()) + { + m_cipher_state->advance_with_server_finished_kemtls(m_transcript_hash.current()); + } + else +#endif + { + // send client finished handshake message (still using handshake traffic secrets) + send_handshake_message(m_handshake_state.sent(Finished_13(m_cipher_state.get(), + m_transcript_hash.current()))); + + // derives the application traffic secrets and _replaces_ the handshake traffic secrets + // Note: this MUST happen AFTER the client finished message was sent! + m_cipher_state->advance_with_server_finished(m_transcript_hash.previous()); + m_cipher_state->advance_with_client_finished(m_transcript_hash.current()); + } // TODO: save session and invoke tls_session_established callback diff --git a/src/lib/tls/tls13/tls_handshake_state_13.cpp b/src/lib/tls/tls13/tls_handshake_state_13.cpp index 8c086fd4fec..307bb6864ab 100644 --- a/src/lib/tls/tls13/tls_handshake_state_13.cpp +++ b/src/lib/tls/tls13/tls_handshake_state_13.cpp @@ -61,4 +61,12 @@ Finished_13& Handshake_State_13_Base::store(Finished_13 finished, const bool fro return target.value(); } +#if defined(BOTAN_HAS_KEMTLS) +KEM_Encapsulation& Handshake_State_13_Base::store(KEM_Encapsulation kem_ct, const bool) +{ + m_client_kem_encapsulation = std::move(kem_ct); + return m_client_kem_encapsulation.value(); +} +#endif + } diff --git a/src/lib/tls/tls13/tls_handshake_state_13.h b/src/lib/tls/tls13/tls_handshake_state_13.h index 6abafb08d35..11dff78faa3 100644 --- a/src/lib/tls/tls13/tls_handshake_state_13.h +++ b/src/lib/tls/tls13/tls_handshake_state_13.h @@ -41,6 +41,10 @@ class BOTAN_TEST_API Handshake_State_13_Base const Finished_13& client_finished() const { return get(m_client_finished); } const Finished_13& server_finished() const { return get(m_server_finished); } +#if defined(BOTAN_HAS_KEMTLS) + const KEM_Encapsulation& client_kem_encapsulation() const { return get(m_client_kem_encapsulation); } +#endif + protected: Handshake_State_13_Base(Connection_Side whoami) : m_side(whoami) {} @@ -53,6 +57,10 @@ class BOTAN_TEST_API Handshake_State_13_Base Certificate_Verify_13& store(Certificate_Verify_13 certificate_verify, const bool from_peer); Finished_13& store(Finished_13 finished, const bool from_peer); +#if defined(BOTAN_HAS_KEMTLS) + KEM_Encapsulation& store(KEM_Encapsulation kem_ct, const bool from_peer); +#endif + private: template const MessageT& get(const std::optional& opt) const @@ -81,6 +89,10 @@ class BOTAN_TEST_API Handshake_State_13_Base std::optional m_server_verify; std::optional m_server_finished; std::optional m_client_finished; + +#if defined(BOTAN_HAS_KEMTLS) + std::optional m_client_kem_encapsulation; +#endif }; } diff --git a/src/lib/tls/tls_algos.cpp b/src/lib/tls/tls_algos.cpp index 81f174b7cb9..f5642bb7247 100644 --- a/src/lib/tls/tls_algos.cpp +++ b/src/lib/tls/tls_algos.cpp @@ -140,6 +140,13 @@ Group_Params group_param_from_string(const std::string& group_name) if(group_name == "ffdhe/ietf/8192") return Group_Params::FFDHE_8192; + if(group_name == "Kyber-512-r3") + return Group_Params::KYBER_512; + if(group_name == "Kyber-768-r3") + return Group_Params::KYBER_768; + if(group_name == "Kyber-1024-r3") + return Group_Params::KYBER_1024; + return Group_Params::NONE; // unknown } @@ -173,6 +180,13 @@ std::string group_param_to_string(Group_Params group) case Group_Params::FFDHE_8192: return "ffdhe/ietf/8192"; + case Group_Params::KYBER_512: + return "Kyber-512-r3"; + case Group_Params::KYBER_768: + return "Kyber-768-r3"; + case Group_Params::KYBER_1024: + return "Kyber-1024-r3"; + default: return ""; } diff --git a/src/lib/tls/tls_algos.h b/src/lib/tls/tls_algos.h index 4e4388df15b..5e67399f165 100644 --- a/src/lib/tls/tls_algos.h +++ b/src/lib/tls/tls_algos.h @@ -137,6 +137,11 @@ enum class Group_Params : uint16_t { FFDHE_4096 = 258, FFDHE_6144 = 259, FFDHE_8192 = 260, + + // as found in the KEMTLS reference implementation (thomwiggers/rustls) + KYBER_512 = 0x01FC, + KYBER_768 = 0x01FD, + KYBER_1024 = 0x01FE, }; std::string group_param_to_string(Group_Params group); diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index ca534d0c4b6..9a8401ef36a 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -615,6 +615,8 @@ class BOTAN_UNSTABLE_API Key_Share final : public Extension */ void erase(); + bool requests_kemtls() const; + explicit Key_Share(TLS_Data_Reader& reader, uint16_t extension_size, Handshake_Type message_type); diff --git a/src/lib/tls/tls_extensions_key_share.cpp b/src/lib/tls/tls_extensions_key_share.cpp index a0d19bdbcba..8c65ef99464 100644 --- a/src/lib/tls/tls_extensions_key_share.cpp +++ b/src/lib/tls/tls_extensions_key_share.cpp @@ -16,6 +16,12 @@ #include #include +#if defined(BOTAN_HAS_KEMTLS) +#if defined(BOTAN_HAS_KYBER) + #include +#endif +#endif + #if defined(BOTAN_HAS_CURVE_25519) #include #endif @@ -55,6 +61,46 @@ namespace { group == Group_Params::FFDHE_8192; } +[[maybe_unused]] constexpr bool is_kem(const Group_Params group) + { + // TODO: We currently support Kyber only but technically KEMTLS could + // also work with other KEMs (eg. McEliece, RSA, ...) + return + group == Group_Params::KYBER_512 || + group == Group_Params::KYBER_768 || + group == Group_Params::KYBER_1024; + } + +#if defined(BOTAN_HAS_KEMTLS) + +std::unique_ptr make_kem_private_key(Named_Group group, RandomNumberGenerator& rng) + { +#if defined(BOTAN_HAS_KYBER) + std::unique_ptr skey; + + if(group == Group_Params::KYBER_512) + { + skey = std::make_unique(rng, KyberMode::Kyber512); + } + else if(group == Group_Params::KYBER_768) + { + skey = std::make_unique(rng, KyberMode::Kyber768); + } + else if(group == Group_Params::KYBER_1024) + { + skey = std::make_unique(rng, KyberMode::Kyber1024); + } + + skey->set_binary_encoding(KyberKeyEncoding::Raw); + return skey; +#else + BOTAN_UNUSED(group, rng); + return nullptr; +#endif + } + +#endif + class Key_Share_Entry { public: @@ -106,15 +152,24 @@ class Key_Share_Entry auto skey = std::make_unique(rng, DL_Group(cb.tls_decode_group_param(group))); m_key_exchange = skey->public_value(); m_private_key = std::move(skey); -#if defined(BOTAN_HAS_CURVE_25519) } +#if defined(BOTAN_HAS_CURVE_25519) else if(is_x25519(group)) { auto skey = std::make_unique(rng); m_key_exchange = skey->public_value(); m_private_key = std::move(skey); + } #endif +#if defined(BOTAN_HAS_KEMTLS) + else if(is_kem(group)) + { + auto skey = make_kem_private_key(group, rng); + BOTAN_ASSERT(skey, "Failed to create KEM private key"); + m_key_exchange = skey->public_key()->public_key_bits(); + m_private_key = std::move(skey); } +#endif else { throw Decoding_Error("cannot create a key offering without a group definition"); @@ -150,6 +205,15 @@ class Key_Share_Entry BOTAN_ASSERT_NOMSG(m_private_key != nullptr); BOTAN_ASSERT_NOMSG(m_group == received.m_group); +#if defined(BOTAN_HAS_KEMTLS) + if(is_kem(m_group)) + { + // TODO: shared secret length is always 32 in thomwiggers/rustls, but is it? + return PK_KEM_Decryptor(*m_private_key, rng).decrypt( + received.m_key_exchange, 32, std::vector()); + } +#endif + PK_Key_Agreement ka(*m_private_key, rng, "Raw"); if(is_ecdh(m_group)) @@ -243,6 +307,9 @@ class Key_Share_ServerHello m_server_share.erase(); } + bool requests_kemtls() const + { return is_kem(m_server_share.group()); } + private: Key_Share_Entry m_server_share; }; @@ -548,6 +615,13 @@ void Key_Share::erase() std::visit([](auto& key_share) { key_share.erase(); }, m_impl->key_share); } +bool Key_Share::requests_kemtls() const + { + // TODO: this won't work if kemtls is requested in an HRR + return std::holds_alternative(m_impl->key_share) + && std::get(m_impl->key_share).requests_kemtls(); + } + } // Botan::TLS #endif // HAS_TLS_13 diff --git a/src/lib/tls/tls_handshake_transitions.cpp b/src/lib/tls/tls_handshake_transitions.cpp index db7f4b36bb6..919870ad5ee 100644 --- a/src/lib/tls/tls_handshake_transitions.cpp +++ b/src/lib/tls/tls_handshake_transitions.cpp @@ -77,6 +77,9 @@ uint32_t bitmask_for_handshake_type(Handshake_Type type) case HELLO_RETRY_REQUEST: // RFC 8446 return (1 << 18); + case KEM_ENCAPSULATION: + return (1 << 19); + // allow explicitly disabling new handshakes case HANDSHAKE_NONE: return 0; @@ -107,7 +110,8 @@ std::string handshake_mask_to_string(uint32_t mask, char combiner) FINISHED, END_OF_EARLY_DATA, ENCRYPTED_EXTENSIONS, - KEY_UPDATE + KEY_UPDATE, + KEM_ENCAPSULATION }; std::ostringstream o; diff --git a/src/lib/tls/tls_magic.h b/src/lib/tls/tls_magic.h index a53c467054e..7ed249df0cc 100644 --- a/src/lib/tls/tls_magic.h +++ b/src/lib/tls/tls_magic.h @@ -83,6 +83,9 @@ enum Handshake_Type { KEY_UPDATE = 24, // RFC 8446 (TLS 1.3) + // Note: this value is chosen to be compatible with thomwiggers/rustls implementation + KEM_ENCAPSULATION = 32, // KEMTLS + HELLO_RETRY_REQUEST = 253, // Not a wire value (HRR appears as an ordinary Server Hello) HANDSHAKE_CCS = 254, // Not a wire value (TLS 1.3 uses this value for 'message_hash' -- RFC 8446 4.4.1) HANDSHAKE_NONE = 255 // Null value diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 8cabb8c83d8..60430fff34c 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -385,6 +385,11 @@ class BOTAN_UNSTABLE_API Server_Hello_13 : public Server_Hello * @returns the selected version as indicated by the supported_versions extension */ Protocol_Version selected_version() const override; + + /** + * @returns true iff the Key_Share extension selected a KEM. + */ + bool requests_kemtls() const; }; class BOTAN_UNSTABLE_API Hello_Retry_Request final : public Server_Hello_13 @@ -524,6 +529,11 @@ class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message const std::string& hostname, bool use_ocsp) const; + /** + * Return the public key of the leaf certificate + */ + std::unique_ptr public_key() const; + std::vector serialize() const override; private: @@ -863,6 +873,26 @@ class BOTAN_UNSTABLE_API Key_Update final : public Handshake_Message bool m_update_requested; }; +#if defined(BOTAN_HAS_KEMTLS) +class BOTAN_UNSTABLE_API KEM_Encapsulation final : public Handshake_Message + { + public: + Handshake_Type type() const override + { return KEM_ENCAPSULATION; } + + KEM_Encapsulation(const Public_Key& peer_key, RandomNumberGenerator& rng); + + std::vector serialize() const override; + + const secure_vector& secret() const + { return m_secret; } + + private: + secure_vector m_ciphertext; + secure_vector m_secret; + }; +#endif + #if defined(BOTAN_HAS_TLS_13) namespace { @@ -892,6 +922,9 @@ using Handshake_Message_13 = std::variant< Certificate_13, // Certificate_Req_13, Certificate_Verify_13, +#if defined(BOTAN_HAS_KEMTLS) + KEM_Encapsulation, +#endif Finished_13>; using Handshake_Message_13_Ref = as_wrapped_references_t; @@ -914,6 +947,9 @@ using Server_Handshake_13_Message_Ref = as_wrapped_references_t; // Post-Handshake Messages // Key_Update>; diff --git a/src/tests/data/tls-policy/default_tls13.txt b/src/tests/data/tls-policy/default_tls13.txt index cf4b61bf0bd..8e38c4f2d74 100644 --- a/src/tests/data/tls-policy/default_tls13.txt +++ b/src/tests/data/tls-policy/default_tls13.txt @@ -9,7 +9,7 @@ macs = AEAD SHA-256 SHA-384 SHA-1 signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = CECPQ1 ECDH DH -key_exchange_groups = x25519 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups = Kyber-512-r3 x25519 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false