#![allow(dead_code)]
use super::{
NamedGroup, ReadCursor, SignatureScheme, handshake::hs_type, put_u8, put_u16, with_len_u8,
with_len_u16, with_len_u24,
};
use crate::tls::Error;
use alloc::vec::Vec;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ServerKeyExchange {
pub(crate) group: NamedGroup,
pub(crate) point: Vec<u8>,
pub(crate) scheme: SignatureScheme,
pub(crate) signature: Vec<u8>,
}
impl ServerKeyExchange {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::new();
put_u8(&mut out, hs_type::SERVER_KEY_EXCHANGE);
with_len_u24(&mut out, |b| {
put_u8(b, 0x03); put_u16(b, self.group.0);
with_len_u8(b, |p| p.extend_from_slice(&self.point));
put_u16(b, self.scheme.0);
with_len_u16(b, |s| s.extend_from_slice(&self.signature));
});
out
}
pub(crate) fn decode(body: &[u8]) -> Result<Self, Error> {
let mut c = ReadCursor::new(body);
let curve_type = c.u8()?;
if curve_type != 0x03 {
return Err(Error::IllegalParameter);
}
let group = NamedGroup(c.u16()?);
let point = c.vec_u8()?.to_vec();
let scheme = SignatureScheme(c.u16()?);
let signature = c.vec_u16()?.to_vec();
c.expect_empty()?;
Ok(ServerKeyExchange {
group,
point,
scheme,
signature,
})
}
}
pub(crate) fn signed_message(
client_random: &[u8; 32],
server_random: &[u8; 32],
group: NamedGroup,
point: &[u8],
) -> Vec<u8> {
let mut out = Vec::with_capacity(32 + 32 + 1 + 2 + 1 + point.len());
out.extend_from_slice(client_random);
out.extend_from_slice(server_random);
out.push(0x03); out.extend_from_slice(&group.0.to_be_bytes());
with_len_u8(&mut out, |p| p.extend_from_slice(point));
out
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ClientKeyExchange {
pub(crate) point: Vec<u8>,
}
impl ClientKeyExchange {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::new();
put_u8(&mut out, hs_type::CLIENT_KEY_EXCHANGE);
with_len_u24(&mut out, |b| {
with_len_u8(b, |p| p.extend_from_slice(&self.point));
});
out
}
pub(crate) fn decode(body: &[u8]) -> Result<Self, Error> {
let mut c = ReadCursor::new(body);
let point = c.vec_u8()?.to_vec();
c.expect_empty()?;
Ok(ClientKeyExchange { point })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct CertificateRequest12 {
pub(crate) cert_types: Vec<u8>,
pub(crate) sig_schemes: Vec<SignatureScheme>,
pub(crate) cas: Vec<Vec<u8>>,
}
impl CertificateRequest12 {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::new();
put_u8(&mut out, hs_type::CERTIFICATE_REQUEST);
with_len_u24(&mut out, |b| {
with_len_u8(b, |t| t.extend_from_slice(&self.cert_types));
with_len_u16(b, |s| {
for sch in &self.sig_schemes {
put_u16(s, sch.0);
}
});
with_len_u16(b, |cas| {
for ca in &self.cas {
with_len_u16(cas, |d| d.extend_from_slice(ca));
}
});
});
out
}
pub(crate) fn decode(body: &[u8]) -> Result<Self, Error> {
let mut c = ReadCursor::new(body);
let cert_types = c.vec_u8()?.to_vec();
let sig_bytes = c.vec_u16()?;
if sig_bytes.len() % 2 != 0 {
return Err(Error::Decode);
}
let mut sig_cur = ReadCursor::new(sig_bytes);
let mut sig_schemes = Vec::with_capacity(sig_bytes.len() / 2);
while !sig_cur.is_empty() {
sig_schemes.push(SignatureScheme(sig_cur.u16()?));
}
let ca_bytes = c.vec_u16()?;
let mut ca_cur = ReadCursor::new(ca_bytes);
let mut cas = Vec::new();
while !ca_cur.is_empty() {
cas.push(ca_cur.vec_u16()?.to_vec());
}
c.expect_empty()?;
Ok(CertificateRequest12 {
cert_types,
sig_schemes,
cas,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub(crate) struct ServerHelloDone;
impl ServerHelloDone {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(4);
put_u8(&mut out, hs_type::SERVER_HELLO_DONE);
with_len_u24(&mut out, |_| {});
out
}
pub(crate) fn decode(body: &[u8]) -> Result<Self, Error> {
if !body.is_empty() {
return Err(Error::Decode);
}
Ok(ServerHelloDone)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct NewSessionTicket12 {
pub(crate) lifetime: u32,
pub(crate) ticket: Vec<u8>,
}
impl NewSessionTicket12 {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::new();
put_u8(&mut out, hs_type::NEW_SESSION_TICKET);
with_len_u24(&mut out, |b| {
b.extend_from_slice(&self.lifetime.to_be_bytes());
with_len_u16(b, |t| t.extend_from_slice(&self.ticket));
});
out
}
pub(crate) fn decode(body: &[u8]) -> Result<Self, Error> {
let mut c = ReadCursor::new(body);
let lifetime = c.take(4)?;
let lifetime = u32::from_be_bytes([lifetime[0], lifetime[1], lifetime[2], lifetime[3]]);
let ticket = c.vec_u16()?.to_vec();
c.expect_empty()?;
Ok(NewSessionTicket12 { lifetime, ticket })
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub(crate) struct HelloRequest;
impl HelloRequest {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(4);
put_u8(&mut out, hs_type::HELLO_REQUEST);
with_len_u24(&mut out, |_| {});
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tls::codec::read_handshake;
fn parse_one(bytes: &[u8], expected_ty: u8) -> Vec<u8> {
let mut c = ReadCursor::new(bytes);
let (ty, body) = read_handshake(&mut c).unwrap();
assert_eq!(ty, expected_ty);
body.to_vec()
}
#[test]
fn server_key_exchange_roundtrip() {
let ske = ServerKeyExchange {
group: NamedGroup::SECP256R1,
point: alloc::vec![0x04; 65], scheme: SignatureScheme::ECDSA_SECP256R1_SHA256,
signature: alloc::vec![0xab; 72],
};
let bytes = ske.encode();
let body = parse_one(&bytes, hs_type::SERVER_KEY_EXCHANGE);
assert_eq!(ServerKeyExchange::decode(&body).unwrap(), ske);
}
#[test]
fn server_key_exchange_truncated() {
let body = [0x03u8, 0x00, 0x17];
assert!(matches!(
ServerKeyExchange::decode(&body),
Err(Error::Decode)
));
}
#[test]
fn server_key_exchange_rejects_explicit_curve() {
let body = [
0x01u8, 0x00, 0x17, ];
assert!(matches!(
ServerKeyExchange::decode(&body),
Err(Error::IllegalParameter)
));
}
#[test]
fn client_key_exchange_roundtrip() {
let cke = ClientKeyExchange {
point: alloc::vec![0x04; 65],
};
let bytes = cke.encode();
let body = parse_one(&bytes, hs_type::CLIENT_KEY_EXCHANGE);
assert_eq!(ClientKeyExchange::decode(&body).unwrap(), cke);
}
#[test]
fn client_key_exchange_truncated() {
let body = [65u8, 0x04];
assert!(matches!(
ClientKeyExchange::decode(&body),
Err(Error::Decode)
));
}
#[test]
fn certificate_request_12_roundtrip() {
let cr = CertificateRequest12 {
cert_types: alloc::vec![1u8, 64u8], sig_schemes: alloc::vec![
SignatureScheme::ED25519,
SignatureScheme::ECDSA_SECP256R1_SHA256,
SignatureScheme::RSA_PSS_RSAE_SHA256,
],
cas: alloc::vec![alloc::vec![0xdeu8; 16], alloc::vec![0xadu8; 8],],
};
let bytes = cr.encode();
let body = parse_one(&bytes, hs_type::CERTIFICATE_REQUEST);
assert_eq!(CertificateRequest12::decode(&body).unwrap(), cr);
}
#[test]
fn certificate_request_12_empty_cas() {
let cr = CertificateRequest12 {
cert_types: alloc::vec![1u8, 64u8],
sig_schemes: alloc::vec![SignatureScheme::ECDSA_SECP256R1_SHA256],
cas: Vec::new(),
};
let bytes = cr.encode();
let body = parse_one(&bytes, hs_type::CERTIFICATE_REQUEST);
assert_eq!(CertificateRequest12::decode(&body).unwrap(), cr);
}
#[test]
fn certificate_request_12_truncated() {
let body = [3u8, 0x01];
assert!(matches!(
CertificateRequest12::decode(&body),
Err(Error::Decode)
));
}
#[test]
fn certificate_request_12_odd_sig_list() {
let body = [
1u8, 0x01, 0x00, 0x03, 0x04, 0x03, 0x05, 0x00, 0x00, ];
assert!(matches!(
CertificateRequest12::decode(&body),
Err(Error::Decode)
));
}
#[test]
fn server_hello_done_roundtrip() {
let bytes = ServerHelloDone.encode();
assert_eq!(bytes, alloc::vec![hs_type::SERVER_HELLO_DONE, 0, 0, 0]);
let body = parse_one(&bytes, hs_type::SERVER_HELLO_DONE);
assert_eq!(ServerHelloDone::decode(&body).unwrap(), ServerHelloDone);
}
#[test]
fn server_hello_done_rejects_nonempty_body() {
assert!(matches!(
ServerHelloDone::decode(&[0x00]),
Err(Error::Decode)
));
}
#[test]
fn hello_request_encode() {
let bytes = HelloRequest.encode();
assert_eq!(bytes, alloc::vec![hs_type::HELLO_REQUEST, 0, 0, 0]);
}
#[test]
fn new_session_ticket_12_roundtrip() {
let nst = NewSessionTicket12 {
lifetime: 7200,
ticket: alloc::vec![0xab; 64],
};
let bytes = nst.encode();
let body = parse_one(&bytes, hs_type::NEW_SESSION_TICKET);
assert_eq!(NewSessionTicket12::decode(&body).unwrap(), nst);
}
#[test]
fn new_session_ticket_12_empty_ticket() {
let nst = NewSessionTicket12 {
lifetime: 0,
ticket: Vec::new(),
};
let bytes = nst.encode();
let body = parse_one(&bytes, hs_type::NEW_SESSION_TICKET);
assert_eq!(NewSessionTicket12::decode(&body).unwrap(), nst);
}
#[test]
fn new_session_ticket_12_truncated() {
let body = [0u8, 0, 0];
assert!(matches!(
NewSessionTicket12::decode(&body),
Err(Error::Decode)
));
}
#[test]
fn signed_message_layout() {
let cr = [0x11u8; 32];
let sr = [0x22u8; 32];
let point = [0x04u8, 0xaa, 0xbb, 0xcc];
let m = signed_message(&cr, &sr, NamedGroup::SECP256R1, &point);
let mut expected = Vec::new();
expected.extend_from_slice(&cr);
expected.extend_from_slice(&sr);
expected.push(0x03); expected.extend_from_slice(&NamedGroup::SECP256R1.0.to_be_bytes());
expected.push(point.len() as u8);
expected.extend_from_slice(&point);
assert_eq!(m, expected);
}
#[test]
fn ec_point_formats_roundtrip() {
use crate::tls::codec::extension::{ec_point_formats, parse_ec_point_formats};
let (_, body) = ec_point_formats();
assert_eq!(body, alloc::vec![1u8, 0u8]);
assert_eq!(parse_ec_point_formats(&body).unwrap(), alloc::vec![0u8]);
}
#[test]
fn ec_point_formats_rejects_length_mismatch() {
use crate::tls::codec::extension::parse_ec_point_formats;
assert!(parse_ec_point_formats(&[1u8, 1u8, 2u8]).is_err());
}
}