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

TLS handshake data and keying material #160

Merged
merged 3 commits into from
Apr 9, 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
53 changes: 53 additions & 0 deletions wtransport/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,15 @@ use crate::datagram::Datagram;
use crate::driver::utils::varint_w2q;
use crate::driver::Driver;
use crate::error::ConnectionError;
use crate::error::ExportKeyingMaterialError;
use crate::error::SendDatagramError;
use crate::stream::OpeningBiStream;
use crate::stream::OpeningUniStream;
use crate::stream::RecvStream;
use crate::stream::SendStream;
use crate::tls::Certificate;
use crate::tls::CertificateChain;
use crate::tls::HandshakeData;
use crate::SessionId;
use crate::VarInt;
use std::net::SocketAddr;
Expand Down Expand Up @@ -350,4 +354,53 @@ impl Connection {
pub fn rtt(&self) -> Duration {
self.quic_connection.rtt()
}

/// Derive keying material from this connection's TLS session secrets.
///
/// When both peers call this method with the same `label` and `context`
/// arguments and `output` buffers of equal length, they will get the
/// same sequence of bytes in `output`. These bytes are cryptographically
/// strong and pseudorandom, and are suitable for use as keying material.
///
/// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
pub fn export_keying_material(
&self,
output: &mut [u8],
label: &[u8],
context: &[u8],
) -> Result<(), ExportKeyingMaterialError> {
self.quic_connection
.export_keying_material(output, label, context)
.map_err(|_: quinn::crypto::ExportKeyingMaterialError| ExportKeyingMaterialError)
}

/// Returns the peer's identity as a certificate chain if available.
///
/// This function returns an `Option` containing a [`CertificateChain`]. If the peer's identity
/// is available, it is converted into a `CertificateChain` and returned. If the peer's identity
/// is not available, `None` is returned.
pub fn peer_identity(&self) -> Option<CertificateChain> {
self.quic_connection.peer_identity().map(|any| {
any.downcast::<Vec<rustls::Certificate>>()
.expect("rustls certificate vector")
.into_iter()
.map(|c| Certificate::from_der(c.0).expect("valid certificate"))
.collect()
})
}

/// Retrieves handshake data associated with the connection.
pub fn handshake_data(&self) -> HandshakeData {
let hd = self
.quic_connection
.handshake_data()
.expect("fully established connection")
.downcast::<quinn::crypto::rustls::HandshakeData>()
.expect("valid downcast");

HandshakeData {
alpn: hd.protocol,
server_name: hd.server_name,
}
}
}
7 changes: 7 additions & 0 deletions wtransport/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,10 @@ impl Display for QuicProtoError {
f.write_fmt(format_args!("{}{}", self.reason, code))
}
}

/// Error returned by [`Connection::export_keying_material`](crate::Connection::export_keying_material).
///
/// This error occurs if the requested output length is too large.
#[derive(Debug, thiserror::Error)]
#[error("cannot derive keying material as requested output length is too large")]
pub struct ExportKeyingMaterialError;
25 changes: 25 additions & 0 deletions wtransport/src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ impl AsRef<[Certificate]> for CertificateChain {
}
}

impl FromIterator<Certificate> for CertificateChain {
fn from_iter<T: IntoIterator<Item = Certificate>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}

/// Represents an TLS identity consisting of a certificate chain and a private key.
#[derive(Debug)]
pub struct Identity {
Expand Down Expand Up @@ -410,6 +416,25 @@ impl Display for Sha256Digest {
}
}

/// Represents data related to a TLS handshake process.
#[derive(Clone, Debug)]
pub struct HandshakeData {
pub(crate) alpn: Option<Vec<u8>>,
pub(crate) server_name: Option<String>,
}

impl HandshakeData {
/// Application-Layer Protocol Negotiation (ALPN) data.
pub fn alpn(&self) -> Option<&[u8]> {
self.alpn.as_deref()
}

/// The server name associated with the handshake data.
pub fn server_name(&self) -> Option<&str> {
self.server_name.as_deref()
}
}

impl FromStr for Sha256Digest {
type Err = InvalidDigest;

Expand Down
Loading