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

feat(s2n-quic-transport): support exporting from TLS sessions #1984

Merged
merged 1 commit into from
Oct 11, 2023
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
2 changes: 1 addition & 1 deletion examples/resumption/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ edition = "2021"

[dependencies]
s2n-quic = { version = "1", path = "../../quic/s2n-quic", features = ["provider-tls-s2n", "unstable_resumption"]}
s2n-tls = { version = "=0.0.36", features = ["quic"] }
s2n-tls = { version = "=0.0.39", features = ["quic"] }
tokio = { version = "1", features = ["full"] }
4 changes: 2 additions & 2 deletions netbench/netbench-driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ netbench = { version = "0.1", path = "../netbench" }
probe = "0.5"
s2n-quic = { path = "../../quic/s2n-quic", features = ["provider-tls-s2n"] }
s2n-quic-core = { path = "../../quic/s2n-quic-core", features = ["testing"] }
s2n-tls = { version = "0.0.36" }
s2n-tls-tokio = { version = "0.0.36" }
s2n-tls = { version = "0.0.39" }
s2n-tls-tokio = { version = "0.0.39" }
structopt = "0.3"
tokio = { version = "1", features = ["io-util", "net", "time", "rt-multi-thread"] }
tokio-native-tls = "0.3"
Expand Down
28 changes: 28 additions & 0 deletions quic/s2n-quic-core/src/crypto/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,29 @@ pub struct ApplicationParameters<'a> {
pub transport_parameters: &'a [u8],
}

#[derive(Debug)]
#[non_exhaustive]
pub enum TlsExportError {
#[non_exhaustive]
Failure,
}

impl TlsExportError {
pub fn failure() -> Self {
TlsExportError::Failure
}
}

pub trait TlsSession: Send + Sync {
/// See <https://datatracker.ietf.org/doc/html/rfc5705> and <https://www.rfc-editor.org/rfc/rfc8446>.
fn tls_exporter(
&self,
label: &[u8],
context: &[u8],
output: &mut [u8],
) -> Result<(), TlsExportError>;
}

//= https://www.rfc-editor.org/rfc/rfc9000#section-4
//= type=TODO
//= tracking-issue=332
Expand Down Expand Up @@ -64,6 +87,11 @@ pub trait Context<Crypto: crate::crypto::CryptoSuite> {
//# peer's Finished message.
fn on_handshake_complete(&mut self) -> Result<(), crate::transport::Error>;

fn on_tls_exporter_ready(
&mut self,
session: &impl TlsSession,
) -> Result<(), crate::transport::Error>;

/// Receives data from the initial packet space
///
/// A `max_len` may be provided to indicate how many bytes the TLS implementation
Expand Down
7 changes: 7 additions & 0 deletions quic/s2n-quic-core/src/crypto/tls/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,13 @@ where
Ok(())
}

fn on_tls_exporter_ready(
&mut self,
_: &impl super::TlsSession,
) -> Result<(), crate::transport::Error> {
Ok(())
}

fn receive_initial(&mut self, max_len: Option<usize>) -> Option<Bytes> {
self.log("rx initial");
self.initial.rx(max_len)
Expand Down
34 changes: 34 additions & 0 deletions quic/s2n-quic-core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,37 @@ impl IntoEvent<Timestamp> for crate::time::Timestamp {
Timestamp(self)
}
}

#[derive(Clone)]
pub struct TlsSession<'a> {
session: &'a dyn crate::crypto::tls::TlsSession,
}

impl<'a> TlsSession<'a> {
#[doc(hidden)]
pub fn new(session: &'a dyn crate::crypto::tls::TlsSession) -> TlsSession<'a> {
TlsSession { session }
}

pub fn tls_exporter(
&self,
label: &[u8],
context: &[u8],
output: &mut [u8],
) -> Result<(), crate::crypto::tls::TlsExportError> {
self.session.tls_exporter(label, context, output)
}
}

impl<'a> crate::event::IntoEvent<TlsSession<'a>> for TlsSession<'a> {
#[inline]
fn into_event(self) -> Self {
self
}
}

impl core::fmt::Debug for TlsSession<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TlsSession").finish_non_exhaustive()
}
}
87 changes: 87 additions & 0 deletions quic/s2n-quic-core/src/event/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,14 @@ pub mod api {
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct TlsExporterReady<'a> {
pub session: crate::event::TlsSession<'a>,
}
impl<'a> Event for TlsExporterReady<'a> {
const NAME: &'static str = "connectivity:tls_exporter_ready";
}
#[derive(Clone, Debug)]
#[non_exhaustive]
#[doc = " Path challenge updated"]
pub struct PathChallengeUpdated<'a> {
pub path_challenge_status: PathChallengeStatus,
Expand Down Expand Up @@ -2048,6 +2056,17 @@ pub mod tracing {
tracing :: event ! (target : "handshake_status_updated" , parent : id , tracing :: Level :: DEBUG , status = tracing :: field :: debug (status));
}
#[inline]
fn on_tls_exporter_ready(
&mut self,
context: &mut Self::ConnectionContext,
_meta: &api::ConnectionMeta,
event: &api::TlsExporterReady,
) {
let id = context.id();
let api::TlsExporterReady { session } = event;
tracing :: event ! (target : "tls_exporter_ready" , parent : id , tracing :: Level :: DEBUG , session = tracing :: field :: debug (session));
}
#[inline]
fn on_path_challenge_updated(
&mut self,
context: &mut Self::ConnectionContext,
Expand Down Expand Up @@ -3985,6 +4004,19 @@ pub mod builder {
}
}
#[derive(Clone, Debug)]
pub struct TlsExporterReady<'a> {
pub session: crate::event::TlsSession<'a>,
}
impl<'a> IntoEvent<api::TlsExporterReady<'a>> for TlsExporterReady<'a> {
#[inline]
fn into_event(self) -> api::TlsExporterReady<'a> {
let TlsExporterReady { session } = self;
api::TlsExporterReady {
session: session.into_event(),
}
}
}
#[derive(Clone, Debug)]
#[doc = " Path challenge updated"]
pub struct PathChallengeUpdated<'a> {
pub path_challenge_status: PathChallengeStatus,
Expand Down Expand Up @@ -4985,6 +5017,18 @@ mod traits {
let _ = meta;
let _ = event;
}
#[doc = "Called when the `TlsExporterReady` event is triggered"]
#[inline]
fn on_tls_exporter_ready(
&mut self,
context: &mut Self::ConnectionContext,
meta: &ConnectionMeta,
event: &TlsExporterReady,
) {
let _ = context;
let _ = meta;
let _ = event;
}
#[doc = "Called when the `PathChallengeUpdated` event is triggered"]
#[inline]
fn on_path_challenge_updated(
Expand Down Expand Up @@ -5629,6 +5673,16 @@ mod traits {
(self.1).on_handshake_status_updated(&mut context.1, meta, event);
}
#[inline]
fn on_tls_exporter_ready(
&mut self,
context: &mut Self::ConnectionContext,
meta: &ConnectionMeta,
event: &TlsExporterReady,
) {
(self.0).on_tls_exporter_ready(&mut context.0, meta, event);
(self.1).on_tls_exporter_ready(&mut context.1, meta, event);
}
#[inline]
fn on_path_challenge_updated(
&mut self,
context: &mut Self::ConnectionContext,
Expand Down Expand Up @@ -6099,6 +6153,8 @@ mod traits {
fn on_connection_migration_denied(&mut self, event: builder::ConnectionMigrationDenied);
#[doc = "Publishes a `HandshakeStatusUpdated` event to the publisher's subscriber"]
fn on_handshake_status_updated(&mut self, event: builder::HandshakeStatusUpdated);
#[doc = "Publishes a `TlsExporterReady` event to the publisher's subscriber"]
fn on_tls_exporter_ready(&mut self, event: builder::TlsExporterReady);
#[doc = "Publishes a `PathChallengeUpdated` event to the publisher's subscriber"]
fn on_path_challenge_updated(&mut self, event: builder::PathChallengeUpdated);
#[doc = "Publishes a `TlsClientHello` event to the publisher's subscriber"]
Expand Down Expand Up @@ -6435,6 +6491,15 @@ mod traits {
self.subscriber.on_event(&self.meta, &event);
}
#[inline]
fn on_tls_exporter_ready(&mut self, event: builder::TlsExporterReady) {
let event = event.into_event();
self.subscriber
.on_tls_exporter_ready(self.context, &self.meta, &event);
self.subscriber
.on_connection_event(self.context, &self.meta, &event);
self.subscriber.on_event(&self.meta, &event);
}
#[inline]
fn on_path_challenge_updated(&mut self, event: builder::PathChallengeUpdated) {
let event = event.into_event();
self.subscriber
Expand Down Expand Up @@ -6580,6 +6645,7 @@ pub mod testing {
pub ecn_state_changed: u32,
pub connection_migration_denied: u32,
pub handshake_status_updated: u32,
pub tls_exporter_ready: u32,
pub path_challenge_updated: u32,
pub tls_client_hello: u32,
pub tls_server_hello: u32,
Expand Down Expand Up @@ -6659,6 +6725,7 @@ pub mod testing {
ecn_state_changed: 0,
connection_migration_denied: 0,
handshake_status_updated: 0,
tls_exporter_ready: 0,
path_challenge_updated: 0,
tls_client_hello: 0,
tls_server_hello: 0,
Expand Down Expand Up @@ -7026,6 +7093,17 @@ pub mod testing {
self.output.push(format!("{meta:?} {event:?}"));
}
}
fn on_tls_exporter_ready(
&mut self,
_context: &mut Self::ConnectionContext,
meta: &api::ConnectionMeta,
event: &api::TlsExporterReady,
) {
self.tls_exporter_ready += 1;
if self.location.is_some() {
self.output.push(format!("{meta:?} {event:?}"));
}
}
fn on_path_challenge_updated(
&mut self,
_context: &mut Self::ConnectionContext,
Expand Down Expand Up @@ -7278,6 +7356,7 @@ pub mod testing {
pub ecn_state_changed: u32,
pub connection_migration_denied: u32,
pub handshake_status_updated: u32,
pub tls_exporter_ready: u32,
pub path_challenge_updated: u32,
pub tls_client_hello: u32,
pub tls_server_hello: u32,
Expand Down Expand Up @@ -7347,6 +7426,7 @@ pub mod testing {
ecn_state_changed: 0,
connection_migration_denied: 0,
handshake_status_updated: 0,
tls_exporter_ready: 0,
path_challenge_updated: 0,
tls_client_hello: 0,
tls_server_hello: 0,
Expand Down Expand Up @@ -7671,6 +7751,13 @@ pub mod testing {
self.output.push(format!("{event:?}"));
}
}
fn on_tls_exporter_ready(&mut self, event: builder::TlsExporterReady) {
self.tls_exporter_ready += 1;
let event = event.into_event();
if self.location.is_some() {
self.output.push(format!("{event:?}"));
}
}
fn on_path_challenge_updated(&mut self, event: builder::PathChallengeUpdated) {
self.path_challenge_updated += 1;
let event = event.into_event();
Expand Down
5 changes: 5 additions & 0 deletions quic/s2n-quic-events/events/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ struct HandshakeStatusUpdated {
status: HandshakeStatus,
}

#[event("connectivity:tls_exporter_ready")]
struct TlsExporterReady<'a> {
session: crate::event::TlsSession<'a>,
}

#[event("connectivity:path_challenge_updated")]
/// Path challenge updated
struct PathChallengeUpdated<'a> {
Expand Down
18 changes: 18 additions & 0 deletions quic/s2n-quic-rustls/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ pub struct Session {
server_name: Option<ServerName>,
}

impl tls::TlsSession for Session {
fn tls_exporter(
&self,
label: &[u8],
context: &[u8],
output: &mut [u8],
) -> Result<(), tls::TlsExportError> {
match self
.connection
.export_keying_material(output, label, Some(context))
{
Ok(_) => Ok(()),
Err(_) => Err(tls::TlsExportError::failure()),
}
}
}

impl fmt::Debug for Session {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Session")
Expand Down Expand Up @@ -149,6 +166,7 @@ impl Session {
if !self.emitted_handshake_complete {
self.rx_phase.transition();
context.on_handshake_complete()?;
context.on_tls_exporter_ready(self)?;
}

self.emitted_handshake_complete = true;
Expand Down
2 changes: 1 addition & 1 deletion quic/s2n-quic-tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ libc = "0.2"
s2n-codec = { version = "=0.7.0", path = "../../common/s2n-codec", default-features = false }
s2n-quic-core = { version = "=0.28.0", path = "../s2n-quic-core", default-features = false, features = ["alloc"] }
s2n-quic-crypto = { version = "=0.29.0", path = "../s2n-quic-crypto", default-features = false }
s2n-tls = { version = "=0.0.36", features = ["quic"] }
s2n-tls = { version = "=0.0.39", features = ["quic"] }

[dev-dependencies]
checkers = "0.6"
Expand Down
14 changes: 14 additions & 0 deletions quic/s2n-quic-tls/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ impl CryptoSuite for Session {
type RetryKey = <Suite as CryptoSuite>::RetryKey;
}

impl tls::TlsSession for Session {
fn tls_exporter(
&self,
label: &[u8],
context: &[u8],
output: &mut [u8],
) -> Result<(), tls::TlsExportError> {
self.connection
.tls_exporter(label, context, output)
.map_err(|_| tls::TlsExportError::failure())
}
}

impl tls::Session for Session {
fn poll<W>(&mut self, context: &mut W) -> Poll<Result<(), transport::Error>>
where
Expand Down Expand Up @@ -109,6 +122,7 @@ impl tls::Session for Session {
if !self.handshake_complete {
self.state.on_handshake_complete();
context.on_handshake_complete()?;
context.on_tls_exporter_ready(self)?;
self.handshake_complete = true;
}
Poll::Ready(Ok(()))
Expand Down
11 changes: 11 additions & 0 deletions quic/s2n-quic-transport/src/space/session_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,17 @@ impl<'a, Config: endpoint::Config, Pub: event::ConnectionPublisher>
Ok(())
}

fn on_tls_exporter_ready(
&mut self,
session: &impl tls::TlsSession,
) -> Result<(), transport::Error> {
self.publisher
.on_tls_exporter_ready(event::builder::TlsExporterReady {
session: s2n_quic_core::event::TlsSession::new(session),
});
Ok(())
}

fn on_handshake_complete(&mut self) -> Result<(), transport::Error> {
// After the handshake is complete, the handshake crypto stream should be completely
// finished
Expand Down
1 change: 1 addition & 0 deletions quic/s2n-quic/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod client_handshake_confirm;
#[cfg(not(target_os = "windows"))]
mod mtls;

mod exporter;
mod issue_1361;
mod issue_1427;
mod issue_1464;
Expand Down
Loading
Loading