refactor: 将 DTLS 本地依赖迁移到 libs 目录

This commit is contained in:
mofeng-git
2026-10-01 22:46:36 +08:00
parent 2f60e61dd5
commit 4f59e78179
99 changed files with 2 additions and 2 deletions

View File

@@ -0,0 +1,241 @@
#[cfg(test)]
mod handshake_cache_test;
use std::collections::HashMap;
use std::io::BufReader;
use std::sync::Arc;
use sha2::{Digest, Sha256};
use tokio::sync::Mutex;
use crate::cipher_suite::*;
use crate::handshake::*;
#[derive(Clone, Debug)]
pub(crate) struct HandshakeCacheItem {
typ: HandshakeType,
is_client: bool,
epoch: u16,
message_sequence: u16,
data: Vec<u8>,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct HandshakeCachePullRule {
pub(crate) typ: HandshakeType,
pub(crate) epoch: u16,
pub(crate) is_client: bool,
pub(crate) optional: bool,
}
#[derive(Clone)]
pub(crate) struct HandshakeCache {
cache: Arc<Mutex<Vec<HandshakeCacheItem>>>,
}
impl HandshakeCache {
pub(crate) fn new() -> Self {
HandshakeCache {
cache: Arc::new(Mutex::new(vec![])),
}
}
pub(crate) async fn push(
&mut self,
data: Vec<u8>,
epoch: u16,
message_sequence: u16,
typ: HandshakeType,
is_client: bool,
) -> bool {
let mut cache = self.cache.lock().await;
for i in &*cache {
if i.message_sequence == message_sequence && i.is_client == is_client {
return false;
}
}
cache.push(HandshakeCacheItem {
typ,
is_client,
epoch,
message_sequence,
data,
});
true
}
// returns a list handshakes that match the requested rules
// the list will contain null entries for rules that can't be satisfied
// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies)
pub(crate) async fn pull(&self, rules: &[HandshakeCachePullRule]) -> Vec<HandshakeCacheItem> {
let cache = self.cache.lock().await;
let mut out = vec![];
for r in rules {
let mut item: Option<HandshakeCacheItem> = None;
for c in &*cache {
if c.typ == r.typ && c.is_client == r.is_client && c.epoch == r.epoch {
if let Some(x) = &item {
if x.message_sequence < c.message_sequence {
item = Some(c.clone());
}
} else {
item = Some(c.clone());
}
}
}
if let Some(c) = item {
out.push(c);
}
}
out
}
// full_pull_map pulls all handshakes between rules[0] to rules[len(rules)-1] as map.
pub(crate) async fn full_pull_map(
&self,
start_seq: isize,
rules: &[HandshakeCachePullRule],
) -> Result<(isize, HashMap<HandshakeType, HandshakeMessage>)> {
let cache = self.cache.lock().await;
let mut ci = HashMap::new();
for r in rules {
let mut item: Option<HandshakeCacheItem> = None;
for c in &*cache {
if c.typ == r.typ && c.is_client == r.is_client && c.epoch == r.epoch {
if let Some(x) = &item {
if x.message_sequence < c.message_sequence {
item = Some(c.clone());
}
} else {
item = Some(c.clone());
}
}
}
if !r.optional && item.is_none() {
// Missing mandatory message.
return Err(Error::Other("Missing mandatory message".to_owned()));
}
if let Some(c) = item {
ci.insert(r.typ, c);
}
}
let mut out = HashMap::new();
let mut seq = start_seq;
for r in rules {
let t = r.typ;
if let Some(i) = ci.get(&t) {
let mut reader = BufReader::new(i.data.as_slice());
let raw_handshake = Handshake::unmarshal(&mut reader)?;
if seq as u16 != raw_handshake.handshake_header.message_sequence {
// There is a gap. Some messages are not arrived.
return Err(Error::Other(
"There is a gap. Some messages are not arrived.".to_owned(),
));
}
seq += 1;
out.insert(t, raw_handshake.handshake_message);
}
}
Ok((seq, out))
}
// pull_and_merge calls pull and then merges the results, ignoring any null entries
pub(crate) async fn pull_and_merge(&self, rules: &[HandshakeCachePullRule]) -> Vec<u8> {
let mut merged = vec![];
for p in &self.pull(rules).await {
merged.extend_from_slice(&p.data);
}
merged
}
// session_hash returns the session hash for Extended Master Secret support
// https://tools.ietf.org/html/draft-ietf-tls-session-hash-06#section-4
pub(crate) async fn session_hash(
&self,
hf: CipherSuiteHash,
epoch: u16,
additional: &[u8],
) -> Result<Vec<u8>> {
let mut merged = vec![];
// Order defined by https://tools.ietf.org/html/rfc5246#section-7.3
let handshake_buffer = self
.pull(&[
HandshakeCachePullRule {
typ: HandshakeType::ClientHello,
epoch,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::ServerHello,
epoch,
is_client: false,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::Certificate,
epoch,
is_client: false,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::ServerKeyExchange,
epoch,
is_client: false,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::CertificateRequest,
epoch,
is_client: false,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::ServerHelloDone,
epoch,
is_client: false,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::Certificate,
epoch,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: HandshakeType::ClientKeyExchange,
epoch,
is_client: true,
optional: false,
},
])
.await;
for p in &handshake_buffer {
merged.extend_from_slice(&p.data);
}
merged.extend_from_slice(additional);
let mut hasher = match hf {
CipherSuiteHash::Sha256 => Sha256::new(),
};
hasher.update(&merged);
let result = hasher.finalize();
Ok(result.as_slice().to_vec())
}
}

View File

@@ -0,0 +1,658 @@
use super::*;
#[tokio::test]
async fn test_handshake_cache_single_push() -> Result<()> {
let tests = vec![
(
"Single Push",
vec![HandshakeCacheItem {
typ: 0.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
}],
vec![HandshakeCachePullRule {
typ: 0.into(),
epoch: 0,
is_client: true,
optional: false,
}],
vec![0x00],
),
(
"Multi Push",
vec![
HandshakeCacheItem {
typ: 0.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: 2.into(),
is_client: true,
epoch: 0,
message_sequence: 2,
data: vec![0x02],
},
],
vec![
HandshakeCachePullRule {
typ: 0.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 2.into(),
epoch: 0,
is_client: true,
optional: false,
},
],
vec![0x00, 0x01, 0x02],
),
(
"Multi Push, Rules set order",
vec![
HandshakeCacheItem {
typ: 2.into(),
is_client: true,
epoch: 0,
message_sequence: 2,
data: vec![0x02],
},
HandshakeCacheItem {
typ: 0.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
],
vec![
HandshakeCachePullRule {
typ: 0.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 2.into(),
epoch: 0,
is_client: true,
optional: false,
},
],
vec![0x00, 0x01, 0x02],
),
(
"Multi Push, Dupe Seqnum",
vec![
HandshakeCacheItem {
typ: 0.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
],
vec![
HandshakeCachePullRule {
typ: 0.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: true,
optional: false,
},
],
vec![0x00, 0x01],
),
(
"Multi Push, Dupe Seqnum Client/Server",
vec![
HandshakeCacheItem {
typ: 0.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: 1.into(),
is_client: false,
epoch: 0,
message_sequence: 1,
data: vec![0x02],
},
],
vec![
HandshakeCachePullRule {
typ: 0.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: false,
optional: false,
},
],
vec![0x00, 0x01, 0x02],
),
(
"Multi Push, Dupe Seqnum with Unique HandshakeType",
vec![
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: 2.into(),
is_client: true,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: 3.into(),
is_client: false,
epoch: 0,
message_sequence: 0,
data: vec![0x02],
},
],
vec![
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 2.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 3.into(),
epoch: 0,
is_client: false,
optional: false,
},
],
vec![0x00, 0x01, 0x02],
),
(
"Multi Push, Wrong epoch",
vec![
HandshakeCacheItem {
typ: 1.into(),
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: 2.into(),
is_client: true,
epoch: 1,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: 2.into(),
is_client: true,
epoch: 0,
message_sequence: 2,
data: vec![0x11],
},
HandshakeCacheItem {
typ: 3.into(),
is_client: false,
epoch: 0,
message_sequence: 0,
data: vec![0x02],
},
HandshakeCacheItem {
typ: 3.into(),
is_client: false,
epoch: 1,
message_sequence: 0,
data: vec![0x12],
},
HandshakeCacheItem {
typ: 3.into(),
is_client: false,
epoch: 2,
message_sequence: 0,
data: vec![0x12],
},
],
vec![
HandshakeCachePullRule {
typ: 1.into(),
epoch: 0,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 2.into(),
epoch: 1,
is_client: true,
optional: false,
},
HandshakeCachePullRule {
typ: 3.into(),
epoch: 0,
is_client: false,
optional: false,
},
],
vec![0x00, 0x01, 0x02],
),
];
for (name, inputs, rules, expected) in tests {
let mut h = HandshakeCache::new();
for i in inputs {
h.push(i.data, i.epoch, i.message_sequence, i.typ, i.is_client)
.await;
}
let verify_data = h.pull_and_merge(&rules).await;
assert_eq!(
verify_data, expected,
"handshakeCache '{name}' exp:{expected:?} actual {verify_data:?}",
);
}
Ok(())
}
#[tokio::test]
async fn test_handshake_cache_session_hash() -> Result<()> {
let tests = vec![
(
"Standard Handshake",
vec![
HandshakeCacheItem {
typ: HandshakeType::ClientHello,
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHello,
is_client: false,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: HandshakeType::Certificate,
is_client: false,
epoch: 0,
message_sequence: 2,
data: vec![0x02],
},
HandshakeCacheItem {
typ: HandshakeType::ServerKeyExchange,
is_client: false,
epoch: 0,
message_sequence: 3,
data: vec![0x03],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHelloDone,
is_client: false,
epoch: 0,
message_sequence: 4,
data: vec![0x04],
},
HandshakeCacheItem {
typ: HandshakeType::ClientKeyExchange,
is_client: true,
epoch: 0,
message_sequence: 5,
data: vec![0x05],
},
],
vec![
0x17, 0xe8, 0x8d, 0xb1, 0x87, 0xaf, 0xd6, 0x2c, 0x16, 0xe5, 0xde, 0xbf, 0x3e, 0x65,
0x27, 0xcd, 0x00, 0x6b, 0xc0, 0x12, 0xbc, 0x90, 0xb5, 0x1a, 0x81, 0x0c, 0xd8, 0x0c,
0x2d, 0x51, 0x1f, 0x43,
],
),
(
"Handshake With Client Cert Request",
vec![
HandshakeCacheItem {
typ: HandshakeType::ClientHello,
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHello,
is_client: false,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: HandshakeType::Certificate,
is_client: false,
epoch: 0,
message_sequence: 2,
data: vec![0x02],
},
HandshakeCacheItem {
typ: HandshakeType::ServerKeyExchange,
is_client: false,
epoch: 0,
message_sequence: 3,
data: vec![0x03],
},
HandshakeCacheItem {
typ: HandshakeType::CertificateRequest,
is_client: false,
epoch: 0,
message_sequence: 4,
data: vec![0x04],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHelloDone,
is_client: false,
epoch: 0,
message_sequence: 5,
data: vec![0x05],
},
HandshakeCacheItem {
typ: HandshakeType::ClientKeyExchange,
is_client: true,
epoch: 0,
message_sequence: 6,
data: vec![0x06],
},
],
vec![
0x57, 0x35, 0x5a, 0xc3, 0x30, 0x3c, 0x14, 0x8f, 0x11, 0xae, 0xf7, 0xcb, 0x17, 0x94,
0x56, 0xb9, 0x23, 0x2c, 0xde, 0x33, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x2f, 0xcb, 0x93,
0x25, 0x74, 0x9a, 0x6b,
],
),
(
"Handshake Ignores after ClientKeyExchange",
vec![
HandshakeCacheItem {
typ: HandshakeType::ClientHello,
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHello,
is_client: false,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: HandshakeType::Certificate,
is_client: false,
epoch: 0,
message_sequence: 2,
data: vec![0x02],
},
HandshakeCacheItem {
typ: HandshakeType::ServerKeyExchange,
is_client: false,
epoch: 0,
message_sequence: 3,
data: vec![0x03],
},
HandshakeCacheItem {
typ: HandshakeType::CertificateRequest,
is_client: false,
epoch: 0,
message_sequence: 4,
data: vec![0x04],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHelloDone,
is_client: false,
epoch: 0,
message_sequence: 5,
data: vec![0x05],
},
HandshakeCacheItem {
typ: HandshakeType::ClientKeyExchange,
is_client: true,
epoch: 0,
message_sequence: 6,
data: vec![0x06],
},
HandshakeCacheItem {
typ: HandshakeType::CertificateVerify,
is_client: true,
epoch: 0,
message_sequence: 7,
data: vec![0x07],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: true,
epoch: 1,
message_sequence: 7,
data: vec![0x08],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: false,
epoch: 1,
message_sequence: 7,
data: vec![0x09],
},
],
vec![
0x57, 0x35, 0x5a, 0xc3, 0x30, 0x3c, 0x14, 0x8f, 0x11, 0xae, 0xf7, 0xcb, 0x17, 0x94,
0x56, 0xb9, 0x23, 0x2c, 0xde, 0x33, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x2f, 0xcb, 0x93,
0x25, 0x74, 0x9a, 0x6b,
],
),
(
"Handshake Ignores wrong epoch",
vec![
HandshakeCacheItem {
typ: HandshakeType::ClientHello,
is_client: true,
epoch: 0,
message_sequence: 0,
data: vec![0x00],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHello,
is_client: false,
epoch: 0,
message_sequence: 1,
data: vec![0x01],
},
HandshakeCacheItem {
typ: HandshakeType::Certificate,
is_client: false,
epoch: 0,
message_sequence: 2,
data: vec![0x02],
},
HandshakeCacheItem {
typ: HandshakeType::ServerKeyExchange,
is_client: false,
epoch: 0,
message_sequence: 3,
data: vec![0x03],
},
HandshakeCacheItem {
typ: HandshakeType::CertificateRequest,
is_client: false,
epoch: 0,
message_sequence: 4,
data: vec![0x04],
},
HandshakeCacheItem {
typ: HandshakeType::ServerHelloDone,
is_client: false,
epoch: 0,
message_sequence: 5,
data: vec![0x05],
},
HandshakeCacheItem {
typ: HandshakeType::ClientKeyExchange,
is_client: true,
epoch: 0,
message_sequence: 6,
data: vec![0x06],
},
HandshakeCacheItem {
typ: HandshakeType::CertificateVerify,
is_client: true,
epoch: 0,
message_sequence: 7,
data: vec![0x07],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: true,
epoch: 0,
message_sequence: 7,
data: vec![0xf0],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: false,
epoch: 0,
message_sequence: 7,
data: vec![0xf1],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: true,
epoch: 1,
message_sequence: 7,
data: vec![0x08],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: false,
epoch: 1,
message_sequence: 7,
data: vec![0x09],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: true,
epoch: 0,
message_sequence: 7,
data: vec![0xf0],
},
HandshakeCacheItem {
typ: HandshakeType::Finished,
is_client: false,
epoch: 0,
message_sequence: 7,
data: vec![0xf1],
},
],
vec![
0x57, 0x35, 0x5a, 0xc3, 0x30, 0x3c, 0x14, 0x8f, 0x11, 0xae, 0xf7, 0xcb, 0x17, 0x94,
0x56, 0xb9, 0x23, 0x2c, 0xde, 0x33, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x2f, 0xcb, 0x93,
0x25, 0x74, 0x9a, 0x6b,
],
),
];
for (name, inputs, expected) in tests {
let mut h = HandshakeCache::new();
for i in inputs {
h.push(i.data, i.epoch, i.message_sequence, i.typ, i.is_client)
.await;
}
let verify_data = h.session_hash(CipherSuiteHash::Sha256, 0, &[]).await?;
assert_eq!(
verify_data, expected,
"handshakeCacheSessionHassh '{name}' exp: {expected:?} actual {verify_data:?}"
);
}
Ok(())
}

View File

@@ -0,0 +1,50 @@
use std::io::{Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::*;
// msg_len for Handshake messages assumes an extra 12 bytes for
// sequence, Fragment and version information
pub(crate) const HANDSHAKE_HEADER_LENGTH: usize = 12;
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct HandshakeHeader {
pub(crate) handshake_type: HandshakeType,
pub(crate) length: u32, // uint24 in spec
pub(crate) message_sequence: u16,
pub(crate) fragment_offset: u32, // uint24 in spec
pub(crate) fragment_length: u32, // uint24 in spec
}
impl HandshakeHeader {
pub fn size(&self) -> usize {
1 + 3 + 2 + 3 + 3
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_u8(self.handshake_type as u8)?;
writer.write_u24::<BigEndian>(self.length)?;
writer.write_u16::<BigEndian>(self.message_sequence)?;
writer.write_u24::<BigEndian>(self.fragment_offset)?;
writer.write_u24::<BigEndian>(self.fragment_length)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let handshake_type = reader.read_u8()?.into();
let length = reader.read_u24::<BigEndian>()?;
let message_sequence = reader.read_u16::<BigEndian>()?;
let fragment_offset = reader.read_u24::<BigEndian>()?;
let fragment_length = reader.read_u24::<BigEndian>()?;
Ok(HandshakeHeader {
handshake_type,
length,
message_sequence,
fragment_offset,
fragment_length,
})
}
}

View File

@@ -0,0 +1,70 @@
use std::io::{Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::*;
#[cfg(test)]
mod handshake_message_certificate_test;
const HANDSHAKE_MESSAGE_CERTIFICATE_LENGTH_FIELD_SIZE: usize = 3;
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct HandshakeMessageCertificate {
pub(crate) certificate: Vec<Vec<u8>>,
}
impl HandshakeMessageCertificate {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::Certificate
}
pub fn size(&self) -> usize {
let mut len = 3;
for r in &self.certificate {
len += HANDSHAKE_MESSAGE_CERTIFICATE_LENGTH_FIELD_SIZE + r.len();
}
len
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
let mut payload_size = 0;
for r in &self.certificate {
payload_size += HANDSHAKE_MESSAGE_CERTIFICATE_LENGTH_FIELD_SIZE + r.len();
}
// Total Payload Size
writer.write_u24::<BigEndian>(payload_size as u32)?;
for r in &self.certificate {
// Certificate Length
writer.write_u24::<BigEndian>(r.len() as u32)?;
// Certificate body
writer.write_all(r)?;
}
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let mut certificate: Vec<Vec<u8>> = vec![];
let payload_size = reader.read_u24::<BigEndian>()? as usize;
let mut offset = 0;
while offset < payload_size {
let certificate_len = reader.read_u24::<BigEndian>()? as usize;
offset += HANDSHAKE_MESSAGE_CERTIFICATE_LENGTH_FIELD_SIZE;
let mut buf = vec![0; certificate_len];
reader.read_exact(&mut buf)?;
offset += certificate_len;
certificate.push(buf);
}
Ok(HandshakeMessageCertificate { certificate })
}
}

View File

@@ -0,0 +1,79 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_certificate() -> Result<()> {
let raw_certificate = vec![
0x00, 0x01, 0x8c, 0x00, 0x01, 0x89, 0x30, 0x82, 0x01, 0x85, 0x30, 0x82, 0x01, 0x2b, 0x02,
0x14, 0x7d, 0x00, 0xcf, 0x07, 0xfc, 0xe2, 0xb6, 0xb8, 0x3f, 0x72, 0xeb, 0x11, 0x36, 0x1b,
0xf6, 0x39, 0xf1, 0x3c, 0x33, 0x41, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x04, 0x03, 0x02, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53,
0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30,
0x1e, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x30, 0x32, 0x35, 0x30, 0x38, 0x35, 0x31, 0x31, 0x32,
0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x35, 0x30, 0x38, 0x35, 0x31, 0x31, 0x32,
0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d,
0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x59, 0x30,
0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xf9, 0xb1, 0x62, 0xd6, 0x07, 0xae,
0xc3, 0x36, 0x34, 0xf5, 0xa3, 0x09, 0x39, 0x86, 0xe7, 0x3b, 0x59, 0xf7, 0x4a, 0x1d, 0xf4,
0x97, 0x4f, 0x91, 0x40, 0x56, 0x1b, 0x3d, 0x6c, 0x5a, 0x38, 0x10, 0x15, 0x58, 0xf5, 0xa4,
0xcc, 0xdf, 0xd5, 0xf5, 0x4a, 0x35, 0x40, 0x0f, 0x9f, 0x54, 0xb7, 0xe9, 0xe2, 0xae, 0x63,
0x83, 0x6a, 0x4c, 0xfc, 0xc2, 0x5f, 0x78, 0xa0, 0xbb, 0x46, 0x54, 0xa4, 0xda, 0x30, 0x0a,
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45,
0x02, 0x20, 0x47, 0x1a, 0x5f, 0x58, 0x2a, 0x74, 0x33, 0x6d, 0xed, 0xac, 0x37, 0x21, 0xfa,
0x76, 0x5a, 0x4d, 0x78, 0x68, 0x1a, 0xdd, 0x80, 0xa4, 0xd4, 0xb7, 0x7f, 0x7d, 0x78, 0xb3,
0xfb, 0xf3, 0x95, 0xfb, 0x02, 0x21, 0x00, 0xc0, 0x73, 0x30, 0xda, 0x2b, 0xc0, 0x0c, 0x9e,
0xb2, 0x25, 0x0d, 0x46, 0xb0, 0xbc, 0x66, 0x7f, 0x71, 0x66, 0xbf, 0x16, 0xb3, 0x80, 0x78,
0xd0, 0x0c, 0xef, 0xcc, 0xf5, 0xc1, 0x15, 0x0f, 0x58,
];
let mut reader = BufReader::new(raw_certificate.as_slice());
let c = HandshakeMessageCertificate::unmarshal(&mut reader)?;
//TODO: add x509 parse
// certificate, err := x509.ParseCertificate(c.certificate[0])
// if err != nil {
// t.Error(err)
// }
// copyCertificatePrivateMembers(certificate, parsedCertificate)
// if !reflect.DeepEqual(certificate, parsedCertificate) {
// t.Errorf("handshakeMessageCertificate unmarshal: got %#v, want %#v", c, parsedCertificate)
// }
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_certificate,
"handshakeMessageCertificate marshal: got {raw:?}, want {raw_certificate:?}"
);
Ok(())
}
#[test]
fn test_empty_handshake_message_certificate() -> Result<()> {
let raw_certificate = vec![0x00, 0x00, 0x00];
let expected_certificate = HandshakeMessageCertificate {
certificate: vec![],
};
let mut reader = BufReader::new(raw_certificate.as_slice());
let c = HandshakeMessageCertificate::unmarshal(&mut reader)?;
assert_eq!(
c, expected_certificate,
"handshakeMessageCertificate unmarshal: got {c:?}, want {expected_certificate:?}",
);
Ok(())
}

View File

@@ -0,0 +1,77 @@
#[cfg(test)]
mod handshake_message_certificate_request_test;
use std::io::{Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::*;
use crate::client_certificate_type::*;
use crate::signature_hash_algorithm::*;
/*
A non-anonymous server can optionally request a certificate from
the client, if appropriate for the selected cipher suite. This
message, if sent, will immediately follow the ServerKeyExchange
message (if it is sent; otherwise, this message follows the
server's Certificate message).
*/
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageCertificateRequest {
pub(crate) certificate_types: Vec<ClientCertificateType>,
pub(crate) signature_hash_algorithms: Vec<SignatureHashAlgorithm>,
}
const HANDSHAKE_MESSAGE_CERTIFICATE_REQUEST_MIN_LENGTH: usize = 5;
impl HandshakeMessageCertificateRequest {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::CertificateRequest
}
pub fn size(&self) -> usize {
1 + self.certificate_types.len() + 2 + self.signature_hash_algorithms.len() * 2 + 2
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_u8(self.certificate_types.len() as u8)?;
for v in &self.certificate_types {
writer.write_u8(*v as u8)?;
}
writer.write_u16::<BigEndian>(2 * self.signature_hash_algorithms.len() as u16)?;
for v in &self.signature_hash_algorithms {
writer.write_u8(v.hash as u8)?;
writer.write_u8(v.signature as u8)?;
}
writer.write_all(&[0x00, 0x00])?; // Distinguished Names Length
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let certificate_types_length = reader.read_u8()?;
let mut certificate_types = vec![];
for _ in 0..certificate_types_length {
let cert_type = reader.read_u8()?.into();
certificate_types.push(cert_type);
}
let signature_hash_algorithms_length = reader.read_u16::<BigEndian>()?;
let mut signature_hash_algorithms = vec![];
for _ in (0..signature_hash_algorithms_length).step_by(2) {
let hash = reader.read_u8()?.into();
let signature = reader.read_u8()?.into();
signature_hash_algorithms.push(SignatureHashAlgorithm { hash, signature });
}
Ok(HandshakeMessageCertificateRequest {
certificate_types,
signature_hash_algorithms,
})
}
}

View File

@@ -0,0 +1,64 @@
use std::io::{BufReader, BufWriter};
use super::*;
use crate::signature_hash_algorithm::*;
#[test]
fn test_handshake_message_certificate_request() -> Result<()> {
let raw_certificate_request = vec![
0x02, 0x01, 0x40, 0x00, 0x0C, 0x04, 0x03, 0x04, 0x01, 0x05, 0x03, 0x05, 0x01, 0x06, 0x01,
0x02, 0x01, 0x00, 0x00,
];
let parsed_certificate_request = HandshakeMessageCertificateRequest {
certificate_types: vec![
ClientCertificateType::RsaSign,
ClientCertificateType::EcdsaSign,
],
signature_hash_algorithms: vec![
SignatureHashAlgorithm {
hash: HashAlgorithm::Sha256,
signature: SignatureAlgorithm::Ecdsa,
},
SignatureHashAlgorithm {
hash: HashAlgorithm::Sha256,
signature: SignatureAlgorithm::Rsa,
},
SignatureHashAlgorithm {
hash: HashAlgorithm::Sha384,
signature: SignatureAlgorithm::Ecdsa,
},
SignatureHashAlgorithm {
hash: HashAlgorithm::Sha384,
signature: SignatureAlgorithm::Rsa,
},
SignatureHashAlgorithm {
hash: HashAlgorithm::Sha512,
signature: SignatureAlgorithm::Rsa,
},
SignatureHashAlgorithm {
hash: HashAlgorithm::Sha1,
signature: SignatureAlgorithm::Rsa,
},
],
};
let mut reader = BufReader::new(raw_certificate_request.as_slice());
let c = HandshakeMessageCertificateRequest::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_certificate_request,
"parsedCertificateRequest unmarshal: got {c:?}, want {parsed_certificate_request:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_certificate_request,
"parsedCertificateRequest marshal: got {raw:?}, want {raw_certificate_request:?}"
);
Ok(())
}

View File

@@ -0,0 +1,52 @@
#[cfg(test)]
mod handshake_message_certificate_verify_test;
use std::io::{Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::*;
use crate::signature_hash_algorithm::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageCertificateVerify {
pub(crate) algorithm: SignatureHashAlgorithm,
pub(crate) signature: Vec<u8>,
}
const HANDSHAKE_MESSAGE_CERTIFICATE_VERIFY_MIN_LENGTH: usize = 4;
impl HandshakeMessageCertificateVerify {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::CertificateVerify
}
pub fn size(&self) -> usize {
1 + 1 + 2 + self.signature.len()
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_u8(self.algorithm.hash as u8)?;
writer.write_u8(self.algorithm.signature as u8)?;
writer.write_u16::<BigEndian>(self.signature.len() as u16)?;
writer.write_all(&self.signature)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let hash_algorithm = reader.read_u8()?.into();
let signature_algorithm = reader.read_u8()?.into();
let signature_length = reader.read_u16::<BigEndian>()? as usize;
let mut signature = vec![0; signature_length];
reader.read_exact(&mut signature)?;
Ok(HandshakeMessageCertificateVerify {
algorithm: SignatureHashAlgorithm {
hash: hash_algorithm,
signature: signature_algorithm,
},
signature,
})
}
}

View File

@@ -0,0 +1,40 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_certificate_request() -> Result<()> {
let raw_certificate_verify = vec![
0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x6b, 0x63, 0x17, 0xad, 0xbe, 0xb7, 0x7b,
0x0f, 0x86, 0x73, 0x39, 0x1e, 0xba, 0xb3, 0x50, 0x9c, 0xce, 0x9c, 0xe4, 0x8b, 0xe5, 0x13,
0x07, 0x59, 0x18, 0x1f, 0xe5, 0xa0, 0x2b, 0xca, 0xa6, 0xad, 0x02, 0x21, 0x00, 0xd3, 0xb5,
0x01, 0xbe, 0x87, 0x6c, 0x04, 0xa1, 0xdc, 0x28, 0xaa, 0x5f, 0xf7, 0x1e, 0x9c, 0xc0, 0x1e,
0x00, 0x2c, 0xe5, 0x94, 0xbb, 0x03, 0x0e, 0xf1, 0xcb, 0x28, 0x22, 0x33, 0x23, 0x88, 0xad,
];
let parsed_certificate_verify = HandshakeMessageCertificateVerify {
algorithm: SignatureHashAlgorithm {
hash: raw_certificate_verify[0].into(),
signature: raw_certificate_verify[1].into(),
},
signature: raw_certificate_verify[4..].to_vec(),
};
let mut reader = BufReader::new(raw_certificate_verify.as_slice());
let c = HandshakeMessageCertificateVerify::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_certificate_verify,
"handshakeMessageCertificate unmarshal: got {c:?}, want {parsed_certificate_verify:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_certificate_verify,
"handshakeMessageCertificateVerify marshal: got {raw:?}, want {raw_certificate_verify:?}"
);
Ok(())
}

View File

@@ -0,0 +1,196 @@
#[cfg(test)]
mod handshake_message_client_hello_test;
use std::fmt;
use std::io::{BufReader, BufWriter};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::handshake_random::*;
use super::*;
use crate::cipher_suite::*;
use crate::compression_methods::*;
use crate::extension::*;
use crate::record_layer::record_layer_header::*;
/*
When a client first connects to a server it is required to send
the client hello as its first message. The client can also send a
client hello in response to a hello request or on its own
initiative in order to renegotiate the security parameters in an
existing connection.
*/
#[derive(Clone)]
pub struct HandshakeMessageClientHello {
pub(crate) version: ProtocolVersion,
pub(crate) random: HandshakeRandom,
pub(crate) cookie: Vec<u8>,
pub(crate) cipher_suites: Vec<CipherSuiteId>,
pub(crate) compression_methods: CompressionMethods,
pub(crate) extensions: Vec<Extension>,
}
impl PartialEq for HandshakeMessageClientHello {
fn eq(&self, other: &Self) -> bool {
if !(self.version == other.version
&& self.random == other.random
&& self.cookie == other.cookie
&& self.compression_methods == other.compression_methods
&& self.extensions == other.extensions
&& self.cipher_suites.len() == other.cipher_suites.len())
{
return false;
}
for i in 0..self.cipher_suites.len() {
if self.cipher_suites[i] != other.cipher_suites[i] {
return false;
}
}
true
}
}
impl fmt::Debug for HandshakeMessageClientHello {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut cipher_suites_str = String::new();
for cipher_suite in &self.cipher_suites {
cipher_suites_str += &cipher_suite.to_string();
cipher_suites_str += " ";
}
let s = [
format!("version: {:?} random: {:?}", self.version, self.random),
format!("cookie: {:?}", self.cookie),
format!("cipher_suites: {cipher_suites_str:?}"),
format!("compression_methods: {:?}", self.compression_methods),
format!("extensions: {:?}", self.extensions),
];
write!(f, "{}", s.join(" "))
}
}
const HANDSHAKE_MESSAGE_CLIENT_HELLO_VARIABLE_WIDTH_START: usize = 34;
impl HandshakeMessageClientHello {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::ClientHello
}
pub fn size(&self) -> usize {
let mut len = 0;
len += 2; // version.major+minor
len += self.random.size();
// SessionID
len += 1;
len += 1 + self.cookie.len();
len += 2 + 2 * self.cipher_suites.len();
len += self.compression_methods.size();
len += 2;
for extension in &self.extensions {
len += extension.size();
}
len
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
if self.cookie.len() > 255 {
return Err(Error::ErrCookieTooLong);
}
writer.write_u8(self.version.major)?;
writer.write_u8(self.version.minor)?;
self.random.marshal(writer)?;
// SessionID
writer.write_u8(0x00)?;
writer.write_u8(self.cookie.len() as u8)?;
writer.write_all(&self.cookie)?;
writer.write_u16::<BigEndian>(2 * self.cipher_suites.len() as u16)?;
for cipher_suite in &self.cipher_suites {
writer.write_u16::<BigEndian>(*cipher_suite as u16)?;
}
self.compression_methods.marshal(writer)?;
let mut extension_buffer = vec![];
{
let mut extension_writer = BufWriter::<&mut Vec<u8>>::new(extension_buffer.as_mut());
for extension in &self.extensions {
extension.marshal(&mut extension_writer)?;
}
}
writer.write_u16::<BigEndian>(extension_buffer.len() as u16)?;
writer.write_all(&extension_buffer)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let major = reader.read_u8()?;
let minor = reader.read_u8()?;
let random = HandshakeRandom::unmarshal(reader)?;
// Session ID
reader.read_u8()?;
let cookie_len = reader.read_u8()? as usize;
let mut cookie = vec![0; cookie_len];
reader.read_exact(&mut cookie)?;
let cipher_suites_len = reader.read_u16::<BigEndian>()? as usize / 2;
let mut cipher_suites = vec![];
for _ in 0..cipher_suites_len {
let id: CipherSuiteId = reader.read_u16::<BigEndian>()?.into();
//let cipher_suite = cipher_suite_for_id(id)?;
cipher_suites.push(id);
}
let compression_methods = CompressionMethods::unmarshal(reader)?;
let mut extensions = vec![];
let extension_buffer_len = reader.read_u16::<BigEndian>()? as usize;
let mut extension_buffer = vec![0u8; extension_buffer_len];
reader.read_exact(&mut extension_buffer)?;
let mut offset = 0;
while offset < extension_buffer_len {
let mut extension_reader = BufReader::new(&extension_buffer[offset..]);
if let Ok(extension) = Extension::unmarshal(&mut extension_reader) {
extensions.push(extension);
} else {
log::warn!(
"Unsupported Extension Type {} {}",
extension_buffer[offset],
extension_buffer[offset + 1]
);
}
let extension_len =
u16::from_be_bytes([extension_buffer[offset + 2], extension_buffer[offset + 3]])
as usize;
offset += 4 + extension_len;
}
Ok(HandshakeMessageClientHello {
version: ProtocolVersion { major, minor },
random,
cookie,
cipher_suites,
compression_methods,
extensions,
})
}
}

View File

@@ -0,0 +1,75 @@
use std::io::{BufReader, BufWriter};
use std::time::{Duration, SystemTime};
use super::*;
use crate::curve::named_curve::*;
use crate::extension::extension_supported_elliptic_curves::*;
#[test]
fn test_handshake_message_client_hello() -> Result<()> {
let raw_client_hello = vec![
0xfe, 0xfd, 0xb6, 0x2f, 0xce, 0x5c, 0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42,
0x62, 0x15, 0xad, 0x16, 0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec,
0xd8, 0x3d, 0xdc, 0x4b, 0x00, 0x14, 0xe6, 0x14, 0x3a, 0x1b, 0x04, 0xea, 0x9e, 0x7a, 0x14,
0xd6, 0x6c, 0x57, 0xd0, 0x0e, 0x32, 0x85, 0x76, 0x18, 0xde, 0xd8, 0x00, 0x04, 0xc0, 0x2b,
0xc0, 0x0a, 0x01, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x1d,
];
let gmt_unix_time = if let Some(unix_time) =
SystemTime::UNIX_EPOCH.checked_add(Duration::new(3056586332u64, 0))
{
unix_time
} else {
SystemTime::UNIX_EPOCH
};
let parsed_client_hello = HandshakeMessageClientHello {
version: ProtocolVersion {
major: 0xFE,
minor: 0xFD,
},
random: HandshakeRandom {
gmt_unix_time,
random_bytes: [
0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42, 0x62, 0x15, 0xad, 0x16, 0xc9,
0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec, 0xd8, 0x3d, 0xdc, 0x4b,
],
},
cookie: vec![
0xe6, 0x14, 0x3a, 0x1b, 0x04, 0xea, 0x9e, 0x7a, 0x14, 0xd6, 0x6c, 0x57, 0xd0, 0x0e,
0x32, 0x85, 0x76, 0x18, 0xde, 0xd8,
],
cipher_suites: vec![
CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256,
CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_256_Cbc_Sha,
//Box::<CipherSuiteTLSEcdheEcdsaWithAes128GcmSha256>::default(),
//Box::<CipherSuiteTLSEcdheEcdsaWithAes256CbcSha>::default(),
],
compression_methods: CompressionMethods {
ids: vec![CompressionMethodId::Null],
},
extensions: vec![Extension::SupportedEllipticCurves(
ExtensionSupportedEllipticCurves {
elliptic_curves: vec![NamedCurve::X25519],
},
)],
};
let mut reader = BufReader::new(raw_client_hello.as_slice());
let c = HandshakeMessageClientHello::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_client_hello,
"handshakeMessageClientHello unmarshal: got {c:?}, want {parsed_client_hello:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_client_hello,
"handshakeMessageClientHello marshal: got {raw:?}, want {raw_client_hello:?}"
);
Ok(())
}

View File

@@ -0,0 +1,70 @@
#[cfg(test)]
mod handshake_message_client_key_exchange_test;
use std::io::{Read, Write};
use byteorder::{BigEndian, WriteBytesExt};
use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageClientKeyExchange {
pub(crate) identity_hint: Vec<u8>,
pub(crate) public_key: Vec<u8>,
}
impl HandshakeMessageClientKeyExchange {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::ClientKeyExchange
}
pub fn size(&self) -> usize {
if !self.public_key.is_empty() {
1 + self.public_key.len()
} else {
2 + self.identity_hint.len()
}
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
if (!self.identity_hint.is_empty() && !self.public_key.is_empty())
|| (self.identity_hint.is_empty() && self.public_key.is_empty())
{
return Err(Error::ErrInvalidClientKeyExchange);
}
if !self.public_key.is_empty() {
writer.write_u8(self.public_key.len() as u8)?;
writer.write_all(&self.public_key)?;
} else {
writer.write_u16::<BigEndian>(self.identity_hint.len() as u16)?;
writer.write_all(&self.identity_hint)?;
}
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let mut data = vec![];
reader.read_to_end(&mut data)?;
// If parsed as PSK return early and only populate PSK Identity Hint
let psk_length = ((data[0] as u16) << 8) | data[1] as u16;
if data.len() == psk_length as usize + 2 {
return Ok(HandshakeMessageClientKeyExchange {
identity_hint: data[2..].to_vec(),
public_key: vec![],
});
}
let public_key_length = data[0] as usize;
if data.len() != public_key_length + 1 {
return Err(Error::ErrBufferTooSmall);
}
Ok(HandshakeMessageClientKeyExchange {
identity_hint: vec![],
public_key: data[1..].to_vec(),
})
}
}

View File

@@ -0,0 +1,35 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_client_key_exchange() -> Result<()> {
let raw_client_key_exchange = vec![
0x20, 0x26, 0x78, 0x4a, 0x78, 0x70, 0xc1, 0xf9, 0x71, 0xea, 0x50, 0x4a, 0xb5, 0xbb, 0x00,
0x76, 0x02, 0x05, 0xda, 0xf7, 0xd0, 0x3f, 0xe3, 0xf7, 0x4e, 0x8a, 0x14, 0x6f, 0xb7, 0xe0,
0xc0, 0xff, 0x54,
];
let parsed_client_key_exchange = HandshakeMessageClientKeyExchange {
identity_hint: vec![],
public_key: raw_client_key_exchange[1..].to_vec(),
};
let mut reader = BufReader::new(raw_client_key_exchange.as_slice());
let c = HandshakeMessageClientKeyExchange::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_client_key_exchange,
"parsedCertificateRequest unmarshal: got {c:?}, want {parsed_client_key_exchange:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_client_key_exchange,
"handshakeMessageClientKeyExchange marshal: got {raw:?}, want {raw_client_key_exchange:?}"
);
Ok(())
}

View File

@@ -0,0 +1,34 @@
#[cfg(test)]
mod handshake_message_finished_test;
use std::io::{Read, Write};
use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageFinished {
pub(crate) verify_data: Vec<u8>,
}
impl HandshakeMessageFinished {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::Finished
}
pub fn size(&self) -> usize {
self.verify_data.len()
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&self.verify_data)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let mut verify_data: Vec<u8> = vec![];
reader.read_to_end(&mut verify_data)?;
Ok(HandshakeMessageFinished { verify_data })
}
}

View File

@@ -0,0 +1,32 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_finished() -> Result<()> {
let raw_finished = vec![
0x01, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
];
let parsed_finished = HandshakeMessageFinished {
verify_data: raw_finished.clone(),
};
let mut reader = BufReader::new(raw_finished.as_slice());
let c = HandshakeMessageFinished::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_finished,
"handshakeMessageFinished unmarshal: got {c:?}, want {parsed_finished:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_finished,
"handshakeMessageFinished marshal: got {raw:?}, want {raw_finished:?}"
);
Ok(())
}

View File

@@ -0,0 +1,75 @@
#[cfg(test)]
mod handshake_message_hello_verify_request_test;
use std::io::{Read, Write};
use byteorder::{ReadBytesExt, WriteBytesExt};
use super::*;
use crate::record_layer::record_layer_header::*;
/*
The definition of HelloVerifyRequest is as follows:
struct {
ProtocolVersion server_version;
opaque cookie<0..2^8-1>;
} HelloVerifyRequest;
The HelloVerifyRequest message type is hello_verify_request(3).
When the client sends its ClientHello message to the server, the server
MAY respond with a HelloVerifyRequest message. This message contains
a stateless cookie generated using the technique of [PHOTURIS]. The
client MUST retransmit the ClientHello with the cookie added.
*/
/// ## Specifications
///
/// * [RFC 6347 §4.2.1]
///
/// [RFC 6347 §4.2.1]: https://tools.ietf.org/html/rfc6347#section-4.2.1
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageHelloVerifyRequest {
pub(crate) version: ProtocolVersion,
pub(crate) cookie: Vec<u8>,
}
impl HandshakeMessageHelloVerifyRequest {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::HelloVerifyRequest
}
pub fn size(&self) -> usize {
1 + 1 + 1 + self.cookie.len()
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
if self.cookie.len() > 255 {
return Err(Error::ErrCookieTooLong);
}
writer.write_u8(self.version.major)?;
writer.write_u8(self.version.minor)?;
writer.write_u8(self.cookie.len() as u8)?;
writer.write_all(&self.cookie)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let major = reader.read_u8()?;
let minor = reader.read_u8()?;
let cookie_length = reader.read_u8()?;
let mut cookie = vec![];
reader.read_to_end(&mut cookie)?;
if cookie.len() < cookie_length as usize {
return Err(Error::ErrBufferTooSmall);
}
Ok(HandshakeMessageHelloVerifyRequest {
version: ProtocolVersion { major, minor },
cookie,
})
}
}

View File

@@ -0,0 +1,40 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_hello_verify_request() -> Result<()> {
let raw_hello_verify_request = vec![
0xfe, 0xff, 0x14, 0x25, 0xfb, 0xee, 0xb3, 0x7c, 0x95, 0xcf, 0x00, 0xeb, 0xad, 0xe2, 0xef,
0xc7, 0xfd, 0xbb, 0xed, 0xf7, 0x1f, 0x6c, 0xcd,
];
let parsed_hello_verify_request = HandshakeMessageHelloVerifyRequest {
version: ProtocolVersion {
major: 0xFE,
minor: 0xFF,
},
cookie: vec![
0x25, 0xfb, 0xee, 0xb3, 0x7c, 0x95, 0xcf, 0x00, 0xeb, 0xad, 0xe2, 0xef, 0xc7, 0xfd,
0xbb, 0xed, 0xf7, 0x1f, 0x6c, 0xcd,
],
};
let mut reader = BufReader::new(raw_hello_verify_request.as_slice());
let c = HandshakeMessageHelloVerifyRequest::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_hello_verify_request,
"parsed_hello_verify_request unmarshal: got {c:?}, want {parsed_hello_verify_request:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_hello_verify_request,
"parsed_hello_verify_request marshal: got {raw:?}, want {raw_hello_verify_request:?}"
);
Ok(())
}

View File

@@ -0,0 +1,151 @@
#[cfg(test)]
mod handshake_message_server_hello_test;
use std::fmt;
use std::io::{BufReader, BufWriter};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::handshake_random::*;
use super::*;
use crate::cipher_suite::*;
use crate::compression_methods::*;
use crate::extension::*;
use crate::record_layer::record_layer_header::*;
/*
The server will send this message in response to a ClientHello
message when it was able to find an acceptable set of algorithms.
If it cannot find such a match, it will respond with a handshake
failure alert.
https://tools.ietf.org/html/rfc5246#section-7.4.1.3
*/
#[derive(Clone)]
pub struct HandshakeMessageServerHello {
pub(crate) version: ProtocolVersion,
pub(crate) random: HandshakeRandom,
pub(crate) cipher_suite: CipherSuiteId,
pub(crate) compression_method: CompressionMethodId,
pub(crate) extensions: Vec<Extension>,
}
impl PartialEq for HandshakeMessageServerHello {
fn eq(&self, other: &Self) -> bool {
self.version == other.version
&& self.random == other.random
&& self.compression_method == other.compression_method
&& self.extensions == other.extensions
&& self.cipher_suite == other.cipher_suite
}
}
impl fmt::Debug for HandshakeMessageServerHello {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = [
format!("version: {:?} random: {:?}", self.version, self.random),
format!("cipher_suites: {:?}", self.cipher_suite),
format!("compression_method: {:?}", self.compression_method),
format!("extensions: {:?}", self.extensions),
];
write!(f, "{}", s.join(" "))
}
}
impl HandshakeMessageServerHello {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::ServerHello
}
pub fn size(&self) -> usize {
let mut len = 2 + self.random.size();
// SessionID
len += 1;
len += 2;
len += 1;
len += 2;
for extension in &self.extensions {
len += extension.size();
}
len
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
writer.write_u8(self.version.major)?;
writer.write_u8(self.version.minor)?;
self.random.marshal(writer)?;
// SessionID
writer.write_u8(0x00)?;
writer.write_u16::<BigEndian>(self.cipher_suite as u16)?;
writer.write_u8(self.compression_method as u8)?;
let mut extension_buffer = vec![];
{
let mut extension_writer = BufWriter::<&mut Vec<u8>>::new(extension_buffer.as_mut());
for extension in &self.extensions {
extension.marshal(&mut extension_writer)?;
}
}
writer.write_u16::<BigEndian>(extension_buffer.len() as u16)?;
writer.write_all(&extension_buffer)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let major = reader.read_u8()?;
let minor = reader.read_u8()?;
let random = HandshakeRandom::unmarshal(reader)?;
// Session ID
let session_id_len = reader.read_u8()? as usize;
let mut session_id_buffer = vec![0u8; session_id_len];
reader.read_exact(&mut session_id_buffer)?;
let cipher_suite: CipherSuiteId = reader.read_u16::<BigEndian>()?.into();
let compression_method = reader.read_u8()?.into();
let mut extensions = vec![];
let extension_buffer_len = reader.read_u16::<BigEndian>()? as usize;
let mut extension_buffer = vec![0u8; extension_buffer_len];
reader.read_exact(&mut extension_buffer)?;
let mut offset = 0;
while offset < extension_buffer_len {
let mut extension_reader = BufReader::new(&extension_buffer[offset..]);
if let Ok(extension) = Extension::unmarshal(&mut extension_reader) {
extensions.push(extension);
} else {
log::warn!(
"Unsupported Extension Type {} {}",
extension_buffer[offset],
extension_buffer[offset + 1]
);
}
let extension_len =
u16::from_be_bytes([extension_buffer[offset + 2], extension_buffer[offset + 3]])
as usize;
offset += 4 + extension_len;
}
Ok(HandshakeMessageServerHello {
version: ProtocolVersion { major, minor },
random,
cipher_suite,
compression_method,
extensions,
})
}
}

View File

@@ -0,0 +1,56 @@
use std::io::{BufReader, BufWriter};
use std::time::{Duration, SystemTime};
use super::*;
#[test]
fn test_handshake_message_server_hello() -> Result<()> {
let raw_server_hello = vec![
0xfe, 0xfd, 0x21, 0x63, 0x32, 0x21, 0x81, 0x0e, 0x98, 0x6c, 0x85, 0x3d, 0xa4, 0x39, 0xaf,
0x5f, 0xd6, 0x5c, 0xcc, 0x20, 0x7f, 0x7c, 0x78, 0xf1, 0x5f, 0x7e, 0x1c, 0xb7, 0xa1, 0x1e,
0xcf, 0x63, 0x84, 0x28, 0x00, 0xc0, 0x2b, 0x00, 0x00, 0x00,
];
let gmt_unix_time = if let Some(unix_time) =
SystemTime::UNIX_EPOCH.checked_add(Duration::new(560149025u64, 0))
{
unix_time
} else {
SystemTime::UNIX_EPOCH
};
let parsed_server_hello = HandshakeMessageServerHello {
version: ProtocolVersion {
major: 0xFE,
minor: 0xFD,
},
random: HandshakeRandom {
gmt_unix_time,
random_bytes: [
0x81, 0x0e, 0x98, 0x6c, 0x85, 0x3d, 0xa4, 0x39, 0xaf, 0x5f, 0xd6, 0x5c, 0xcc, 0x20,
0x7f, 0x7c, 0x78, 0xf1, 0x5f, 0x7e, 0x1c, 0xb7, 0xa1, 0x1e, 0xcf, 0x63, 0x84, 0x28,
],
},
cipher_suite: CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256,
compression_method: CompressionMethodId::Null,
extensions: vec![],
};
let mut reader = BufReader::new(raw_server_hello.as_slice());
let c = HandshakeMessageServerHello::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_server_hello,
"handshakeMessageServerHello unmarshal: got {c:?}, want {parsed_server_hello:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_server_hello,
"handshakeMessageServerHello marshal: got {raw:?}, want {raw_server_hello:?}"
);
Ok(())
}

View File

@@ -0,0 +1,27 @@
#[cfg(test)]
mod handshake_message_server_hello_done_test;
use std::io::{Read, Write};
use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageServerHelloDone;
impl HandshakeMessageServerHelloDone {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::ServerHelloDone
}
pub fn size(&self) -> usize {
0
}
pub fn marshal<W: Write>(&self, _writer: &mut W) -> Result<()> {
Ok(())
}
pub fn unmarshal<R: Read>(_reader: &mut R) -> Result<Self> {
Ok(HandshakeMessageServerHelloDone {})
}
}

View File

@@ -0,0 +1,28 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_server_hello_done() -> Result<()> {
let raw_server_hello_done = vec![];
let parsed_server_hello_done = HandshakeMessageServerHelloDone {};
let mut reader = BufReader::new(raw_server_hello_done.as_slice());
let c = HandshakeMessageServerHelloDone::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_server_hello_done,
"handshakeMessageServerHelloDone unmarshal: got {c:?}, want {parsed_server_hello_done:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_server_hello_done,
"handshakeMessageServerHelloDone marshal: got {raw:?}, want {raw_server_hello_done:?}"
);
Ok(())
}

View File

@@ -0,0 +1,133 @@
#[cfg(test)]
mod handshake_message_server_key_exchange_test;
use std::io::{Read, Write};
use byteorder::{BigEndian, WriteBytesExt};
use super::*;
use crate::curve::named_curve::*;
use crate::curve::*;
use crate::signature_hash_algorithm::*;
// Structure supports ECDH and PSK
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeMessageServerKeyExchange {
pub(crate) identity_hint: Vec<u8>,
pub(crate) elliptic_curve_type: EllipticCurveType,
pub(crate) named_curve: NamedCurve,
pub(crate) public_key: Vec<u8>,
pub(crate) algorithm: SignatureHashAlgorithm,
pub(crate) signature: Vec<u8>,
}
impl HandshakeMessageServerKeyExchange {
pub fn handshake_type(&self) -> HandshakeType {
HandshakeType::ServerKeyExchange
}
pub fn size(&self) -> usize {
if !self.identity_hint.is_empty() {
2 + self.identity_hint.len()
} else {
1 + 2 + 1 + self.public_key.len() + 2 + 2 + self.signature.len()
}
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
if !self.identity_hint.is_empty() {
writer.write_u16::<BigEndian>(self.identity_hint.len() as u16)?;
writer.write_all(&self.identity_hint)?;
return Ok(writer.flush()?);
}
writer.write_u8(self.elliptic_curve_type as u8)?;
writer.write_u16::<BigEndian>(self.named_curve as u16)?;
writer.write_u8(self.public_key.len() as u8)?;
writer.write_all(&self.public_key)?;
writer.write_u8(self.algorithm.hash as u8)?;
writer.write_u8(self.algorithm.signature as u8)?;
writer.write_u16::<BigEndian>(self.signature.len() as u16)?;
writer.write_all(&self.signature)?;
Ok(writer.flush()?)
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let mut data = vec![];
reader.read_to_end(&mut data)?;
// If parsed as PSK return early and only populate PSK Identity Hint
let psk_length = ((data[0] as u16) << 8) | data[1] as u16;
if data.len() == psk_length as usize + 2 {
return Ok(HandshakeMessageServerKeyExchange {
identity_hint: data[2..].to_vec(),
elliptic_curve_type: EllipticCurveType::Unsupported,
named_curve: NamedCurve::Unsupported,
public_key: vec![],
algorithm: SignatureHashAlgorithm {
hash: HashAlgorithm::Unsupported,
signature: SignatureAlgorithm::Unsupported,
},
signature: vec![],
});
}
let elliptic_curve_type = data[0].into();
if data[1..].len() < 2 {
return Err(Error::ErrBufferTooSmall);
}
let named_curve = (((data[1] as u16) << 8) | data[2] as u16).into();
if data.len() < 4 {
return Err(Error::ErrBufferTooSmall);
}
let public_key_length = data[3] as usize;
let mut offset = 4 + public_key_length;
if data.len() < offset {
return Err(Error::ErrBufferTooSmall);
}
let public_key = data[4..offset].to_vec();
if data.len() <= offset {
return Err(Error::ErrBufferTooSmall);
}
let hash_algorithm = data[offset].into();
offset += 1;
if data.len() <= offset {
return Err(Error::ErrBufferTooSmall);
}
let signature_algorithm = data[offset].into();
offset += 1;
if data.len() < offset + 2 {
return Err(Error::ErrBufferTooSmall);
}
let signature_length = (((data[offset] as u16) << 8) | data[offset + 1] as u16) as usize;
offset += 2;
if data.len() < offset + signature_length {
return Err(Error::ErrBufferTooSmall);
}
let signature = data[offset..offset + signature_length].to_vec();
Ok(HandshakeMessageServerKeyExchange {
identity_hint: vec![],
elliptic_curve_type,
named_curve,
public_key,
algorithm: SignatureHashAlgorithm {
hash: hash_algorithm,
signature: signature_algorithm,
},
signature,
})
}
}

View File

@@ -0,0 +1,50 @@
use std::io::{BufReader, BufWriter};
use super::*;
#[test]
fn test_handshake_message_server_key_exchange() -> Result<()> {
let raw_server_key_exchange = vec![
0x03, 0x00, 0x1d, 0x41, 0x04, 0x0c, 0xb9, 0xa3, 0xb9, 0x90, 0x71, 0x35, 0x4a, 0x08, 0x66,
0xaf, 0xd6, 0x88, 0x58, 0x29, 0x69, 0x98, 0xf1, 0x87, 0x0f, 0xb5, 0xa8, 0xcd, 0x92, 0xf6,
0x2b, 0x08, 0x0c, 0xd4, 0x16, 0x5b, 0xcc, 0x81, 0xf2, 0x58, 0x91, 0x8e, 0x62, 0xdf, 0xc1,
0xec, 0x72, 0xe8, 0x47, 0x24, 0x42, 0x96, 0xb8, 0x7b, 0xee, 0xe7, 0x0d, 0xdc, 0x44, 0xec,
0xf3, 0x97, 0x6b, 0x1b, 0x45, 0x28, 0xac, 0x3f, 0x35, 0x02, 0x03, 0x00, 0x47, 0x30, 0x45,
0x02, 0x21, 0x00, 0xb2, 0x0b, 0x22, 0x95, 0x3d, 0x56, 0x57, 0x6a, 0x3f, 0x85, 0x30, 0x6f,
0x55, 0xc3, 0xf4, 0x24, 0x1b, 0x21, 0x07, 0xe5, 0xdf, 0xba, 0x24, 0x02, 0x68, 0x95, 0x1f,
0x6e, 0x13, 0xbd, 0x9f, 0xaa, 0x02, 0x20, 0x49, 0x9c, 0x9d, 0xdf, 0x84, 0x60, 0x33, 0x27,
0x96, 0x9e, 0x58, 0x6d, 0x72, 0x13, 0xe7, 0x3a, 0xe8, 0xdf, 0x43, 0x75, 0xc7, 0xb9, 0x37,
0x6e, 0x90, 0xe5, 0x3b, 0x81, 0xd4, 0xda, 0x68, 0xcd,
];
let parsed_server_key_exchange = HandshakeMessageServerKeyExchange {
identity_hint: vec![],
elliptic_curve_type: EllipticCurveType::NamedCurve,
named_curve: NamedCurve::X25519,
public_key: raw_server_key_exchange[4..69].to_vec(),
algorithm: SignatureHashAlgorithm {
hash: HashAlgorithm::Sha1,
signature: SignatureAlgorithm::Ecdsa,
},
signature: raw_server_key_exchange[73..144].to_vec(),
};
let mut reader = BufReader::new(raw_server_key_exchange.as_slice());
let c = HandshakeMessageServerKeyExchange::unmarshal(&mut reader)?;
assert_eq!(
c, parsed_server_key_exchange,
"handshakeMessageServerKeyExchange unmarshal: got {c:?}, want {parsed_server_key_exchange:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
c.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_server_key_exchange,
"handshakeMessageServerKeyExchange marshal: got {raw:?}, want {raw_server_key_exchange:?}"
);
Ok(())
}

View File

@@ -0,0 +1,71 @@
use std::io::{self, Read, Write};
use std::time::{Duration, SystemTime};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use rand::Rng;
pub const RANDOM_BYTES_LENGTH: usize = 28;
pub const HANDSHAKE_RANDOM_LENGTH: usize = RANDOM_BYTES_LENGTH + 4;
/// ## Specifications
///
/// * [RFC 4346 §7.4.1.2]
///
/// [RFC 4346 §7.4.1.2]: https://tools.ietf.org/html/rfc4346#section-7.4.1.2
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandshakeRandom {
pub gmt_unix_time: SystemTime,
pub random_bytes: [u8; RANDOM_BYTES_LENGTH],
}
impl Default for HandshakeRandom {
fn default() -> Self {
HandshakeRandom {
gmt_unix_time: SystemTime::UNIX_EPOCH,
random_bytes: [0u8; RANDOM_BYTES_LENGTH],
}
}
}
impl HandshakeRandom {
pub fn size(&self) -> usize {
4 + RANDOM_BYTES_LENGTH
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> io::Result<()> {
let secs = match self.gmt_unix_time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(d) => d.as_secs() as u32,
Err(_) => 0,
};
writer.write_u32::<BigEndian>(secs)?;
writer.write_all(&self.random_bytes)?;
writer.flush()
}
pub fn unmarshal<R: Read>(reader: &mut R) -> io::Result<Self> {
let secs = reader.read_u32::<BigEndian>()?;
let gmt_unix_time = if let Some(unix_time) =
SystemTime::UNIX_EPOCH.checked_add(Duration::new(secs as u64, 0))
{
unix_time
} else {
SystemTime::UNIX_EPOCH
};
let mut random_bytes = [0u8; RANDOM_BYTES_LENGTH];
reader.read_exact(&mut random_bytes)?;
Ok(HandshakeRandom {
gmt_unix_time,
random_bytes,
})
}
// populate fills the HandshakeRandom with random values
// may be called multiple times
pub fn populate(&mut self) {
self.gmt_unix_time = SystemTime::now();
rand::rng().fill(&mut self.random_bytes);
}
}

View File

@@ -0,0 +1,70 @@
use std::io::{BufReader, BufWriter};
use std::time::{Duration, SystemTime};
use super::*;
use crate::compression_methods::*;
use crate::handshake::handshake_message_client_hello::*;
use crate::handshake::handshake_random::HandshakeRandom;
use crate::record_layer::record_layer_header::ProtocolVersion;
#[test]
fn test_handshake_message() -> Result<()> {
let raw_handshake_message = vec![
0x01, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xfe, 0xfd, 0xb6,
0x2f, 0xce, 0x5c, 0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42, 0x62, 0x15, 0xad,
0x16, 0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec, 0xd8, 0x3d, 0xdc,
0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let parsed_handshake = Handshake {
handshake_header: HandshakeHeader {
handshake_type: HandshakeType::ClientHello,
length: 0x29,
message_sequence: 0,
fragment_offset: 0,
fragment_length: 0x29,
},
handshake_message: HandshakeMessage::ClientHello(HandshakeMessageClientHello {
version: ProtocolVersion {
major: 0xFE,
minor: 0xFD,
},
random: HandshakeRandom {
gmt_unix_time: if let Some(unix_time) =
SystemTime::UNIX_EPOCH.checked_add(Duration::new(3056586332u64, 0))
{
unix_time
} else {
SystemTime::UNIX_EPOCH
},
random_bytes: [
0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42, 0x62, 0x15, 0xad, 0x16,
0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec, 0xd8, 0x3d,
0xdc, 0x4b,
],
},
cookie: vec![],
cipher_suites: vec![],
compression_methods: CompressionMethods { ids: vec![] },
extensions: vec![],
}),
};
let mut reader = BufReader::new(raw_handshake_message.as_slice());
let h = Handshake::unmarshal(&mut reader)?;
assert_eq!(
h, parsed_handshake,
"handshakeMessageClientHello unmarshal: got {h:?}, want {parsed_handshake:?}"
);
let mut raw = vec![];
{
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
h.marshal(&mut writer)?;
}
assert_eq!(
raw, raw_handshake_message,
"handshakeMessageClientHello marshal: got {raw:?}, want {raw_handshake_message:?}"
);
Ok(())
}

View File

@@ -0,0 +1,242 @@
pub mod handshake_cache;
pub mod handshake_header;
pub mod handshake_message_certificate;
pub mod handshake_message_certificate_request;
pub mod handshake_message_certificate_verify;
pub mod handshake_message_client_hello;
pub mod handshake_message_client_key_exchange;
pub mod handshake_message_finished;
pub mod handshake_message_hello_verify_request;
pub mod handshake_message_server_hello;
pub mod handshake_message_server_hello_done;
pub mod handshake_message_server_key_exchange;
pub mod handshake_random;
#[cfg(test)]
mod handshake_test;
use std::fmt;
use std::io::{Read, Write};
use handshake_header::*;
use handshake_message_certificate::*;
use handshake_message_certificate_request::*;
use handshake_message_certificate_verify::*;
use handshake_message_client_hello::*;
use handshake_message_client_key_exchange::*;
use handshake_message_finished::*;
use handshake_message_hello_verify_request::*;
use handshake_message_server_hello::*;
use handshake_message_server_hello_done::*;
use handshake_message_server_key_exchange::*;
use super::content::*;
use super::error::*;
/// ## Specifications
///
/// * [RFC 5246 §7.4]
///
/// [RFC 5246 §7.4]: https://tools.ietf.org/html/rfc5246#section-7.4
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum HandshakeType {
HelloRequest = 0,
ClientHello = 1,
ServerHello = 2,
HelloVerifyRequest = 3,
Certificate = 11,
ServerKeyExchange = 12,
CertificateRequest = 13,
ServerHelloDone = 14,
CertificateVerify = 15,
ClientKeyExchange = 16,
Finished = 20,
#[default]
Invalid,
}
impl fmt::Display for HandshakeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
HandshakeType::HelloRequest => write!(f, "HelloRequest"),
HandshakeType::ClientHello => write!(f, "ClientHello"),
HandshakeType::ServerHello => write!(f, "ServerHello"),
HandshakeType::HelloVerifyRequest => write!(f, "HelloVerifyRequest"),
HandshakeType::Certificate => write!(f, "Certificate"),
HandshakeType::ServerKeyExchange => write!(f, "ServerKeyExchange"),
HandshakeType::CertificateRequest => write!(f, "CertificateRequest"),
HandshakeType::ServerHelloDone => write!(f, "ServerHelloDone"),
HandshakeType::CertificateVerify => write!(f, "CertificateVerify"),
HandshakeType::ClientKeyExchange => write!(f, "ClientKeyExchange"),
HandshakeType::Finished => write!(f, "Finished"),
HandshakeType::Invalid => write!(f, "Invalid"),
}
}
}
impl From<u8> for HandshakeType {
fn from(val: u8) -> Self {
match val {
0 => HandshakeType::HelloRequest,
1 => HandshakeType::ClientHello,
2 => HandshakeType::ServerHello,
3 => HandshakeType::HelloVerifyRequest,
11 => HandshakeType::Certificate,
12 => HandshakeType::ServerKeyExchange,
13 => HandshakeType::CertificateRequest,
14 => HandshakeType::ServerHelloDone,
15 => HandshakeType::CertificateVerify,
16 => HandshakeType::ClientKeyExchange,
20 => HandshakeType::Finished,
_ => HandshakeType::Invalid,
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum HandshakeMessage {
//HelloRequest(errNotImplemented),
ClientHello(HandshakeMessageClientHello),
ServerHello(HandshakeMessageServerHello),
HelloVerifyRequest(HandshakeMessageHelloVerifyRequest),
Certificate(HandshakeMessageCertificate),
ServerKeyExchange(HandshakeMessageServerKeyExchange),
CertificateRequest(HandshakeMessageCertificateRequest),
ServerHelloDone(HandshakeMessageServerHelloDone),
CertificateVerify(HandshakeMessageCertificateVerify),
ClientKeyExchange(HandshakeMessageClientKeyExchange),
Finished(HandshakeMessageFinished),
}
impl HandshakeMessage {
pub fn handshake_type(&self) -> HandshakeType {
match self {
HandshakeMessage::ClientHello(msg) => msg.handshake_type(),
HandshakeMessage::ServerHello(msg) => msg.handshake_type(),
HandshakeMessage::HelloVerifyRequest(msg) => msg.handshake_type(),
HandshakeMessage::Certificate(msg) => msg.handshake_type(),
HandshakeMessage::ServerKeyExchange(msg) => msg.handshake_type(),
HandshakeMessage::CertificateRequest(msg) => msg.handshake_type(),
HandshakeMessage::ServerHelloDone(msg) => msg.handshake_type(),
HandshakeMessage::CertificateVerify(msg) => msg.handshake_type(),
HandshakeMessage::ClientKeyExchange(msg) => msg.handshake_type(),
HandshakeMessage::Finished(msg) => msg.handshake_type(),
}
}
pub fn size(&self) -> usize {
match self {
HandshakeMessage::ClientHello(msg) => msg.size(),
HandshakeMessage::ServerHello(msg) => msg.size(),
HandshakeMessage::HelloVerifyRequest(msg) => msg.size(),
HandshakeMessage::Certificate(msg) => msg.size(),
HandshakeMessage::ServerKeyExchange(msg) => msg.size(),
HandshakeMessage::CertificateRequest(msg) => msg.size(),
HandshakeMessage::ServerHelloDone(msg) => msg.size(),
HandshakeMessage::CertificateVerify(msg) => msg.size(),
HandshakeMessage::ClientKeyExchange(msg) => msg.size(),
HandshakeMessage::Finished(msg) => msg.size(),
}
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
match self {
HandshakeMessage::ClientHello(msg) => msg.marshal(writer)?,
HandshakeMessage::ServerHello(msg) => msg.marshal(writer)?,
HandshakeMessage::HelloVerifyRequest(msg) => msg.marshal(writer)?,
HandshakeMessage::Certificate(msg) => msg.marshal(writer)?,
HandshakeMessage::ServerKeyExchange(msg) => msg.marshal(writer)?,
HandshakeMessage::CertificateRequest(msg) => msg.marshal(writer)?,
HandshakeMessage::ServerHelloDone(msg) => msg.marshal(writer)?,
HandshakeMessage::CertificateVerify(msg) => msg.marshal(writer)?,
HandshakeMessage::ClientKeyExchange(msg) => msg.marshal(writer)?,
HandshakeMessage::Finished(msg) => msg.marshal(writer)?,
}
Ok(())
}
}
// The handshake protocol is responsible for selecting a cipher spec and
// generating a master secret, which together comprise the primary
// cryptographic parameters associated with a secure session. The
// handshake protocol can also optionally authenticate parties who have
// certificates signed by a trusted certificate authority.
// https://tools.ietf.org/html/rfc5246#section-7.3
#[derive(PartialEq, Debug, Clone)]
pub struct Handshake {
pub(crate) handshake_header: HandshakeHeader,
pub(crate) handshake_message: HandshakeMessage,
}
impl Handshake {
pub fn new(handshake_message: HandshakeMessage) -> Self {
Handshake {
handshake_header: HandshakeHeader {
handshake_type: handshake_message.handshake_type(),
length: handshake_message.size() as u32,
message_sequence: 0,
fragment_offset: 0,
fragment_length: handshake_message.size() as u32,
},
handshake_message,
}
}
pub fn content_type(&self) -> ContentType {
ContentType::Handshake
}
pub fn size(&self) -> usize {
self.handshake_header.size() + self.handshake_message.size()
}
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
self.handshake_header.marshal(writer)?;
self.handshake_message.marshal(writer)?;
Ok(())
}
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
let handshake_header = HandshakeHeader::unmarshal(reader)?;
let handshake_message = match handshake_header.handshake_type {
HandshakeType::ClientHello => {
HandshakeMessage::ClientHello(HandshakeMessageClientHello::unmarshal(reader)?)
}
HandshakeType::ServerHello => {
HandshakeMessage::ServerHello(HandshakeMessageServerHello::unmarshal(reader)?)
}
HandshakeType::HelloVerifyRequest => HandshakeMessage::HelloVerifyRequest(
HandshakeMessageHelloVerifyRequest::unmarshal(reader)?,
),
HandshakeType::Certificate => {
HandshakeMessage::Certificate(HandshakeMessageCertificate::unmarshal(reader)?)
}
HandshakeType::ServerKeyExchange => HandshakeMessage::ServerKeyExchange(
HandshakeMessageServerKeyExchange::unmarshal(reader)?,
),
HandshakeType::CertificateRequest => HandshakeMessage::CertificateRequest(
HandshakeMessageCertificateRequest::unmarshal(reader)?,
),
HandshakeType::ServerHelloDone => HandshakeMessage::ServerHelloDone(
HandshakeMessageServerHelloDone::unmarshal(reader)?,
),
HandshakeType::CertificateVerify => HandshakeMessage::CertificateVerify(
HandshakeMessageCertificateVerify::unmarshal(reader)?,
),
HandshakeType::ClientKeyExchange => HandshakeMessage::ClientKeyExchange(
HandshakeMessageClientKeyExchange::unmarshal(reader)?,
),
HandshakeType::Finished => {
HandshakeMessage::Finished(HandshakeMessageFinished::unmarshal(reader)?)
}
_ => return Err(Error::ErrNotImplemented),
};
Ok(Handshake {
handshake_header,
handshake_message,
})
}
}