use serde::{Deserialize, Serialize};
use std::fmt::Display;
use crate::enums::{
AuthenticatedEncryptionWithAssociatedData, CertificateCompressionAlgorithm,
KeyDerivationFunction,
};
use crate::{
ApplicationProtocol, CipherSuite, ECPointFormat, ExtensionId, ProtocolVersion, SignatureScheme,
SupportedGroup, enums::CompressionAlgorithm,
};
use rama_net::address::Domain;
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
pub struct ClientHello {
pub(super) protocol_version: ProtocolVersion,
pub(super) cipher_suites: Vec<CipherSuite>,
pub(super) compression_algorithms: Vec<CompressionAlgorithm>,
pub(super) extensions: Vec<ClientHelloExtension>,
}
impl ClientHello {
#[must_use]
pub fn new(
protocol_version: ProtocolVersion,
cipher_suites: Vec<CipherSuite>,
compression_algorithms: Vec<CompressionAlgorithm>,
extensions: Vec<ClientHelloExtension>,
) -> Self {
Self {
protocol_version,
cipher_suites,
compression_algorithms,
extensions,
}
}
#[must_use]
pub fn protocol_version(&self) -> ProtocolVersion {
self.protocol_version
}
#[must_use]
pub fn cipher_suites(&self) -> &[CipherSuite] {
&self.cipher_suites[..]
}
#[must_use]
pub fn compression_algorithms(&self) -> &[CompressionAlgorithm] {
&self.compression_algorithms[..]
}
#[must_use]
pub fn extensions(&self) -> &[ClientHelloExtension] {
&self.extensions[..]
}
#[must_use]
pub fn ext_server_name(&self) -> Option<&Domain> {
for ext in &self.extensions {
if let ClientHelloExtension::ServerName(domain) = ext {
return domain.as_ref();
}
}
None
}
#[must_use]
pub fn ext_supported_groups(&self) -> Option<&[SupportedGroup]> {
for ext in &self.extensions {
if let ClientHelloExtension::SupportedGroups(groups) = ext {
return Some(&groups[..]);
}
}
None
}
#[must_use]
pub fn ext_ec_point_formats(&self) -> Option<&[ECPointFormat]> {
for ext in &self.extensions {
if let ClientHelloExtension::ECPointFormats(formats) = ext {
return Some(&formats[..]);
}
}
None
}
#[must_use]
pub fn ext_signature_algorithms(&self) -> Option<&[SignatureScheme]> {
for ext in &self.extensions {
if let ClientHelloExtension::SignatureAlgorithms(algos) = ext {
return Some(&algos[..]);
}
}
None
}
#[must_use]
pub fn ext_alpn(&self) -> Option<&[ApplicationProtocol]> {
for ext in &self.extensions {
if let ClientHelloExtension::ApplicationLayerProtocolNegotiation(alpns) = ext {
return Some(&alpns[..]);
}
}
None
}
#[must_use]
pub fn ext_alps(&self) -> Option<&[ApplicationProtocol]> {
for ext in &self.extensions {
if let ClientHelloExtension::ApplicationSettings { protocols, .. } = ext {
return Some(&protocols[..]);
}
}
None
}
#[must_use]
pub fn supported_versions(&self) -> Option<&[ProtocolVersion]> {
for ext in &self.extensions {
if let ClientHelloExtension::SupportedVersions(versions) = ext {
return Some(&versions[..]);
}
}
None
}
#[must_use]
pub fn has_encrypted_client_hello(&self) -> bool {
self.extensions
.iter()
.any(|ext| matches!(ext, ClientHelloExtension::EncryptedClientHello(_)))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
pub enum ClientHelloExtension {
ServerName(Option<Domain>),
SupportedGroups(Vec<SupportedGroup>),
ECPointFormats(Vec<ECPointFormat>),
SignatureAlgorithms(Vec<SignatureScheme>),
ApplicationLayerProtocolNegotiation(Vec<ApplicationProtocol>),
ApplicationSettings {
protocols: Vec<ApplicationProtocol>,
new_codepoint: bool,
},
SupportedVersions(Vec<ProtocolVersion>),
CertificateCompression(Vec<CertificateCompressionAlgorithm>),
RecordSizeLimit(u16),
DelegatedCredentials(Vec<SignatureScheme>),
EncryptedClientHello(ECHClientHello),
Opaque {
id: ExtensionId,
data: Vec<u8>,
},
}
impl ClientHelloExtension {
#[must_use]
pub fn id(&self) -> ExtensionId {
match self {
Self::ServerName(_) => ExtensionId::SERVER_NAME,
Self::SupportedGroups(_) => ExtensionId::SUPPORTED_GROUPS,
Self::ECPointFormats(_) => ExtensionId::EC_POINT_FORMATS,
Self::SignatureAlgorithms(_) => ExtensionId::SIGNATURE_ALGORITHMS,
Self::ApplicationLayerProtocolNegotiation(_) => {
ExtensionId::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
}
Self::ApplicationSettings { new_codepoint, .. } => {
if *new_codepoint {
ExtensionId::APPLICATION_SETTINGS
} else {
ExtensionId::OLD_APPLICATION_SETTINGS
}
}
Self::SupportedVersions(_) => ExtensionId::SUPPORTED_VERSIONS,
Self::CertificateCompression(_) => ExtensionId::COMPRESS_CERTIFICATE,
Self::DelegatedCredentials(_) => ExtensionId::DELEGATED_CREDENTIAL,
Self::RecordSizeLimit(_) => ExtensionId::RECORD_SIZE_LIMIT,
Self::EncryptedClientHello(_) => ExtensionId::ENCRYPTED_CLIENT_HELLO,
Self::Opaque { id, .. } => *id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
pub enum ECHClientHello {
Outer(ECHClientHelloOuter),
Inner,
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
pub struct ECHClientHelloOuter {
pub cipher_suite: HpkeSymmetricCipherSuite,
pub config_id: u8,
pub enc: Vec<u8>,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
pub struct HpkeSymmetricCipherSuite {
pub kdf_id: KeyDerivationFunction,
pub aead_id: AuthenticatedEncryptionWithAssociatedData,
}
impl Display for HpkeSymmetricCipherSuite {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{},{}", self.kdf_id, self.aead_id)
}
}