use crate::{
calendar::DateTime,
codec::{Decode, Encode},
collections::{ArrayVectorCopy, ArrayVectorU8, Vector},
misc::{
Lease, Secret, SingleTypeStorage,
counter_writer::{CounterWriterBytesTy, u16_write},
},
rng::CryptoRng,
tls::{
AlertDescription, CipherSuite, MaxFragmentLength, NamedGroup, TlsConfig, TlsError, TlsMode,
de::De,
misc::{tls_error_fatal, u8_chunk, u16_chunk},
protocol::{
alpn::Alpn, extension::Extension, extension_ty::ExtensionTy,
key_share_client_hello::KeyShareClientHello, key_share_entry::KeyShareEntry,
named_group::NamedGroupAgreement, protocol_version::ProtocolVersion,
protocol_versions::SupportedVersionsClient, server_name_list::ServerNameList,
signature_algorithms::SignatureAlgorithms,
signature_algorithms_cert::SignatureAlgorithmsCert, supported_groups::SupportedGroups,
},
tls_config::TlsConfigInner,
tls_decode_wrapper::TlsDecodeWrapper,
tls_encode_wrapper::TlsEncodeWrapper,
},
x509::CvPolicy,
};
#[derive(Debug)]
pub(crate) struct ClientHello<G, TC> {
generic: G,
legacy_session_id: ArrayVectorCopy<u8, 32>,
random: [u8; 32],
supported_versions: SupportedVersionsClient,
tls_config: TC,
}
impl<G, TC> ClientHello<G, TC> {
pub(crate) fn new<RNG>(generic: G, rng: &mut RNG, tls_config: TC) -> Self
where
RNG: CryptoRng,
{
Self {
generic,
legacy_session_id: ArrayVectorCopy::from_array({
let mut array = [0; 32];
rng.fill_slice(&mut array);
array
}),
random: {
let mut array = [0u8; 32];
rng.fill_slice(&mut array);
array
},
supported_versions: SupportedVersionsClient::new(ArrayVectorCopy::from_array([
ProtocolVersion::Tls13,
])),
tls_config,
}
}
pub(crate) fn generic(&self) -> &G {
&self.generic
}
pub(crate) fn legacy_session_id(&self) -> &ArrayVectorCopy<u8, 32> {
&self.legacy_session_id
}
pub(crate) fn supported_versions(&self) -> &SupportedVersionsClient {
&self.supported_versions
}
pub(crate) fn tls_config(&self) -> &TC {
&self.tls_config
}
}
impl<'de, TM> Decode<'de, De>
for ClientHello<KeyShareClientHello<&'de [u8]>, TlsConfigInner<&'de [u8], TM>>
where
TM: TlsMode,
{
#[inline]
fn decode(dw: &mut TlsDecodeWrapper<'de>) -> crate::Result<Self> {
let err = TlsError::InvalidClientHelloLength;
let _legacy_version = <[u8; 2] as Decode<'_, De>>::decode(dw)?;
let random = <[u8; 32] as Decode<'de, De>>::decode(dw)?;
let legacy_session_id = u8_chunk(dw, err, |el| Ok(el.bytes()))?.try_into()?;
let mut alpn = None;
let mut cipher_suites = ArrayVectorCopy::new();
let mut key_shares_opt = None;
let mut last_ty = None;
let mut max_fragment_length = None;
let mut server_name = None;
let mut signature_algorithms_opt = None;
let mut signature_algorithms_cert = None;
let mut supported_groups_opt = None;
let mut supported_versions_opt = None;
{
let bytes = u16_chunk(dw, TlsError::InvalidCipherSuite, |el| Ok(el.bytes()))?;
for [b0, b1] in bytes.as_chunks::<2>().0 {
if let Ok(elem) = CipherSuite::try_from(u16::from_be_bytes([*b0, *b1])) {
cipher_suites.push(elem)?;
}
}
}
let _legacy_compression_methods @ Ok([1, 0]) = <[u8; 2] as Decode<'de, De>>::decode(dw) else {
return tls_error_fatal(
TlsError::InvalidLegacyCompressionMethods,
AlertDescription::IllegalParameter,
);
};
u16_chunk(dw, err, |local_dw| {
while !local_dw.bytes().is_empty() {
let Ok(extension_ty) = ExtensionTy::decode(local_dw) else {
u16_chunk(local_dw, err, |_bytes| Ok(()))?;
continue;
};
last_ty = Some(extension_ty);
u16_chunk(local_dw, err, |local_local_dw| {
manage_extension(
&mut alpn,
local_local_dw,
extension_ty,
&mut key_shares_opt,
&mut max_fragment_length,
&mut server_name,
&mut signature_algorithms_opt,
&mut signature_algorithms_cert,
&mut supported_groups_opt,
&mut supported_versions_opt,
)
})?;
}
Ok(())
})?;
let Some(supported_versions) = supported_versions_opt else {
return Err(TlsError::MissingSupportedVersions.into());
};
let Some(signature_algorithms) = signature_algorithms_opt else {
return Err(TlsError::MissingSignatureAlgorithms.into());
};
let Some(supported_groups) = supported_groups_opt else {
return Err(TlsError::MissingSupportedGroups.into());
};
let Some(key_shares) = key_shares_opt else {
return Err(TlsError::MissingKeyShares.into());
};
Ok(Self {
generic: key_shares,
legacy_session_id,
random,
supported_versions,
tls_config: TlsConfigInner {
alpn,
cipher_suites,
cv_policy: CvPolicy::new(DateTime::default()),
max_fragment_length,
max_fragment_length_send: None,
supported_groups,
public_key: Vector::new(),
secret_key: Secret::default(),
server_name,
signature_algorithms,
signature_algorithms_cert,
trust_anchors: Vector::new(),
mode: TM::default(),
},
})
}
}
impl<TC, TM> Encode<De>
for ClientHello<&ArrayVectorU8<NamedGroupAgreement, { NamedGroup::len() }>, TC>
where
TC: Lease<TlsConfig<TM>> + SingleTypeStorage<Item = TM>,
{
#[inline]
fn encode(&self, ew: &mut TlsEncodeWrapper<'_>) -> crate::Result<()> {
let _ = ew.buffer().extend_from_copyable_slices([
u16::from(ProtocolVersion::Tls12).to_be_bytes().as_slice(),
&self.random[..],
&[self.legacy_session_id.len()][..],
&self.legacy_session_id,
u16::from(self.tls_config.lease().inner.cipher_suites.len().wrapping_mul(2))
.to_be_bytes()
.as_slice(),
{
let mut cipher_suites = ArrayVectorCopy::<_, { 2 * CipherSuite::ALL.len() }>::new();
for cipher_suite in &self.tls_config.lease().inner.cipher_suites {
cipher_suites.extend_from_copyable_slice(&u16::from(*cipher_suite).to_be_bytes())?;
}
cipher_suites
}
.as_slice(),
&[1, 0], ])?;
u16_write(CounterWriterBytesTy::IgnoresLen, None, ew, |local_ew| {
if let Some(elem) = &self.tls_config.lease().inner.alpn {
Extension::new(ExtensionTy::ApplicationLayerProtocolNegotiation, elem).encode(local_ew)?;
}
{
let mut client_shares = ArrayVectorU8::new();
for secret in self.generic {
client_shares
.push(KeyShareEntry { group: secret.named_group(), opaque: secret.public_key()? })?;
}
Extension::new(ExtensionTy::KeyShare, KeyShareClientHello { client_shares })
.encode(local_ew)?;
}
if let Some(max_fragment_length) = self.tls_config.lease().inner.max_fragment_length {
Extension::new(ExtensionTy::MaxFragmentLength, max_fragment_length).encode(local_ew)?;
}
if let Some(name) = self.tls_config.lease().inner.server_name.as_ref() {
Extension::new(ExtensionTy::ServerName, name).encode(local_ew)?;
}
Extension::new(
ExtensionTy::SignatureAlgorithms,
&self.tls_config.lease().inner.signature_algorithms,
)
.encode(local_ew)?;
Extension::new(
ExtensionTy::SignatureAlgorithmsCert,
&self.tls_config.lease().inner.signature_algorithms_cert,
)
.encode(local_ew)?;
Extension::new(ExtensionTy::SupportedGroups, &self.tls_config.lease().inner.supported_groups)
.encode(local_ew)?;
Extension::new(ExtensionTy::SupportedVersions, &self.supported_versions).encode(local_ew)?;
crate::Result::Ok(())
})?;
Ok(())
}
}
fn duplicated_error(is_some: bool) -> crate::Result<()> {
if is_some {
return Err(TlsError::DuplicatedClientHelloParameters.into());
}
Ok(())
}
fn manage_extension<'de>(
alpn: &mut Option<Alpn>,
dw: &mut TlsDecodeWrapper<'de>,
extension_ty: ExtensionTy,
key_shares: &mut Option<KeyShareClientHello<&'de [u8]>>,
max_fragment_length: &mut Option<MaxFragmentLength>,
server_name: &mut Option<ServerNameList>,
signature_algorithms: &mut Option<SignatureAlgorithms>,
signature_algorithms_cert: &mut Option<SignatureAlgorithmsCert>,
supported_groups: &mut Option<SupportedGroups>,
supported_versions_opt: &mut Option<SupportedVersionsClient>,
) -> crate::Result<()> {
match extension_ty {
ExtensionTy::ApplicationLayerProtocolNegotiation => {
duplicated_error(alpn.is_some())?;
*alpn = Some(Alpn::decode(dw)?);
}
ExtensionTy::MaxFragmentLength => {
duplicated_error(max_fragment_length.is_some())?;
*max_fragment_length = Some(MaxFragmentLength::decode(dw)?);
}
ExtensionTy::KeyShare => {
duplicated_error(key_shares.is_some())?;
*key_shares = Some(KeyShareClientHello::<&[u8]>::decode(dw)?);
}
ExtensionTy::ServerName => {
duplicated_error(server_name.is_some())?;
*server_name = Some(ServerNameList::decode(dw)?);
}
ExtensionTy::SignatureAlgorithms => {
duplicated_error(signature_algorithms.is_some())?;
*signature_algorithms = Some(SignatureAlgorithms::decode(dw)?);
}
ExtensionTy::SignatureAlgorithmsCert => {
duplicated_error(signature_algorithms_cert.is_some())?;
*signature_algorithms_cert = Some(SignatureAlgorithmsCert::decode(dw)?);
}
ExtensionTy::SupportedGroups => {
duplicated_error(supported_groups.is_some())?;
*supported_groups = Some(SupportedGroups::decode(dw)?);
}
ExtensionTy::SupportedVersions => {
duplicated_error(supported_versions_opt.is_some())?;
*supported_versions_opt = Some(SupportedVersionsClient::decode(dw)?);
}
ExtensionTy::CertificateAuthorities
| ExtensionTy::ClientCertificateType
| ExtensionTy::Cookie
| ExtensionTy::EarlyData
| ExtensionTy::Heartbeat
| ExtensionTy::Padding
| ExtensionTy::PostHandshakeAuth
| ExtensionTy::PreSharedKey
| ExtensionTy::PskKeyExchangeModes
| ExtensionTy::ServerCertificateType
| ExtensionTy::SignedCertificateTimestamp
| ExtensionTy::StatusRequest
| ExtensionTy::UseSrtp => {}
ExtensionTy::OidFilters => {
return Err(TlsError::MismatchedExtension.into());
}
}
Ok(())
}