use std::sync::Arc;
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
use rustls::quic::{ClientConnection, Connection, KeyChange, ServerConnection, Version};
use rustls::{
CertificateError, ClientConfig, DigitallySignedStruct, Error as RustlsError, RootCertStore,
ServerConfig, SignatureScheme,
};
use super::tls::{
PacketProtectionRequest, PacketProtectionSpace, ProtectedPacket, ProtectionProof,
QuicHandshakeTranscript, QuicPacketProtectionProvider, QuicTlsError, RustlsQuicCryptoProvider,
RustlsQuicProviderSide, TranscriptHash,
};
use crate::bytes::{Bytes, BytesMut};
use crate::cx::Cx;
use crate::net::atp::protocol::quic_frames::QuicFrame;
use crate::net::atp::protocol::varint::VarInt;
use crate::net::quic_core::{ConnectionId, LongHeader, LongPacketType, PacketHeader};
use crate::net::quic_native::endpoint::{OutgoingPacket, QuicUdpEndpoint};
use std::net::SocketAddr;
use std::time::Duration;
const HANDSHAKE_RECV_TIMEOUT: Duration = Duration::from_secs(10);
const HANDSHAKE_MAX_FLIGHTS: usize = 64;
const QUIC_AEAD_TAG_LEN: usize = 16;
const HANDSHAKE_PACKET_NUMBER_LEN: u8 = 4;
pub const ATP_QUIC_ALPN: &[u8] = b"atpq/1";
fn handshake_failure(code: &'static str) -> QuicTlsError {
QuicTlsError::CryptoProviderFailure {
provider: "rustls-quic-handshake",
code,
}
}
fn invalid_certificate(error: CertificateError) -> RustlsError {
RustlsError::InvalidCertificate(error)
}
fn is_unknown_issuer(error: &RustlsError) -> bool {
matches!(
error,
RustlsError::InvalidCertificate(CertificateError::UnknownIssuer)
)
}
fn san_matches_server_name(
san: &x509_parser::extensions::SubjectAlternativeName<'_>,
server_name: &ServerName<'_>,
) -> bool {
san.general_names
.iter()
.any(|name| match (name, server_name) {
(
x509_parser::extensions::GeneralName::DNSName(presented),
ServerName::DnsName(expected),
) => presented.eq_ignore_ascii_case(expected.as_ref()),
(
x509_parser::extensions::GeneralName::IPAddress(presented),
ServerName::IpAddress(expected),
) => {
let expected: std::net::IpAddr = (*expected).into();
match expected {
std::net::IpAddr::V4(addr) => *presented == addr.octets().as_slice(),
std::net::IpAddr::V6(addr) => *presented == addr.octets().as_slice(),
}
}
_ => false,
})
}
fn verify_pinned_end_entity_shape(
end_entity: &CertificateDer<'_>,
server_name: &ServerName<'_>,
now: UnixTime,
) -> Result<(), RustlsError> {
let (_, parsed) = x509_parser::parse_x509_certificate(end_entity.as_ref())
.map_err(|_| invalid_certificate(CertificateError::BadEncoding))?;
let now = i64::try_from(now.as_secs())
.map_err(|_| invalid_certificate(CertificateError::BadEncoding))?;
let validity = parsed.validity();
if now < validity.not_before.timestamp() {
return Err(invalid_certificate(CertificateError::NotValidYet));
}
if now > validity.not_after.timestamp() {
return Err(invalid_certificate(CertificateError::Expired));
}
match parsed
.extended_key_usage()
.map_err(|_| invalid_certificate(CertificateError::BadEncoding))?
{
Some(usage) if usage.value.server_auth || usage.value.any => {}
_ => return Err(invalid_certificate(CertificateError::InvalidPurpose)),
}
if parsed
.key_usage()
.map_err(|_| invalid_certificate(CertificateError::BadEncoding))?
.is_some_and(|usage| !usage.value.digital_signature())
{
return Err(invalid_certificate(CertificateError::InvalidPurpose));
}
let san = parsed
.subject_alternative_name()
.map_err(|_| invalid_certificate(CertificateError::BadEncoding))?
.ok_or_else(|| invalid_certificate(CertificateError::NotValidForName))?;
if !san_matches_server_name(san.value, server_name) {
return Err(invalid_certificate(CertificateError::NotValidForName));
}
Ok(())
}
#[derive(Debug)]
struct WebPkiOrPinnedEndEntityVerifier {
webpki: Arc<rustls::client::WebPkiServerVerifier>,
pinned_end_entities: Vec<CertificateDer<'static>>,
}
impl ServerCertVerifier for WebPkiOrPinnedEndEntityVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &ServerName<'_>,
ocsp_response: &[u8],
now: UnixTime,
) -> Result<ServerCertVerified, RustlsError> {
match self.webpki.verify_server_cert(
end_entity,
intermediates,
server_name,
ocsp_response,
now,
) {
Ok(verified) => Ok(verified),
Err(error)
if is_unknown_issuer(&error)
&& self
.pinned_end_entities
.iter()
.any(|pinned| pinned.as_ref() == end_entity.as_ref()) =>
{
verify_pinned_end_entity_shape(end_entity, server_name, now)?;
Ok(ServerCertVerified::assertion())
}
Err(error) => Err(error),
}
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, RustlsError> {
self.webpki.verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, RustlsError> {
self.webpki.verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.webpki.supported_verify_schemes()
}
fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
self.webpki.root_hint_subjects()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HandshakeLevel {
Initial,
Handshake,
OneRtt,
}
#[derive(Debug, Clone)]
pub struct HandshakeSegment {
pub level: HandshakeLevel,
pub data: Vec<u8>,
}
pub struct QuicHandshakeDriver {
tls: Connection,
provider: RustlsQuicCryptoProvider,
transcript: QuicHandshakeTranscript,
write_level: HandshakeLevel,
handshake_keys_installed: bool,
one_rtt_keys_installed: bool,
crypto_send_offset: [u64; 3],
}
fn level_index(level: HandshakeLevel) -> usize {
match level {
HandshakeLevel::Initial => 0,
HandshakeLevel::Handshake => 1,
HandshakeLevel::OneRtt => 2,
}
}
fn level_protection_space(level: HandshakeLevel) -> PacketProtectionSpace {
match level {
HandshakeLevel::Initial => PacketProtectionSpace::Initial,
HandshakeLevel::Handshake => PacketProtectionSpace::Handshake,
HandshakeLevel::OneRtt => PacketProtectionSpace::OneRtt,
}
}
fn long_packet_type_space(packet_type: LongPacketType) -> Option<PacketProtectionSpace> {
match packet_type {
LongPacketType::Initial => Some(PacketProtectionSpace::Initial),
LongPacketType::Handshake => Some(PacketProtectionSpace::Handshake),
_ => None,
}
}
impl QuicHandshakeDriver {
pub fn client(
config: Arc<ClientConfig>,
server_name: ServerName<'static>,
transport_params: Vec<u8>,
) -> Result<Self, QuicTlsError> {
let conn = ClientConnection::new(config, Version::V1, server_name, transport_params)
.map_err(|_| handshake_failure("client_connection_init"))?;
let provider = RustlsQuicCryptoProvider::new_v1(RustlsQuicProviderSide::Client)?;
Ok(Self::new(Connection::Client(conn), provider))
}
pub fn server(
config: Arc<ServerConfig>,
transport_params: Vec<u8>,
) -> Result<Self, QuicTlsError> {
let conn = ServerConnection::new(config, Version::V1, transport_params)
.map_err(|_| handshake_failure("server_connection_init"))?;
let provider = RustlsQuicCryptoProvider::new_v1(RustlsQuicProviderSide::Server)?;
Ok(Self::new(Connection::Server(conn), provider))
}
fn new(tls: Connection, provider: RustlsQuicCryptoProvider) -> Self {
Self {
tls,
provider,
transcript: QuicHandshakeTranscript::new(),
write_level: HandshakeLevel::Initial,
handshake_keys_installed: false,
one_rtt_keys_installed: false,
crypto_send_offset: [0; 3],
}
}
pub fn provider_mut(&mut self) -> &mut RustlsQuicCryptoProvider {
&mut self.provider
}
pub fn assemble_handshake_packet(
&mut self,
segment: &HandshakeSegment,
dst_cid: ConnectionId,
src_cid: ConnectionId,
packet_number: u64,
) -> Result<Vec<u8>, QuicTlsError> {
let packet_type = match segment.level {
HandshakeLevel::Initial => LongPacketType::Initial,
HandshakeLevel::Handshake => LongPacketType::Handshake,
HandshakeLevel::OneRtt => return Err(handshake_failure("onertt_is_not_long_header")),
};
let space = level_protection_space(segment.level);
let offset = self.crypto_send_offset[level_index(segment.level)];
let mut payload = BytesMut::new();
QuicFrame::Crypto {
offset: VarInt::from_u64_unchecked(offset),
data: Bytes::copy_from_slice(&segment.data),
}
.encode(&mut payload)
.map_err(|_| handshake_failure("crypto_frame_encode"))?;
let plaintext = payload.to_vec();
let payload_length = u64::from(HANDSHAKE_PACKET_NUMBER_LEN)
+ plaintext.len() as u64
+ QUIC_AEAD_TAG_LEN as u64;
let header = PacketHeader::Long(LongHeader {
packet_type,
version: 1,
dst_cid,
src_cid,
token: Vec::new(),
payload_length,
packet_number,
packet_number_len: HANDSHAKE_PACKET_NUMBER_LEN,
});
let mut header_bytes = Vec::new();
header
.encode(&mut header_bytes)
.map_err(|_| handshake_failure("long_header_encode"))?;
let protected = self.provider.protect_packet(PacketProtectionRequest {
space,
key_phase: false,
packet_number,
associated_data: &header_bytes,
payload: &plaintext,
})?;
let mut packet = Vec::with_capacity(
header_bytes.len() + protected.ciphertext.len() + protected.tag.len(),
);
packet.extend_from_slice(&header_bytes);
packet.extend_from_slice(&protected.ciphertext);
packet.extend_from_slice(&protected.tag);
self.crypto_send_offset[level_index(segment.level)] += segment.data.len() as u64;
Ok(packet)
}
pub fn recv_handshake_packet(&mut self, packet: &[u8]) -> Result<ConnectionId, QuicTlsError> {
let (header, consumed) = PacketHeader::decode(packet, 0)
.map_err(|_| handshake_failure("packet_header_decode"))?;
let PacketHeader::Long(long_header) = header else {
return Err(handshake_failure("expected_long_header"));
};
let peer_src_cid = long_header.src_cid;
let Some(space) = long_packet_type_space(long_header.packet_type) else {
return Err(handshake_failure("unexpected_long_packet_type"));
};
if consumed > packet.len() {
return Err(handshake_failure("packet_header_overrun"));
}
let header_bytes = &packet[..consumed];
let body = &packet[consumed..];
if body.len() < QUIC_AEAD_TAG_LEN {
return Err(handshake_failure("packet_body_too_short"));
}
let tag_offset = body.len() - QUIC_AEAD_TAG_LEN;
let mut tag = [0u8; QUIC_AEAD_TAG_LEN];
tag.copy_from_slice(&body[tag_offset..]);
let protected = ProtectedPacket {
space,
key_phase: false,
packet_number: long_header.packet_number,
ciphertext: body[..tag_offset].to_vec(),
tag,
proof: ProtectionProof {
provider_kind: self.provider.provider_kind(),
space,
key_phase: false,
generation: 0,
transcript_hash: TranscriptHash::from_bytes([0; 32]),
failure_code: None,
},
};
let unprotected = self
.provider
.unprotect_packet(&protected, header_bytes)
.map_err(|_| handshake_failure("packet_unprotect"))?;
let mut buf: &[u8] = &unprotected.plaintext;
while !buf.is_empty() {
match QuicFrame::decode(&mut buf).map_err(|_| handshake_failure("frame_decode"))? {
Some(QuicFrame::Crypto { data, .. }) => self.read_handshake(data.as_ref())?,
Some(_) => {}
None => break,
}
}
Ok(peer_src_cid)
}
pub fn install_initial_keys(&mut self, dcid: &[u8]) -> Result<(), QuicTlsError> {
self.provider
.derive_keys(PacketProtectionSpace::Initial, &self.transcript, dcid)
.map(|_| ())
}
pub fn pump_outbound(&mut self) -> Result<Vec<HandshakeSegment>, QuicTlsError> {
let mut segments = Vec::new();
loop {
let mut buf = Vec::new();
let key_change = self.tls.write_hs(&mut buf);
let produced = !buf.is_empty();
if produced {
segments.push(HandshakeSegment {
level: self.write_level,
data: buf,
});
}
match key_change {
Some(KeyChange::Handshake { keys }) => {
self.provider
.install_key_change(KeyChange::Handshake { keys }, &self.transcript)?;
self.handshake_keys_installed = true;
self.write_level = HandshakeLevel::Handshake;
}
Some(KeyChange::OneRtt { keys, next }) => {
self.provider
.install_key_change(KeyChange::OneRtt { keys, next }, &self.transcript)?;
self.one_rtt_keys_installed = true;
self.write_level = HandshakeLevel::OneRtt;
}
None => {
if !produced {
break;
}
}
}
}
Ok(segments)
}
pub fn read_handshake(&mut self, data: &[u8]) -> Result<(), QuicTlsError> {
self.tls.read_hs(data).map_err(|_| {
if self.tls.alert().is_some() {
handshake_failure("read_hs_fatal_alert")
} else {
handshake_failure("read_hs_failed")
}
})
}
#[must_use]
pub fn is_complete(&self) -> bool {
!self.tls.is_handshaking()
}
#[must_use]
pub fn one_rtt_keys_installed(&self) -> bool {
self.one_rtt_keys_installed
}
#[must_use]
pub fn handshake_keys_installed(&self) -> bool {
self.handshake_keys_installed
}
#[must_use]
pub fn peer_transport_parameters(&self) -> Option<&[u8]> {
self.tls.quic_transport_parameters()
}
#[must_use]
pub fn provider(&self) -> &RustlsQuicCryptoProvider {
&self.provider
}
#[must_use]
pub fn into_provider(self) -> RustlsQuicCryptoProvider {
self.provider
}
async fn send_pending_flight(
&mut self,
cx: &Cx,
endpoint: &mut QuicUdpEndpoint,
peer: SocketAddr,
dst_cid: ConnectionId,
src_cid: ConnectionId,
packet_number: &mut u64,
) -> Result<usize, QuicTlsError> {
let segments = self.pump_outbound()?;
let mut packets = Vec::new();
for segment in segments {
if segment.level == HandshakeLevel::OneRtt {
continue;
}
let data =
self.assemble_handshake_packet(&segment, dst_cid, src_cid, *packet_number)?;
*packet_number += 1;
packets.push(OutgoingPacket {
dst_addr: peer,
data,
send_time: None,
});
}
let sent = packets.len();
if !packets.is_empty() {
endpoint
.send_batch(cx, &packets)
.await
.map_err(|_| handshake_failure("udp_send"))?;
}
Ok(sent)
}
}
pub async fn client_handshake_over_udp(
cx: &Cx,
endpoint: &mut QuicUdpEndpoint,
server_addr: SocketAddr,
driver: &mut QuicHandshakeDriver,
dcid: ConnectionId,
client_scid: ConnectionId,
) -> Result<(), QuicTlsError> {
driver.install_initial_keys(dcid.as_bytes())?;
let mut packet_number = 0u64;
driver
.send_pending_flight(
cx,
endpoint,
server_addr,
dcid,
client_scid,
&mut packet_number,
)
.await?;
for _ in 0..HANDSHAKE_MAX_FLIGHTS {
if driver.is_complete() {
return Ok(());
}
let received = match crate::time::timeout(
cx.now(),
HANDSHAKE_RECV_TIMEOUT,
endpoint.receive_batch(cx, 16),
)
.await
{
Ok(Ok(packets)) => packets,
Ok(Err(_)) => return Err(handshake_failure("udp_recv")),
Err(_) => return Err(handshake_failure("client_handshake_recv_timeout")),
};
for packet in &received {
driver.recv_handshake_packet(&packet.data)?;
driver
.send_pending_flight(
cx,
endpoint,
server_addr,
dcid,
client_scid,
&mut packet_number,
)
.await?;
}
}
if driver.is_complete() {
Ok(())
} else {
Err(handshake_failure("client_handshake_incomplete"))
}
}
pub async fn server_handshake_over_udp(
cx: &Cx,
endpoint: &mut QuicUdpEndpoint,
driver: &mut QuicHandshakeDriver,
dcid: ConnectionId,
server_scid: ConnectionId,
) -> Result<SocketAddr, QuicTlsError> {
driver.install_initial_keys(dcid.as_bytes())?;
let mut packet_number = 0u64;
let mut peer: Option<(SocketAddr, ConnectionId)> = None;
for _ in 0..HANDSHAKE_MAX_FLIGHTS {
if driver.is_complete() {
return peer
.map(|(addr, _)| addr)
.ok_or_else(|| handshake_failure("server_handshake_no_peer"));
}
let received = match crate::time::timeout(
cx.now(),
HANDSHAKE_RECV_TIMEOUT,
endpoint.receive_batch(cx, 16),
)
.await
{
Ok(Ok(packets)) => packets,
Ok(Err(_)) => return Err(handshake_failure("udp_recv")),
Err(_) => return Err(handshake_failure("server_handshake_recv_timeout")),
};
for packet in &received {
let peer_scid = driver.recv_handshake_packet(&packet.data)?;
if peer.is_none() {
peer = Some((packet.src_addr, peer_scid));
}
if let Some((addr, client_cid)) = peer {
driver
.send_pending_flight(
cx,
endpoint,
addr,
client_cid,
server_scid,
&mut packet_number,
)
.await?;
}
}
}
if driver.is_complete() {
peer.map(|(addr, _)| addr)
.ok_or_else(|| handshake_failure("server_handshake_no_peer"))
} else {
Err(handshake_failure("server_handshake_incomplete"))
}
}
pub fn client_config(
roots: Vec<CertificateDer<'static>>,
alpn: Vec<Vec<u8>>,
) -> Result<Arc<ClientConfig>, QuicTlsError> {
let pinned_end_entities = roots.clone();
let mut root_store = RootCertStore::empty();
for cert in roots {
root_store
.add(cert)
.map_err(|_| handshake_failure("client_root_add_failed"))?;
}
let provider = Arc::new(rustls::crypto::ring::default_provider());
let builder = ClientConfig::builder_with_provider(provider.clone())
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|_| handshake_failure("client_protocol_versions"))?;
let mut config = if pinned_end_entities.is_empty() {
builder
.with_root_certificates(root_store)
.with_no_client_auth()
} else {
let webpki = rustls::client::WebPkiServerVerifier::builder_with_provider(
Arc::new(root_store),
provider,
)
.build()
.map_err(|_| handshake_failure("client_verifier_build"))?;
let verifier = WebPkiOrPinnedEndEntityVerifier {
webpki,
pinned_end_entities,
};
builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(verifier))
.with_no_client_auth()
};
config.alpn_protocols = alpn;
Ok(Arc::new(config))
}
pub fn server_config(
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
alpn: Vec<Vec<u8>>,
) -> Result<Arc<ServerConfig>, QuicTlsError> {
let provider = Arc::new(rustls::crypto::ring::default_provider());
let mut config = ServerConfig::builder_with_provider(provider)
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|_| handshake_failure("server_protocol_versions"))?
.with_no_client_auth()
.with_single_cert(cert_chain, key)
.map_err(|_| handshake_failure("server_single_cert"))?;
config.alpn_protocols = alpn;
Ok(Arc::new(config))
}
#[cfg(test)]
mod tests {
use super::*;
const LEAF_CERT_PEM: &str = "-----BEGIN CERTIFICATE-----\n\
MIIBwTCCAWigAwIBAgIUTQyiZ96ufyKHVqRYRZBXpRQABGMwCgYIKoZIzj0EAwIw\n\
FzEVMBMGA1UEAwwMYXRwcS10ZXN0LWNhMCAXDTI2MDYxNjA1MTYyM1oYDzIxMjYw\n\
NTIzMDUxNjIzWjAUMRIwEAYDVQQDDAlhdHBxLXRlc3QwWTATBgcqhkjOPQIBBggq\n\
hkjOPQMBBwNCAASqge/wCghqQ7mK2i0YFNQQqYuxtyBbxlDvlrJDWhuXLXcrwcK4\n\
eQkpN3QBVt6JLUpAuYpUrQYUSL28G0cYl4hdo4GSMIGPMBoGA1UdEQQTMBGCCWxv\n\
Y2FsaG9zdIcEfwAAATATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAA\n\
MA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUTWWIxYJyvXlJNVcDd8An36rhuMQw\n\
HwYDVR0jBBgwFoAUG872eUJJNl9C6SZHmR9sCRNzvtYwCgYIKoZIzj0EAwIDRwAw\n\
RAIgOkNWPyvljX7zxCWN9sJ/rpX7XV5ubXvNrPdV70sF8oECIGtMuJr6XEmcump1\n\
YuX2YYZ2gAU6aNU/up/PediXcN5u\n\
-----END CERTIFICATE-----\n";
const LEAF_KEY_PEM: &str = "-----BEGIN PRIVATE KEY-----\n\
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgpE59cRbMDhBIZaha\n\
UPAvB8O86PWbkhxy/8cx/FrSa1ShRANCAASqge/wCghqQ7mK2i0YFNQQqYuxtyBb\n\
xlDvlrJDWhuXLXcrwcK4eQkpN3QBVt6JLUpAuYpUrQYUSL28G0cYl4hd\n\
-----END PRIVATE KEY-----\n";
const CA_CERT_PEM: &str = "-----BEGIN CERTIFICATE-----\n\
MIIBlDCCATugAwIBAgIUYOTxo/FMMZjqCnJT+IDmJ2BNux0wCgYIKoZIzj0EAwIw\n\
FzEVMBMGA1UEAwwMYXRwcS10ZXN0LWNhMCAXDTI2MDYxNjA1MTYyM1oYDzIxMjYw\n\
NTIzMDUxNjIzWjAXMRUwEwYDVQQDDAxhdHBxLXRlc3QtY2EwWTATBgcqhkjOPQIB\n\
BggqhkjOPQMBBwNCAASAsNg5paEJFgZwYGu7aCzsZYPyDyjzzcT7fi3O5JHGW0xA\n\
pTqjgqykWTDkyfwdITXWXIfrx2D2+QwoGXOV4OFSo2MwYTAdBgNVHQ4EFgQUG872\n\
eUJJNl9C6SZHmR9sCRNzvtYwHwYDVR0jBBgwFoAUG872eUJJNl9C6SZHmR9sCRNz\n\
vtYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwID\n\
RwAwRAIgFLcs0Qdsy190QfKzpvLj28srfpw6wZ2PURF20N+twm8CIFZMWnG65VsE\n\
WkX8ykcdUfalGtZ1XFOTo+aaWs+3gyI1\n\
-----END CERTIFICATE-----\n";
fn parse_one_cert(pem: &str) -> CertificateDer<'static> {
let mut reader = std::io::BufReader::new(pem.as_bytes());
rustls_pemfile::certs(&mut reader)
.next()
.expect("one cert")
.expect("valid cert pem")
}
fn leaf_cert() -> CertificateDer<'static> {
parse_one_cert(LEAF_CERT_PEM)
}
fn ca_cert() -> CertificateDer<'static> {
parse_one_cert(CA_CERT_PEM)
}
fn leaf_key() -> PrivateKeyDer<'static> {
let mut reader = std::io::BufReader::new(LEAF_KEY_PEM.as_bytes());
rustls_pemfile::private_key(&mut reader)
.expect("read key pem")
.expect("one key")
}
fn drive_to_completion(client: &mut QuicHandshakeDriver, server: &mut QuicHandshakeDriver) {
for _ in 0..16 {
for seg in client.pump_outbound().expect("client pump") {
server.read_handshake(&seg.data).expect("server read");
}
for seg in server.pump_outbound().expect("server pump") {
client.read_handshake(&seg.data).expect("client read");
}
if client.is_complete() && server.is_complete() {
return;
}
}
panic!("handshake did not converge within bound");
}
fn client_rejects_server(
client: &mut QuicHandshakeDriver,
server: &mut QuicHandshakeDriver,
) -> bool {
'drive: for _ in 0..16 {
for seg in client.pump_outbound().expect("client pump") {
let _ = server.read_handshake(&seg.data);
}
for seg in server.pump_outbound().expect("server pump") {
if client.read_handshake(&seg.data).is_err() {
return true;
}
}
if client.is_complete() {
break 'drive;
}
}
false
}
const DCID_BYTES: &[u8] = &[0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18];
#[test]
fn real_tls13_handshake_completes_over_protected_packets() {
let alpn = vec![ATP_QUIC_ALPN.to_vec()];
let server_cfg =
server_config(vec![leaf_cert()], leaf_key(), alpn.clone()).expect("server config");
let client_cfg = client_config(vec![ca_cert()], alpn).expect("client config");
let mut client = QuicHandshakeDriver::client(
client_cfg,
ServerName::try_from("localhost").expect("server name"),
b"client-params".to_vec(),
)
.expect("client driver");
let mut server = QuicHandshakeDriver::server(server_cfg, b"server-params".to_vec())
.expect("server driver");
client
.install_initial_keys(DCID_BYTES)
.expect("client initial keys");
server
.install_initial_keys(DCID_BYTES)
.expect("server initial keys");
let dcid = ConnectionId::new(DCID_BYTES).expect("dcid");
let client_scid = ConnectionId::new(&[0x11, 0x22, 0x33, 0x44]).expect("client scid");
let server_scid = ConnectionId::new(&[0x55, 0x66, 0x77, 0x88]).expect("server scid");
let mut client_pn = 0u64;
let mut server_pn = 0u64;
let assemble_client =
|c: &mut QuicHandshakeDriver, pn: &mut u64, out: &mut Vec<Vec<u8>>| {
for seg in c.pump_outbound().expect("client pump") {
if seg.level == HandshakeLevel::OneRtt {
continue;
}
out.push(
c.assemble_handshake_packet(&seg, dcid, client_scid, *pn)
.expect("client assemble"),
);
*pn += 1;
}
};
let assemble_server =
|s: &mut QuicHandshakeDriver, pn: &mut u64, out: &mut Vec<Vec<u8>>| {
for seg in s.pump_outbound().expect("server pump") {
if seg.level == HandshakeLevel::OneRtt {
continue;
}
out.push(
s.assemble_handshake_packet(&seg, client_scid, server_scid, *pn)
.expect("server assemble"),
);
*pn += 1;
}
};
let mut client_to_server: Vec<Vec<u8>> = Vec::new();
assemble_client(&mut client, &mut client_pn, &mut client_to_server);
for _ in 0..16 {
let mut server_to_client: Vec<Vec<u8>> = Vec::new();
for packet in client_to_server.drain(..) {
server.recv_handshake_packet(&packet).expect("server recv");
assemble_server(&mut server, &mut server_pn, &mut server_to_client);
}
let mut next_client_to_server: Vec<Vec<u8>> = Vec::new();
for packet in server_to_client.drain(..) {
client.recv_handshake_packet(&packet).expect("client recv");
assemble_client(&mut client, &mut client_pn, &mut next_client_to_server);
}
client_to_server = next_client_to_server;
if client.is_complete() && server.is_complete() {
break;
}
}
assert!(
client.is_complete() && server.is_complete(),
"handshake over real protected packets did not complete"
);
assert!(
client.one_rtt_keys_installed() && server.one_rtt_keys_installed(),
"1-RTT keys not installed after packet handshake"
);
assert_eq!(
client.peer_transport_parameters(),
Some(b"server-params".as_slice())
);
}
#[test]
fn real_tls13_handshake_completes_and_installs_one_rtt_keys() {
let alpn = vec![ATP_QUIC_ALPN.to_vec()];
let server_cfg =
server_config(vec![leaf_cert()], leaf_key(), alpn.clone()).expect("server config");
let client_cfg = client_config(vec![ca_cert()], alpn).expect("client config");
let mut client = QuicHandshakeDriver::client(
client_cfg,
ServerName::try_from("localhost").expect("server name"),
b"client-params".to_vec(),
)
.expect("client driver");
let mut server = QuicHandshakeDriver::server(server_cfg, b"server-params".to_vec())
.expect("server driver");
assert!(!client.is_complete());
assert!(!server.is_complete());
drive_to_completion(&mut client, &mut server);
assert!(client.is_complete(), "client handshake incomplete");
assert!(server.is_complete(), "server handshake incomplete");
assert!(client.one_rtt_keys_installed(), "client missing 1-RTT keys");
assert!(server.one_rtt_keys_installed(), "server missing 1-RTT keys");
assert_eq!(
client.peer_transport_parameters(),
Some(b"server-params".as_slice())
);
assert_eq!(
server.peer_transport_parameters(),
Some(b"client-params".as_slice())
);
}
#[test]
fn real_tls13_handshake_completes_with_exact_pinned_leaf() {
let alpn = vec![ATP_QUIC_ALPN.to_vec()];
let server_cfg =
server_config(vec![leaf_cert()], leaf_key(), alpn.clone()).expect("server config");
let client_cfg = client_config(vec![leaf_cert()], alpn).expect("client config");
let mut client = QuicHandshakeDriver::client(
client_cfg,
ServerName::try_from("localhost").expect("server name"),
b"client-params".to_vec(),
)
.expect("client driver");
let mut server = QuicHandshakeDriver::server(server_cfg, b"server-params".to_vec())
.expect("server driver");
drive_to_completion(&mut client, &mut server);
assert!(client.is_complete(), "client handshake incomplete");
assert!(server.is_complete(), "server handshake incomplete");
assert!(client.one_rtt_keys_installed() && server.one_rtt_keys_installed());
}
#[test]
fn exact_pinned_leaf_still_rejects_wrong_server_name() {
let alpn = vec![ATP_QUIC_ALPN.to_vec()];
let server_cfg =
server_config(vec![leaf_cert()], leaf_key(), alpn.clone()).expect("server config");
let client_cfg = client_config(vec![leaf_cert()], alpn).expect("client config");
let mut client = QuicHandshakeDriver::client(
client_cfg,
ServerName::try_from("not-localhost.example").expect("server name"),
b"client-params".to_vec(),
)
.expect("client driver");
let mut server = QuicHandshakeDriver::server(server_cfg, b"server-params".to_vec())
.expect("server driver");
assert!(
client_rejects_server(&mut client, &mut server),
"client must reject a pinned leaf with the wrong SAN"
);
assert!(
!client.is_complete(),
"client must not complete against a wrong-name pinned leaf"
);
}
#[test]
fn handshake_fails_closed_when_client_does_not_trust_server() {
let alpn = vec![ATP_QUIC_ALPN.to_vec()];
let server_cfg =
server_config(vec![leaf_cert()], leaf_key(), alpn.clone()).expect("server config");
let client_cfg = client_config(Vec::new(), alpn).expect("client config builds w/o roots");
let mut client = QuicHandshakeDriver::client(
client_cfg,
ServerName::try_from("localhost").expect("server name"),
b"client-params".to_vec(),
)
.expect("client driver");
let mut server = QuicHandshakeDriver::server(server_cfg, b"server-params".to_vec())
.expect("server driver");
assert!(
client_rejects_server(&mut client, &mut server),
"client must reject the untrusted server certificate"
);
assert!(
!client.is_complete(),
"client must not complete against an untrusted server"
);
}
}