Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client/server: support for KeyLog trait, SSLKEYLOGFILE #465

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, Server
use rustls::client::ResolvesClientCert;
use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider};
use rustls::{
sign::CertifiedKey, ClientConfig, ClientConnection, DigitallySignedStruct, Error,
ProtocolVersion, SignatureScheme, SupportedProtocolVersion,
sign::CertifiedKey, ClientConfig, ClientConnection, DigitallySignedStruct, Error, KeyLog,
KeyLogFile, ProtocolVersion, SignatureScheme, SupportedProtocolVersion,
};

use crate::cipher::{rustls_certified_key, rustls_server_cert_verifier};
use crate::connection::{rustls_connection, Connection};
use crate::crypto_provider::rustls_crypto_provider;
use crate::error::rustls_result::{InvalidParameter, NullParameter};
use crate::error::{self, map_error, rustls_result};
use crate::keylog::{rustls_keylog_log_callback, rustls_keylog_will_log_callback, CallbackKeyLog};
use crate::rslice::NulByte;
use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_str};
use crate::{
Expand Down Expand Up @@ -50,6 +51,7 @@ pub(crate) struct ClientConfigBuilder {
alpn_protocols: Vec<Vec<u8>>,
enable_sni: bool,
cert_resolver: Option<Arc<dyn ResolvesClientCert>>,
key_log: Option<Arc<dyn KeyLog>>,
}

arc_castable! {
Expand Down Expand Up @@ -84,6 +86,7 @@ impl rustls_client_config_builder {
cert_resolver: None,
alpn_protocols: vec![],
enable_sni: true,
key_log: None,
};
to_boxed_mut_ptr(builder)
}
Expand Down Expand Up @@ -137,6 +140,7 @@ impl rustls_client_config_builder {
cert_resolver: None,
alpn_protocols: vec![],
enable_sni: true,
key_log: None,
};

set_boxed_mut_ptr(builder_out, config_builder);
Expand Down Expand Up @@ -422,6 +426,71 @@ impl rustls_client_config_builder {
rustls_result::Ok
}
}

/// Log key material to the file specified by the `SSLKEYLOGFILE` environment variable.
///
/// The key material will be logged in the NSS key log format,
/// <https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format> and is
/// compatible with tools like Wireshark.
///
/// Secrets logged in this manner are **extremely sensitive** and can break the security
/// of past, present and future sessions.
///
/// For more control over which secrets are logged, or to customize the format, prefer
/// `rustls_client_config_builder_set_key_log`.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_set_key_log_file(
builder: *mut rustls_client_config_builder,
) -> rustls_result {
ffi_panic_boundary! {
let builder = try_mut_from_ptr!(builder);
builder.key_log = Some(Arc::new(KeyLogFile::new()));
rustls_result::Ok
}
}

/// Provide callbacks to manage logging key material.
///
/// The `log_cb` argument is mandatory and must not be `NULL` or a `NullParameter` error is
/// returned. The `log_cb` will be invoked with a `client_random` to identify the relevant session,
/// a `label` to identify the purpose of the `secret`, and the `secret` itself. See the
/// Rustls documentation of the `KeyLog` trait for more information on possible labels:
/// <https://docs.rs/rustls/latest/rustls/trait.KeyLog.html#tymethod.log>
///
/// The `will_log_cb` may be `NULL`, in which case all key material will be provided to
/// the `log_cb`. By providing a custom `will_log_cb` you may return `0` for labels you don't
/// wish to log, and non-zero for labels you _do_ wish to log as a performance optimization.
///
/// Both callbacks **must** be thread-safe. Arguments provided to the callback live only for as
/// long as the callback is executing and are not valid after the callback returns. The
/// callbacks must not retain references to the provided data.
///
/// Secrets provided to the `log_cb` are **extremely sensitive** and can break the security
/// of past, present and future sessions.
///
/// See also `rustls_client_config_builder_set_key_log_file` for a simpler way to log
/// to a file specified by the `SSLKEYLOGFILE` environment variable.
#[no_mangle]
pub extern "C" fn rustls_client_config_builder_set_key_log(
builder: *mut rustls_client_config_builder,
log_cb: rustls_keylog_log_callback,
will_log_cb: rustls_keylog_will_log_callback,
) -> rustls_result {
ffi_panic_boundary! {
let builder = try_mut_from_ptr!(builder);
let log_cb = match log_cb {
Some(cb) => cb,
None => return NullParameter,
};

builder.key_log = Some(Arc::new(CallbackKeyLog {
log_cb,
will_log_cb,
}));

rustls_result::Ok
}
}
}

/// Always send the same client certificate.
Expand Down Expand Up @@ -488,6 +557,10 @@ impl rustls_client_config_builder {
config.alpn_protocols = builder.alpn_protocols;
config.enable_sni = builder.enable_sni;

if let Some(key_log) = builder.key_log {
config.key_log = key_log;
}

set_arc_mut_ptr(config_out, config);
rustls_result::Ok
}
Expand Down
88 changes: 88 additions & 0 deletions src/keylog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Provides FFI abstractions for the [`rustls::KeyLog`] trait.

use std::ffi::c_int;
use std::fmt;

use crate::rslice::rustls_str;

/// An optional callback for logging key material.
///
/// See the documentation on `rustls_client_config_builder_set_key_log` and
/// `rustls_server_config_builder_set_key_log` for more information about the
/// lifetimes of the parameters.
pub type rustls_keylog_log_callback = Option<
unsafe extern "C" fn(
label: rustls_str,
client_random: *const u8,
client_random_len: usize,
secret: *const u8,
secret_len: usize,
),
>;

/// An optional callback for deciding if key material will be logged.
///
/// See the documentation on `rustls_client_config_builder_set_key_log` and
/// `rustls_server_config_builder_set_key_log` for more information about the
/// lifetimes of the parameters.
pub type rustls_keylog_will_log_callback = Option<unsafe extern "C" fn(label: rustls_str) -> c_int>;

/// A type alias for a keylog log callback that has been extracted from an option.
pub(crate) type KeylogLogCallback = unsafe extern "C" fn(
label: rustls_str,
client_random: *const u8,
client_random_len: usize,
secret: *const u8,
secret_len: usize,
);

/// An implementation of `rustls::KeyLog` based on C callbacks.
pub(crate) struct CallbackKeyLog {
// We use the crate-internal rust type here - it is _not_ Option wrapped.
pub(crate) log_cb: KeylogLogCallback,
// We use the pub type alias here - it is Option wrapped and may be None.
pub(crate) will_log_cb: rustls_keylog_will_log_callback,
}

impl rustls::KeyLog for CallbackKeyLog {
fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) {
unsafe {
(self.log_cb)(
// Safety: Rustls will never give us a label containing NULL.
rustls_str::try_from(label).unwrap(),
client_random.as_ptr(),
client_random.len(),
secret.as_ptr(),
secret.len(),
);
}
}

fn will_log(&self, label: &str) -> bool {
match self.will_log_cb {
Some(cb) => {
// Safety: Rustls will never give us a label containing NULL.
let label = rustls_str::try_from(label).unwrap();
// Log iff the cb returned non-zero.
!matches!(unsafe { (cb)(label) }, 0)
}
// Default to logging everything.
None => true,
}
}
}

impl fmt::Debug for CallbackKeyLog {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CallbackKeyLog").finish()
}
}

/// Safety: `CallbackKeyLog` is Send because we don't allocate or deallocate any of its
/// fields.
unsafe impl Send for CallbackKeyLog {}

/// Safety: Verifier is Sync if the C code passes us a callback that
/// obeys the concurrency safety requirements documented in
/// `rustls_client_config_builder_set_key_log` and `rustls_server_config_builder_set_key_log`.
unsafe impl Sync for CallbackKeyLog {}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod crypto_provider;
pub mod enums;
mod error;
pub mod io;
pub mod keylog;
pub mod log;
mod panic;
pub mod rslice;
Expand Down
106 changes: 106 additions & 0 deletions src/rustls.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,28 @@ typedef struct rustls_verify_server_cert_params {
typedef uint32_t (*rustls_verify_server_cert_callback)(rustls_verify_server_cert_user_data userdata,
const struct rustls_verify_server_cert_params *params);

/**
* An optional callback for logging key material.
*
* See the documentation on `rustls_client_config_builder_set_key_log` and
* `rustls_server_config_builder_set_key_log` for more information about the
* lifetimes of the parameters.
*/
typedef void (*rustls_keylog_log_callback)(struct rustls_str label,
const uint8_t *client_random,
size_t client_random_len,
const uint8_t *secret,
size_t secret_len);

/**
* An optional callback for deciding if key material will be logged.
*
* See the documentation on `rustls_client_config_builder_set_key_log` and
* `rustls_server_config_builder_set_key_log` for more information about the
* lifetimes of the parameters.
*/
typedef int (*rustls_keylog_will_log_callback)(struct rustls_str label);

typedef size_t rustls_log_level;

typedef struct rustls_log_params {
Expand Down Expand Up @@ -1613,6 +1635,48 @@ rustls_result rustls_client_config_builder_set_certified_key(struct rustls_clien
const struct rustls_certified_key *const *certified_keys,
size_t certified_keys_len);

/**
* Log key material to the file specified by the `SSLKEYLOGFILE` environment variable.
*
* The key material will be logged in the NSS key log format,
* <https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format> and is
* compatible with tools like Wireshark.
*
* Secrets logged in this manner are **extremely sensitive** and can break the security
* of past, present and future sessions.
*
* For more control over which secrets are logged, or to customize the format, prefer
* `rustls_client_config_builder_set_key_log`.
*/
rustls_result rustls_client_config_builder_set_key_log_file(struct rustls_client_config_builder *builder);

/**
* Provide callbacks to manage logging key material.
*
* The `log_cb` argument is mandatory and must not be `NULL` or a `NullParameter` error is
* returned. The `log_cb` will be invoked with a `client_random` to identify the relevant session,
* a `label` to identify the purpose of the `secret`, and the `secret` itself. See the
* Rustls documentation of the `KeyLog` trait for more information on possible labels:
* <https://docs.rs/rustls/latest/rustls/trait.KeyLog.html#tymethod.log>
*
* The `will_log_cb` may be `NULL`, in which case all key material will be provided to
* the `log_cb`. By providing a custom `will_log_cb` you may return `0` for labels you don't
* wish to log, and non-zero for labels you _do_ wish to log as a performance optimization.
*
* Both callbacks **must** be thread-safe. Arguments provided to the callback live only for as
* long as the callback is executing and are not valid after the callback returns. The
* callbacks must not retain references to the provided data.
*
* Secrets provided to the `log_cb` are **extremely sensitive** and can break the security
* of past, present and future sessions.
*
* See also `rustls_client_config_builder_set_key_log_file` for a simpler way to log
* to a file specified by the `SSLKEYLOGFILE` environment variable.
*/
rustls_result rustls_client_config_builder_set_key_log(struct rustls_client_config_builder *builder,
rustls_keylog_log_callback log_cb,
rustls_keylog_will_log_callback will_log_cb);

/**
* Turn a *rustls_client_config_builder (mutable) into a const *rustls_client_config
* (read-only).
Expand Down Expand Up @@ -2218,6 +2282,48 @@ rustls_result rustls_server_config_builder_new_custom(const struct rustls_crypto
void rustls_server_config_builder_set_client_verifier(struct rustls_server_config_builder *builder,
const struct rustls_client_cert_verifier *verifier);

/**
* Log key material to the file specified by the `SSLKEYLOGFILE` environment variable.
*
* The key material will be logged in the NSS key log format,
* <https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format> and is
* compatible with tools like Wireshark.
*
* Secrets logged in this manner are **extremely sensitive** and can break the security
* of past, present and future sessions.
*
* For more control over which secrets are logged, or to customize the format, prefer
* `rustls_server_config_builder_set_key_log`.
*/
rustls_result rustls_server_config_builder_set_key_log_file(struct rustls_server_config_builder *builder);

/**
* Provide callbacks to manage logging key material.
*
* The `log_cb` argument is mandatory and must not be `NULL` or a `NullParameter` error is
* returned. The `log_cb` will be invoked with a `client_random` to identify the relevant session,
* a `label` to identify the purpose of the `secret`, and the `secret` itself. See the
* Rustls documentation of the `KeyLog` trait for more information on possible labels:
* <https://docs.rs/rustls/latest/rustls/trait.KeyLog.html#tymethod.log>
*
* The `will_log_cb` may be `NULL`, in which case all key material will be provided to
* the `log_cb`. By providing a custom `will_log_cb` you may return `0` for labels you don't
* wish to log, and non-zero for labels you _do_ wish to log as a performance optimization.
*
* Both callbacks **must** be thread-safe. Arguments provided to the callback live only for as
* long as the callback is executing and are not valid after the callback returns. The
* callbacks must not retain references to the provided data.
*
* Secrets provided to the `log_cb` are **extremely sensitive** and can break the security
* of past, present and future sessions.
*
* See also `rustls_server_config_builder_set_key_log_file` for a simpler way to log
* to a file specified by the `SSLKEYLOGFILE` environment variable.
*/
rustls_result rustls_server_config_builder_set_key_log(struct rustls_server_config_builder *builder,
rustls_keylog_log_callback log_cb,
rustls_keylog_will_log_callback will_log_cb);

/**
* "Free" a server_config_builder without building it into a rustls_server_config.
*
Expand Down
Loading