Initial commit
All checks were successful
CI / build (push) Successful in 27s
CI / no-std (push) Successful in 25s
CI / clippy (push) Successful in 26s
CI / test (push) Successful in 43s

This commit is contained in:
2026-02-21 09:01:54 +00:00
committed by Kris Kwiatkowski
commit dac2ea8c95
80 changed files with 11796 additions and 0 deletions

242
src/handshake/mod.rs Normal file
View File

@@ -0,0 +1,242 @@
use crate::ProtocolError;
use crate::config::TlsCipherSuite;
use crate::handshake::certificate::CertificateRef;
use crate::handshake::certificate_request::CertificateRequestRef;
use crate::handshake::certificate_verify::{HandshakeVerify, HandshakeVerifyRef};
use crate::handshake::client_hello::ClientHello;
use crate::handshake::encrypted_extensions::EncryptedExtensions;
use crate::handshake::finished::Finished;
use crate::handshake::new_session_ticket::NewSessionTicket;
use crate::handshake::server_hello::ServerHello;
use crate::key_schedule::HashOutputSize;
use crate::parse_buffer::{ParseBuffer, ParseError};
use crate::{buffer::CryptoBuffer, key_schedule::WriteKeySchedule};
use core::fmt::{Debug, Formatter};
use sha2::Digest;
pub mod binder;
pub mod certificate;
pub mod certificate_request;
pub mod certificate_verify;
pub mod client_hello;
pub mod encrypted_extensions;
pub mod finished;
pub mod new_session_ticket;
pub mod server_hello;
// TLS legacy_record_version field — always 0x0303 for TLS 1.3 compatibility (RFC 8446 §5.1)
const LEGACY_VERSION: u16 = 0x0303;
type Random = [u8; 32];
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HandshakeType {
ClientHello = 1,
ServerHello = 2,
NewSessionTicket = 4,
EndOfEarlyData = 5,
EncryptedExtensions = 8,
Certificate = 11,
CertificateRequest = 13,
HandshakeVerify = 15,
Finished = 20,
KeyUpdate = 24,
MessageHash = 254,
}
impl HandshakeType {
pub fn parse(buf: &mut ParseBuffer) -> Result<Self, ParseError> {
match buf.read_u8()? {
1 => Ok(HandshakeType::ClientHello),
2 => Ok(HandshakeType::ServerHello),
4 => Ok(HandshakeType::NewSessionTicket),
5 => Ok(HandshakeType::EndOfEarlyData),
8 => Ok(HandshakeType::EncryptedExtensions),
11 => Ok(HandshakeType::Certificate),
13 => Ok(HandshakeType::CertificateRequest),
15 => Ok(HandshakeType::HandshakeVerify),
20 => Ok(HandshakeType::Finished),
24 => Ok(HandshakeType::KeyUpdate),
254 => Ok(HandshakeType::MessageHash),
_ => Err(ParseError::InvalidData),
}
}
}
#[allow(clippy::large_enum_variant)]
pub enum ClientHandshake<'config, 'a, CipherSuite>
where
CipherSuite: TlsCipherSuite,
{
ClientCert(CertificateRef<'a>),
ClientCertVerify(HandshakeVerify),
ClientHello(ClientHello<'config, CipherSuite>),
Finished(Finished<HashOutputSize<CipherSuite>>),
}
impl<CipherSuite> ClientHandshake<'_, '_, CipherSuite>
where
CipherSuite: TlsCipherSuite,
{
fn handshake_type(&self) -> HandshakeType {
match self {
ClientHandshake::ClientHello(_) => HandshakeType::ClientHello,
ClientHandshake::Finished(_) => HandshakeType::Finished,
ClientHandshake::ClientCert(_) => HandshakeType::Certificate,
ClientHandshake::ClientCertVerify(_) => HandshakeType::HandshakeVerify,
}
}
fn encode_inner(&self, buf: &mut CryptoBuffer<'_>) -> Result<(), ProtocolError> {
match self {
ClientHandshake::ClientHello(inner) => inner.encode(buf),
ClientHandshake::Finished(inner) => inner.encode(buf),
ClientHandshake::ClientCert(inner) => inner.encode(buf),
ClientHandshake::ClientCertVerify(inner) => inner.encode(buf),
}
}
pub(crate) fn encode(&self, buf: &mut CryptoBuffer<'_>) -> Result<(), ProtocolError> {
buf.push(self.handshake_type() as u8)
.map_err(|_| ProtocolError::EncodeError)?;
// Handshake message body is preceded by a 3-byte (u24) length (RFC 8446 §4)
buf.with_u24_length(|buf| self.encode_inner(buf))
}
pub fn finalize(
&self,
buf: &mut CryptoBuffer,
transcript: &mut CipherSuite::Hash,
write_key_schedule: &mut WriteKeySchedule<CipherSuite>,
) -> Result<(), ProtocolError> {
let enc_buf = buf.as_mut_slice();
if let ClientHandshake::ClientHello(hello) = self {
hello.finalize(enc_buf, transcript, write_key_schedule)
} else {
transcript.update(enc_buf);
Ok(())
}
}
pub fn finalize_encrypted(buf: &mut CryptoBuffer, transcript: &mut CipherSuite::Hash) {
let enc_buf = buf.as_slice();
let end = enc_buf.len();
transcript.update(&enc_buf[0..end]);
}
}
#[allow(clippy::large_enum_variant)]
pub enum ServerHandshake<'a, CipherSuite: TlsCipherSuite> {
ServerHello(ServerHello<'a>),
EncryptedExtensions(EncryptedExtensions<'a>),
NewSessionTicket(NewSessionTicket<'a>),
Certificate(CertificateRef<'a>),
CertificateRequest(CertificateRequestRef<'a>),
HandshakeVerify(HandshakeVerifyRef<'a>),
Finished(Finished<HashOutputSize<CipherSuite>>),
}
impl<CipherSuite: TlsCipherSuite> ServerHandshake<'_, CipherSuite> {
#[allow(dead_code)]
pub fn handshake_type(&self) -> HandshakeType {
match self {
ServerHandshake::ServerHello(_) => HandshakeType::ServerHello,
ServerHandshake::EncryptedExtensions(_) => HandshakeType::EncryptedExtensions,
ServerHandshake::NewSessionTicket(_) => HandshakeType::NewSessionTicket,
ServerHandshake::Certificate(_) => HandshakeType::Certificate,
ServerHandshake::CertificateRequest(_) => HandshakeType::CertificateRequest,
ServerHandshake::HandshakeVerify(_) => HandshakeType::HandshakeVerify,
ServerHandshake::Finished(_) => HandshakeType::Finished,
}
}
}
impl<CipherSuite: TlsCipherSuite> Debug for ServerHandshake<'_, CipherSuite> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
ServerHandshake::ServerHello(inner) => Debug::fmt(inner, f),
ServerHandshake::EncryptedExtensions(inner) => Debug::fmt(inner, f),
ServerHandshake::Certificate(inner) => Debug::fmt(inner, f),
ServerHandshake::CertificateRequest(inner) => Debug::fmt(inner, f),
ServerHandshake::HandshakeVerify(inner) => Debug::fmt(inner, f),
ServerHandshake::Finished(inner) => Debug::fmt(inner, f),
ServerHandshake::NewSessionTicket(inner) => Debug::fmt(inner, f),
}
}
}
#[cfg(feature = "defmt")]
impl<'a, CipherSuite: TlsCipherSuite> defmt::Format for ServerHandshake<'a, CipherSuite> {
fn format(&self, f: defmt::Formatter<'_>) {
match self {
ServerHandshake::ServerHello(inner) => defmt::write!(f, "{}", inner),
ServerHandshake::EncryptedExtensions(inner) => defmt::write!(f, "{}", inner),
ServerHandshake::Certificate(inner) => defmt::write!(f, "{}", inner),
ServerHandshake::CertificateRequest(inner) => defmt::write!(f, "{}", inner),
ServerHandshake::HandshakeVerify(inner) => defmt::write!(f, "{}", inner),
ServerHandshake::Finished(inner) => defmt::write!(f, "{}", inner),
ServerHandshake::NewSessionTicket(inner) => defmt::write!(f, "{}", inner),
}
}
}
impl<'a, CipherSuite: TlsCipherSuite> ServerHandshake<'a, CipherSuite> {
pub fn read(
buf: &mut ParseBuffer<'a>,
digest: &mut CipherSuite::Hash,
) -> Result<Self, ProtocolError> {
let handshake_start = buf.offset();
let mut handshake = Self::parse(buf)?;
let handshake_end = buf.offset();
// Capture the current transcript hash into Finished before we update it with this message
if let ServerHandshake::Finished(finished) = &mut handshake {
finished.hash.replace(digest.clone().finalize());
}
digest.update(&buf.as_slice()[handshake_start..handshake_end]);
Ok(handshake)
}
fn parse(buf: &mut ParseBuffer<'a>) -> Result<Self, ProtocolError> {
let handshake_type =
HandshakeType::parse(buf).map_err(|_| ProtocolError::InvalidHandshake)?;
trace!("handshake = {:?}", handshake_type);
let content_len = buf
.read_u24()
.map_err(|_| ProtocolError::InvalidHandshake)?;
let handshake = match handshake_type {
HandshakeType::ServerHello => ServerHandshake::ServerHello(ServerHello::parse(buf)?),
HandshakeType::NewSessionTicket => {
ServerHandshake::NewSessionTicket(NewSessionTicket::parse(buf)?)
}
HandshakeType::EncryptedExtensions => {
ServerHandshake::EncryptedExtensions(EncryptedExtensions::parse(buf)?)
}
HandshakeType::Certificate => ServerHandshake::Certificate(CertificateRef::parse(buf)?),
HandshakeType::CertificateRequest => {
ServerHandshake::CertificateRequest(CertificateRequestRef::parse(buf)?)
}
HandshakeType::HandshakeVerify => {
ServerHandshake::HandshakeVerify(HandshakeVerifyRef::parse(buf)?)
}
HandshakeType::Finished => {
ServerHandshake::Finished(Finished::parse(buf, content_len)?)
}
t => {
warn!("Unimplemented handshake type: {:?}", t);
return Err(ProtocolError::Unimplemented);
}
};
Ok(handshake)
}
}