#![expect(missing_docs)]
use alloc::boxed::Box;
use alloc::vec::Vec;
use crate::crypto::cipher::{EncodableVersion, Payload, Record, RecordError};
use crate::enums::{ContentType, ContentTypeName, HandshakeType, ProtocolVersion};
use crate::error::{AlertDescription, InvalidMessage};
use crate::verify::DigitallySignedStruct;
#[macro_use]
mod macros;
mod client_hello;
pub(crate) use client_hello::{
CertificateStatusRequest, ClientExtensions, ClientHelloPayload, ClientSessionTicket,
ClientTicketRequest, EncryptedClientHello, EncryptedClientHelloOuter, PresharedKeyBinder,
PresharedKeyIdentity, PresharedKeyOffer, PskKeyExchangeModes, ServerNamePayload,
};
mod codec;
use codec::U24;
pub(crate) use codec::{
CERTIFICATE_MAX_SIZE_LIMIT, Codec, LengthPrefixedBuffer, ListLength, MaybeEmpty, NonEmpty,
Reader, SizedPayload, TlsListElement, hex, put_u16, put_u64,
};
mod deframer;
pub(crate) use deframer::{Deframed, Deframer, Delocator, HandshakeAlignedProof, Locator};
mod enums;
#[cfg(test)]
pub(crate) use enums::ECCurveType;
#[cfg(test)]
pub(crate) use enums::tests::{test_enum8, test_enum8_display, test_enum16};
pub(crate) use enums::{
AlertLevel, AlertLevelName, ClientCertificateType, Compression, ExtensionType, KeyUpdateRequest,
};
mod fragmenter;
pub(crate) use fragmenter::{Fragmenter, MAX_FRAGMENT_LEN};
#[macro_use]
mod handshake;
use handshake::HELLO_RETRY_REQUEST_RANDOM;
pub(crate) use handshake::{
ALL_KEY_EXCHANGE_ALGORITHMS, CertificateChain, CertificatePayloadTls13,
CertificateRequestExtensions, CertificateRequestPayload, CertificateRequestPayloadTls13,
CertificateStatus, ClientDhParams, ClientEcdhParams, ClientExtensionsInput,
ClientKeyExchangeParams, CompressedCertificatePayload, Encoding, HelloRetryRequest,
HelloRetryRequestExtensions, KeyShareEntry, KxDecode, NewSessionTicketPayload,
NewSessionTicketPayloadTls13, Random, ServerExtensionsInput, ServerKeyExchange,
ServerKeyExchangeParams, ServerKeyExchangePayload, SessionId, SingleProtocolName,
SupportedEcPointFormats, SupportedProtocolVersions, TransportParameters,
};
#[cfg(test)]
pub(crate) use handshake::{EcParameters, NewSessionTicketExtensions, ServerEcdhParams};
mod server_hello;
pub(crate) use server_hello::{
EchConfigContents, EchConfigPayload, EncryptedExtensions, HpkeKeyConfig, ServerExtensions,
ServerHelloPayload, ServerTicketRequestHint,
};
#[cfg(test)]
mod handshake_test;
pub mod fuzzing {
pub use super::deframer::fuzz_deframer;
use super::{Codec, Fragmenter, Message, Payload, Reader, Record};
use crate::server::ServerSessionValue;
pub fn fuzz_fragmenter(data: &[u8]) {
let mut rdr = Reader::new(data);
let Ok(record) = Record::<Payload<'_>>::read(&mut rdr) else {
return;
};
let mut frg = Fragmenter::default();
frg.set_max_fragment_size(Some(32))
.unwrap();
for record in frg.fragment(record.typ, record.version, record.payload.bytes().into(), 0) {
Message::try_from(&Record {
typ: record.typ,
version: record.version,
payload: Payload::Owned(record.payload.to_vec()),
})
.ok();
}
}
pub fn fuzz_message(data: &[u8]) {
let mut rdr = Reader::new(data);
let Ok(record) = Record::<Payload<'_>>::read(&mut rdr) else {
return;
};
let Ok(msg) = Message::try_from(&record) else {
return;
};
let expected_version = msg.version.encode();
let enc = Record::<Payload<'_>>::from(msg)
.borrow_outbound()
.to_unencrypted_bytes();
assert_eq!(enc[0], data[0]);
assert_eq!([enc[1], enc[2]], expected_version.to_array());
assert_eq!(&enc[3..], &data[3..data.len() - rdr.left()]);
}
pub fn fuzz_server_session_value(data: &[u8]) {
let mut rdr = Reader::new(data);
let _ = ServerSessionValue::read(&mut rdr);
}
}
#[derive(Debug)]
pub(crate) struct Message<'a> {
pub version: EncodableVersion,
pub payload: MessagePayload<'a>,
}
impl Message<'_> {
pub(crate) fn build_alert(level: AlertLevel, desc: AlertDescription) -> Self {
Self {
version: EncodableVersion::Legacy(ProtocolVersion::TLSv1_2),
payload: MessagePayload::Alert(AlertMessagePayload {
level,
description: desc,
}),
}
}
pub(crate) fn build_key_update_notify() -> Self {
Self {
version: EncodableVersion::Legacy(ProtocolVersion::TLSv1_3),
payload: MessagePayload::handshake(HandshakeMessagePayload(
HandshakePayload::KeyUpdate(KeyUpdateRequest::UpdateNotRequested),
)),
}
}
pub(crate) fn build_key_update_request() -> Self {
Self {
version: EncodableVersion::Legacy(ProtocolVersion::TLSv1_3),
payload: MessagePayload::handshake(HandshakeMessagePayload(
HandshakePayload::KeyUpdate(KeyUpdateRequest::UpdateRequested),
)),
}
}
pub(crate) fn into_owned(self) -> Message<'static> {
let Self { version, payload } = self;
Message {
version,
payload: payload.into_owned(),
}
}
#[cfg(test)]
pub(crate) fn into_wire_bytes(self) -> Vec<u8> {
Record::<Payload<'_>>::from(self)
.borrow_outbound()
.to_unencrypted_bytes()
}
pub(crate) fn handshake_type(&self) -> Option<HandshakeType> {
match &self.payload {
MessagePayload::Handshake { parsed, .. } => Some(parsed.0.handshake_type()),
_ => None,
}
}
}
impl<'a> TryFrom<Record<&'a [u8]>> for Message<'a> {
type Error = InvalidMessage;
fn try_from(plain: Record<&'a [u8]>) -> Result<Self, Self::Error> {
Ok(Self {
version: plain.version,
payload: MessagePayload::new(plain.typ, plain.version.version(), plain.payload)?,
})
}
}
impl<'a> TryFrom<&'a Record<Payload<'a>>> for Message<'a> {
type Error = InvalidMessage;
fn try_from(plain: &'a Record<Payload<'a>>) -> Result<Self, Self::Error> {
Ok(Self {
version: plain.version,
payload: MessagePayload::new(
plain.typ,
plain.version.version(),
plain.payload.bytes(),
)?,
})
}
}
pub(crate) fn read_record_header(
r: &mut Reader<'_>,
) -> Result<(ContentType, ProtocolVersion, u16), RecordError> {
let typ = ContentType::read(r).map_err(|_| RecordError::TooShortForHeader)?;
if ContentTypeName::try_from(typ).is_err() {
return Err(RecordError::InvalidContentType);
}
let version = ProtocolVersion::read(r).map_err(|_| RecordError::TooShortForHeader)?;
if version.0 & 0xff00 != 0x0300 {
return Err(RecordError::UnknownProtocolVersion);
}
let len = u16::read(r).map_err(|_| RecordError::TooShortForHeader)?;
if typ != ContentType::ApplicationData && len == 0 {
return Err(RecordError::InvalidEmptyPayload);
}
if len >= MAX_PAYLOAD {
return Err(RecordError::MessageTooLarge);
}
Ok((typ, version, len))
}
#[non_exhaustive]
#[derive(Debug)]
pub(crate) enum MessagePayload<'a> {
Alert(AlertMessagePayload),
Handshake {
parsed: HandshakeMessagePayload<'a>,
encoded: Payload<'a>,
},
HandshakeFlight(Payload<'a>),
ChangeCipherSpec(ChangeCipherSpecPayload),
ApplicationData(Payload<'a>),
}
impl<'a> MessagePayload<'a> {
pub(crate) fn encode(&self, bytes: &mut Vec<u8>) {
match self {
Self::Alert(x) => x.encode(bytes),
Self::Handshake { encoded, .. } => bytes.extend(encoded.bytes()),
Self::HandshakeFlight(x) => bytes.extend(x.bytes()),
Self::ChangeCipherSpec(x) => x.encode(bytes),
Self::ApplicationData(x) => x.encode(bytes),
}
}
pub(crate) fn handshake(parsed: HandshakeMessagePayload<'a>) -> Self {
Self::Handshake {
encoded: Payload::new(parsed.get_encoding()),
parsed,
}
}
pub(crate) fn new(
typ: ContentType,
vers: ProtocolVersion,
payload: &'a [u8],
) -> Result<Self, InvalidMessage> {
let mut r = Reader::new(payload);
match typ {
ContentType::ApplicationData => Ok(Self::ApplicationData(Payload::Borrowed(payload))),
ContentType::Alert => AlertMessagePayload::read(&mut r).map(MessagePayload::Alert),
ContentType::Handshake => {
HandshakeMessagePayload::read_version(&mut r, vers).map(|parsed| Self::Handshake {
parsed,
encoded: Payload::Borrowed(payload),
})
}
ContentType::ChangeCipherSpec => {
ChangeCipherSpecPayload::read(&mut r).map(MessagePayload::ChangeCipherSpec)
}
_ => Err(InvalidMessage::InvalidContentType),
}
}
pub(crate) fn content_type(&self) -> ContentType {
match self {
Self::Alert(_) => ContentType::Alert,
Self::Handshake { .. } | Self::HandshakeFlight(_) => ContentType::Handshake,
Self::ChangeCipherSpec(_) => ContentType::ChangeCipherSpec,
Self::ApplicationData(_) => ContentType::ApplicationData,
}
}
pub(crate) fn into_owned(self) -> MessagePayload<'static> {
use MessagePayload::*;
match self {
Alert(x) => Alert(x),
Handshake { parsed, encoded } => Handshake {
parsed: parsed.into_owned(),
encoded: encoded.into_owned(),
},
HandshakeFlight(x) => HandshakeFlight(x.into_owned()),
ChangeCipherSpec(x) => ChangeCipherSpec(x),
ApplicationData(x) => ApplicationData(x.into_owned()),
}
}
}
impl From<Message<'_>> for Record<Payload<'_>> {
fn from(msg: Message<'_>) -> Self {
let typ = msg.payload.content_type();
let payload = match msg.payload {
MessagePayload::ApplicationData(payload) => payload.into_owned(),
_ => {
let mut buf = Vec::new();
msg.payload.encode(&mut buf);
Payload::Owned(buf)
}
};
Self {
typ,
version: msg.version,
payload,
}
}
}
#[derive(Debug)]
pub(crate) struct HandshakeMessagePayload<'a>(pub(crate) HandshakePayload<'a>);
impl<'a> Codec<'a> for HandshakeMessagePayload<'a> {
fn encode(&self, bytes: &mut Vec<u8>) {
self.payload_encode(bytes, Encoding::Standard);
}
fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
Self::read_version(r, ProtocolVersion::TLSv1_2)
}
}
impl<'a> HandshakeMessagePayload<'a> {
pub(crate) fn read_version(
r: &mut Reader<'a>,
vers: ProtocolVersion,
) -> Result<Self, InvalidMessage> {
let typ = HandshakeType::read(r)?;
let len = U24::read(r)?.0 as usize;
r.sub(len)?
.all("HandshakeMessagePayload", |sub| {
Ok(Self(match typ {
HandshakeType::HelloRequest if sub.left() == 0 => {
HandshakePayload::HelloRequest
}
HandshakeType::ClientHello => {
HandshakePayload::ClientHello(ClientHelloPayload::read(sub)?)
}
HandshakeType::ServerHello => {
let version = ProtocolVersion::read(sub)?;
let random = Random::read(sub)?;
if random == HELLO_RETRY_REQUEST_RANDOM {
let mut hrr = HelloRetryRequest::read(sub)?;
hrr.legacy_version = version;
HandshakePayload::HelloRetryRequest(hrr)
} else {
let mut shp = ServerHelloPayload::read(sub)?;
shp.legacy_version = version;
shp.random = random;
HandshakePayload::ServerHello(shp)
}
}
HandshakeType::Certificate if vers == ProtocolVersion::TLSv1_3 => {
let p = CertificatePayloadTls13::read(sub)?;
HandshakePayload::CertificateTls13(p)
}
HandshakeType::Certificate => {
HandshakePayload::Certificate(CertificateChain::read(sub)?)
}
HandshakeType::ServerKeyExchange => {
let p = ServerKeyExchangePayload::read(sub)?;
HandshakePayload::ServerKeyExchange(p)
}
HandshakeType::ServerHelloDone => HandshakePayload::ServerHelloDone,
HandshakeType::ClientKeyExchange => {
HandshakePayload::ClientKeyExchange(Payload::read(sub))
}
HandshakeType::CertificateRequest if vers == ProtocolVersion::TLSv1_3 => {
let p = CertificateRequestPayloadTls13::read(sub)?;
HandshakePayload::CertificateRequestTls13(p)
}
HandshakeType::CertificateRequest => {
let p = CertificateRequestPayload::read(sub)?;
HandshakePayload::CertificateRequest(p)
}
HandshakeType::CompressedCertificate => {
HandshakePayload::CompressedCertificate(CompressedCertificatePayload::read(
sub,
)?)
}
HandshakeType::CertificateVerify => {
HandshakePayload::CertificateVerify(DigitallySignedStruct::read(sub)?)
}
HandshakeType::NewSessionTicket if vers == ProtocolVersion::TLSv1_3 => {
let p = NewSessionTicketPayloadTls13::read(sub)?;
HandshakePayload::NewSessionTicketTls13(p)
}
HandshakeType::NewSessionTicket => {
let p = NewSessionTicketPayload::read(sub)?;
HandshakePayload::NewSessionTicket(p)
}
HandshakeType::EncryptedExtensions => HandshakePayload::EncryptedExtensions(
Box::new(EncryptedExtensions::read(sub)?),
),
HandshakeType::KeyUpdate => {
HandshakePayload::KeyUpdate(KeyUpdateRequest::read(sub)?)
}
HandshakeType::EndOfEarlyData => HandshakePayload::EndOfEarlyData,
HandshakeType::Finished => HandshakePayload::Finished(Payload::read(sub)),
HandshakeType::CertificateStatus => {
HandshakePayload::CertificateStatus(CertificateStatus::read(sub)?)
}
HandshakeType::MessageHash => {
return Err(InvalidMessage::UnexpectedMessage("MessageHash"));
}
HandshakeType::HelloRetryRequest => {
return Err(InvalidMessage::UnexpectedMessage("HelloRetryRequest"));
}
_ => HandshakePayload::Unknown((typ, Payload::read(sub))),
}))
})
}
pub(crate) fn encoding_for_binder_signing(&self) -> Vec<u8> {
let mut ret = self.get_encoding();
let ret_len = ret
.len()
.saturating_sub(self.total_binder_length());
ret.truncate(ret_len);
ret
}
pub(crate) fn total_binder_length(&self) -> usize {
match &self.0 {
HandshakePayload::ClientHello(ch) => match &ch.preshared_key_offer {
Some(offer) => {
let mut binders_encoding = Vec::new();
offer
.binders
.encode(&mut binders_encoding);
binders_encoding.len()
}
_ => 0,
},
_ => 0,
}
}
pub(crate) fn payload_encode(&self, bytes: &mut Vec<u8>, encoding: Encoding) {
self.0
.wire_handshake_type()
.encode(bytes);
let nested = LengthPrefixedBuffer::new(
ListLength::U24 {
max: usize::MAX,
error: InvalidMessage::MessageTooLarge,
},
bytes,
);
match &self.0 {
HandshakePayload::ServerHello(payload) => payload.payload_encode(nested.buf, encoding),
HandshakePayload::HelloRetryRequest(payload) => {
payload.payload_encode(nested.buf, encoding)
}
_ => self.0.encode(nested.buf),
}
}
pub(crate) fn build_handshake_hash(hash: &[u8]) -> Self {
Self(HandshakePayload::MessageHash(Payload::new(hash.to_vec())))
}
pub(crate) fn into_owned(self) -> HandshakeMessagePayload<'static> {
HandshakeMessagePayload(self.0.into_owned())
}
}
#[derive(Debug)]
pub(crate) enum HandshakePayload<'a> {
HelloRequest,
ClientHello(ClientHelloPayload),
ServerHello(ServerHelloPayload),
HelloRetryRequest(HelloRetryRequest),
Certificate(CertificateChain<'a>),
CertificateTls13(CertificatePayloadTls13<'a>),
CompressedCertificate(CompressedCertificatePayload<'a>),
ServerKeyExchange(ServerKeyExchangePayload),
CertificateRequest(CertificateRequestPayload),
CertificateRequestTls13(CertificateRequestPayloadTls13),
CertificateVerify(DigitallySignedStruct),
ServerHelloDone,
EndOfEarlyData,
ClientKeyExchange(Payload<'a>),
NewSessionTicket(NewSessionTicketPayload),
NewSessionTicketTls13(NewSessionTicketPayloadTls13),
EncryptedExtensions(Box<EncryptedExtensions<'a>>),
KeyUpdate(KeyUpdateRequest),
Finished(Payload<'a>),
CertificateStatus(CertificateStatus<'a>),
MessageHash(Payload<'a>),
Unknown((HandshakeType, Payload<'a>)),
}
impl HandshakePayload<'_> {
fn encode(&self, bytes: &mut Vec<u8>) {
use self::HandshakePayload::*;
match self {
HelloRequest | ServerHelloDone | EndOfEarlyData => {}
ClientHello(x) => x.encode(bytes),
ServerHello(x) => x.encode(bytes),
HelloRetryRequest(x) => x.encode(bytes),
Certificate(x) => x.encode(bytes),
CertificateTls13(x) => x.encode(bytes),
CompressedCertificate(x) => x.encode(bytes),
ServerKeyExchange(x) => x.encode(bytes),
ClientKeyExchange(x) => x.encode(bytes),
CertificateRequest(x) => x.encode(bytes),
CertificateRequestTls13(x) => x.encode(bytes),
CertificateVerify(x) => x.encode(bytes),
NewSessionTicket(x) => x.encode(bytes),
NewSessionTicketTls13(x) => x.encode(bytes),
EncryptedExtensions(x) => x.encode(bytes),
KeyUpdate(x) => x.encode(bytes),
Finished(x) => x.encode(bytes),
CertificateStatus(x) => x.encode(bytes),
MessageHash(x) => x.encode(bytes),
Unknown((_, x)) => x.encode(bytes),
}
}
pub(crate) fn handshake_type(&self) -> HandshakeType {
use self::HandshakePayload::*;
match self {
HelloRequest => HandshakeType::HelloRequest,
ClientHello(_) => HandshakeType::ClientHello,
ServerHello(_) => HandshakeType::ServerHello,
HelloRetryRequest(_) => HandshakeType::HelloRetryRequest,
Certificate(_) | CertificateTls13(_) => HandshakeType::Certificate,
CompressedCertificate(_) => HandshakeType::CompressedCertificate,
ServerKeyExchange(_) => HandshakeType::ServerKeyExchange,
CertificateRequest(_) | CertificateRequestTls13(_) => HandshakeType::CertificateRequest,
CertificateVerify(_) => HandshakeType::CertificateVerify,
ServerHelloDone => HandshakeType::ServerHelloDone,
EndOfEarlyData => HandshakeType::EndOfEarlyData,
ClientKeyExchange(_) => HandshakeType::ClientKeyExchange,
NewSessionTicket(_) | NewSessionTicketTls13(_) => HandshakeType::NewSessionTicket,
EncryptedExtensions(_) => HandshakeType::EncryptedExtensions,
KeyUpdate(_) => HandshakeType::KeyUpdate,
Finished(_) => HandshakeType::Finished,
CertificateStatus(_) => HandshakeType::CertificateStatus,
MessageHash(_) => HandshakeType::MessageHash,
Unknown((t, _)) => *t,
}
}
fn wire_handshake_type(&self) -> HandshakeType {
match self.handshake_type() {
HandshakeType::HelloRetryRequest => HandshakeType::ServerHello,
other => other,
}
}
fn into_owned(self) -> HandshakePayload<'static> {
use HandshakePayload::*;
match self {
HelloRequest => HelloRequest,
ClientHello(x) => ClientHello(x),
ServerHello(x) => ServerHello(x),
HelloRetryRequest(x) => HelloRetryRequest(x),
Certificate(x) => Certificate(x.into_owned()),
CertificateTls13(x) => CertificateTls13(x.into_owned()),
CompressedCertificate(x) => CompressedCertificate(x.into_owned()),
ServerKeyExchange(x) => ServerKeyExchange(x),
CertificateRequest(x) => CertificateRequest(x),
CertificateRequestTls13(x) => CertificateRequestTls13(x),
CertificateVerify(x) => CertificateVerify(x),
ServerHelloDone => ServerHelloDone,
EndOfEarlyData => EndOfEarlyData,
ClientKeyExchange(x) => ClientKeyExchange(x.into_owned()),
NewSessionTicket(x) => NewSessionTicket(x),
NewSessionTicketTls13(x) => NewSessionTicketTls13(x),
EncryptedExtensions(x) => EncryptedExtensions(Box::new(x.into_owned())),
KeyUpdate(x) => KeyUpdate(x),
Finished(x) => Finished(x.into_owned()),
CertificateStatus(x) => CertificateStatus(x.into_owned()),
MessageHash(x) => MessageHash(x.into_owned()),
Unknown((t, x)) => Unknown((t, x.into_owned())),
}
}
}
#[derive(Debug)]
pub(crate) struct AlertMessagePayload {
pub level: AlertLevel,
pub description: AlertDescription,
}
impl Codec<'_> for AlertMessagePayload {
fn encode(&self, bytes: &mut Vec<u8>) {
self.level.encode(bytes);
self.description.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
r.all("AlertMessagePayload", |r| {
Ok(Self {
level: AlertLevel::read(r)?,
description: AlertDescription::read(r)?,
})
})
}
}
#[derive(Debug)]
pub(crate) struct ChangeCipherSpecPayload;
impl Codec<'_> for ChangeCipherSpecPayload {
fn encode(&self, bytes: &mut Vec<u8>) {
1u8.encode(bytes);
}
fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
r.all("ChangeCipherSpecPayload", |r| {
let typ = u8::read(r)?;
if typ != 1 {
return Err(InvalidMessage::InvalidCcs);
}
Ok(Self)
})
}
}
pub(crate) const HEADER_SIZE: usize = 1 + 2 + 2;
pub(crate) const MAX_PAYLOAD: u16 = 16_384 + 2048;
#[cfg(test)]
mod tests {
use alloc::vec;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::{format, fs, println};
use super::*;
use crate::error::AlertDescription;
#[test]
fn test_read_fuzz_corpus() {
fn corpus_dir() -> PathBuf {
let from_subcrate = Path::new("../fuzz/corpus/message");
let from_root = Path::new("fuzz/corpus/message");
if from_root.is_dir() {
from_root.to_path_buf()
} else {
from_subcrate.to_path_buf()
}
}
for file in fs::read_dir(corpus_dir()).unwrap() {
let mut f = fs::File::open(file.unwrap().path()).unwrap();
let mut bytes = Vec::new();
f.read_to_end(&mut bytes).unwrap();
let mut rd = Reader::new(&bytes);
let record = Record::<Payload<'_>>::read(&mut rd).unwrap();
println!("{record:?}");
let Ok(msg) = Message::try_from(&record) else {
continue;
};
let enc = Record::<Payload<'_>>::from(msg)
.borrow_outbound()
.to_unencrypted_bytes();
assert_eq!(bytes[0], enc[0]);
assert_eq!(&bytes[3..], &enc[3..]);
assert_eq!(rd.left(), 0);
}
}
#[test]
fn can_read_safari_client_hello_with_ip_address_in_sni_extension() {
let bytes = b"\
\x16\x03\x01\x00\xeb\x01\x00\x00\xe7\x03\x03\xb6\x1f\xe4\x3a\x55\
\x90\x3e\xc0\x28\x9c\x12\xe0\x5c\x84\xea\x90\x1b\xfb\x11\xfc\xbd\
\x25\x55\xda\x9f\x51\x93\x1b\x8d\x92\x66\xfd\x00\x00\x2e\xc0\x2c\
\xc0\x2b\xc0\x24\xc0\x23\xc0\x0a\xc0\x09\xcc\xa9\xc0\x30\xc0\x2f\
\xc0\x28\xc0\x27\xc0\x14\xc0\x13\xcc\xa8\x00\x9d\x00\x9c\x00\x3d\
\x00\x3c\x00\x35\x00\x2f\xc0\x08\xc0\x12\x00\x0a\x01\x00\x00\x90\
\xff\x01\x00\x01\x00\x00\x00\x00\x0e\x00\x0c\x00\x00\x09\x31\x32\
\x37\x2e\x30\x2e\x30\x2e\x31\x00\x17\x00\x00\x00\x0d\x00\x18\x00\
\x16\x04\x03\x08\x04\x04\x01\x05\x03\x02\x03\x08\x05\x08\x05\x05\
\x01\x08\x06\x06\x01\x02\x01\x00\x05\x00\x05\x01\x00\x00\x00\x00\
\x33\x74\x00\x00\x00\x12\x00\x00\x00\x10\x00\x30\x00\x2e\x02\x68\
\x32\x05\x68\x32\x2d\x31\x36\x05\x68\x32\x2d\x31\x35\x05\x68\x32\
\x2d\x31\x34\x08\x73\x70\x64\x79\x2f\x33\x2e\x31\x06\x73\x70\x64\
\x79\x2f\x33\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x0b\x00\x02\
\x01\x00\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19";
let mut rd = Reader::new(bytes);
let record = Record::<Payload<'_>>::read(&mut rd).unwrap();
println!("record = {record:?}");
Message::try_from(&record).unwrap();
}
#[test]
fn alert_is_not_handshake() {
let m = Message::build_alert(AlertLevel::Fatal, AlertDescription::DecodeError);
assert_ne!(m.handshake_type(), Some(HandshakeType::ClientHello));
}
#[test]
fn construct_all_types() {
let samples = [
&b"\x14\x03\x04\x00\x01\x01"[..],
&b"\x15\x03\x04\x00\x02\x01\x16"[..],
&b"\x16\x03\x04\x00\x05\x18\x00\x00\x01\x00"[..],
&b"\x17\x03\x04\x00\x04\x11\x22\x33\x44"[..],
&b"\x18\x03\x04\x00\x04\x11\x22\x33\x44"[..],
];
for &bytes in samples.iter() {
let record = Record::<Payload<'_>>::read(&mut Reader::new(bytes)).unwrap();
println!("record = {record:?}");
let m = Message::try_from(&record);
println!("m' = {m:?}");
}
}
#[test]
fn debug_payload() {
assert_eq!("01020304", format!("{:?}", Payload::new(vec![1, 2, 3, 4])));
assert_eq!(
"01020304",
format!("{:?}", SizedPayload::<u8, NonEmpty>::from(vec![1, 2, 3, 4]))
);
assert_eq!(
"01020304",
format!(
"{:?}",
SizedPayload::<u16, MaybeEmpty>::from(vec![1, 2, 3, 4])
)
);
assert_eq!(
"01020304",
format!(
"{:?}",
SizedPayload::<'static, U24, NonEmpty>::from(Payload::new(vec![1, 2, 3, 4]))
)
);
}
#[test]
fn into_wire_format() {
assert_eq!(
Message::build_key_update_request().into_wire_bytes(),
&[0x16, 0x3, 0x3, 0x0, 0x5, 0x18, 0x0, 0x0, 0x1, 0x1]
);
}
#[test]
fn smoketest() {
let bytes = include_bytes!("../testdata/handshake-test.1.bin");
let mut r = Reader::new(bytes);
while r.any_left() {
let record = Record::<Payload<'_>>::read(&mut r).unwrap();
let out = record
.borrow_outbound()
.to_unencrypted_bytes();
assert!(!out.is_empty());
Message::try_from(&record).unwrap();
}
}
}