use crate::{
asn1::Asn1DecodeWrapperAux,
codec::{Decode, Encode as _},
collections::{ArrayVectorCopy, ArrayVectorU8, SingleTypeStorage},
misc::Lease,
net::{BufStreamReader, Stream, Uri},
rng::CryptoRng,
tls::{
AlertDescription, CHANGE_CIPHER_SPEC, DLFT_MAX_FRAGMENT_LENGTH, HandshakePath,
MAX_CERTIFICATES, NamedGroup, ProtocolVersion, TlsBuffer, TlsConfig, TlsError, TlsMode,
TlsServerEndPoint, TlsStream,
de::De,
key_schedule::KeySchedule,
misc::{fetch_rec_from_stream, manage_err, server_sig_msg},
protocol::{
alert::Alert, certificate::Certificate, certificate_verify::CertificateVerify,
client_hello::ClientHello, encrypted_extensions::EncryptedExtensions, finished::Finished,
handshake::Handshake, handshake_ty::HandshakeTy, named_group::NamedGroupAgreement,
record::Record, record_content_ty::RecordContentTy, server_hello::ServerHello, u24::U24,
},
read_record_info::ReadRecordInfo,
tls_decode_wrapper::TlsDecodeWrapper,
tls_encode_wrapper::TlsEncodeWrapper,
tls_hash::{TlsDigest, TlsHash},
},
x509::{CvEndEntity, CvIntermediate, PublicKeyTy, ServerName, SubjectPublicKeyInfo},
};
use core::{hint::cold_path, ops::Range};
#[derive(Debug, PartialEq)]
pub enum ClientRecordsState {
Terminated(ArrayVectorCopy<u8, { 6 + 30 + 74 }>),
}
#[derive(Debug, PartialEq)]
pub enum ServerRecordsState<T> {
Alert(Alert),
NeedsMoreData,
Terminated(T),
}
#[derive(Debug)]
pub struct ManageRemainingServerRecordsInput {
certificate_pky: PublicKeyTy,
client_cert_requested: bool,
spki_range: Range<usize>,
tls_server_end_point: TlsServerEndPoint,
transcript_digest: TlsDigest,
}
#[derive(Debug)]
pub struct TlsConnector<RNG, S, TC, U> {
buffer: TlsBuffer,
config: TC,
handshake_path: HandshakePath,
has_sent_ccs: bool,
key_schedule: KeySchedule,
max_fragment_length_send: u16,
max_fragment_length: u16,
named_group: NamedGroup,
rng: RNG,
split_begin: usize,
split_len: usize,
stream: S,
transcript_hash: TlsHash,
uri: U,
}
impl<RNG, S, STR, TC, TM, U> TlsConnector<RNG, S, TC, U>
where
STR: Lease<str>,
TC: Lease<TlsConfig<TM>> + SingleTypeStorage<Item = TM>,
U: Lease<Uri<STR>> + SingleTypeStorage<Item = STR>,
{
#[inline]
pub fn new(config: TC, rng: RNG, stream: S, uri: U) -> Self {
let cfg_ref = config.lease();
let key_schedule = KeySchedule::default();
let transcript_hash = key_schedule.cipher_suite().hash_new();
let max_fragment_length =
cfg_ref.max_fragment_length().map_or(DLFT_MAX_FRAGMENT_LENGTH, |el| el.num());
let max_fragment_length_send =
cfg_ref.max_fragment_length_send().map_or(DLFT_MAX_FRAGMENT_LENGTH, |el| el.num());
let named_group = cfg_ref.inner.supported_groups.named_group_list.first().copied();
Self {
buffer: TlsBuffer::new(),
config,
handshake_path: HandshakePath::Full,
has_sent_ccs: false,
key_schedule,
max_fragment_length_send,
max_fragment_length,
named_group: named_group.unwrap_or(NamedGroup::default()),
rng,
split_begin: 0,
split_len: 0,
stream,
transcript_hash,
uri,
}
}
#[inline]
pub const fn handshake_path(&self) -> HandshakePath {
self.handshake_path
}
#[inline]
pub const fn named_group(&self) -> NamedGroup {
self.named_group
}
#[inline]
pub const fn rng(&self) -> &RNG {
&self.rng
}
#[inline]
pub const fn rng_mut(&mut self) -> &mut RNG {
&mut self.rng
}
#[inline]
pub const fn stream(&self) -> &S {
&self.stream
}
#[inline]
pub const fn stream_mut(&mut self) -> &mut S {
&mut self.stream
}
}
impl<RNG, S, STR, TC, TM, U> TlsConnector<RNG, S, TC, U>
where
RNG: CryptoRng,
S: Stream,
STR: Lease<str>,
TC: Lease<TlsConfig<TM>> + SingleTypeStorage<Item = TM>,
TM: TlsMode,
U: Lease<Uri<STR>> + SingleTypeStorage<Item = STR>,
{
#[inline]
pub async fn connect(mut self) -> crate::Result<TlsConnectOutput<RNG, S, TM, U>> {
if TM::TY.is_plain_text() {
return Ok(TlsConnectOutput {
handshake_path: self.handshake_path,
named_group: self.named_group,
rng: self.rng,
server_end_point: TlsServerEndPoint::new(),
tls_stream: TlsStream::new(
self.buffer,
self.key_schedule,
self.max_fragment_length,
self.max_fragment_length_send,
self.stream,
self.config.lease().mode().clone(),
)?,
uri: self.uri,
});
}
let fut = async {
let mut secrets = self.write_client_hello()?;
self.stream.write_all(&self.buffer.writer_buffer).await?;
let mut rri = self.fetch_rec_from_stream::<false>(false).await?;
let mut mrsri = loop {
match self.manage_initial_server_record(&rri, &mut secrets)? {
ServerRecordsState::Alert(alert) => {
self.write_alert(alert).await?;
return Err(TlsError::AbortedHandshake(alert).into());
}
ServerRecordsState::NeedsMoreData => {
rri = self.fetch_rec_from_stream::<false>(false).await?;
}
ServerRecordsState::Terminated(el) => break el,
}
};
self.buffer.writer_buffer.clear();
rri = self.fetch_rec_from_stream::<true>(true).await?;
loop {
match self.manage_remaining_server_records(&mut mrsri, &rri)? {
ServerRecordsState::Alert(alert) => {
self.write_alert(alert).await?;
return Err(TlsError::AbortedHandshake(alert).into());
}
ServerRecordsState::NeedsMoreData => {
rri = self.fetch_rec_from_stream::<false>(true).await?;
}
ServerRecordsState::Terminated(_) => break,
}
}
match self.manage_client_records(&mrsri)? {
ClientRecordsState::Terminated(data) => {
_trace!(target: crate::tls::_TARGET_HS, "Write Finished");
self.stream.write_all(&data).await?;
}
}
Ok(mrsri.tls_server_end_point)
};
let rslt = fut.await;
let kss = self.key_schedule.write_mut().state_mut();
let tls_server_end_point = manage_err(self.has_sent_ccs, kss, rslt, &mut self.stream).await?;
_trace!(target: crate::tls::_TARGET_HS, "Successful handshake");
Ok(TlsConnectOutput {
handshake_path: self.handshake_path,
named_group: self.named_group,
rng: self.rng,
server_end_point: tls_server_end_point,
tls_stream: TlsStream::new(
self.buffer,
self.key_schedule,
self.max_fragment_length,
self.max_fragment_length_send,
self.stream,
self.config.lease().mode().clone(),
)?,
uri: self.uri,
})
}
#[inline]
pub fn manage_client_records(
&mut self,
mrsri: &ManageRemainingServerRecordsInput,
) -> crate::Result<ClientRecordsState> {
*self.buffer.reader_buffer.forbid_clear_mut() = false;
self.buffer.reader_buffer.clear_if_exhausted();
let mut empty_cert = ArrayVectorCopy::<u8, 30>::new();
if mrsri.client_cert_requested {
let unencrypted_msg = [HandshakeTy::Certificate.into(), 0, 0, 4, 0, 0, 0, 0];
self.transcript_hash.update(&unencrypted_msg);
let payload_len: u8 = 25;
let header = [RecordContentTy::ApplicationData.into(), 3, 3, 0, payload_len];
let mut encrypted = ArrayVectorCopy::<u8, { 4 + 4 + 1 }>::new();
let _ = encrypted.extend_from_copyable_slices([
&unencrypted_msg[..],
&[RecordContentTy::Handshake.into()],
])?;
let kss = self.key_schedule.write_mut().state_mut();
let nonce = kss.nonce();
let secret = kss.cipher_key();
let tag = kss.cipher_suite().aes_encrypt(&header, &mut encrypted, nonce, secret)?;
kss.increment_counter();
let _ = empty_cert.extend_from_copyable_slices([header.as_slice(), &encrypted, &tag])?;
}
let (_, ksw) = self.key_schedule.split_mut();
let verify_data = ksw
.state_mut()
.create_finished_verify_data(self.transcript_hash.clone().finalize().lease())?;
let finished = Finished::record_bytes(&verify_data, ksw.state_mut())?;
self.key_schedule.master_secret::<true>(&self.transcript_hash.clone().finalize())?;
let mut terminated = ArrayVectorCopy::new();
let array = [&CHANGE_CIPHER_SPEC[..], &empty_cert, &finished];
let _ = terminated.extend_from_copyable_slices(array)?;
self.has_sent_ccs = true;
Ok(ClientRecordsState::Terminated(terminated))
}
#[inline]
pub fn manage_initial_server_record(
&mut self,
rri: &ReadRecordInfo,
secrets: &mut ArrayVectorU8<NamedGroupAgreement, { NamedGroup::len() }>,
) -> crate::Result<ServerRecordsState<ManageRemainingServerRecordsInput>> {
match rri.outer_ty {
RecordContentTy::Alert => return alert(&self.buffer.reader_buffer, rri),
RecordContentTy::Handshake => {}
RecordContentTy::ApplicationData | RecordContentTy::ChangeCipherSpec => {
return Err(TlsError::InvalidHandshake.into());
}
}
handshake_bytes_adjust(
&mut self.buffer.reader_buffer,
rri,
(&mut self.split_begin, &mut self.split_len),
);
let Some((msg_type, range, mut dw)) = handshake_bytes_decode(
&self.buffer.reader_buffer,
(&mut self.split_begin, &mut self.split_len),
)?
else {
return Ok(ServerRecordsState::NeedsMoreData);
};
if msg_type != HandshakeTy::ServerHello {
return Err(TlsError::InvalidHandshake.into());
}
let server_hello = ServerHello::<'_>::decode(&mut dw)?;
let secret_idx = secrets
.iter_mut()
.position(|el| el.named_group() == server_hello.key_share().group)
.ok_or(TlsError::SecretMismatch)?;
let Some(secret) = secrets.swap_remove(secret_idx.try_into()?) else {
return Err(TlsError::SecretMismatch.into());
};
self.named_group = secret.named_group();
{
self.key_schedule.set_cipher_suite(server_hello.cipher_suite());
self.key_schedule.early_secret()?;
}
let shared_secret = secret.diffie_hellman::<true>(server_hello.key_share().opaque)?;
self.transcript_hash = self.key_schedule.cipher_suite().hash_new();
self.transcript_hash.update(self.buffer.writer_buffer.get(5..).unwrap_or_default());
self.transcript_hash.update(self.buffer.reader_buffer.filled().get(range).unwrap_or_default());
self
.key_schedule
.handshake_secret::<true>(shared_secret.as_ref(), &self.transcript_hash.clone().finalize())?;
self.split_len = 0;
*self.buffer.reader_buffer.forbid_clear_mut() = false;
self.buffer.reader_buffer.clear_if_exhausted();
*self.buffer.reader_buffer.forbid_clear_mut() = true;
Ok(ServerRecordsState::Terminated(ManageRemainingServerRecordsInput {
certificate_pky: PublicKeyTy::default(),
client_cert_requested: false,
spki_range: 0..0,
tls_server_end_point: TlsServerEndPoint::new(),
transcript_digest: TlsDigest::default(),
}))
}
#[inline]
pub fn manage_remaining_server_records(
&mut self,
mrsri: &mut ManageRemainingServerRecordsInput,
rri: &ReadRecordInfo,
) -> crate::Result<ServerRecordsState<()>> {
match rri.inner_ty {
RecordContentTy::Alert => return alert(&self.buffer.reader_buffer, rri),
RecordContentTy::ApplicationData => return Err(TlsError::InvalidHandshake.into()),
RecordContentTy::ChangeCipherSpec => {
return Ok(ServerRecordsState::NeedsMoreData);
}
RecordContentTy::Handshake => {}
}
handshake_bytes_adjust(
&mut self.buffer.reader_buffer,
rri,
(&mut self.split_begin, &mut self.split_len),
);
loop {
let Some((msg_type, range, mut dw)) = handshake_bytes_decode(
&self.buffer.reader_buffer,
(&mut self.split_begin, &mut self.split_len),
)?
else {
break;
};
_trace!(target: crate::tls::_TARGET_HS, "Read handshake: {:?}", msg_type);
let curr_handshake_bytes = self.buffer.reader_buffer.filled().get(range).unwrap_or_default();
self.transcript_hash.update(curr_handshake_bytes);
match msg_type {
HandshakeTy::EncryptedExtensions => {
let ee = EncryptedExtensions::decode(&mut dw)?;
if let Some(err) = Self::check_alpn(self.config.lease(), &ee) {
return Err(err);
}
if let Some(el) = ee.max_fragment_length() {
if Some(el) != self.config.lease().max_fragment_length() {
return Err(TlsError::InvalidNegotiatedMaxFragmentLength.into());
}
self.max_fragment_length = el.num();
self.max_fragment_length_send = self.max_fragment_length_send.min(el.num());
} else if self.config.lease().max_fragment_length().is_some() {
return Err(TlsError::InvalidNegotiatedMaxFragmentLength.into());
}
if self.config.lease().server_name().is_none() && ee.server_name().is_some() {
return Err(crate::Error::TlsErrorReply(
TlsError::InvalidNegotiatedServerName,
AlertDescription::UnsupportedExtension,
));
}
}
HandshakeTy::CertificateRequest => {
mrsri.client_cert_requested = true;
}
HandshakeTy::Certificate => {
Self::manage_certificate(
self.config.lease(),
self.buffer.reader_buffer.filled(),
&self.key_schedule,
mrsri,
&mut dw,
&self.transcript_hash,
self.uri.lease(),
)?;
}
HandshakeTy::CertificateVerify => {
Self::manage_certificate_verify(self.buffer.reader_buffer.filled(), mrsri, &mut dw)?;
mrsri.transcript_digest = self.transcript_hash.clone().finalize();
}
HandshakeTy::Finished => {
*dw.cipher_suite_mut() = self.key_schedule.cipher_suite();
let finished = Finished::decode(&mut dw)?;
if self
.key_schedule
.read_mut()
.state_mut()
.verify_finished_record(mrsri.transcript_digest.lease(), finished.verify_data())
.is_err()
{
return Err(TlsError::DigestCheckFailed.into());
}
return Ok(ServerRecordsState::Terminated(()));
}
HandshakeTy::ClientHello
| HandshakeTy::EndOfEarlyData
| HandshakeTy::KeyUpdate
| HandshakeTy::MessageHash
| HandshakeTy::NewSessionTicket
| HandshakeTy::ServerHello => {
return Err(TlsError::InvalidHandshake.into());
}
}
}
Ok(ServerRecordsState::NeedsMoreData)
}
fn check_alpn(config: &TlsConfig<TM>, ee: &EncryptedExtensions) -> Option<crate::Error> {
match (config.lease().alpn(), ee.alpn()) {
(None, Some(_)) => Some(crate::Error::TlsErrorReply(
TlsError::UnofferedExtension,
AlertDescription::UnsupportedExtension,
)),
(Some(client), Some(server)) => {
for server_el in &server.protocol_name_list {
if server_el.is_empty() {
return Some(crate::Error::TlsErrorReply(
TlsError::EmptyNegotiatedAlpnClient,
AlertDescription::IllegalParameter,
));
}
if client.protocol_name_list.iter().find(|el| *el == server_el).is_none() {
return Some(crate::Error::TlsErrorReply(
TlsError::MismatchedNegotiatedAlpnClient,
AlertDescription::IllegalParameter,
));
}
}
None
}
_ => None,
}
}
#[inline]
pub fn write_client_hello(
&mut self,
) -> crate::Result<ArrayVectorU8<NamedGroupAgreement, { NamedGroup::len() }>> {
_trace!(target: crate::tls::_TARGET_HS, "Write CH");
let mut secrets = ArrayVectorU8::new();
for named_group in &self.config.lease().inner.supported_groups.named_group_list {
secrets.push(named_group.agreement(&mut self.rng)?)?;
}
let handshake = Handshake::new(
HandshakeTy::ClientHello,
ClientHello::new(&secrets, &mut self.rng, self.config.lease()),
);
let record = Record::new(RecordContentTy::Handshake, ProtocolVersion::Tls1, &handshake);
self.buffer.writer_buffer.clear();
record.encode(&mut TlsEncodeWrapper::from_buffer(&mut self.buffer.writer_buffer))?;
*self.buffer.reader_buffer.forbid_clear_mut() = true;
Ok(secrets)
}
#[inline]
async fn fetch_rec_from_stream<const CHECK_CCS: bool>(
&mut self,
decrypt: bool,
) -> crate::Result<ReadRecordInfo> {
Ok(
fetch_rec_from_stream::<_, CHECK_CCS, false>(
decrypt.then(|| self.key_schedule.read_mut().state_mut()),
self.max_fragment_length,
&mut self.buffer.reader_buffer,
&mut self.stream,
)
.await?
.ok_or(TlsError::AbruptDisconnect)?,
)
}
fn manage_certificate(
config: &TlsConfig<TM>,
filled: &[u8],
key_schedule: &KeySchedule,
mrsri: &mut ManageRemainingServerRecordsInput,
remote_dw: &mut TlsDecodeWrapper<'_>,
transcript_hash: &TlsHash,
uri: &Uri<STR>,
) -> crate::Result<()> {
let certificate = Certificate::decode(remote_dw)?;
let [end_entity, intermediates @ ..] = certificate.certificate_list().as_slice() else {
return Err(TlsError::NoCertificate.into());
};
mrsri.tls_server_end_point.extend_from_copyable_slice(
key_schedule.cipher_suite().hash_digest([end_entity.certificate_bytes()]).lease(),
)?;
let cv_end_entity = {
let mut dw = crate::codec::DecodeWrapper::new(
end_entity.certificate_bytes(),
Asn1DecodeWrapperAux::default(),
);
let cert = crate::x509::Certificate::decode(&mut dw)?;
mrsri.certificate_pky = PublicKeyTy::try_from(&cert)?;
let filled_ptr = filled.as_ptr().addr();
let certificate_bytes_ptr = end_entity.certificate_bytes().as_ptr().addr();
let offset = certificate_bytes_ptr.wrapping_sub(filled_ptr);
mrsri.spki_range = dw.decode_aux.spki_range();
mrsri.spki_range.start = mrsri.spki_range.start.wrapping_add(offset);
mrsri.spki_range.end = mrsri.spki_range.end.wrapping_add(offset);
let sig = dw.decode_aux.tbs_cert(end_entity.certificate_bytes()).unwrap_or_default();
CvEndEntity::from_certificate(cert, sig)?
};
if !TM::TY.is_unverified() {
if let Some(sn_list) = &config.inner.server_name {
let [sn] = sn_list.server_name_list.as_inner()?;
let server_name = ServerName::from_domain(sn.name().as_bytes());
cv_end_entity.validate_subject_name([server_name])?;
} else {
let server_name = ServerName::from_ascii_bytes(uri.hostname().as_bytes())?;
cv_end_entity.validate_subject_name([server_name])?;
}
}
let mut cv_intermediates = ArrayVectorU8::<_, MAX_CERTIFICATES>::new();
for intermediate in intermediates {
let cv_intermediate = {
let mut local_dw = crate::codec::DecodeWrapper::new(
intermediate.certificate_bytes(),
Asn1DecodeWrapperAux::default(),
);
CvIntermediate::from_certificate(
crate::x509::Certificate::decode(&mut local_dw)?,
local_dw.decode_aux.tbs_cert(intermediate.certificate_bytes()).unwrap_or_default(),
)?
};
cv_intermediates.push(cv_intermediate)?;
}
mrsri.transcript_digest = transcript_hash.clone().finalize();
if TM::TY.is_unverified() {
return Ok(());
}
drop(cv_end_entity.validate_chain(
cv_intermediates.as_slice(),
config.cv_policy(),
config.trust_anchors(),
)?);
Ok(())
}
fn manage_certificate_verify(
filled: &[u8],
mrsri: &mut ManageRemainingServerRecordsInput,
remote_dw: &mut TlsDecodeWrapper<'_>,
) -> crate::Result<()> {
let certificate_verify = CertificateVerify::decode(remote_dw)?;
let msg = server_sig_msg(mrsri.transcript_digest.lease())?;
let spki = &SubjectPublicKeyInfo::<&[u8]>::decode(&mut crate::codec::DecodeWrapper::new(
filled.get(mrsri.spki_range.clone()).unwrap_or_default(),
Asn1DecodeWrapperAux::default(),
))?;
if mrsri.certificate_pky != certificate_verify.algorithm().cert_pkt() {
return Err(TlsError::MismatchedCertificatePkAndSignature.into());
}
if certificate_verify
.algorithm()
.handshake_st()
.validate_signature(
&msg,
spki.subject_public_key.bytes().lease(),
certificate_verify.signature(),
)
.is_err()
{
return Err(TlsError::BadSignature.into());
}
Ok(())
}
#[inline]
async fn write_alert(&mut self, alert: Alert) -> crate::Result<()> {
if !alert.is_close_notify() {
return Ok(());
}
let kss = self.key_schedule.write_mut().state_mut();
if kss.cipher_key().is_empty() {
self.stream.write_all(&alert.record_bytes_unencrypted()).await?;
} else {
self.stream.write_all(&alert.record_bytes(kss)?).await?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct TlsConnectOutput<RNG, S, TM, U> {
pub handshake_path: HandshakePath,
pub named_group: NamedGroup,
pub rng: RNG,
pub server_end_point: TlsServerEndPoint,
pub tls_stream: TlsStream<S, TM, true>,
pub uri: U,
}
#[inline]
fn alert<T>(
reader_buffer: &BufStreamReader,
rri: &ReadRecordInfo,
) -> crate::Result<ServerRecordsState<T>> {
let plaintext = reader_buffer.current().get(..rri.plaintext_len).unwrap_or_default();
let dw = &mut TlsDecodeWrapper::from_bytes(plaintext);
Ok(ServerRecordsState::Alert(Alert::decode(dw)?))
}
#[inline]
fn handshake_bytes_adjust(
reader_buffer: &mut BufStreamReader,
rri: &ReadRecordInfo,
(split_begin, split_len): (&mut usize, &mut usize),
) {
let ant_end_idx = reader_buffer.antecedent_end_idx();
if *split_len == 0 {
*split_begin = ant_end_idx;
*split_len = rri.plaintext_len;
} else {
cold_path();
let dest = split_begin.wrapping_add(*split_len);
let src_begin = ant_end_idx;
let src_end = ant_end_idx.wrapping_add(rri.plaintext_len);
reader_buffer.buffer_mut().copy_within(src_begin..src_end, dest);
*split_len = split_len.wrapping_add(rri.plaintext_len);
}
}
#[inline]
fn handshake_bytes_decode<'rb>(
reader_buffer: &'rb BufStreamReader,
(split_begin, split_len): (&mut usize, &mut usize),
) -> crate::Result<Option<(HandshakeTy, Range<usize>, TlsDecodeWrapper<'rb>)>> {
let end_idx = split_begin.wrapping_add(*split_len);
let plaintext = reader_buffer.filled().get(*split_begin..end_idx).unwrap_or_default();
if plaintext.len() < 4 {
cold_path();
return Ok(None);
}
let mut dw = TlsDecodeWrapper::from_bytes(plaintext);
let msg_type = HandshakeTy::try_from(<u8 as Decode<De>>::decode(&mut dw)?)?;
let len: usize = <U24 as Decode<'_, De>>::decode(&mut dw)?.into();
let total_len = 4usize.wrapping_add(len);
if *split_len < total_len {
cold_path();
return Ok(None);
}
*dw.bytes_mut() = dw.bytes().get(..len).unwrap_or_default();
let range = *split_begin..split_begin.wrapping_add(total_len);
*split_begin = split_begin.wrapping_add(range.len());
*split_len = split_len.wrapping_sub(range.len());
Ok(Some((msg_type, range, dw)))
}