use serde::{Deserialize, Serialize};
use crate::model::tls::{CipherSuite, NamedGroup};
use crate::parser::record::TlsRecordType;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum RecordDirection {
ClientToServer,
ServerToClient,
}
impl RecordDirection {
#[must_use]
pub const fn arrow(self) -> &'static str {
match self {
RecordDirection::ClientToServer => "\u{2192}",
RecordDirection::ServerToClient => "\u{2190}",
}
}
#[must_use]
pub const fn short(self) -> &'static str {
match self {
RecordDirection::ClientToServer => "C\u{2192}S",
RecordDirection::ServerToClient => "S\u{2192}C",
}
}
}
#[derive(Clone, Debug, Serialize)]
pub struct RecordEvent {
pub direction: RecordDirection,
pub timestamp_ms: u64,
pub sequence: u32,
pub outer_type: TlsRecordType,
pub outer_length: u16,
pub raw: Vec<u8>,
pub body: RecordBody,
}
#[derive(Clone, Debug, Serialize)]
pub enum RecordBody {
Handshake(DecodedHandshake),
EncryptedHandshake {
inferred_label: &'static str,
ciphertext_preview: Vec<u8>,
},
ChangeCipherSpec,
}
#[derive(Clone, Debug, Serialize)]
pub enum DecodedHandshake {
ClientHello(Box<DecodedClientHello>),
ServerHello(Box<DecodedServerHello>),
HelloRetryRequest(Box<DecodedServerHello>),
Unknown {
msg_type: u8,
raw: Vec<u8>,
},
}
impl DecodedHandshake {
#[must_use]
pub const fn label(&self) -> &'static str {
match self {
DecodedHandshake::ClientHello(_) => "ClientHello",
DecodedHandshake::ServerHello(_) => "ServerHello",
DecodedHandshake::HelloRetryRequest(_) => "HelloRetryRequest",
DecodedHandshake::Unknown { msg_type, .. } => handshake_type_name(*msg_type),
}
}
}
#[must_use]
pub const fn handshake_type_name(msg_type: u8) -> &'static str {
match msg_type {
1 => "ClientHello",
2 => "ServerHello",
4 => "NewSessionTicket",
5 => "EndOfEarlyData",
8 => "EncryptedExtensions",
11 => "Certificate",
12 => "ServerKeyExchange",
13 => "CertificateRequest",
14 => "ServerHelloDone",
15 => "CertificateVerify",
16 => "ClientKeyExchange",
20 => "Finished",
22 => "CertificateStatus",
24 => "KeyUpdate",
254 => "MessageHash",
_ => "Unknown handshake",
}
}
#[derive(Clone, Debug, Serialize)]
pub struct DecodedClientHello {
pub legacy_version: u16,
#[serde(with = "serde_bytes_array32")]
pub random: [u8; 32],
pub session_id: Vec<u8>,
pub cipher_suites: Vec<(u16, Option<CipherSuite>)>,
pub compression_methods: Vec<u8>,
pub extensions: Vec<DecodedExtension>,
}
#[derive(Clone, Debug, Serialize)]
pub struct DecodedServerHello {
pub legacy_version: u16,
#[serde(with = "serde_bytes_array32")]
pub random: [u8; 32],
pub session_id_echo: Vec<u8>,
pub cipher_suite: (u16, Option<CipherSuite>),
pub compression_method: u8,
pub extensions: Vec<DecodedExtension>,
}
#[derive(Clone, Debug, Serialize)]
pub struct DecodedExtension {
pub ext_type: u16,
pub name: &'static str,
pub raw: Vec<u8>,
pub body: ExtensionBody,
}
#[derive(Clone, Debug, Serialize)]
pub enum ExtensionBody {
ServerName(Vec<ServerNameEntry>),
SupportedVersions(Vec<u16>),
SignatureAlgorithms(Vec<u16>),
KeyShare(Vec<KeyShareEntry>),
SupportedGroups(Vec<NamedGroup>),
Alpn(Vec<Vec<u8>>),
PskKeyExchangeModes(Vec<u8>),
Cookie(Vec<u8>),
PreSharedKey {
identities: Vec<PskIdentity>,
binders_len: usize,
},
EarlyData,
Opaque,
}
#[derive(Clone, Debug, Serialize)]
pub struct ServerNameEntry {
pub name_type: u8,
pub name: Vec<u8>,
}
#[derive(Clone, Debug, Serialize)]
pub struct KeyShareEntry {
pub group: NamedGroup,
pub group_code: u16,
pub key_exchange: Vec<u8>,
}
#[derive(Clone, Debug, Serialize)]
pub struct PskIdentity {
pub identity: Vec<u8>,
pub obfuscated_ticket_age: u32,
}
mod serde_bytes_array32 {
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8; 32], s: S) -> Result<S::Ok, S::Error> {
s.serialize_bytes(bytes)
}
#[allow(dead_code)]
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 32], D::Error> {
use serde::de::Error;
let v: Vec<u8> = serde::Deserialize::deserialize(d)?;
v.try_into()
.map_err(|_| D::Error::custom("expected 32 bytes"))
}
}