use alloc::vec::Vec;
use pki_types::ServerName;
use super::config::ClientConfig;
use super::hs::{ClientHelloInput, ClientState};
use crate::client::EchStatus;
use crate::common_state::{CommonState, EarlyDataEvent, Event, Side};
use crate::conn::private::SideOutput;
use crate::conn::split::SplitConnection;
use crate::conn::{
ClientNext, Connection, DataKind, NeedsInput, SideCommonOutput, SideData, Tcp, Transport,
VerifyPeerIdentity,
};
#[cfg(doc)]
use crate::crypto;
use crate::crypto::cipher::{OutboundPlain, Payload};
use crate::enums::ApplicationProtocol;
use crate::error::{ApiMisuse, Error};
use crate::msgs::{ClientExtensionsInput, TransportParameters};
use crate::quic::{self, Quic};
use crate::sync::Arc;
use crate::tracing::trace;
use crate::verify::ServerIdentity;
impl Connection<ClientSide, Tcp> {
pub fn early_data(&mut self) -> Option<WriteEarlyData<'_>> {
let Self { side, common, .. } = self;
WriteEarlyData::new(&mut side.early_data, common)
}
}
pub struct ClientConnectionBuilder {
pub(crate) config: Arc<ClientConfig>,
pub(crate) name: ServerName<'static>,
pub(crate) alpn_protocols: Option<Vec<ApplicationProtocol<'static>>>,
}
impl ClientConnectionBuilder {
pub fn with_alpn(mut self, alpn_protocols: Vec<ApplicationProtocol<'static>>) -> Self {
self.alpn_protocols = Some(alpn_protocols);
self
}
pub fn build(self, tls: &mut Vec<u8>) -> Result<Connection<ClientSide, Tcp>, Error> {
let Self {
config,
name,
alpn_protocols,
} = self;
let alpn_protocols = alpn_protocols.unwrap_or_else(|| config.alpn_protocols.clone());
Connection::for_client(
config,
name,
ClientExtensionsInput::from_alpn(alpn_protocols),
Tcp,
tls,
)
}
pub fn build_quic(
self,
version: quic::Version,
params: Vec<u8>,
) -> Result<Connection<ClientSide, Quic>, Error> {
let suites = &self
.config
.provider()
.tls13_cipher_suites;
if suites.is_empty() {
return Err(ApiMisuse::QuicRequiresTls13Support.into());
}
if !suites
.iter()
.any(|scs| scs.quic.is_some())
{
return Err(ApiMisuse::NoQuicCompatibleCipherSuites.into());
}
let exts = ClientExtensionsInput {
transport_parameters: Some(match version {
quic::Version::V1 | quic::Version::V2 => {
TransportParameters::Quic(Payload::new(params))
}
}),
..ClientExtensionsInput::from_alpn(
self.alpn_protocols
.unwrap_or_else(|| self.config.alpn_protocols.clone()),
)
};
let quic = Quic {
version,
..Quic::default()
};
let mut tls = Vec::new();
let conn = Connection::for_client(self.config, self.name, exts, quic, &mut tls)?;
debug_assert!(tls.is_empty());
Ok(conn)
}
pub fn start_handshake(self, tls: &mut Vec<u8>) -> Result<NeedsInput<ClientSide>, Error> {
let Self {
config,
name,
alpn_protocols,
} = self;
let alpn_protocols = alpn_protocols.unwrap_or_else(|| config.alpn_protocols.clone());
Ok(NeedsInput::new(Connection::for_client(
config,
name,
ClientExtensionsInput::from_alpn(alpn_protocols),
Tcp,
tls,
)?))
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ClientHandshake {
NeedsInput(NeedsInput<ClientSide>),
VerifyServerIdentity(VerifyPeerIdentity<ClientSide, Tcp>),
Complete(SplitConnection<ClientSide>),
}
impl TryFrom<Connection<ClientSide, Tcp>> for ClientHandshake {
type Error = Error;
fn try_from(conn: Connection<ClientSide, Tcp>) -> Result<Self, Error> {
Ok(match ClientNext::try_from(conn)? {
ClientNext::NeedsInput(conn) => Self::NeedsInput(NeedsInput(conn)),
ClientNext::VerifyServerIdentity(verify) => Self::VerifyServerIdentity(verify),
ClientNext::Complete(conn) => Self::Complete(SplitConnection::try_from(conn)?),
})
}
}
impl NeedsInput<ClientSide> {
pub fn early_data(&mut self) -> Option<WriteEarlyData<'_>> {
let Connection { side, common, .. } = &mut self.0;
WriteEarlyData::new(&mut side.early_data, common)
}
}
pub struct WriteEarlyData<'a> {
early_data: &'a mut EarlyData,
common: &'a mut CommonState,
}
impl<'a> WriteEarlyData<'a> {
fn new(early_data: &'a mut Option<EarlyData>, common: &'a mut CommonState) -> Option<Self> {
let early_data = early_data.as_mut()?;
match early_data.state {
EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted => {
Some(WriteEarlyData { early_data, common })
}
_ => None,
}
}
#[must_use]
pub fn write(
&mut self,
plaintext: OutboundPlain<'_>,
tls: &mut Vec<u8>,
) -> Result<usize, Error> {
let state = &mut self.early_data;
let plaintext = match state.state {
EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted => {
let take = Ord::min(plaintext.len(), state.left);
state.left -= take;
plaintext.split_at(take).0
}
EarlyDataState::AcceptedFinished => return Ok(0),
};
self.common
.send
.send_appdata_encrypt(DataKind::Early(plaintext), tls)
}
pub fn bytes_left(&self) -> usize {
self.early_data.left
}
}
impl<T: Transport> Connection<ClientSide, T> {
pub(crate) fn for_client(
config: Arc<ClientConfig>,
name: ServerName<'static>,
extra_exts: ClientExtensionsInput,
mut transport: T,
tls: &mut Vec<u8>,
) -> Result<Self, Error> {
let mut common = CommonState::new(Side::Client, config.fips());
common
.send
.set_max_fragment_size(config.max_fragment_size)?;
let mut side = ClientSide::default();
let protocol = transport.protocol();
let mut output = SideCommonOutput {
side: &mut side,
quic: transport.quic(),
common: &mut common,
tls,
};
let input = ClientHelloInput::new(name, &extra_exts, protocol, &mut output, config)?;
let state = input.start_handshake(extra_exts, &mut output)?;
Ok(Self {
state: Ok(state),
side,
common,
transport,
})
}
}
#[derive(Debug, Default)]
pub struct ClientSide {
early_data: Option<EarlyData>,
ech_status: EchStatus,
}
impl ClientSide {
pub fn is_early_data_accepted(&self) -> bool {
matches!(
&self.early_data,
Some(EarlyData {
state: EarlyDataState::Accepted | EarlyDataState::AcceptedFinished,
..
})
)
}
pub fn ech_status(&self) -> EchStatus {
self.ech_status
}
}
impl SideData for ClientSide {
type Handshake = ClientHandshake;
type QuicHandshake = ();
type PeerIdentity<'a> = ServerIdentity<'static, 'a>;
}
impl SideOutput for ClientSide {
fn emit(&mut self, ev: Event) {
match ev {
Event::EchStatus(ech) => self.ech_status = ech,
Event::EarlyData(event) => match (event, &mut self.early_data) {
(EarlyDataEvent::Enable(sz), None) => self.early_data = Some(EarlyData::new(sz)),
(EarlyDataEvent::Start, Some(early_data)) => {
assert_eq!(early_data.state, EarlyDataState::Ready);
early_data.state = EarlyDataState::Sending;
}
(EarlyDataEvent::Accepted, Some(early_data)) => {
trace!("EarlyData accepted");
assert_eq!(early_data.state, EarlyDataState::Sending);
early_data.state = EarlyDataState::Accepted;
}
(EarlyDataEvent::Rejected, _) => self.early_data = None,
(EarlyDataEvent::Finished, Some(early_data)) => {
trace!("EarlyData finished");
early_data.state = match early_data.state {
EarlyDataState::Accepted => EarlyDataState::AcceptedFinished,
_ => panic!("bad EarlyData state"),
}
}
_ => unreachable!(),
},
_ => unreachable!(),
}
}
}
impl crate::conn::private::Side for ClientSide {
type State = ClientState;
}
#[derive(Debug)]
pub(super) struct EarlyData {
state: EarlyDataState,
left: usize,
}
impl EarlyData {
fn new(left: usize) -> Self {
Self {
state: EarlyDataState::Ready,
left,
}
}
}
#[derive(Debug, PartialEq)]
enum EarlyDataState {
Ready,
Sending,
Accepted,
AcceptedFinished,
}