summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel McCarney <daniel@binaryparadox.net>2024-09-12 10:43:41 -0400
committerDaniel McCarney <daniel@binaryparadox.net>2024-09-26 09:47:03 -0400
commitc73b2e1333ed002ebda283bc20c203ca70356b71 (patch)
tree374548c7d6367249b5fc7166f045825edd976a52
parente5a7037cf9a43b9228c1353b87a6c78bb15e8d6d (diff)
client/server: support for KeyLog trait, SSLKEYLOGFILE
For debugging purposes it's quite helpful to be able to log session secrets to a file specified by the `SSLKEYLOGFILE`, for example to use with Wireshark to decrypt session traffic. This commit adds two methods to rustls-ffi for both client and server configurations to facilitate this: 1. `rustls_server_config_builder_set_key_log_file()` and `rustls_client_config_builder_set_key_log_file()` enable using the Rustls `KeyLogFile` implementation of the `KeyLog` trait. This option simply honours the `SSLKEYLOGFILE` env var and spits out a NSS formatted key log file appropriate for use with Wireshark and other tools that support this format. 2. `rustls_server_config_builder_set_key_log()` and `rustls_client_config_builder_set_key_log()` enable providing C callbacks that will be invoked to decide which secrets are logged, and to do the logging. This allows for fine-grained control over how secrets are logged and may be more appropriate for applications that already handle this task for other TLS backends (e.g. curl). The client and server examples are updated to optionally use these new features. If the `SSLKEYLOG` env. var is set, both will use the `_set_key_log_file()` fns to set up the standard file based key logging. If the `STDERRKEYLOG` env var is set then both will use the `_set_key_log()` fns to set up custom callbacks that will print the hex-encoded secret data to stderr as a simple demonstration.
-rw-r--r--src/client.rs77
-rw-r--r--src/keylog.rs88
-rw-r--r--src/lib.rs1
-rw-r--r--src/rustls.h106
-rw-r--r--src/server.rs75
-rw-r--r--tests/client.c16
-rw-r--r--tests/client_server.rs35
-rw-r--r--tests/common.c55
-rw-r--r--tests/common.h4
-rw-r--r--tests/server.c16
10 files changed, 470 insertions, 3 deletions
diff --git a/src/client.rs b/src/client.rs
index a167905..363354b 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -9,8 +9,8 @@ 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};
@@ -18,6 +18,7 @@ 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::{
@@ -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! {
@@ -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)
}
@@ -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);
@@ -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.
@@ -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
}
diff --git a/src/keylog.rs b/src/keylog.rs
new file mode 100644
index 0000000..a2b2c21
--- /dev/null
+++ b/src/keylog.rs
@@ -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 {}
diff --git a/src/lib.rs b/src/lib.rs
index be565a7..044f3c4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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;
diff --git a/src/rustls.h b/src/rustls.h
index 47f093d..e4f9be1 100644
--- a/src/rustls.h
+++ b/src/rustls.h
@@ -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 {
@@ -1614,6 +1636,48 @@ rustls_result rustls_client_config_builder_set_certified_key(struct rustls_clien
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).
*/
@@ -2219,6 +2283,48 @@ void rustls_server_config_builder_set_client_verifier(struct rustls_server_confi
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.
*
* Normally builders are built into rustls_server_configs via `rustls_server_config_builder_build`
diff --git a/src/server.rs b/src/server.rs
index 0e94ac6..a2b97d8 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -11,13 +11,14 @@ use rustls::server::{
WebPkiClientVerifier,
};
use rustls::sign::CertifiedKey;
-use rustls::{ProtocolVersion, SignatureScheme, SupportedProtocolVersion};
+use rustls::{KeyLog, KeyLogFile, ProtocolVersion, SignatureScheme, SupportedProtocolVersion};
use crate::cipher::{rustls_certified_key, rustls_client_cert_verifier};
use crate::connection::{rustls_connection, Connection};
use crate::crypto_provider::rustls_crypto_provider;
use crate::error::rustls_result::NullParameter;
use crate::error::{map_error, rustls_result};
+use crate::keylog::{rustls_keylog_log_callback, rustls_keylog_will_log_callback, CallbackKeyLog};
use crate::rslice::{rustls_slice_bytes, rustls_slice_slice_bytes, rustls_slice_u16, rustls_str};
use crate::session::{
rustls_session_store_get_callback, rustls_session_store_put_callback, SessionStoreBroker,
@@ -53,6 +54,7 @@ pub(crate) struct ServerConfigBuilder {
session_storage: Option<Arc<dyn StoresServerSessions + Send + Sync>>,
alpn_protocols: Vec<Vec<u8>>,
ignore_client_order: Option<bool>,
+ key_log: Option<Arc<dyn KeyLog>>,
}
arc_castable! {
@@ -85,6 +87,7 @@ impl rustls_server_config_builder {
session_storage: None,
alpn_protocols: vec![],
ignore_client_order: None,
+ key_log: None,
};
to_boxed_mut_ptr(builder)
}
@@ -139,6 +142,7 @@ impl rustls_server_config_builder {
session_storage: None,
alpn_protocols: vec![],
ignore_client_order: None,
+ key_log: None,
};
set_boxed_mut_ptr(builder_out, builder);
rustls_result::Ok
@@ -161,6 +165,71 @@ impl rustls_server_config_builder {
}
}
+ /// 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`.
+ #[no_mangle]
+ pub extern "C" fn rustls_server_config_builder_set_key_log_file(
+ builder: *mut rustls_server_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_server_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_server_config_builder_set_key_log(
+ builder: *mut rustls_server_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
+ }
+ }
+
/// "Free" a server_config_builder without building it into a rustls_server_config.
///
/// Normally builders are built into rustls_server_configs via `rustls_server_config_builder_build`
@@ -297,6 +366,10 @@ impl rustls_server_config_builder {
config.ignore_client_order = ignore_client_order;
}
+ if let Some(key_log) = builder.key_log {
+ config.key_log = key_log;
+ }
+
set_arc_mut_ptr(config_out, config);
rustls_result::Ok
}
diff --git a/tests/client.c b/tests/client.c
index 65ee621..a147023 100644
--- a/tests/client.c
+++ b/tests/client.c
@@ -506,6 +506,22 @@ main(int argc, const char **argv)
goto cleanup;
}
+ if(getenv("SSLKEYLOGFILE")) {
+ result = rustls_client_config_builder_set_key_log_file(config_builder);
+ if(result != RUSTLS_RESULT_OK) {
+ print_error("enabling keylog", result);
+ goto cleanup;
+ }
+ }
+ else if(getenv("STDERRKEYLOG")) {
+ result = rustls_client_config_builder_set_key_log(
+ config_builder, stderr_key_log_cb, NULL);
+ if(result != RUSTLS_RESULT_OK) {
+ print_error("enabling keylog", result);
+ goto cleanup;
+ }
+ }
+
char *auth_cert = getenv("AUTH_CERT");
char *auth_key = getenv("AUTH_KEY");
if((auth_cert && !auth_key) || (!auth_cert && auth_key)) {
diff --git a/tests/client_server.rs b/tests/client_server.rs
index d0820c2..f794240 100644
--- a/tests/client_server.rs
+++ b/tests/client_server.rs
@@ -33,6 +33,24 @@ fn client_server_integration() {
client_tests: standard_client_tests(valgrind.clone()),
};
+ let keylogfile_server = TestCase {
+ name: "SSLKEYLOG server",
+ server_opts: ServerOptions {
+ valgrind: valgrind.clone(),
+ env: vec![("SSLKEYLOGFILE", "/tmp/rustls-ffi.server.key")],
+ },
+ client_tests: standard_client_tests(valgrind.clone()),
+ };
+
+ let stderrkeylog_server = TestCase {
+ name: "STDERRKEYLOG server",
+ server_opts: ServerOptions {
+ valgrind: valgrind.clone(),
+ env: vec![("STDERRKEYLOG", "1")],
+ },
+ client_tests: standard_client_tests(valgrind.clone()),
+ };
+
let mandatory_client_auth_server = TestCase {
name: "Mandatory client auth tests",
server_opts: ServerOptions {
@@ -123,6 +141,8 @@ fn client_server_integration() {
TestCases(vec![
standard_server,
vectored_server,
+ keylogfile_server,
+ stderrkeylog_server,
mandatory_client_auth_server,
mandatory_client_auth_server_with_crls,
custom_ciphersuites,
@@ -166,6 +186,21 @@ fn standard_client_tests(valgrind: Option<String>) -> Vec<ClientTest> {
],
expect_error: false,
},
+ ClientTest {
+ name: "SSLKEYLOGFILE",
+ valgrind: valgrind.clone(),
+ env: vec![
+ ("CA_FILE", "testdata/minica.pem"),
+ ("SSLKEYLOGFILE", "/tmp/rustls-ffi.client.key"),
+ ],
+ expect_error: false,
+ },
+ ClientTest {
+ name: "STDERRKEYLOG",
+ valgrind: valgrind.clone(),
+ env: vec![("CA_FILE", "testdata/minica.pem"), ("STDERRKEYLOG", "1")],
+ expect_error: false,
+ },
]
}
diff --git a/tests/common.c b/tests/common.c
index 55d2084..b03df1a 100644
--- a/tests/common.c
+++ b/tests/common.c
@@ -443,6 +443,61 @@ cleanup:
return custom_provider;
}
+// hex encode the given data buffer, returning a new NULL terminated buffer
+// with the result, or NULL if memory allocation fails.
+//
+// Caller owns the returned buffer and must free it.
+static char *
+hex_encode(const unsigned char *data, size_t len)
+{
+ // Two output chars per input char, plus the NULL terminator.
+ char *hex_str = (char *)malloc((len * 2) + 1);
+ if(!hex_str) {
+ return NULL;
+ }
+
+ for(size_t i = 0; i < len; i++) {
+ snprintf(hex_str + (i * 2), 3, "%02x", data[i]);
+ }
+
+ hex_str[len * 2] = '\0';
+ return hex_str;
+}
+
+void
+stderr_key_log_cb(rustls_str label, const unsigned char *client_random,
+ size_t client_random_len, const unsigned char *secret,
+ size_t secret_len)
+{
+ char *client_random_str = NULL;
+ char *secret_str = NULL;
+
+ client_random_str = hex_encode(client_random, client_random_len);
+ if(client_random_str == NULL) {
+ goto cleanup;
+ }
+
+ secret_str = hex_encode(secret, secret_len);
+ if(secret_str == NULL) {
+ goto cleanup;
+ }
+
+ fprintf(stderr,
+ "SSLKEYLOG: label=%.*s client_random=%s secret=%s\n",
+ (int)label.len,
+ label.data,
+ client_random_str,
+ secret_str);
+
+cleanup:
+ if(client_random_str != NULL) {
+ free(client_random_str);
+ }
+ if(secret_str != NULL) {
+ free(secret_str);
+ }
+}
+
// TLS 1.2 and TLS 1.3, matching Rustls default.
const uint16_t default_tls_versions[] = { 0x0303, 0x0304 };
diff --git a/tests/common.h b/tests/common.h
index 1980c05..fd23490 100644
--- a/tests/common.h
+++ b/tests/common.h
@@ -136,6 +136,10 @@ const struct rustls_certified_key *load_cert_and_key(const char *certfile,
const struct rustls_crypto_provider *default_provider_with_custom_ciphersuite(
const char *custom_ciphersuite_name);
+void stderr_key_log_cb(rustls_str label, const unsigned char *client_random,
+ size_t client_random_len, const unsigned char *secret,
+ size_t secret_len);
+
extern const uint16_t default_tls_versions[];
extern const size_t default_tls_versions_len;
diff --git a/tests/server.c b/tests/server.c
index b353272..3c6846f 100644
--- a/tests/server.c
+++ b/tests/server.c
@@ -360,6 +360,22 @@ main(int argc, const char **argv)
client_cert_verifier);
}
+ if(getenv("SSLKEYLOGFILE")) {
+ result = rustls_server_config_builder_set_key_log_file(config_builder);
+ if(result != RUSTLS_RESULT_OK) {
+ print_error("enabling keylog", result);
+ goto cleanup;
+ }
+ }
+ else if(getenv("STDERRKEYLOG")) {
+ result = rustls_server_config_builder_set_key_log(
+ config_builder, stderr_key_log_cb, NULL);
+ if(result != RUSTLS_RESULT_OK) {
+ print_error("enabling keylog", result);
+ goto cleanup;
+ }
+ }
+
result = rustls_server_config_builder_build(config_builder, &server_config);
if(result != RUSTLS_RESULT_OK) {
print_error("building server config", result);