summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);