mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-28 16:30:48 +08:00
refactor: 将 DTLS 本地依赖迁移到 libs 目录
This commit is contained in:
241
libs/dtls/src/handshake/handshake_cache.rs
Normal file
241
libs/dtls/src/handshake/handshake_cache.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
658
libs/dtls/src/handshake/handshake_cache/handshake_cache_test.rs
Normal file
658
libs/dtls/src/handshake/handshake_cache/handshake_cache_test.rs
Normal 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(())
|
||||
}
|
||||
50
libs/dtls/src/handshake/handshake_header.rs
Normal file
50
libs/dtls/src/handshake/handshake_header.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
70
libs/dtls/src/handshake/handshake_message_certificate.rs
Normal file
70
libs/dtls/src/handshake/handshake_message_certificate.rs
Normal 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 })
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
196
libs/dtls/src/handshake/handshake_message_client_hello.rs
Normal file
196
libs/dtls/src/handshake/handshake_message_client_hello.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
34
libs/dtls/src/handshake/handshake_message_finished.rs
Normal file
34
libs/dtls/src/handshake/handshake_message_finished.rs
Normal 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 })
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
151
libs/dtls/src/handshake/handshake_message_server_hello.rs
Normal file
151
libs/dtls/src/handshake/handshake_message_server_hello.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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 {})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
133
libs/dtls/src/handshake/handshake_message_server_key_exchange.rs
Normal file
133
libs/dtls/src/handshake/handshake_message_server_key_exchange.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
71
libs/dtls/src/handshake/handshake_random.rs
Normal file
71
libs/dtls/src/handshake/handshake_random.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
70
libs/dtls/src/handshake/handshake_test.rs
Normal file
70
libs/dtls/src/handshake/handshake_test.rs
Normal 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(())
|
||||
}
|
||||
242
libs/dtls/src/handshake/mod.rs
Normal file
242
libs/dtls/src/handshake/mod.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user