Skip to content

Commit

Permalink
Support exporting from TLS sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark-Simulacrum committed Oct 4, 2023
1 parent ea3e878 commit 72220ca
Show file tree
Hide file tree
Showing 14 changed files with 221 additions and 46 deletions.
34 changes: 33 additions & 1 deletion quic/s2n-quic-core/src/crypto/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@ pub struct ApplicationParameters<'a> {
pub transport_parameters: &'a [u8],
}

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

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

pub fn unsupported() -> Self {
TlsExportError::Unsupported
}
}

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 @@ -62,7 +91,10 @@ pub trait Context<Crypto: crate::crypto::CryptoSuite> {
//# TLS stack has reported that the handshake is complete. This happens
//# when the TLS stack has both sent a Finished message and verified the
//# peer's Finished message.
fn on_handshake_complete(&mut self) -> Result<(), crate::transport::Error>;
fn on_handshake_complete(
&mut self,
raw: &impl TlsSession,
) -> Result<(), crate::transport::Error>;

/// Receives data from the initial packet space
///
Expand Down
39 changes: 37 additions & 2 deletions quic/s2n-quic-core/src/crypto/tls/null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ impl Default for Endpoint {
}
}

pub struct DummyTlsSession;

impl crate::crypto::tls::TlsSession for DummyTlsSession {
fn tls_exporter(
&self,
_: &[u8],
_: &[u8],
_: &mut [u8],
) -> Result<(), crate::crypto::tls::TlsExportError> {
Err(crate::crypto::tls::TlsExportError::unsupported())
}
}

impl crypto::tls::Endpoint for Endpoint {
type Session = Session;

Expand Down Expand Up @@ -170,6 +183,17 @@ pub mod client {
Complete,
}

impl tls::TlsSession for Session {
fn tls_exporter(
&self,
_label: &[u8],
_context: &[u8],
_output: &mut [u8],
) -> Result<(), tls::TlsExportError> {
Err(tls::TlsExportError::Unsupported)
}
}

impl Session {
#[inline]
pub fn poll<C: tls::Context<super::Session>>(
Expand Down Expand Up @@ -215,7 +239,7 @@ pub mod client {
},
)?;

context.on_handshake_complete()?;
context.on_handshake_complete(&*self)?;

*self = Self::Complete;

Expand All @@ -238,6 +262,17 @@ pub mod server {
Complete,
}

impl tls::TlsSession for Session {
fn tls_exporter(
&self,
_label: &[u8],
_context: &[u8],
_output: &mut [u8],
) -> Result<(), tls::TlsExportError> {
Err(tls::TlsExportError::Unsupported)
}
}

impl Session {
#[inline]
pub fn poll<C: tls::Context<super::Session>>(
Expand Down Expand Up @@ -278,7 +313,7 @@ pub mod server {
}

*self = Self::Complete;
context.on_handshake_complete()?;
context.on_handshake_complete(&*self)?;

return Ok(()).into();
}
Expand Down
4 changes: 3 additions & 1 deletion quic/s2n-quic-core/src/crypto/tls/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use futures_test::task::new_count_waker;
use s2n_codec::{DecoderBuffer, DecoderValue, Encoder as _, EncoderBuffer, EncoderValue};
use std::{collections::VecDeque, fmt::Debug};

use super::TlsSession;

pub mod certificates {
macro_rules! pem {
($name:ident, $path:expr) => {
Expand Down Expand Up @@ -702,7 +704,7 @@ where
Ok(())
}

fn on_handshake_complete(&mut self) -> Result<(), transport::Error> {
fn on_handshake_complete(&mut self, _: &impl TlsSession) -> Result<(), transport::Error> {
assert!(
!self.handshake_complete,
"handshake complete called multiple times"
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()
}
}
34 changes: 20 additions & 14 deletions quic/s2n-quic-core/src/event/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,10 +462,12 @@ pub mod api {
#[derive(Clone, Debug)]
#[non_exhaustive]
#[doc = " Events tracking the progress of handshake status"]
pub enum HandshakeStatus {
pub enum HandshakeStatus<'a> {
#[non_exhaustive]
#[doc = " The handshake has completed."]
Complete {},
Complete {
session: crate::event::TlsSession<'a>,
},
#[non_exhaustive]
#[doc = " The handshake has been confirmed."]
Confirmed {},
Expand Down Expand Up @@ -876,10 +878,10 @@ pub mod api {
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct HandshakeStatusUpdated {
pub status: HandshakeStatus,
pub struct HandshakeStatusUpdated<'a> {
pub status: HandshakeStatus<'a>,
}
impl Event for HandshakeStatusUpdated {
impl<'a> Event for HandshakeStatusUpdated<'a> {
const NAME: &'static str = "connectivity:handshake_status_updated";
}
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -3203,9 +3205,11 @@ pub mod builder {
}
#[derive(Clone, Debug)]
#[doc = " Events tracking the progress of handshake status"]
pub enum HandshakeStatus {
pub enum HandshakeStatus<'a> {
#[doc = " The handshake has completed."]
Complete,
Complete {
session: crate::event::TlsSession<'a>,
},
#[doc = " The handshake has been confirmed."]
Confirmed,
#[doc = " A HANDSHAKE_DONE frame was delivered or received."]
Expand All @@ -3220,12 +3224,14 @@ pub mod builder {
#[doc = " HANDSHAKE_DONE frame until it is acked by the peer."]
HandshakeDoneLost,
}
impl IntoEvent<api::HandshakeStatus> for HandshakeStatus {
impl<'a> IntoEvent<api::HandshakeStatus<'a>> for HandshakeStatus<'a> {
#[inline]
fn into_event(self) -> api::HandshakeStatus {
fn into_event(self) -> api::HandshakeStatus<'a> {
use api::HandshakeStatus::*;
match self {
Self::Complete => Complete {},
Self::Complete { session } => Complete {
session: session.into_event(),
},
Self::Confirmed => Confirmed {},
Self::HandshakeDoneAcked => HandshakeDoneAcked {},
Self::HandshakeDoneLost => HandshakeDoneLost {},
Expand Down Expand Up @@ -3897,12 +3903,12 @@ pub mod builder {
}
}
#[derive(Clone, Debug)]
pub struct HandshakeStatusUpdated {
pub status: HandshakeStatus,
pub struct HandshakeStatusUpdated<'a> {
pub status: HandshakeStatus<'a>,
}
impl IntoEvent<api::HandshakeStatusUpdated> for HandshakeStatusUpdated {
impl<'a> IntoEvent<api::HandshakeStatusUpdated<'a>> for HandshakeStatusUpdated<'a> {
#[inline]
fn into_event(self) -> api::HandshakeStatusUpdated {
fn into_event(self) -> api::HandshakeStatusUpdated<'a> {
let HandshakeStatusUpdated { status } = self;
api::HandshakeStatusUpdated {
status: status.into_event(),
Expand Down
6 changes: 4 additions & 2 deletions quic/s2n-quic-events/events/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,9 +826,11 @@ enum EcnState {
}

/// Events tracking the progress of handshake status
enum HandshakeStatus {
enum HandshakeStatus<'a> {
/// The handshake has completed.
Complete,
Complete {
session: crate::event::TlsSession<'a>,
},
/// The handshake has been confirmed.
Confirmed,
/// A HANDSHAKE_DONE frame was delivered or received.
Expand Down
4 changes: 2 additions & 2 deletions quic/s2n-quic-events/events/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ struct ConnectionMigrationDenied {
}

#[event("connectivity:handshake_status_updated")]
struct HandshakeStatusUpdated {
status: HandshakeStatus,
struct HandshakeStatusUpdated<'a> {
status: HandshakeStatus<'a>,
}

#[event("connectivity:path_challenge_updated")]
Expand Down
19 changes: 18 additions & 1 deletion 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 @@ -148,7 +165,7 @@ impl Session {
// the handshake is complete!
if !self.emitted_handshake_complete {
self.rx_phase.transition();
context.on_handshake_complete()?;
context.on_handshake_complete(&*self)?;
}

self.emitted_handshake_complete = true;
Expand Down
13 changes: 12 additions & 1 deletion quic/s2n-quic-tls/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ 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> {
Err(tls::TlsExportError::unsupported())
}
}

impl tls::Session for Session {
fn poll<W>(&mut self, context: &mut W) -> Poll<Result<(), transport::Error>>
where
Expand Down Expand Up @@ -108,7 +119,7 @@ impl tls::Session for Session {
// s2n-tls has indicated that the handshake is complete
if !self.handshake_complete {
self.state.on_handshake_complete();
context.on_handshake_complete()?;
context.on_handshake_complete(&*self)?;
self.handshake_complete = true;
}
Poll::Ready(Ok(()))
Expand Down
13 changes: 9 additions & 4 deletions quic/s2n-quic-transport/src/space/handshake_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::{contexts::WriteContext, endpoint, sync::flag, transmission};
use s2n_quic_core::{
ack,
event::{self, ConnectionPublisher},
event::{self, ConnectionPublisher, TlsSession},
frame::HandshakeDone,
packet::number::PacketNumber,
};
Expand Down Expand Up @@ -102,13 +102,16 @@ impl HandshakeStatus {
&mut self,
endpoint_type: endpoint::Type,
publisher: &mut Pub,
session: &impl s2n_quic_core::crypto::tls::TlsSession,
) {
debug_assert!(
matches!(self, Self::InProgress),
"on_handshake_complete should only be called once."
);
publisher.on_handshake_status_updated(event::builder::HandshakeStatusUpdated {
status: event::builder::HandshakeStatus::Complete,
status: event::builder::HandshakeStatus::Complete {
session: TlsSession::new(session),
},
});

if endpoint_type.is_server() {
Expand Down Expand Up @@ -243,7 +246,8 @@ mod tests {
//= type=test
//# the TLS handshake is considered confirmed at the
//# server when the handshake completes.
status.on_handshake_complete(endpoint::Type::Server, &mut publisher);
let session = s2n_quic_core::crypto::tls::null::DummyTlsSession;
status.on_handshake_complete(endpoint::Type::Server, &mut publisher, &session);
assert!(status.is_confirmed());
assert!(status.is_complete());

Expand Down Expand Up @@ -309,7 +313,8 @@ mod tests {
assert!(!status.is_complete());
assert!(!status.is_confirmed());

status.on_handshake_complete(endpoint::Type::Client, &mut publisher);
let session = s2n_quic_core::crypto::tls::null::DummyTlsSession;
status.on_handshake_complete(endpoint::Type::Client, &mut publisher, &session);
assert!(status.is_complete());

assert!(
Expand Down
Loading

0 comments on commit 72220ca

Please sign in to comment.