mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-30 01:10:50 +08:00
refactor: 将 DTLS 本地依赖迁移到 libs 目录
This commit is contained in:
209
libs/dtls/src/flight/flight0.rs
Normal file
209
libs/dtls/src/flight/flight0.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use std::fmt;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use rand::Rng;
|
||||
|
||||
use super::flight2::*;
|
||||
use super::*;
|
||||
use crate::config::*;
|
||||
use crate::conn::*;
|
||||
use crate::curve::named_curve::NamedCurve;
|
||||
use crate::error::Error;
|
||||
use crate::extension::*;
|
||||
use crate::handshake::*;
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight0;
|
||||
|
||||
impl fmt::Display for Flight0 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 0")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight0 {
|
||||
async fn parse(
|
||||
&self,
|
||||
_tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
let (seq, msgs) = match cache
|
||||
.full_pull_map(
|
||||
0,
|
||||
&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
state.handshake_recv_sequence = seq;
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::ClientHello) {
|
||||
// Validate type
|
||||
let client_hello = match message {
|
||||
HandshakeMessage::ClientHello(client_hello) => client_hello,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if client_hello.version != PROTOCOL_VERSION1_2 {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::ProtocolVersion,
|
||||
}),
|
||||
Some(Error::ErrUnsupportedProtocolVersion),
|
||||
));
|
||||
}
|
||||
|
||||
state.remote_random = client_hello.random.clone();
|
||||
|
||||
if let Ok(id) =
|
||||
find_matching_cipher_suite(&client_hello.cipher_suites, &cfg.local_cipher_suites)
|
||||
{
|
||||
if let Ok(cipher_suite) = cipher_suite_for_id(id) {
|
||||
log::debug!(
|
||||
"[handshake:{}] use cipher suite: {}",
|
||||
srv_cli_str(state.is_client),
|
||||
cipher_suite.to_string()
|
||||
);
|
||||
let mut cs = state.cipher_suite.lock().await;
|
||||
*cs = Some(cipher_suite);
|
||||
}
|
||||
} else {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrCipherSuiteNoIntersection),
|
||||
));
|
||||
}
|
||||
|
||||
for extension in &client_hello.extensions {
|
||||
match extension {
|
||||
Extension::SupportedEllipticCurves(e) => {
|
||||
if e.elliptic_curves.is_empty() {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrNoSupportedEllipticCurves),
|
||||
));
|
||||
}
|
||||
for curve in e.elliptic_curves.iter() {
|
||||
if curve != &NamedCurve::Unsupported {
|
||||
state.named_curve = *curve;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Extension::UseSrtp(e) => {
|
||||
if let Ok(profile) = find_matching_srtp_profile(
|
||||
&e.protection_profiles,
|
||||
&cfg.local_srtp_protection_profiles,
|
||||
) {
|
||||
state.srtp_protection_profile = profile;
|
||||
} else {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrServerNoMatchingSrtpProfile),
|
||||
));
|
||||
}
|
||||
}
|
||||
Extension::UseExtendedMasterSecret(_) => {
|
||||
if cfg.extended_master_secret != ExtendedMasterSecretType::Disable {
|
||||
state.extended_master_secret = true;
|
||||
}
|
||||
}
|
||||
Extension::ServerName(e) => {
|
||||
state.server_name.clone_from(&e.server_name); // remote server name
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.extended_master_secret == ExtendedMasterSecretType::Require
|
||||
&& !state.extended_master_secret
|
||||
{
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrServerRequiredButNoClientEms),
|
||||
));
|
||||
}
|
||||
|
||||
if state.local_keypair.is_none() {
|
||||
state.local_keypair = match state.named_curve.generate_keypair() {
|
||||
Ok(local_keypar) => Some(local_keypar),
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::IllegalParameter,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Box::new(Flight2 {}))
|
||||
} else {
|
||||
Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
_cache: &HandshakeCache,
|
||||
_cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
// Initialize
|
||||
state.cookie = vec![0; COOKIE_LENGTH];
|
||||
rand::rng().fill(state.cookie.as_mut_slice());
|
||||
|
||||
//TODO: figure out difference between golang's atom store and rust atom store
|
||||
let zero_epoch = 0;
|
||||
state.local_epoch.store(zero_epoch, Ordering::SeqCst);
|
||||
state.remote_epoch.store(zero_epoch, Ordering::SeqCst);
|
||||
|
||||
state.named_curve = DEFAULT_NAMED_CURVE;
|
||||
state.local_random.populate();
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
193
libs/dtls/src/flight/flight1.rs
Normal file
193
libs/dtls/src/flight/flight1.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use std::fmt;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::flight3::*;
|
||||
use super::*;
|
||||
use crate::compression_methods::*;
|
||||
use crate::config::*;
|
||||
use crate::conn::*;
|
||||
use crate::content::*;
|
||||
use crate::curve::named_curve::*;
|
||||
use crate::error::Error;
|
||||
use crate::extension::extension_server_name::*;
|
||||
use crate::extension::extension_supported_elliptic_curves::*;
|
||||
use crate::extension::extension_supported_point_formats::*;
|
||||
use crate::extension::extension_supported_signature_algorithms::*;
|
||||
use crate::extension::extension_use_extended_master_secret::*;
|
||||
use crate::extension::extension_use_srtp::*;
|
||||
use crate::extension::renegotiation_info::ExtensionRenegotiationInfo;
|
||||
use crate::extension::*;
|
||||
use crate::handshake::handshake_message_client_hello::*;
|
||||
use crate::handshake::*;
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
use crate::record_layer::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight1;
|
||||
|
||||
impl fmt::Display for Flight1 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 1")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight1 {
|
||||
async fn parse(
|
||||
&self,
|
||||
tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
// HelloVerifyRequest can be skipped by the server,
|
||||
// so allow ServerHello during flight1 also
|
||||
let (seq, msgs) = match cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::HelloVerifyRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: true,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
{
|
||||
// No valid message received. Keep reading
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
if msgs.contains_key(&HandshakeType::ServerHello) {
|
||||
// Flight1 and flight2 were skipped.
|
||||
// Parse as flight3.
|
||||
let flight3 = Flight3 {};
|
||||
return flight3.parse(tx, state, cache, cfg).await;
|
||||
}
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::HelloVerifyRequest) {
|
||||
// DTLS 1.2 clients must not assume that the server will use the protocol version
|
||||
// specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
|
||||
let h = match message {
|
||||
HandshakeMessage::HelloVerifyRequest(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if h.version != PROTOCOL_VERSION1_0 && h.version != PROTOCOL_VERSION1_2 {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::ProtocolVersion,
|
||||
}),
|
||||
Some(Error::ErrUnsupportedProtocolVersion),
|
||||
));
|
||||
}
|
||||
|
||||
state.cookie.clone_from(&h.cookie);
|
||||
state.handshake_recv_sequence = seq;
|
||||
Ok(Box::new(Flight3 {}))
|
||||
} else {
|
||||
Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
_cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
let zero_epoch = 0;
|
||||
state.local_epoch.store(zero_epoch, Ordering::SeqCst);
|
||||
state.remote_epoch.store(zero_epoch, Ordering::SeqCst);
|
||||
|
||||
state.named_curve = DEFAULT_NAMED_CURVE;
|
||||
state.cookie = vec![];
|
||||
state.local_random.populate();
|
||||
|
||||
let mut extensions = vec![
|
||||
Extension::SupportedSignatureAlgorithms(ExtensionSupportedSignatureAlgorithms {
|
||||
signature_hash_algorithms: cfg.local_signature_schemes.clone(),
|
||||
}),
|
||||
Extension::RenegotiationInfo(ExtensionRenegotiationInfo {
|
||||
renegotiated_connection: 0,
|
||||
}),
|
||||
];
|
||||
|
||||
if cfg.local_psk_callback.is_none() {
|
||||
extensions.extend_from_slice(&[
|
||||
Extension::SupportedEllipticCurves(ExtensionSupportedEllipticCurves {
|
||||
elliptic_curves: vec![NamedCurve::P256, NamedCurve::X25519, NamedCurve::P384],
|
||||
}),
|
||||
Extension::SupportedPointFormats(ExtensionSupportedPointFormats {
|
||||
point_formats: vec![ELLIPTIC_CURVE_POINT_FORMAT_UNCOMPRESSED],
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
if !cfg.local_srtp_protection_profiles.is_empty() {
|
||||
extensions.push(Extension::UseSrtp(ExtensionUseSrtp {
|
||||
protection_profiles: cfg.local_srtp_protection_profiles.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
if cfg.extended_master_secret == ExtendedMasterSecretType::Request
|
||||
|| cfg.extended_master_secret == ExtendedMasterSecretType::Require
|
||||
{
|
||||
extensions.push(Extension::UseExtendedMasterSecret(
|
||||
ExtensionUseExtendedMasterSecret { supported: true },
|
||||
));
|
||||
}
|
||||
|
||||
if !cfg.server_name.is_empty() {
|
||||
extensions.push(Extension::ServerName(ExtensionServerName {
|
||||
server_name: cfg.server_name.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(vec![Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ClientHello(
|
||||
HandshakeMessageClientHello {
|
||||
version: PROTOCOL_VERSION1_2,
|
||||
random: state.local_random.clone(),
|
||||
cookie: state.cookie.clone(),
|
||||
|
||||
cipher_suites: cfg.local_cipher_suites.clone(),
|
||||
compression_methods: default_compression_methods(),
|
||||
extensions,
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
}])
|
||||
}
|
||||
}
|
||||
131
libs/dtls/src/flight/flight2.rs
Normal file
131
libs/dtls/src/flight/flight2.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use std::fmt;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::flight0::*;
|
||||
use super::flight4::*;
|
||||
use super::*;
|
||||
use crate::content::*;
|
||||
use crate::error::Error;
|
||||
use crate::handshake::handshake_message_hello_verify_request::*;
|
||||
use crate::handshake::*;
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight2;
|
||||
|
||||
impl fmt::Display for Flight2 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 2")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight2 {
|
||||
fn has_retransmit(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn parse(
|
||||
&self,
|
||||
tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
let (seq, msgs) = match cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
{
|
||||
// No valid message received. Keep reading
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
|
||||
// Client may retransmit the first ClientHello when HelloVerifyRequest is dropped.
|
||||
// Parse as flight 0 in this case.
|
||||
Err(_) => return Flight0 {}.parse(tx, state, cache, cfg).await,
|
||||
};
|
||||
|
||||
state.handshake_recv_sequence = seq;
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::ClientHello) {
|
||||
// Validate type
|
||||
let client_hello = match message {
|
||||
HandshakeMessage::ClientHello(client_hello) => client_hello,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if client_hello.version != PROTOCOL_VERSION1_2 {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::ProtocolVersion,
|
||||
}),
|
||||
Some(Error::ErrUnsupportedProtocolVersion),
|
||||
));
|
||||
}
|
||||
|
||||
if client_hello.cookie.is_empty() {
|
||||
return Err((None, None));
|
||||
}
|
||||
|
||||
if state.cookie != client_hello.cookie {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::AccessDenied,
|
||||
}),
|
||||
Some(Error::ErrCookieMismatch),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Box::new(Flight4 {}))
|
||||
} else {
|
||||
Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
_cache: &HandshakeCache,
|
||||
_cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
state.handshake_send_sequence = 0;
|
||||
Ok(vec![Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::HelloVerifyRequest(
|
||||
HandshakeMessageHelloVerifyRequest {
|
||||
version: PROTOCOL_VERSION1_2,
|
||||
cookie: state.cookie.clone(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
}])
|
||||
}
|
||||
}
|
||||
470
libs/dtls/src/flight/flight3.rs
Normal file
470
libs/dtls/src/flight/flight3.rs
Normal file
@@ -0,0 +1,470 @@
|
||||
use std::fmt;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::*;
|
||||
|
||||
use super::flight5::*;
|
||||
use super::*;
|
||||
use crate::cipher_suite::cipher_suite_for_id;
|
||||
use crate::compression_methods::*;
|
||||
use crate::config::*;
|
||||
use crate::content::*;
|
||||
use crate::curve::named_curve::*;
|
||||
use crate::error::Error;
|
||||
use crate::extension::extension_server_name::*;
|
||||
use crate::extension::extension_supported_elliptic_curves::*;
|
||||
use crate::extension::extension_supported_point_formats::*;
|
||||
use crate::extension::extension_supported_signature_algorithms::*;
|
||||
use crate::extension::extension_use_extended_master_secret::*;
|
||||
use crate::extension::extension_use_srtp::*;
|
||||
use crate::extension::renegotiation_info::ExtensionRenegotiationInfo;
|
||||
use crate::extension::*;
|
||||
use crate::handshake::handshake_message_client_hello::*;
|
||||
use crate::handshake::handshake_message_server_key_exchange::*;
|
||||
use crate::handshake::*;
|
||||
use crate::prf::{prf_pre_master_secret, prf_psk_pre_master_secret};
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
use crate::record_layer::*;
|
||||
use crate::{find_matching_cipher_suite, find_matching_srtp_profile};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight3;
|
||||
|
||||
impl fmt::Display for Flight3 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 3")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight3 {
|
||||
async fn parse(
|
||||
&self,
|
||||
_tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
// Clients may receive multiple HelloVerifyRequest messages with different cookies.
|
||||
// Clients SHOULD handle this by sending a new ClientHello with a cookie in response
|
||||
// to the new HelloVerifyRequest. RFC 6347 Section 4.2.1
|
||||
if let Ok((seq, msgs)) = cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::HelloVerifyRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: true,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Some(message) = msgs.get(&HandshakeType::HelloVerifyRequest) {
|
||||
// DTLS 1.2 clients must not assume that the server will use the protocol version
|
||||
// specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
|
||||
let h = match message {
|
||||
HandshakeMessage::HelloVerifyRequest(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// DTLS 1.2 clients must not assume that the server will use the protocol version
|
||||
// specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
|
||||
if h.version != PROTOCOL_VERSION1_0 && h.version != PROTOCOL_VERSION1_2 {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::ProtocolVersion,
|
||||
}),
|
||||
Some(Error::ErrUnsupportedProtocolVersion),
|
||||
));
|
||||
}
|
||||
|
||||
state.cookie.clone_from(&h.cookie);
|
||||
state.handshake_recv_sequence = seq;
|
||||
return Ok(Box::new(Flight3 {}) as Box<dyn Flight + Send + Sync>);
|
||||
}
|
||||
}
|
||||
|
||||
let result = if cfg.local_psk_callback.is_some() {
|
||||
cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: true,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: true,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: true,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
};
|
||||
|
||||
let (seq, msgs) = match result {
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
state.handshake_recv_sequence = seq;
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::ServerHello) {
|
||||
let h = match message {
|
||||
HandshakeMessage::ServerHello(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if h.version != PROTOCOL_VERSION1_2 {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::ProtocolVersion,
|
||||
}),
|
||||
Some(Error::ErrUnsupportedProtocolVersion),
|
||||
));
|
||||
}
|
||||
|
||||
for extension in &h.extensions {
|
||||
match extension {
|
||||
Extension::UseSrtp(e) => {
|
||||
let profile = match find_matching_srtp_profile(
|
||||
&e.protection_profiles,
|
||||
&cfg.local_srtp_protection_profiles,
|
||||
) {
|
||||
Ok(profile) => profile,
|
||||
Err(_) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::IllegalParameter,
|
||||
}),
|
||||
Some(Error::ErrClientNoMatchingSrtpProfile),
|
||||
))
|
||||
}
|
||||
};
|
||||
state.srtp_protection_profile = profile;
|
||||
}
|
||||
Extension::UseExtendedMasterSecret(_) => {
|
||||
if cfg.extended_master_secret != ExtendedMasterSecretType::Disable {
|
||||
state.extended_master_secret = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
if cfg.extended_master_secret == ExtendedMasterSecretType::Require
|
||||
&& !state.extended_master_secret
|
||||
{
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrClientRequiredButNoServerEms),
|
||||
));
|
||||
}
|
||||
if !cfg.local_srtp_protection_profiles.is_empty()
|
||||
&& state.srtp_protection_profile == SrtpProtectionProfile::Unsupported
|
||||
{
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrRequestedButNoSrtpExtension),
|
||||
));
|
||||
}
|
||||
if find_matching_cipher_suite(&[h.cipher_suite], &cfg.local_cipher_suites).is_err() {
|
||||
debug!(
|
||||
"[handshake:{}] use cipher suite: {}",
|
||||
srv_cli_str(state.is_client),
|
||||
h.cipher_suite
|
||||
);
|
||||
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrCipherSuiteNoIntersection),
|
||||
));
|
||||
}
|
||||
|
||||
let cipher_suite = match cipher_suite_for_id(h.cipher_suite) {
|
||||
Ok(cipher_suite) => cipher_suite,
|
||||
Err(_) => {
|
||||
debug!(
|
||||
"[handshake:{}] use cipher suite: {}",
|
||||
srv_cli_str(state.is_client),
|
||||
h.cipher_suite
|
||||
);
|
||||
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrInvalidCipherSuite),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
trace!(
|
||||
"[handshake:{}] use cipher suite: {}",
|
||||
srv_cli_str(state.is_client),
|
||||
cipher_suite.to_string()
|
||||
);
|
||||
{
|
||||
let mut cs = state.cipher_suite.lock().await;
|
||||
*cs = Some(cipher_suite);
|
||||
}
|
||||
state.remote_random = h.random.clone();
|
||||
}
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::Certificate) {
|
||||
let h = match message {
|
||||
HandshakeMessage::Certificate(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
state.peer_certificates.clone_from(&h.certificate);
|
||||
}
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::ServerKeyExchange) {
|
||||
let h = match message {
|
||||
HandshakeMessage::ServerKeyExchange(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if let Err((alert, err)) = handle_server_key_exchange(state, cfg, h).await {
|
||||
return Err((alert, err));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::CertificateRequest) {
|
||||
match message {
|
||||
HandshakeMessage::CertificateRequest(_) => {}
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
state.remote_requested_certificate = true;
|
||||
}
|
||||
|
||||
Ok(Box::new(Flight5 {}) as Box<dyn Flight + Send + Sync>)
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
_cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
let mut extensions = vec![
|
||||
Extension::SupportedSignatureAlgorithms(ExtensionSupportedSignatureAlgorithms {
|
||||
signature_hash_algorithms: cfg.local_signature_schemes.clone(),
|
||||
}),
|
||||
Extension::RenegotiationInfo(ExtensionRenegotiationInfo {
|
||||
renegotiated_connection: 0,
|
||||
}),
|
||||
];
|
||||
|
||||
if cfg.local_psk_callback.is_none() {
|
||||
extensions.extend_from_slice(&[
|
||||
Extension::SupportedEllipticCurves(ExtensionSupportedEllipticCurves {
|
||||
elliptic_curves: vec![NamedCurve::P256, NamedCurve::X25519, NamedCurve::P384],
|
||||
}),
|
||||
Extension::SupportedPointFormats(ExtensionSupportedPointFormats {
|
||||
point_formats: vec![ELLIPTIC_CURVE_POINT_FORMAT_UNCOMPRESSED],
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
if !cfg.local_srtp_protection_profiles.is_empty() {
|
||||
extensions.push(Extension::UseSrtp(ExtensionUseSrtp {
|
||||
protection_profiles: cfg.local_srtp_protection_profiles.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
if cfg.extended_master_secret == ExtendedMasterSecretType::Request
|
||||
|| cfg.extended_master_secret == ExtendedMasterSecretType::Require
|
||||
{
|
||||
extensions.push(Extension::UseExtendedMasterSecret(
|
||||
ExtensionUseExtendedMasterSecret { supported: true },
|
||||
));
|
||||
}
|
||||
|
||||
if !cfg.server_name.is_empty() {
|
||||
extensions.push(Extension::ServerName(ExtensionServerName {
|
||||
server_name: cfg.server_name.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(vec![Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ClientHello(
|
||||
HandshakeMessageClientHello {
|
||||
version: PROTOCOL_VERSION1_2,
|
||||
random: state.local_random.clone(),
|
||||
cookie: state.cookie.clone(),
|
||||
|
||||
cipher_suites: cfg.local_cipher_suites.clone(),
|
||||
compression_methods: default_compression_methods(),
|
||||
extensions,
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_server_key_exchange(
|
||||
state: &mut State,
|
||||
cfg: &HandshakeConfig,
|
||||
h: &HandshakeMessageServerKeyExchange,
|
||||
) -> Result<(), (Option<Alert>, Option<Error>)> {
|
||||
if let Some(local_psk_callback) = &cfg.local_psk_callback {
|
||||
let psk = match local_psk_callback(&h.identity_hint).await {
|
||||
Ok(psk) => psk,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.identity_hint.clone_from(&h.identity_hint);
|
||||
state.pre_master_secret = prf_psk_pre_master_secret(&psk);
|
||||
} else {
|
||||
let local_keypair = match h.named_curve.generate_keypair() {
|
||||
Ok(local_keypair) => local_keypair,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.pre_master_secret = match prf_pre_master_secret(
|
||||
&h.public_key,
|
||||
&local_keypair.private_key,
|
||||
local_keypair.curve,
|
||||
) {
|
||||
Ok(pre_master_secret) => pre_master_secret,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.local_keypair = Some(local_keypair);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
853
libs/dtls/src/flight/flight4.rs
Normal file
853
libs/dtls/src/flight/flight4.rs
Normal file
@@ -0,0 +1,853 @@
|
||||
use std::fmt;
|
||||
use std::io::BufWriter;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::*;
|
||||
|
||||
use super::flight6::*;
|
||||
use super::*;
|
||||
use crate::cipher_suite::*;
|
||||
use crate::client_certificate_type::*;
|
||||
use crate::compression_methods::*;
|
||||
use crate::config::*;
|
||||
use crate::content::*;
|
||||
use crate::crypto::*;
|
||||
use crate::curve::named_curve::*;
|
||||
use crate::curve::*;
|
||||
use crate::error::Error;
|
||||
use crate::extension::extension_supported_elliptic_curves::*;
|
||||
use crate::extension::extension_supported_point_formats::*;
|
||||
use crate::extension::extension_use_extended_master_secret::*;
|
||||
use crate::extension::extension_use_srtp::*;
|
||||
use crate::extension::renegotiation_info::ExtensionRenegotiationInfo;
|
||||
use crate::extension::*;
|
||||
use crate::handshake::handshake_message_certificate::*;
|
||||
use crate::handshake::handshake_message_certificate_request::*;
|
||||
use crate::handshake::handshake_message_server_hello::*;
|
||||
use crate::handshake::handshake_message_server_hello_done::*;
|
||||
use crate::handshake::handshake_message_server_key_exchange::*;
|
||||
use crate::handshake::*;
|
||||
use crate::prf::*;
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
use crate::record_layer::*;
|
||||
use crate::signature_hash_algorithm::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight4;
|
||||
|
||||
impl fmt::Display for Flight4 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 4")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight4 {
|
||||
async fn parse(
|
||||
&self,
|
||||
tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
let (seq, msgs) = match cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: true,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateVerify,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
let client_key_exchange = if let Some(HandshakeMessage::ClientKeyExchange(h)) =
|
||||
msgs.get(&HandshakeType::ClientKeyExchange)
|
||||
{
|
||||
h
|
||||
} else {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
));
|
||||
};
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::Certificate) {
|
||||
let h = match message {
|
||||
HandshakeMessage::Certificate(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.peer_certificates.clone_from(&h.certificate);
|
||||
trace!(
|
||||
"[handshake] PeerCertificates4 {}",
|
||||
state.peer_certificates.len()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::CertificateVerify) {
|
||||
let h = match message {
|
||||
HandshakeMessage::CertificateVerify(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if state.peer_certificates.is_empty() {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::NoCertificate,
|
||||
}),
|
||||
Some(Error::ErrCertificateVerifyNoCertificate),
|
||||
));
|
||||
}
|
||||
|
||||
let plain_text = cache
|
||||
.pull_and_merge(&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
])
|
||||
.await;
|
||||
|
||||
// Verify that the pair of hash algorithm and signature is listed.
|
||||
let mut valid_signature_scheme = false;
|
||||
for ss in &cfg.local_signature_schemes {
|
||||
if ss.hash == h.algorithm.hash && ss.signature == h.algorithm.signature {
|
||||
valid_signature_scheme = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !valid_signature_scheme {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrNoAvailableSignatureSchemes),
|
||||
));
|
||||
}
|
||||
|
||||
if let Err(err) = verify_certificate_verify(
|
||||
&plain_text,
|
||||
&h.algorithm,
|
||||
&h.signature,
|
||||
&state.peer_certificates,
|
||||
cfg.insecure_verification,
|
||||
) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
|
||||
let mut chains = vec![];
|
||||
let mut verified = false;
|
||||
if cfg.client_auth as u8 >= ClientAuthType::VerifyClientCertIfGiven as u8 {
|
||||
if let Some(client_cert_verifier) = &cfg.client_cert_verifier {
|
||||
chains =
|
||||
match verify_client_cert(&state.peer_certificates, client_cert_verifier) {
|
||||
Ok(chains) => chains,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(Error::ErrInvalidCertificate),
|
||||
));
|
||||
}
|
||||
|
||||
verified = true
|
||||
}
|
||||
if let Some(verify_peer_certificate) = &cfg.verify_peer_certificate {
|
||||
if let Err(err) = verify_peer_certificate(&state.peer_certificates, &chains) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
state.peer_certificates_verified = verified
|
||||
} else if !state.peer_certificates.is_empty() {
|
||||
// A certificate was received, but we haven't seen a CertificateVerify
|
||||
// keep reading until we receive one
|
||||
return Err((None, None));
|
||||
}
|
||||
|
||||
{
|
||||
let mut cipher_suite = state.cipher_suite.lock().await;
|
||||
if let Some(cipher_suite) = &mut *cipher_suite {
|
||||
if !cipher_suite.is_initialized() {
|
||||
let mut server_random = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(server_random.as_mut());
|
||||
let _ = state.local_random.marshal(&mut writer);
|
||||
}
|
||||
let mut client_random = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(client_random.as_mut());
|
||||
let _ = state.remote_random.marshal(&mut writer);
|
||||
}
|
||||
|
||||
let mut pre_master_secret = vec![];
|
||||
if let Some(local_psk_callback) = &cfg.local_psk_callback {
|
||||
let psk = match local_psk_callback(&client_key_exchange.identity_hint).await
|
||||
{
|
||||
Ok(psk) => psk,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state
|
||||
.identity_hint
|
||||
.clone_from(&client_key_exchange.identity_hint);
|
||||
pre_master_secret = prf_psk_pre_master_secret(&psk);
|
||||
} else if let Some(local_keypair) = &state.local_keypair {
|
||||
pre_master_secret = match prf_pre_master_secret(
|
||||
&client_key_exchange.public_key,
|
||||
&local_keypair.private_key,
|
||||
local_keypair.curve,
|
||||
) {
|
||||
Ok(pre_master_secret) => pre_master_secret,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::IllegalParameter,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if state.extended_master_secret {
|
||||
let hf = cipher_suite.hash_func();
|
||||
let session_hash =
|
||||
match cache.session_hash(hf, cfg.initial_epoch, &[]).await {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.master_secret = match prf_extended_master_secret(
|
||||
&pre_master_secret,
|
||||
&session_hash,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(ms) => ms,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
state.master_secret = match prf_master_secret(
|
||||
&pre_master_secret,
|
||||
&client_random,
|
||||
&server_random,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(ms) => ms,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Err(err) = cipher_suite.init(
|
||||
&state.master_secret,
|
||||
&client_random,
|
||||
&server_random,
|
||||
false,
|
||||
) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, encrypted packets can be handled
|
||||
let (done_tx, mut done_rx) = mpsc::channel(1);
|
||||
if let Err(err) = tx.send(done_tx).await {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(Error::Other(err.to_string())),
|
||||
));
|
||||
}
|
||||
|
||||
done_rx.recv().await;
|
||||
|
||||
let (seq, msgs) = match cache
|
||||
.full_pull_map(
|
||||
seq,
|
||||
&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::Finished,
|
||||
epoch: cfg.initial_epoch + 1,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
// No valid message received. Keep reading
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
state.handshake_recv_sequence = seq;
|
||||
|
||||
if let Some(HandshakeMessage::Finished(h)) = msgs.get(&HandshakeType::Finished) {
|
||||
h
|
||||
} else {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
));
|
||||
};
|
||||
|
||||
match cfg.client_auth {
|
||||
ClientAuthType::RequireAnyClientCert => {
|
||||
trace!(
|
||||
"{} peer_certificates.len() {}",
|
||||
srv_cli_str(state.is_client),
|
||||
state.peer_certificates.len(),
|
||||
);
|
||||
if state.peer_certificates.is_empty() {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::NoCertificate,
|
||||
}),
|
||||
Some(Error::ErrClientCertificateRequired),
|
||||
));
|
||||
}
|
||||
}
|
||||
ClientAuthType::VerifyClientCertIfGiven => {
|
||||
if !state.peer_certificates.is_empty() && !state.peer_certificates_verified {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(Error::ErrClientCertificateNotVerified),
|
||||
));
|
||||
}
|
||||
}
|
||||
ClientAuthType::RequireAndVerifyClientCert => {
|
||||
if state.peer_certificates.is_empty() {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::NoCertificate,
|
||||
}),
|
||||
Some(Error::ErrClientCertificateRequired),
|
||||
));
|
||||
}
|
||||
if !state.peer_certificates_verified {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(Error::ErrClientCertificateNotVerified),
|
||||
));
|
||||
}
|
||||
}
|
||||
ClientAuthType::NoClientCert | ClientAuthType::RequestClientCert => {
|
||||
return Ok(Box::new(Flight6 {}) as Box<dyn Flight + Send + Sync>);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Box::new(Flight6 {}) as Box<dyn Flight + Send + Sync>)
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
_cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
let mut extensions = vec![Extension::RenegotiationInfo(ExtensionRenegotiationInfo {
|
||||
renegotiated_connection: 0,
|
||||
})];
|
||||
if (cfg.extended_master_secret == ExtendedMasterSecretType::Request
|
||||
|| cfg.extended_master_secret == ExtendedMasterSecretType::Require)
|
||||
&& state.extended_master_secret
|
||||
{
|
||||
extensions.push(Extension::UseExtendedMasterSecret(
|
||||
ExtensionUseExtendedMasterSecret { supported: true },
|
||||
));
|
||||
}
|
||||
|
||||
if state.srtp_protection_profile != SrtpProtectionProfile::Unsupported {
|
||||
extensions.push(Extension::UseSrtp(ExtensionUseSrtp {
|
||||
protection_profiles: vec![state.srtp_protection_profile],
|
||||
}));
|
||||
}
|
||||
|
||||
if cfg.local_psk_callback.is_none() {
|
||||
extensions.extend_from_slice(&[
|
||||
Extension::SupportedEllipticCurves(ExtensionSupportedEllipticCurves {
|
||||
elliptic_curves: vec![NamedCurve::P256, NamedCurve::X25519, NamedCurve::P384],
|
||||
}),
|
||||
Extension::SupportedPointFormats(ExtensionSupportedPointFormats {
|
||||
point_formats: vec![ELLIPTIC_CURVE_POINT_FORMAT_UNCOMPRESSED],
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
let mut pkts = vec![Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ServerHello(
|
||||
HandshakeMessageServerHello {
|
||||
version: PROTOCOL_VERSION1_2,
|
||||
random: state.local_random.clone(),
|
||||
cipher_suite: {
|
||||
let cipher_suite = state.cipher_suite.lock().await;
|
||||
if let Some(cipher_suite) = &*cipher_suite {
|
||||
cipher_suite.id()
|
||||
} else {
|
||||
CipherSuiteId::Unsupported
|
||||
}
|
||||
},
|
||||
compression_method: default_compression_methods().ids[0],
|
||||
extensions,
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
}];
|
||||
|
||||
if cfg.local_psk_callback.is_none() {
|
||||
let certificate = match cfg.get_certificate(&cfg.server_name) {
|
||||
Ok(cert) => cert,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::HandshakeFailure,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::Certificate(
|
||||
HandshakeMessageCertificate {
|
||||
certificate: certificate
|
||||
.certificate
|
||||
.iter()
|
||||
.map(|x| x.as_ref().to_owned())
|
||||
.collect(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
|
||||
let mut server_random = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(server_random.as_mut());
|
||||
let _ = state.local_random.marshal(&mut writer);
|
||||
}
|
||||
let mut client_random = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(client_random.as_mut());
|
||||
let _ = state.remote_random.marshal(&mut writer);
|
||||
}
|
||||
|
||||
// Find compatible signature scheme
|
||||
let signature_hash_algo = match select_signature_scheme(
|
||||
&cfg.local_signature_schemes,
|
||||
&certificate.private_key,
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(local_keypair) = &state.local_keypair {
|
||||
let signature = match generate_key_signature(
|
||||
&client_random,
|
||||
&server_random,
|
||||
&local_keypair.public_key,
|
||||
state.named_curve,
|
||||
&certificate.private_key, /*, signature_hash_algo.hash*/
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.local_key_signature = signature;
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ServerKeyExchange(
|
||||
HandshakeMessageServerKeyExchange {
|
||||
identity_hint: vec![],
|
||||
elliptic_curve_type: EllipticCurveType::NamedCurve,
|
||||
named_curve: state.named_curve,
|
||||
public_key: local_keypair.public_key.clone(),
|
||||
algorithm: SignatureHashAlgorithm {
|
||||
hash: signature_hash_algo.hash,
|
||||
signature: signature_hash_algo.signature,
|
||||
},
|
||||
signature: state.local_key_signature.clone(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
}
|
||||
|
||||
if cfg.client_auth as u8 > ClientAuthType::NoClientCert as u8 {
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::CertificateRequest(
|
||||
HandshakeMessageCertificateRequest {
|
||||
certificate_types: vec![
|
||||
ClientCertificateType::RsaSign,
|
||||
ClientCertificateType::EcdsaSign,
|
||||
],
|
||||
signature_hash_algorithms: cfg.local_signature_schemes.clone(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
}
|
||||
} else if let Some(local_psk_identity_hint) = &cfg.local_psk_identity_hint {
|
||||
// To help the client in selecting which identity to use, the server
|
||||
// can provide a "PSK identity hint" in the ServerKeyExchange message.
|
||||
// If no hint is provided, the ServerKeyExchange message is omitted.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4279#section-2
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ServerKeyExchange(
|
||||
HandshakeMessageServerKeyExchange {
|
||||
identity_hint: local_psk_identity_hint.clone(),
|
||||
elliptic_curve_type: EllipticCurveType::Unsupported,
|
||||
named_curve: NamedCurve::Unsupported,
|
||||
public_key: vec![],
|
||||
algorithm: SignatureHashAlgorithm {
|
||||
hash: HashAlgorithm::Unsupported,
|
||||
signature: SignatureAlgorithm::Unsupported,
|
||||
},
|
||||
signature: vec![],
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
}
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ServerHelloDone(
|
||||
HandshakeMessageServerHelloDone {},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
|
||||
Ok(pkts)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::*;
|
||||
use crate::error::Result;
|
||||
|
||||
struct MockCipherSuite {}
|
||||
|
||||
impl CipherSuite for MockCipherSuite {
|
||||
fn to_string(&self) -> String {
|
||||
"MockCipherSuite".into()
|
||||
}
|
||||
fn id(&self) -> CipherSuiteId {
|
||||
unimplemented!();
|
||||
}
|
||||
fn certificate_type(&self) -> ClientCertificateType {
|
||||
unimplemented!();
|
||||
}
|
||||
fn hash_func(&self) -> CipherSuiteHash {
|
||||
unimplemented!();
|
||||
}
|
||||
fn is_psk(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn is_initialized(&self) -> bool {
|
||||
panic!("is_initialized called with Certificate but not CertificateVerify");
|
||||
}
|
||||
|
||||
// Generate the internal encryption state
|
||||
fn init(
|
||||
&mut self,
|
||||
_master_secret: &[u8],
|
||||
_client_random: &[u8],
|
||||
_server_random: &[u8],
|
||||
_is_client: bool,
|
||||
) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn encrypt(&self, _pkt_rlh: &RecordLayerHeader, _raw: &[u8]) -> Result<Vec<u8>> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn decrypt(&self, _input: &[u8]) -> Result<Vec<u8>> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that if a client sends a certificate they must also send a `CertificateVerify`
|
||||
// message. The `Flight4` must not interact with the `cipher_suite` if the `CertificateVerify`
|
||||
// is missing.
|
||||
#[tokio::test]
|
||||
async fn test_flight4_process_certificateverify() {
|
||||
let mut state = State {
|
||||
cipher_suite: Arc::new(Mutex::new(Some(Box::new(MockCipherSuite {})))),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let raw_certificate = vec![
|
||||
0x0b, 0x00, 0x01, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x9b, 0x00, 0x01,
|
||||
0x98, 0x00, 0x01, 0x95, 0x30, 0x82, 0x01, 0x91, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03,
|
||||
0x02, 0x01, 0x02, 0x02, 0x11, 0x01, 0x65, 0x03, 0x3f, 0x4d, 0x0b, 0x9a, 0x62, 0x91,
|
||||
0xdb, 0x4d, 0x28, 0x2c, 0x1f, 0xd6, 0x73, 0x32, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86,
|
||||
0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x00, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32,
|
||||
0x30, 0x35, 0x31, 0x35, 0x31, 0x38, 0x34, 0x33, 0x35, 0x35, 0x5a, 0x17, 0x0d, 0x32,
|
||||
0x32, 0x30, 0x36, 0x31, 0x35, 0x31, 0x38, 0x34, 0x33, 0x35, 0x35, 0x5a, 0x30, 0x00,
|
||||
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, 0xc3,
|
||||
0xb7, 0x13, 0x1a, 0x0a, 0xfc, 0xd0, 0x82, 0xf8, 0x94, 0x5e, 0xc0, 0x77, 0x07, 0x81,
|
||||
0x28, 0xc9, 0xcb, 0x08, 0x84, 0x50, 0x6b, 0xf0, 0x22, 0xe8, 0x79, 0xb9, 0x15, 0x33,
|
||||
0xc4, 0x56, 0xa1, 0xd3, 0x1b, 0x24, 0xe3, 0x61, 0xbd, 0x4d, 0x65, 0x80, 0x6b, 0x5d,
|
||||
0x96, 0x48, 0xa2, 0x44, 0x9e, 0xce, 0xe8, 0x65, 0xd6, 0x3c, 0xe0, 0x9b, 0x6b, 0xa1,
|
||||
0x36, 0x34, 0xb2, 0x39, 0xe2, 0x03, 0x00, 0xa3, 0x81, 0x92, 0x30, 0x81, 0x8f, 0x30,
|
||||
0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02,
|
||||
0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08,
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
|
||||
0x05, 0x07, 0x03, 0x01, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
|
||||
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
|
||||
0x04, 0x16, 0x04, 0x14, 0xb1, 0x1a, 0xe3, 0xeb, 0x6f, 0x7c, 0xc3, 0x8f, 0xba, 0x6f,
|
||||
0x1c, 0xe8, 0xf0, 0x23, 0x08, 0x50, 0x8d, 0x3c, 0xea, 0x31, 0x30, 0x2e, 0x06, 0x03,
|
||||
0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04, 0x24, 0x30, 0x22, 0x82, 0x20, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
|
||||
0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x06, 0x31, 0x43, 0xac, 0x03, 0x45, 0x79,
|
||||
0x3c, 0xd7, 0x5f, 0x6e, 0x6a, 0xf8, 0x0e, 0xfd, 0x35, 0x49, 0xee, 0x1b, 0xbc, 0x47,
|
||||
0xce, 0xe3, 0x39, 0xec, 0xe4, 0x62, 0xe1, 0x30, 0x1a, 0xa1, 0x89, 0x02, 0x20, 0x35,
|
||||
0xcd, 0x7a, 0x15, 0x68, 0x09, 0x50, 0x49, 0x9e, 0x3e, 0x05, 0xd7, 0xc2, 0x69, 0x3f,
|
||||
0x9c, 0x0c, 0x98, 0x92, 0x65, 0xec, 0xae, 0x44, 0xfe, 0xe5, 0x68, 0xb8, 0x09, 0x78,
|
||||
0x7f, 0x6b, 0x77,
|
||||
];
|
||||
|
||||
let raw_client_key_exchange = vec![
|
||||
0x10, 0x00, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x96,
|
||||
0xed, 0x0c, 0xee, 0xf3, 0x11, 0xb1, 0x9d, 0x8b, 0x1c, 0x02, 0x7f, 0x06, 0x7c, 0x57,
|
||||
0x7a, 0x14, 0xa6, 0x41, 0xde, 0x63, 0x57, 0x9e, 0xcd, 0x34, 0x54, 0xba, 0x37, 0x4d,
|
||||
0x34, 0x15, 0x18,
|
||||
];
|
||||
|
||||
let mut cache = HandshakeCache::new();
|
||||
cache
|
||||
.push(raw_certificate, 0, 0, HandshakeType::Certificate, true)
|
||||
.await;
|
||||
cache
|
||||
.push(
|
||||
raw_client_key_exchange,
|
||||
0,
|
||||
1,
|
||||
HandshakeType::ClientKeyExchange,
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
|
||||
let cfg = HandshakeConfig::default();
|
||||
|
||||
let (mut tx, _rx) = mpsc::channel::<mpsc::Sender<()>>(1);
|
||||
|
||||
let f = Flight4 {};
|
||||
let res = f.parse(&mut tx, &mut state, &cache, &cfg).await;
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
779
libs/dtls/src/flight/flight5.rs
Normal file
779
libs/dtls/src/flight/flight5.rs
Normal file
@@ -0,0 +1,779 @@
|
||||
use std::fmt;
|
||||
use std::io::{BufReader, BufWriter};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::flight3::*;
|
||||
use super::*;
|
||||
use crate::change_cipher_spec::ChangeCipherSpec;
|
||||
use crate::content::*;
|
||||
use crate::crypto::*;
|
||||
use crate::curve::named_curve::*;
|
||||
use crate::curve::*;
|
||||
use crate::error::Error;
|
||||
use crate::handshake::handshake_message_certificate::*;
|
||||
use crate::handshake::handshake_message_certificate_verify::*;
|
||||
use crate::handshake::handshake_message_client_key_exchange::*;
|
||||
use crate::handshake::handshake_message_finished::*;
|
||||
use crate::handshake::handshake_message_server_key_exchange::*;
|
||||
use crate::handshake::*;
|
||||
use crate::prf::*;
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
use crate::record_layer::*;
|
||||
use crate::signature_hash_algorithm::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight5;
|
||||
|
||||
impl fmt::Display for Flight5 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 5")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight5 {
|
||||
fn is_last_recv_flight(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn parse(
|
||||
&self,
|
||||
_tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
let (_seq, msgs) = match cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence,
|
||||
&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::Finished,
|
||||
epoch: cfg.initial_epoch + 1,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
let finished =
|
||||
if let Some(HandshakeMessage::Finished(h)) = msgs.get(&HandshakeType::Finished) {
|
||||
h
|
||||
} else {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
));
|
||||
};
|
||||
|
||||
let plain_text = cache
|
||||
.pull_and_merge(&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateVerify,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Finished,
|
||||
epoch: cfg.initial_epoch + 1,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
])
|
||||
.await;
|
||||
|
||||
{
|
||||
let cipher_suite = state.cipher_suite.lock().await;
|
||||
if let Some(cipher_suite) = &*cipher_suite {
|
||||
let expected_verify_data = match prf_verify_data_server(
|
||||
&state.master_secret,
|
||||
&plain_text,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(d) => d,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if expected_verify_data != finished.verify_data {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::HandshakeFailure,
|
||||
}),
|
||||
Some(Error::ErrVerifyDataMismatch),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Box::new(Flight5 {}))
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
let certificate = if !cfg.local_certificates.is_empty() {
|
||||
let cert = match cfg.get_certificate(&cfg.server_name) {
|
||||
Ok(cert) => cert,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::HandshakeFailure,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
Some(cert)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut pkts = vec![];
|
||||
|
||||
if state.remote_requested_certificate {
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::Certificate(
|
||||
HandshakeMessageCertificate {
|
||||
certificate: if let Some(cert) = &certificate {
|
||||
cert.certificate
|
||||
.iter()
|
||||
.map(|x| x.as_ref().to_owned())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
}
|
||||
|
||||
let mut client_key_exchange = HandshakeMessageClientKeyExchange {
|
||||
identity_hint: vec![],
|
||||
public_key: vec![],
|
||||
};
|
||||
if cfg.local_psk_callback.is_none() {
|
||||
if let Some(local_keypair) = &state.local_keypair {
|
||||
client_key_exchange
|
||||
.public_key
|
||||
.clone_from(&local_keypair.public_key);
|
||||
}
|
||||
} else if let Some(local_psk_identity_hint) = &cfg.local_psk_identity_hint {
|
||||
client_key_exchange
|
||||
.identity_hint
|
||||
.clone_from(local_psk_identity_hint);
|
||||
}
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::ClientKeyExchange(
|
||||
client_key_exchange,
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
|
||||
let server_key_exchange_data = cache
|
||||
.pull_and_merge(&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
}])
|
||||
.await;
|
||||
|
||||
let mut server_key_exchange = HandshakeMessageServerKeyExchange {
|
||||
identity_hint: vec![],
|
||||
elliptic_curve_type: EllipticCurveType::Unsupported,
|
||||
named_curve: NamedCurve::Unsupported,
|
||||
public_key: vec![],
|
||||
algorithm: SignatureHashAlgorithm {
|
||||
hash: HashAlgorithm::Unsupported,
|
||||
signature: SignatureAlgorithm::Unsupported,
|
||||
},
|
||||
signature: vec![],
|
||||
};
|
||||
|
||||
// handshakeMessageServerKeyExchange is optional for PSK
|
||||
if server_key_exchange_data.is_empty() {
|
||||
if let Err((alert, err)) =
|
||||
handle_server_key_exchange(state, cfg, &server_key_exchange).await
|
||||
{
|
||||
return Err((alert, err));
|
||||
}
|
||||
} else {
|
||||
let mut reader = BufReader::new(server_key_exchange_data.as_slice());
|
||||
let raw_handshake = match Handshake::unmarshal(&mut reader) {
|
||||
Ok(h) => h,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::UnexpectedMessage,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
match raw_handshake.handshake_message {
|
||||
HandshakeMessage::ServerKeyExchange(h) => server_key_exchange = h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::UnexpectedMessage,
|
||||
}),
|
||||
Some(Error::ErrInvalidContentType),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Append not-yet-sent packets
|
||||
let mut merged = vec![];
|
||||
let mut seq_pred = state.handshake_send_sequence as u16;
|
||||
for p in &mut pkts {
|
||||
let h = match &mut p.record.content {
|
||||
Content::Handshake(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(Error::ErrInvalidContentType),
|
||||
))
|
||||
}
|
||||
};
|
||||
h.handshake_header.message_sequence = seq_pred;
|
||||
seq_pred += 1;
|
||||
|
||||
let mut raw = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
|
||||
if let Err(err) = h.marshal(&mut writer) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
merged.extend_from_slice(&raw);
|
||||
}
|
||||
|
||||
if let Err((alert, err)) =
|
||||
initialize_cipher_suite(state, cache, cfg, &server_key_exchange, &merged).await
|
||||
{
|
||||
return Err((alert, err));
|
||||
}
|
||||
|
||||
// If the client has sent a certificate with signing ability, a digitally-signed
|
||||
// CertificateVerify message is sent to explicitly verify possession of the
|
||||
// private key in the certificate.
|
||||
if state.remote_requested_certificate && !cfg.local_certificates.is_empty() {
|
||||
let mut plain_text = cache
|
||||
.pull_and_merge(&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
])
|
||||
.await;
|
||||
|
||||
plain_text.extend_from_slice(&merged);
|
||||
|
||||
// Find compatible signature scheme
|
||||
let signature_hash_algo = match select_signature_scheme(
|
||||
&cfg.local_signature_schemes,
|
||||
&certificate.as_ref().unwrap().private_key,
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let cert_verify = match generate_certificate_verify(
|
||||
&plain_text,
|
||||
&certificate.as_ref().unwrap().private_key, /*, signature_hash_algo.hash*/
|
||||
) {
|
||||
Ok(cert) => cert,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
state.local_certificates_verify = cert_verify;
|
||||
|
||||
let mut p = Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::CertificateVerify(
|
||||
HandshakeMessageCertificateVerify {
|
||||
algorithm: signature_hash_algo,
|
||||
signature: state.local_certificates_verify.clone(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
};
|
||||
|
||||
let h = match &mut p.record.content {
|
||||
Content::Handshake(h) => h,
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(Error::ErrInvalidContentType),
|
||||
))
|
||||
}
|
||||
};
|
||||
h.handshake_header.message_sequence = seq_pred;
|
||||
|
||||
// seqPred++ // this is the last use of seqPred
|
||||
|
||||
let mut raw = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
|
||||
if let Err(err) = h.marshal(&mut writer) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
merged.extend_from_slice(&raw);
|
||||
|
||||
pkts.push(p);
|
||||
}
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::ChangeCipherSpec(ChangeCipherSpec {}),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
});
|
||||
|
||||
if state.local_verify_data.is_empty() {
|
||||
let mut plain_text = cache
|
||||
.pull_and_merge(&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateVerify,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Finished,
|
||||
epoch: cfg.initial_epoch + 1,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
])
|
||||
.await;
|
||||
|
||||
plain_text.extend_from_slice(&merged);
|
||||
|
||||
let cipher_suite = state.cipher_suite.lock().await;
|
||||
if let Some(cipher_suite) = &*cipher_suite {
|
||||
state.local_verify_data = match prf_verify_data_client(
|
||||
&state.master_secret,
|
||||
&plain_text,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
1,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::Finished(
|
||||
HandshakeMessageFinished {
|
||||
verify_data: state.local_verify_data.clone(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: true,
|
||||
reset_local_sequence_number: true,
|
||||
});
|
||||
|
||||
Ok(pkts)
|
||||
}
|
||||
}
|
||||
async fn initialize_cipher_suite(
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
h: &HandshakeMessageServerKeyExchange,
|
||||
sending_plain_text: &[u8],
|
||||
) -> Result<(), (Option<Alert>, Option<Error>)> {
|
||||
let mut cipher_suite = state.cipher_suite.lock().await;
|
||||
|
||||
if let Some(cipher_suite) = &*cipher_suite {
|
||||
if cipher_suite.is_initialized() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut client_random = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(client_random.as_mut());
|
||||
let _ = state.local_random.marshal(&mut writer);
|
||||
}
|
||||
let mut server_random = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(server_random.as_mut());
|
||||
let _ = state.remote_random.marshal(&mut writer);
|
||||
}
|
||||
|
||||
if let Some(cipher_suite) = &*cipher_suite {
|
||||
if state.extended_master_secret {
|
||||
let session_hash = match cache
|
||||
.session_hash(
|
||||
cipher_suite.hash_func(),
|
||||
cfg.initial_epoch,
|
||||
sending_plain_text,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
state.master_secret = match prf_extended_master_secret(
|
||||
&state.pre_master_secret,
|
||||
&session_hash,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::IllegalParameter,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
state.master_secret = match prf_master_secret(
|
||||
&state.pre_master_secret,
|
||||
&client_random,
|
||||
&server_random,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.local_psk_callback.is_none() {
|
||||
// Verify that the pair of hash algorithm and signiture is listed.
|
||||
let mut valid_signature_scheme = false;
|
||||
for ss in &cfg.local_signature_schemes {
|
||||
if ss.hash == h.algorithm.hash && ss.signature == h.algorithm.signature {
|
||||
valid_signature_scheme = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !valid_signature_scheme {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InsufficientSecurity,
|
||||
}),
|
||||
Some(Error::ErrNoAvailableSignatureSchemes),
|
||||
));
|
||||
}
|
||||
|
||||
let expected_msg =
|
||||
value_key_message(&client_random, &server_random, &h.public_key, h.named_curve);
|
||||
if let Err(err) = verify_key_signature(
|
||||
&expected_msg,
|
||||
&h.algorithm,
|
||||
&h.signature,
|
||||
&state.peer_certificates,
|
||||
cfg.insecure_verification,
|
||||
) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
|
||||
let mut chains = vec![];
|
||||
if !cfg.insecure_skip_verify {
|
||||
chains = match verify_server_cert(
|
||||
&state.peer_certificates,
|
||||
&cfg.server_cert_verifier,
|
||||
&cfg.server_name,
|
||||
) {
|
||||
Ok(chains) => chains,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(verify_peer_certificate) = &cfg.verify_peer_certificate {
|
||||
if let Err(err) = verify_peer_certificate(&state.peer_certificates, &chains) {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::BadCertificate,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cipher_suite) = &mut *cipher_suite {
|
||||
if let Err(err) =
|
||||
cipher_suite.init(&state.master_secret, &client_random, &server_random, true)
|
||||
{
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
190
libs/dtls/src/flight/flight6.rs
Normal file
190
libs/dtls/src/flight/flight6.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use std::fmt;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::*;
|
||||
use crate::change_cipher_spec::*;
|
||||
use crate::content::*;
|
||||
use crate::handshake::handshake_message_finished::*;
|
||||
use crate::handshake::*;
|
||||
use crate::prf::*;
|
||||
use crate::record_layer::record_layer_header::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Flight6;
|
||||
|
||||
impl fmt::Display for Flight6 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Flight 6")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Flight for Flight6 {
|
||||
fn is_last_send_flight(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn parse(
|
||||
&self,
|
||||
_tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
|
||||
let (_, msgs) = match cache
|
||||
.full_pull_map(
|
||||
state.handshake_recv_sequence - 1,
|
||||
&[HandshakeCachePullRule {
|
||||
typ: HandshakeType::Finished,
|
||||
epoch: cfg.initial_epoch + 1,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
}],
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((seq, msgs)) => (seq, msgs),
|
||||
// No valid message received. Keep reading
|
||||
Err(_) => return Err((None, None)),
|
||||
};
|
||||
|
||||
if let Some(message) = msgs.get(&HandshakeType::Finished) {
|
||||
match message {
|
||||
HandshakeMessage::Finished(_) => {}
|
||||
_ => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
None,
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Other party retransmitted the last flight.
|
||||
Ok(Box::new(Flight6 {}))
|
||||
}
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
|
||||
let mut pkts = vec![Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
0,
|
||||
Content::ChangeCipherSpec(ChangeCipherSpec {}),
|
||||
),
|
||||
should_encrypt: false,
|
||||
reset_local_sequence_number: false,
|
||||
}];
|
||||
|
||||
if state.local_verify_data.is_empty() {
|
||||
let plain_text = cache
|
||||
.pull_and_merge(&[
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHello,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: false,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Certificate,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::ClientKeyExchange,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::CertificateVerify,
|
||||
epoch: cfg.initial_epoch,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
HandshakeCachePullRule {
|
||||
typ: HandshakeType::Finished,
|
||||
epoch: cfg.initial_epoch + 1,
|
||||
is_client: true,
|
||||
optional: false,
|
||||
},
|
||||
])
|
||||
.await;
|
||||
|
||||
let cipher_suite = state.cipher_suite.lock().await;
|
||||
if let Some(cipher_suite) = &*cipher_suite {
|
||||
state.local_verify_data = match prf_verify_data_server(
|
||||
&state.master_secret,
|
||||
&plain_text,
|
||||
cipher_suite.hash_func(),
|
||||
) {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
return Err((
|
||||
Some(Alert {
|
||||
alert_level: AlertLevel::Fatal,
|
||||
alert_description: AlertDescription::InternalError,
|
||||
}),
|
||||
Some(err),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pkts.push(Packet {
|
||||
record: RecordLayer::new(
|
||||
PROTOCOL_VERSION1_2,
|
||||
1,
|
||||
Content::Handshake(Handshake::new(HandshakeMessage::Finished(
|
||||
HandshakeMessageFinished {
|
||||
verify_data: state.local_verify_data.clone(),
|
||||
},
|
||||
))),
|
||||
),
|
||||
should_encrypt: true,
|
||||
reset_local_sequence_number: true,
|
||||
});
|
||||
|
||||
Ok(pkts)
|
||||
}
|
||||
}
|
||||
87
libs/dtls/src/flight/mod.rs
Normal file
87
libs/dtls/src/flight/mod.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
pub(crate) mod flight0;
|
||||
pub(crate) mod flight1;
|
||||
pub(crate) mod flight2;
|
||||
pub(crate) mod flight3;
|
||||
pub(crate) mod flight4;
|
||||
pub(crate) mod flight5;
|
||||
pub(crate) mod flight6;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::alert::*;
|
||||
use crate::error::Error;
|
||||
use crate::handshake::handshake_cache::*;
|
||||
use crate::handshaker::*;
|
||||
use crate::record_layer::*;
|
||||
use crate::state::*;
|
||||
|
||||
/*
|
||||
DTLS messages are grouped into a series of message flights, according
|
||||
to the diagrams below. Although each Flight of messages may consist
|
||||
of a number of messages, they should be viewed as monolithic for the
|
||||
purpose of timeout and retransmission.
|
||||
https://tools.ietf.org/html/rfc4347#section-4.2.4
|
||||
Client Server
|
||||
------ ------
|
||||
Waiting Flight 0
|
||||
|
||||
ClientHello --------> Flight 1
|
||||
|
||||
<------- HelloVerifyRequest Flight 2
|
||||
|
||||
ClientHello --------> Flight 3
|
||||
|
||||
ServerHello \
|
||||
Certificate* \
|
||||
ServerKeyExchange* Flight 4
|
||||
CertificateRequest* /
|
||||
<-------- ServerHelloDone /
|
||||
|
||||
Certificate* \
|
||||
ClientKeyExchange \
|
||||
CertificateVerify* Flight 5
|
||||
[ChangeCipherSpec] /
|
||||
Finished --------> /
|
||||
|
||||
[ChangeCipherSpec] \ Flight 6
|
||||
<-------- Finished /
|
||||
|
||||
*/
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Packet {
|
||||
pub(crate) record: RecordLayer,
|
||||
pub(crate) should_encrypt: bool,
|
||||
pub(crate) reset_local_sequence_number: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub(crate) trait Flight: fmt::Display + fmt::Debug {
|
||||
fn is_last_send_flight(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn is_last_recv_flight(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn has_retransmit(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn parse(
|
||||
&self,
|
||||
tx: &mut mpsc::Sender<mpsc::Sender<()>>,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)>;
|
||||
|
||||
async fn generate(
|
||||
&self,
|
||||
state: &mut State,
|
||||
cache: &HandshakeCache,
|
||||
cfg: &HandshakeConfig,
|
||||
) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)>;
|
||||
}
|
||||
Reference in New Issue
Block a user