Skip to content

Commit

Permalink
add callback to export secrets for SSLKEYLOGFILE
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Volokitin committed May 8, 2024
1 parent adda2dd commit 6037524
Show file tree
Hide file tree
Showing 14 changed files with 207 additions and 30 deletions.
16 changes: 16 additions & 0 deletions src/lib/tls/tls12/tls_client_impl_12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,14 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state,
}

state.compute_session_keys(state.resume_master_secret());
if(policy().allow_ssl_key_log_file()) {
// draft-thomson-tls-keylogfile-00 Section 3.2
// An implementation of TLS 1.2 (and also earlier versions) use
// the label "CLIENT_RANDOM" to identify the "master" secret for
// the connection.
callbacks().tls_ssl_key_log_data(
"CLIENT_RANDOM", state.client_hello()->random(), state.session_keys().master_secret());
}

if(state.server_hello()->supports_session_ticket()) {
state.set_expected_next(Handshake_Type::NewSessionTicket);
Expand Down Expand Up @@ -598,6 +606,14 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state,
state.handshake_io(), state, policy(), *m_creds, state.maybe_server_public_key(), m_info.hostname(), rng()));

state.compute_session_keys();
if(policy().allow_ssl_key_log_file()) {
// draft-thomson-tls-keylogfile-00 Section 3.2
// An implementation of TLS 1.2 (and also earlier versions) use
// the label "CLIENT_RANDOM" to identify the "master" secret for
// the connection.
callbacks().tls_ssl_key_log_data(
"CLIENT_RANDOM", state.client_hello()->random(), state.session_keys().master_secret());
}

if(state.received_handshake_msg(Handshake_Type::CertificateRequest) && !state.client_certs()->empty()) {
auto private_key =
Expand Down
16 changes: 16 additions & 0 deletions src/lib/tls/tls12/tls_server_impl_12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,14 @@ void Server_Impl_12::process_client_key_exchange_msg(Server_Handshake_State& pen
new Client_Key_Exchange(contents, pending_state, pending_state.server_rsa_kex_key(), *m_creds, policy(), rng()));

pending_state.compute_session_keys();
if(policy().allow_ssl_key_log_file()) {
// draft-thomson-tls-keylogfile-00 Section 3.2
// An implementation of TLS 1.2 (and also earlier versions) use
// the label "CLIENT_RANDOM" to identify the "master" secret for
// the connection.
callbacks().tls_ssl_key_log_data(
"CLIENT_RANDOM", pending_state.client_hello()->random(), pending_state.session_keys().master_secret());
}
}

void Server_Impl_12::process_change_cipher_spec_msg(Server_Handshake_State& pending_state) {
Expand Down Expand Up @@ -676,6 +684,14 @@ void Server_Impl_12::session_resume(Server_Handshake_State& pending_state, const

pending_state.mark_as_resumption();
pending_state.compute_session_keys(session.session.master_secret());
if(policy().allow_ssl_key_log_file()) {
// draft-thomson-tls-keylogfile-00 Section 3.2
// An implementation of TLS 1.2 (and also earlier versions) use
// the label "CLIENT_RANDOM" to identify the "master" secret for
// the connection.
callbacks().tls_ssl_key_log_data(
"CLIENT_RANDOM", pending_state.client_hello()->random(), pending_state.session_keys().master_secret());
}
pending_state.set_resume_certs(session.session.peer_certs());

// Give the application a chance for a final veto before fully
Expand Down
46 changes: 41 additions & 5 deletions src/lib/tls/tls13/tls_cipher_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,11 @@ constexpr size_t NONCE_LENGTH = 12;
std::unique_ptr<Cipher_State> Cipher_State::init_with_server_hello(const Connection_Side side,
secure_vector<uint8_t>&& shared_secret,
const Ciphersuite& cipher,
const Transcript_Hash& transcript_hash) {
const Transcript_Hash& transcript_hash,
const Secrets_Callback& callbacks) {
auto cs = std::unique_ptr<Cipher_State>(new Cipher_State(side, cipher.prf_algo()));
cs->advance_without_psk();
cs->advance_with_server_hello(cipher, std::move(shared_secret), transcript_hash);
cs->advance_with_server_hello(cipher, std::move(shared_secret), transcript_hash, callbacks);
return cs;
}

Expand All @@ -135,7 +136,8 @@ std::unique_ptr<Cipher_State> Cipher_State::init_with_psk(const Connection_Side
return cs;
}

void Cipher_State::advance_with_client_hello(const Transcript_Hash& transcript_hash) {
void Cipher_State::advance_with_client_hello(const Transcript_Hash& transcript_hash,
const Secrets_Callback& callbacks) {
BOTAN_ASSERT_NOMSG(m_state == State::PskBinder);

zap(m_binder_key);
Expand All @@ -148,20 +150,37 @@ void Cipher_State::advance_with_client_hello(const Transcript_Hash& transcript_h

m_exporter_master_secret = derive_secret(m_early_secret, "e exp master", transcript_hash);

// draft-thomson-tls-keylogfile-00 Section 3.1
// An implementation of TLS 1.3 use
// the label "EARLY_EXPORTER_MASTER_SECRET"
// to identify the secret
// that is using for early exporters
callbacks.tls_log_secret("EARLY_EXPORTER_MASTER_SECRET", m_exporter_master_secret);

m_salt = derive_secret(m_early_secret, "derived", empty_hash());
zap(m_early_secret);

m_state = State::EarlyTraffic;
}

void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash) {
void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcript_hash,
const Secrets_Callback& callbacks) {
BOTAN_ASSERT_NOMSG(m_state == State::HandshakeTraffic);

const auto master_secret = hkdf_extract(secure_vector<uint8_t>(m_hash->output_length(), 0x00));

auto client_application_traffic_secret = derive_secret(master_secret, "c ap traffic", transcript_hash);
auto server_application_traffic_secret = derive_secret(master_secret, "s ap traffic", transcript_hash);

// draft-thomson-tls-keylogfile-00 Section 3.1
// An implementation of TLS 1.3 use
// the label "CLIENT_TRAFFIC_SECRET_0"
// and "SERVER_TRAFFIC_SECRET_0"
// to identify the secrets are using to protect
// the connection.
callbacks.tls_log_secret("CLIENT_TRAFFIC_SECRET_0", client_application_traffic_secret);
callbacks.tls_log_secret("SERVER_TRAFFIC_SECRET_0", server_application_traffic_secret);

// Note: the secrets for processing client's application data
// are not derived before the client's Finished message
// was seen and the handshake can be considered finished.
Expand All @@ -177,6 +196,13 @@ void Cipher_State::advance_with_server_finished(const Transcript_Hash& transcrip

m_exporter_master_secret = derive_secret(master_secret, "exp master", transcript_hash);

// draft-thomson-tls-keylogfile-00 Section 3.1
// An implementation of TLS 1.3 use
// the label "EXPORTER_SECRET"
// to identify the secret
// that is used in generating exporters(rfc8446 Section 7.5).
callbacks.tls_log_secret("EXPORTER_SECRET", m_exporter_master_secret);

m_state = State::ServerApplicationTraffic;
}

Expand Down Expand Up @@ -460,7 +486,8 @@ void Cipher_State::advance_with_psk(PSK_Type type, secure_vector<uint8_t>&& psk)

void Cipher_State::advance_with_server_hello(const Ciphersuite& cipher,
secure_vector<uint8_t>&& shared_secret,
const Transcript_Hash& transcript_hash) {
const Transcript_Hash& transcript_hash,
const Secrets_Callback& callbacks) {
BOTAN_ASSERT_NOMSG(m_state == State::EarlyTraffic);
BOTAN_ASSERT_NOMSG(!m_encrypt);
BOTAN_ASSERT_NOMSG(!m_decrypt);
Expand All @@ -474,6 +501,15 @@ void Cipher_State::advance_with_server_hello(const Ciphersuite& cipher,
const auto client_handshake_traffic_secret = derive_secret(handshake_secret, "c hs traffic", transcript_hash);
const auto server_handshake_traffic_secret = derive_secret(handshake_secret, "s hs traffic", transcript_hash);

// draft-thomson-tls-keylogfile-00 Section 3.1
// An implementation of TLS 1.3 use
// the label "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
// and "SERVER_HANDSHAKE_TRAFFIC_SECRET"
// to identify the secrets
// are using to protect handshake messages.
callbacks.tls_log_secret("CLIENT_HANDSHAKE_TRAFFIC_SECRET", client_handshake_traffic_secret);
callbacks.tls_log_secret("SERVER_HANDSHAKE_TRAFFIC_SECRET", server_handshake_traffic_secret);

if(m_connection_side == Connection_Side::Server) {
derive_read_traffic_key(client_handshake_traffic_secret, true);
derive_write_traffic_key(server_handshake_traffic_secret, true);
Expand Down
37 changes: 33 additions & 4 deletions src/lib/tls/tls13/tls_cipher_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#ifndef BOTAN_TLS_CIPHER_STATE_H_
#define BOTAN_TLS_CIPHER_STATE_H_

#include <functional>

#include <botan/secmem.h>
#include <botan/tls_magic.h>
#include <botan/tls_messages.h>
Expand All @@ -28,6 +30,31 @@ namespace Botan::TLS {

class Ciphersuite;

/*
* Encapsulates the callbacks in the state machine described in RFC 8446 7.1,
* that will make the realisation the SSLKEYLOGFILE for connection debugging
* specified in ietf.org/archive/id/draft-thomson-tls-keylogfile-00.html
*/
class Secrets_Callback {
public:
Secrets_Callback() {}

virtual ~Secrets_Callback() {}

/**
* Allows access to a connection's secret data
*
* Default implementation simply ignores the inputs.
*
* @param label Identifies the reported secret type
* See draft-thomson-tls-keylogfile-00 Section 3.1
* @param secret the actual secret value
*/
virtual void tls_log_secret(std::string_view label, const std::span<const uint8_t>& secret) const {
BOTAN_UNUSED(label, secret);
}
};

/**
* This class implements the key schedule for TLS 1.3 as described in RFC 8446 7.1.
*
Expand Down Expand Up @@ -82,25 +109,27 @@ class BOTAN_TEST_API Cipher_State {
static std::unique_ptr<Cipher_State> init_with_server_hello(Connection_Side side,
secure_vector<uint8_t>&& shared_secret,
const Ciphersuite& cipher,
const Transcript_Hash& transcript_hash);
const Transcript_Hash& transcript_hash,
const Secrets_Callback& callbacks);

/**
* Transition internal secrets/keys for transporting early application data.
* Note that this state transition is legal only for handshakes using PSK.
*/
void advance_with_client_hello(const Transcript_Hash& transcript_hash);
void advance_with_client_hello(const Transcript_Hash& transcript_hash, const Secrets_Callback& callbacks);

/**
* Transition internal secrets/keys for transporting handshake data.
*/
void advance_with_server_hello(const Ciphersuite& cipher,
secure_vector<uint8_t>&& shared_secret,
const Transcript_Hash& transcript_hash);
const Transcript_Hash& transcript_hash,
const Secrets_Callback& callbacks);

/**
* Transition internal secrets/keys for transporting application data.
*/
void advance_with_server_finished(const Transcript_Hash& transcript_hash);
void advance_with_server_finished(const Transcript_Hash& transcript_hash, const Secrets_Callback& callbacks);

/**
* Transition to the final internal state allowing to create resumptions.
Expand Down
24 changes: 19 additions & 5 deletions src/lib/tls/tls13/tls_client_impl_13.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ bool Client_Impl_13::is_handshake_complete() const {
return m_handshake_state.handshake_finished();
}

void Client_Impl_13::tls_log_secret(std::string_view label, const std::span<const uint8_t>& secret) const {
if(policy().allow_ssl_key_log_file()) {
callbacks().tls_ssl_key_log_data(label, m_handshake_state.client_hello().random(), secret);
}
}

std::optional<Session_with_Handle> Client_Impl_13::find_session_for_resumption() {
// RFC 8446 4.6.1
// Clients MUST only resume if the new SNI value is valid for the
Expand Down Expand Up @@ -322,12 +328,19 @@ void Client_Impl_13::handle(const Server_Hello_13& sh) {
// TODO: When implementing early data, `advance_with_client_hello` must
// happen _before_ encrypting any early application data.
// Same when we want to support early key export.
m_cipher_state->advance_with_client_hello(m_transcript_hash.previous());
m_cipher_state->advance_with_server_hello(cipher.value(), std::move(shared_secret), m_transcript_hash.current());
m_cipher_state->advance_with_client_hello(m_transcript_hash.previous(),
static_cast<const Secrets_Callback&>(*this));
m_cipher_state->advance_with_server_hello(cipher.value(),
std::move(shared_secret),
m_transcript_hash.current(),
static_cast<const Secrets_Callback&>(*this));
} else {
m_resumed_session.reset(); // might have been set if we attempted a resumption
m_cipher_state = Cipher_State::init_with_server_hello(
m_side, std::move(shared_secret), cipher.value(), m_transcript_hash.current());
m_cipher_state = Cipher_State::init_with_server_hello(m_side,
std::move(shared_secret),
cipher.value(),
m_transcript_hash.current(),
static_cast<const Secrets_Callback&>(*this));
}

callbacks().tls_examine_extensions(sh.extensions(), Connection_Side::Server, Handshake_Type::ServerHello);
Expand Down Expand Up @@ -566,7 +579,8 @@ void Client_Impl_13::handle(const Finished_13& finished_msg) {

// Derives the secrets for receiving application data but defers
// the derivation of sending application data.
m_cipher_state->advance_with_server_finished(m_transcript_hash.current());
m_cipher_state->advance_with_server_finished(m_transcript_hash.current(),
static_cast<const Secrets_Callback&>(*this));

auto flight = aggregate_handshake_messages();

Expand Down
9 changes: 8 additions & 1 deletion src/lib/tls/tls13/tls_client_impl_13.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <botan/tls_server_info.h>
#include <botan/internal/tls_channel_impl_13.h>
#include <botan/internal/tls_cipher_state.h>
#include <botan/internal/tls_handshake_state_13.h>
#include <botan/internal/tls_handshake_transitions.h>

Expand All @@ -24,7 +25,8 @@ namespace TLS {
/**
* SSL/TLS Client 1.3 implementation
*/
class Client_Impl_13 : public Channel_Impl_13 {
class Client_Impl_13 : public Channel_Impl_13,
public Secrets_Callback {
public:
/**
* Set up a new TLS client session
Expand Down Expand Up @@ -78,6 +80,11 @@ class Client_Impl_13 : public Channel_Impl_13 {
*/
bool is_handshake_complete() const override;

/*
* Allows access to a connection's secret data
*/
void tls_log_secret(std::string_view label, const std::span<const uint8_t>& secret) const override;

private:
void process_handshake_msg(Handshake_Message_13 msg) override;
void process_post_handshake_msg(Post_Handshake_Message_13 msg) override;
Expand Down
25 changes: 19 additions & 6 deletions src/lib/tls/tls13/tls_server_impl_13.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ bool Server_Impl_13::is_handshake_complete() const {
return m_handshake_state.handshake_finished();
}

void Server_Impl_13::tls_log_secret(std::string_view label, const std::span<const uint8_t>& secret) const {
if(policy().allow_ssl_key_log_file()) {
callbacks().tls_ssl_key_log_data(label, m_handshake_state.client_hello().random(), secret);
}
}

void Server_Impl_13::downgrade() {
BOTAN_ASSERT_NOMSG(expects_downgrade());

Expand Down Expand Up @@ -287,14 +293,20 @@ void Server_Impl_13::handle_reply_to_client_hello(Server_Hello_13 server_hello)

if(uses_psk) {
BOTAN_ASSERT_NONNULL(psk_cipher_state);
psk_cipher_state->advance_with_client_hello(m_transcript_hash.previous());
psk_cipher_state->advance_with_server_hello(
cipher, my_keyshare->take_shared_secret(), m_transcript_hash.current());
psk_cipher_state->advance_with_client_hello(m_transcript_hash.previous(),
static_cast<const Secrets_Callback&>(*this));
psk_cipher_state->advance_with_server_hello(cipher,
my_keyshare->take_shared_secret(),
m_transcript_hash.current(),
static_cast<const Secrets_Callback&>(*this));

return std::move(psk_cipher_state);
} else {
return Cipher_State::init_with_server_hello(
m_side, my_keyshare->take_shared_secret(), cipher, m_transcript_hash.current());
return Cipher_State::init_with_server_hello(m_side,
my_keyshare->take_shared_secret(),
cipher,
m_transcript_hash.current(),
static_cast<const Secrets_Callback&>(*this));
}
}();

Expand Down Expand Up @@ -375,7 +387,8 @@ void Server_Impl_13::handle_reply_to_client_hello(Server_Hello_13 server_hello)

flight.send();

m_cipher_state->advance_with_server_finished(m_transcript_hash.current());
m_cipher_state->advance_with_server_finished(m_transcript_hash.current(),
static_cast<const Secrets_Callback&>(*this));

if(m_handshake_state.has_certificate_request()) {
// RFC 8446 4.4.2
Expand Down
6 changes: 5 additions & 1 deletion src/lib/tls/tls13/tls_server_impl_13.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define BOTAN_TLS_SERVER_IMPL_13_H_

#include <botan/internal/tls_channel_impl_13.h>
#include <botan/internal/tls_cipher_state.h>
#include <botan/internal/tls_handshake_state_13.h>
#include <botan/internal/tls_handshake_transitions.h>

Expand All @@ -18,7 +19,8 @@ namespace Botan::TLS {
/**
* SSL/TLS Server 1.3 implementation
*/
class Server_Impl_13 : public Channel_Impl_13 {
class Server_Impl_13 : public Channel_Impl_13,
public Secrets_Callback {
public:
explicit Server_Impl_13(const std::shared_ptr<Callbacks>& callbacks,
const std::shared_ptr<Session_Manager>& session_manager,
Expand All @@ -36,6 +38,8 @@ class Server_Impl_13 : public Channel_Impl_13 {

bool is_handshake_complete() const override;

void tls_log_secret(std::string_view label, const std::span<const uint8_t>& secret) const override;

private:
void process_handshake_msg(Handshake_Message_13 msg) override;
void process_post_handshake_msg(Post_Handshake_Message_13 msg) override;
Expand Down
Loading

0 comments on commit 6037524

Please sign in to comment.