use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::ops::{Deref, DerefMut};
use pki_types::DnsName;
use super::codec::{
Codec, LengthPrefixedBuffer, ListLength, MaybeEmpty, NonEmpty, Reader, SizedPayload,
TlsListElement, TlsListIter,
};
use super::enums::{CertificateStatusType, Compression, ExtensionType, PskKeyExchangeMode};
use super::handshake::{
DuplicateExtensionChecker, Encoding, KeyShareEntry, Random, SessionId, SupportedEcPointFormats,
SupportedProtocolVersions, has_duplicates,
};
use crate::crypto::cipher::Payload;
use crate::crypto::hpke::HpkeSymmetricCipherSuite;
use crate::crypto::kx::NamedGroup;
use crate::crypto::{CipherSuite, SignatureScheme};
use crate::enums::{
ApplicationProtocol, CertificateCompressionAlgorithm, CertificateType, EchClientHelloType,
ProtocolVersion,
};
use crate::error::InvalidMessage;
use crate::log::warn;
use crate::msgs::enums::ServerNameType;
use crate::verify::DistinguishedName;
#[derive(Clone, Debug)]
pub(crate) struct ClientHelloPayload {
pub(crate) client_version: ProtocolVersion,
pub(crate) random: Random,
pub(crate) session_id: SessionId,
pub(crate) cipher_suites: Vec<CipherSuite>,
pub(crate) compression_methods: Vec<Compression>,
pub(crate) extensions: Box<ClientExtensions<'static>>,
}
impl ClientHelloPayload {
pub(crate) fn ech_inner_encoding(&self, to_compress: Vec<ExtensionType>) -> Vec<u8> {
let mut bytes = Vec::new();
self.payload_encode(&mut bytes, Encoding::EchInnerHello { to_compress });
bytes
}
pub(crate) fn payload_encode(&self, bytes: &mut Vec<u8>, purpose: Encoding) {
self.client_version.encode(bytes);
self.random.encode(bytes);
match purpose {
Encoding::EchInnerHello { .. } => SessionId::empty().encode(bytes),
_ => self.session_id.encode(bytes),
}
self.cipher_suites.encode(bytes);
self.compression_methods.encode(bytes);
let to_compress = match purpose {
Encoding::EchInnerHello { to_compress } if !to_compress.is_empty() => to_compress,
_ => {
self.extensions.encode(bytes);
return;
}
};
let mut compressed = self.extensions.clone();
for e in &to_compress {
compressed.clear(*e);
}
compressed.encrypted_client_hello_outer = Some(to_compress);
compressed.encode(bytes);
}
pub(crate) fn has_keyshare_extension_with_duplicates(&self) -> bool {
self.key_shares
.as_ref()
.map(|entries| {
has_duplicates::<_, _, u16>(
entries
.iter()
.map(|kse| u16::from(kse.group)),
)
})
.unwrap_or_default()
}
pub(crate) fn has_certificate_compression_extension_with_duplicates(&self) -> bool {
if let Some(algs) = &self.certificate_compression_algorithms {
has_duplicates::<_, _, u16>(algs.iter().copied())
} else {
false
}
}
}
impl Codec<'_> for ClientHelloPayload {
fn encode(&self, bytes: &mut Vec<u8>) {
self.payload_encode(bytes, Encoding::Standard)
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
r.all("ClientHelloPayload", |r| {
Ok(Self {
client_version: ProtocolVersion::read(r)?,
random: Random::read(r)?,
session_id: SessionId::read(r)?,
cipher_suites: Vec::read(r)?,
compression_methods: Vec::read(r)?,
extensions: Box::new(ClientExtensions::read(r)?.into_owned()),
})
})
}
}
impl Deref for ClientHelloPayload {
type Target = ClientExtensions<'static>;
fn deref(&self) -> &Self::Target {
&self.extensions
}
}
impl DerefMut for ClientHelloPayload {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.extensions
}
}
extension_struct! {
pub(crate) struct ClientExtensions<'a> {
ExtensionType::ServerName =>
pub(crate) server_name: Option<ServerNamePayload<'a>>,
ExtensionType::StatusRequest =>
pub(crate) certificate_status_request: Option<CertificateStatusRequest>,
ExtensionType::EllipticCurves =>
pub(crate) named_groups: Option<Vec<NamedGroup>>,
ExtensionType::ECPointFormats =>
pub(crate) ec_point_formats: Option<SupportedEcPointFormats>,
ExtensionType::SignatureAlgorithms =>
pub(crate) signature_schemes: Option<Vec<SignatureScheme>>,
ExtensionType::ALProtocolNegotiation =>
pub(crate) protocols: Option<Vec<ApplicationProtocol<'a>>>,
ExtensionType::ClientCertificateType =>
pub(crate) client_certificate_types: Option<Vec<CertificateType>>,
ExtensionType::ServerCertificateType =>
pub(crate) server_certificate_types: Option<Vec<CertificateType>>,
ExtensionType::ExtendedMasterSecret =>
pub(crate) extended_master_secret_request: Option<()>,
ExtensionType::CompressCertificate =>
pub(crate) certificate_compression_algorithms: Option<Vec<CertificateCompressionAlgorithm>>,
ExtensionType::SessionTicket =>
pub(crate) session_ticket: Option<ClientSessionTicket>,
ExtensionType::PreSharedKey =>
pub(crate) preshared_key_offer: Option<PresharedKeyOffer>,
ExtensionType::EarlyData =>
pub(crate) early_data_request: Option<()>,
ExtensionType::SupportedVersions =>
pub(crate) supported_versions: Option<SupportedProtocolVersions>,
ExtensionType::Cookie =>
pub(crate) cookie: Option<SizedPayload<'a, u16, NonEmpty>>,
ExtensionType::PSKKeyExchangeModes =>
pub(crate) preshared_key_modes: Option<PskKeyExchangeModes>,
ExtensionType::CertificateAuthorities =>
pub(crate) certificate_authority_names: Option<Vec<DistinguishedName>>,
ExtensionType::KeyShare =>
pub(crate) key_shares: Option<Vec<KeyShareEntry>>,
ExtensionType::TransportParameters =>
pub(crate) transport_parameters: Option<Payload<'a>>,
ExtensionType::TicketRequest =>
pub(crate) ticket_request: Option<ClientTicketRequest>,
ExtensionType::RenegotiationInfo =>
pub(crate) renegotiation_info: Option<SizedPayload<'a, u8>>,
ExtensionType::EncryptedClientHello =>
pub(crate) encrypted_client_hello: Option<EncryptedClientHello>,
ExtensionType::EncryptedClientHelloOuterExtensions =>
pub(crate) encrypted_client_hello_outer: Option<Vec<ExtensionType>>,
} + {
pub(crate) order_seed: u16,
pub(crate) contiguous_extensions: Vec<ExtensionType>,
}
}
impl ClientExtensions<'_> {
pub(crate) fn into_owned(self) -> ClientExtensions<'static> {
let Self {
server_name,
certificate_status_request,
named_groups,
ec_point_formats,
signature_schemes,
protocols,
client_certificate_types,
server_certificate_types,
extended_master_secret_request,
certificate_compression_algorithms,
session_ticket,
preshared_key_offer,
early_data_request,
supported_versions,
cookie,
preshared_key_modes,
certificate_authority_names,
key_shares,
transport_parameters,
ticket_request,
renegotiation_info,
encrypted_client_hello,
encrypted_client_hello_outer,
order_seed,
contiguous_extensions,
} = self;
ClientExtensions {
server_name: server_name.map(|x| x.into_owned()),
certificate_status_request,
named_groups,
ec_point_formats,
signature_schemes,
protocols: protocols.map(|ps| {
ps.into_iter()
.map(|p| p.to_owned())
.collect::<Vec<_>>()
}),
client_certificate_types,
server_certificate_types,
extended_master_secret_request,
certificate_compression_algorithms,
session_ticket,
preshared_key_offer,
early_data_request,
supported_versions,
cookie: cookie.map(|x| x.into_owned()),
preshared_key_modes,
certificate_authority_names,
key_shares,
transport_parameters: transport_parameters.map(|x| x.into_owned()),
ticket_request,
renegotiation_info: renegotiation_info.map(|x| x.into_owned()),
encrypted_client_hello,
encrypted_client_hello_outer,
order_seed,
contiguous_extensions,
}
}
pub(crate) fn used_extensions_in_encoding_order(&self) -> Vec<ExtensionType> {
let mut exts = self.order_insensitive_extensions_in_random_order();
exts.extend(&self.contiguous_extensions);
if self
.encrypted_client_hello_outer
.is_some()
{
exts.push(ExtensionType::EncryptedClientHelloOuterExtensions);
}
if self.encrypted_client_hello.is_some() {
exts.push(ExtensionType::EncryptedClientHello);
}
if self.preshared_key_offer.is_some() {
exts.push(ExtensionType::PreSharedKey);
}
exts
}
fn order_insensitive_extensions_in_random_order(&self) -> Vec<ExtensionType> {
let mut order = self.collect_used();
order.retain(|ext| {
!(matches!(
*ext,
ExtensionType::PreSharedKey
| ExtensionType::EncryptedClientHello
| ExtensionType::EncryptedClientHelloOuterExtensions
) || self.contiguous_extensions.contains(ext))
});
order.sort_by_cached_key(|new_ext| {
let seed = ((self.order_seed as u32) << 16) | (u16::from(*new_ext) as u32);
low_quality_integer_hash(seed)
});
order
}
}
impl<'a> Codec<'a> for ClientExtensions<'a> {
fn encode(&self, bytes: &mut Vec<u8>) {
let order = self.used_extensions_in_encoding_order();
if order.is_empty() {
return;
}
let body = LengthPrefixedBuffer::new(ListLength::U16, bytes);
for item in order {
self.encode_one(item, body.buf);
}
}
fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
let mut out = Self::default();
if !r.any_left() {
return Ok(out);
}
let mut checker = DuplicateExtensionChecker::new();
let len = usize::from(u16::read(r)?);
let mut sub = r.sub(len)?;
while sub.any_left() {
let typ = out.read_one(&mut sub, |unknown| checker.check(unknown))?;
if typ == ExtensionType::PreSharedKey && sub.any_left() {
return Err(InvalidMessage::PreSharedKeyIsNotFinalExtension);
}
}
Ok(out)
}
}
#[derive(Clone, Debug)]
pub(crate) enum EncryptedClientHello {
Outer(EncryptedClientHelloOuter),
Inner,
}
impl Codec<'_> for EncryptedClientHello {
fn encode(&self, bytes: &mut Vec<u8>) {
match self {
Self::Outer(payload) => {
EchClientHelloType::ClientHelloOuter.encode(bytes);
payload.encode(bytes);
}
Self::Inner => {
EchClientHelloType::ClientHelloInner.encode(bytes);
}
}
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
match EchClientHelloType::read(r)? {
EchClientHelloType::ClientHelloOuter => {
Ok(Self::Outer(EncryptedClientHelloOuter::read(r)?))
}
EchClientHelloType::ClientHelloInner => Ok(Self::Inner),
_ => Err(InvalidMessage::InvalidContentType),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct EncryptedClientHelloOuter {
pub cipher_suite: HpkeSymmetricCipherSuite,
pub config_id: u8,
pub enc: SizedPayload<'static, u16, MaybeEmpty>,
pub payload: SizedPayload<'static, u16, NonEmpty>,
}
impl Codec<'_> for EncryptedClientHelloOuter {
fn encode(&self, bytes: &mut Vec<u8>) {
self.cipher_suite.encode(bytes);
self.config_id.encode(bytes);
self.enc.encode(bytes);
self.payload.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
Ok(Self {
cipher_suite: HpkeSymmetricCipherSuite::read(r)?,
config_id: u8::read(r)?,
enc: SizedPayload::read(r)?.into_owned(),
payload: SizedPayload::read(r)?.into_owned(),
})
}
}
#[derive(Clone, Debug)]
pub(crate) enum ServerNamePayload<'a> {
SingleDnsName(DnsName<'a>),
IpAddress,
Invalid,
}
impl ServerNamePayload<'_> {
pub(super) fn into_owned(self) -> ServerNamePayload<'static> {
match self {
Self::SingleDnsName(d) => ServerNamePayload::SingleDnsName(d.to_owned()),
Self::IpAddress => ServerNamePayload::IpAddress,
Self::Invalid => ServerNamePayload::Invalid,
}
}
const SIZE_LEN: ListLength = ListLength::NonZeroU16 {
empty_error: InvalidMessage::IllegalEmptyList("ServerNames"),
};
pub(crate) fn to_dns_name_normalized(&self) -> Option<DnsName<'static>> {
match self {
Self::SingleDnsName(dns_name) => Some(dns_name.to_lowercase_owned()),
Self::IpAddress => None,
Self::Invalid => None,
}
}
}
impl<'a> Codec<'a> for ServerNamePayload<'a> {
fn encode(&self, bytes: &mut Vec<u8>) {
let server_name_list = LengthPrefixedBuffer::new(Self::SIZE_LEN, bytes);
let ServerNamePayload::SingleDnsName(dns_name) = self else {
return;
};
ServerNameType::HostName.encode(server_name_list.buf);
let name_slice = dns_name.as_ref().as_bytes();
(name_slice.len() as u16).encode(server_name_list.buf);
server_name_list
.buf
.extend_from_slice(name_slice);
}
fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
let mut found = None;
let len = Self::SIZE_LEN.read(r)?;
let mut sub = r.sub(len)?;
while sub.any_left() {
let typ = ServerNameType::read(&mut sub)?;
let payload = match typ {
ServerNameType::HostName => HostNamePayload::read(&mut sub)?,
_ => {
sub.rest();
break;
}
};
if found.is_some() {
warn!("Illegal SNI extension: duplicate host_name received");
return Err(InvalidMessage::InvalidServerName);
}
found = match payload {
HostNamePayload::HostName(dns_name) => {
Some(Self::SingleDnsName(dns_name.to_owned()))
}
HostNamePayload::IpAddress(_invalid) => {
warn!("Illegal SNI extension: IP address presented as hostname ({_invalid:?})");
Some(Self::IpAddress)
}
HostNamePayload::Invalid(_invalid) => {
warn!(
"Illegal SNI hostname received {:?}",
String::from_utf8_lossy(_invalid.bytes())
);
Some(Self::Invalid)
}
};
}
Ok(found.unwrap_or(Self::Invalid))
}
}
impl<'a> From<&DnsName<'a>> for ServerNamePayload<'static> {
fn from(value: &DnsName<'a>) -> Self {
Self::SingleDnsName(trim_hostname_trailing_dot_for_sni(value))
}
}
fn trim_hostname_trailing_dot_for_sni(dns_name: &DnsName<'_>) -> DnsName<'static> {
let dns_name_str = dns_name.as_ref();
if dns_name_str.ends_with('.') {
let trimmed = &dns_name_str[0..dns_name_str.len() - 1];
DnsName::try_from(trimmed)
.unwrap()
.to_owned()
} else {
dns_name.to_owned()
}
}
#[derive(Clone, Debug)]
pub(crate) enum HostNamePayload {
HostName(DnsName<'static>),
IpAddress(SizedPayload<'static, u16, NonEmpty>),
Invalid(SizedPayload<'static, u16, NonEmpty>),
}
impl HostNamePayload {
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
use pki_types::ServerName;
let raw = SizedPayload::<u16, NonEmpty>::read(r)?;
match ServerName::try_from(raw.bytes()) {
Ok(ServerName::DnsName(d)) => Ok(Self::HostName(d.to_owned())),
Ok(ServerName::IpAddress(_)) => Ok(Self::IpAddress(raw.into_owned())),
Ok(_) | Err(_) => Ok(Self::Invalid(raw.into_owned())),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum CertificateStatusRequest {
Ocsp(OcspCertificateStatusRequest),
Unknown((CertificateStatusType, Payload<'static>)),
}
impl Codec<'_> for CertificateStatusRequest {
fn encode(&self, bytes: &mut Vec<u8>) {
match self {
Self::Ocsp(r) => r.encode(bytes),
Self::Unknown((typ, payload)) => {
typ.encode(bytes);
payload.encode(bytes);
}
}
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
let typ = CertificateStatusType::read(r)?;
match typ {
CertificateStatusType::OCSP => {
let ocsp_req = OcspCertificateStatusRequest::read(r)?;
Ok(Self::Ocsp(ocsp_req))
}
_ => {
let data = Payload::read(r).into_owned();
Ok(Self::Unknown((typ, data)))
}
}
}
}
impl CertificateStatusRequest {
pub(crate) fn build_ocsp() -> Self {
let ocsp = OcspCertificateStatusRequest {
responder_ids: Vec::new(),
extensions: SizedPayload::from(Payload::new(Vec::new())),
};
Self::Ocsp(ocsp)
}
}
#[derive(Clone, Debug)]
pub(crate) struct OcspCertificateStatusRequest {
pub(crate) responder_ids: Vec<ResponderId>,
pub(crate) extensions: SizedPayload<'static, u16, MaybeEmpty>,
}
impl Codec<'_> for OcspCertificateStatusRequest {
fn encode(&self, bytes: &mut Vec<u8>) {
CertificateStatusType::OCSP.encode(bytes);
self.responder_ids.encode(bytes);
self.extensions.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
Ok(Self {
responder_ids: Vec::read(r)?,
extensions: SizedPayload::read(r)?.into_owned(),
})
}
}
wrapped_payload!(pub(crate) struct ResponderId, SizedPayload<u16, MaybeEmpty>,);
impl TlsListElement for ResponderId {
const SIZE_LEN: ListLength = ListLength::U16;
}
#[derive(Clone, Debug)]
pub(crate) struct PresharedKeyOffer {
pub(crate) identities: Vec<PresharedKeyIdentity>,
pub(crate) binders: Vec<PresharedKeyBinder>,
}
impl PresharedKeyOffer {
pub(crate) fn new(id: PresharedKeyIdentity, binder: Vec<u8>) -> Self {
Self {
identities: vec![id],
binders: vec![PresharedKeyBinder::from(binder)],
}
}
}
impl Codec<'_> for PresharedKeyOffer {
fn encode(&self, bytes: &mut Vec<u8>) {
self.identities.encode(bytes);
self.binders.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
Ok(Self {
identities: Vec::read(r)?,
binders: Vec::read(r)?,
})
}
}
#[derive(Clone, Debug)]
pub(crate) struct PresharedKeyIdentity {
pub(crate) identity: SizedPayload<'static, u16, NonEmpty>,
pub(crate) obfuscated_ticket_age: u32,
}
impl PresharedKeyIdentity {
pub(crate) fn new(id: Vec<u8>, age: u32) -> Self {
Self {
identity: SizedPayload::from(Payload::new(id)),
obfuscated_ticket_age: age,
}
}
}
impl Codec<'_> for PresharedKeyIdentity {
fn encode(&self, bytes: &mut Vec<u8>) {
self.identity.encode(bytes);
self.obfuscated_ticket_age.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
Ok(Self {
identity: SizedPayload::read(r)?.into_owned(),
obfuscated_ticket_age: u32::read(r)?,
})
}
}
impl TlsListElement for PresharedKeyIdentity {
const SIZE_LEN: ListLength = ListLength::NonZeroU16 {
empty_error: InvalidMessage::IllegalEmptyList("PskIdentities"),
};
}
wrapped_payload!(
pub(crate) struct PresharedKeyBinder, SizedPayload<u8, NonEmpty>,
);
impl TlsListElement for PresharedKeyBinder {
const SIZE_LEN: ListLength = ListLength::NonZeroU16 {
empty_error: InvalidMessage::IllegalEmptyList("PskBinders"),
};
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PskKeyExchangeModes {
pub(crate) psk_dhe: bool,
pub(crate) psk: bool,
}
impl Codec<'_> for PskKeyExchangeModes {
fn encode(&self, bytes: &mut Vec<u8>) {
let inner = LengthPrefixedBuffer::new(PskKeyExchangeMode::SIZE_LEN, bytes);
if self.psk_dhe {
PskKeyExchangeMode::PSK_DHE_KE.encode(inner.buf);
}
if self.psk {
PskKeyExchangeMode::PSK_KE.encode(inner.buf);
}
}
fn read(reader: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
let mut psk_dhe = false;
let mut psk = false;
for ke in TlsListIter::<PskKeyExchangeMode>::new(reader)? {
match ke? {
PskKeyExchangeMode::PSK_DHE_KE => psk_dhe = true,
PskKeyExchangeMode::PSK_KE => psk = true,
_ => continue,
};
}
Ok(Self { psk_dhe, psk })
}
}
impl TlsListElement for PskKeyExchangeMode {
const SIZE_LEN: ListLength = ListLength::NonZeroU8 {
empty_error: InvalidMessage::IllegalEmptyList("PskKeyExchangeModes"),
};
}
#[derive(Clone, Debug)]
pub(crate) enum ClientSessionTicket {
Request,
Offer(Payload<'static>),
}
impl<'a> Codec<'a> for ClientSessionTicket {
fn encode(&self, bytes: &mut Vec<u8>) {
match self {
Self::Request => (),
Self::Offer(p) => p.encode(bytes),
}
}
fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
Ok(match r.left() {
0 => Self::Request,
_ => Self::Offer(Payload::read(r).into_owned()),
})
}
}
fn low_quality_integer_hash(mut x: u32) -> u32 {
x = x
.wrapping_add(0x7ed55d16)
.wrapping_add(x << 12);
x = (x ^ 0xc761c23c) ^ (x >> 19);
x = x
.wrapping_add(0x165667b1)
.wrapping_add(x << 5);
x = x.wrapping_add(0xd3a2646c) ^ (x << 9);
x = x
.wrapping_add(0xfd7046c5)
.wrapping_add(x << 3);
x = (x ^ 0xb55a4f09) ^ (x >> 16);
x
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct ClientTicketRequest {
pub(crate) new_session_count: u8,
pub(crate) resumption_count: u8,
}
impl Codec<'_> for ClientTicketRequest {
fn encode(&self, bytes: &mut Vec<u8>) {
self.new_session_count.encode(bytes);
self.resumption_count.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
Ok(Self {
new_session_count: u8::read(r)?,
resumption_count: u8::read(r)?,
})
}
}