2023-05-04 00:38:05 +00:00
|
|
|
use crate::ca_pool::{load_ca_pool, EpfCaPool};
|
|
|
|
use crate::danger_trace;
|
|
|
|
use crate::error::EpfHandshakeError;
|
|
|
|
use crate::pki::{
|
|
|
|
EPFCertificate, EpfPkiCertificateOps, EpfPrivateKey, EpfPublicKey, EPFPKI_PUBLIC_KEY_LENGTH,
|
|
|
|
};
|
|
|
|
use crate::protocol::{
|
|
|
|
encode_packet, recv_packet, EpfApplicationData, EpfClientHello, EpfClientState, EpfFinished,
|
|
|
|
EpfMessage, EpfServerHello, EpfServerState, PACKET_APPLICATION_DATA, PACKET_CLIENT_HELLO,
|
|
|
|
PACKET_FINISHED, PACKET_SERVER_HELLO, PROTOCOL_VERSION,
|
|
|
|
};
|
2023-05-03 16:31:57 +00:00
|
|
|
use async_trait::async_trait;
|
|
|
|
use chacha20poly1305::aead::{Aead, Payload};
|
2023-05-04 00:38:05 +00:00
|
|
|
use chacha20poly1305::{AeadCore, Key, KeyInit, XChaCha20Poly1305, XNonce};
|
2023-05-04 00:24:26 +00:00
|
|
|
use ed25519_dalek::{SecretKey, SigningKey};
|
|
|
|
use log::{debug, trace};
|
2023-05-03 16:31:57 +00:00
|
|
|
use rand::rngs::OsRng;
|
2023-05-04 00:38:05 +00:00
|
|
|
use rand::Rng;
|
|
|
|
use std::error::Error;
|
|
|
|
use std::io;
|
2023-05-03 16:31:57 +00:00
|
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
2023-05-04 00:38:05 +00:00
|
|
|
use x25519_dalek::{x25519, PublicKey, StaticSecret};
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
///// CLIENT /////
|
|
|
|
|
|
|
|
pub struct EpfClientUpgraded<T: AsyncWriteExt + AsyncReadExt> {
|
|
|
|
inner: T,
|
|
|
|
state: EpfClientState,
|
|
|
|
client_random: [u8; 24],
|
|
|
|
server_random: [u8; 16],
|
|
|
|
client_cert: Option<EPFCertificate>,
|
|
|
|
packet_queue: Vec<EpfMessage>,
|
|
|
|
server_cert: Option<EPFCertificate>,
|
|
|
|
cipher: Option<XChaCha20Poly1305>,
|
|
|
|
private_key: EpfPrivateKey,
|
2023-05-04 00:38:05 +00:00
|
|
|
public_key: PublicKey,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
#[derive(Debug)]
|
2023-05-03 16:31:57 +00:00
|
|
|
pub enum ClientAuthentication {
|
|
|
|
Cert(Box<EPFCertificate>, EpfPrivateKey),
|
2023-05-04 00:38:05 +00:00
|
|
|
Ephemeral,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait EpfClientUpgradable {
|
2023-05-04 00:38:05 +00:00
|
|
|
async fn upgrade(self, auth: ClientAuthentication) -> EpfClientUpgraded<Self>
|
|
|
|
where
|
|
|
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send;
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-05-04 00:38:05 +00:00
|
|
|
impl<T> EpfClientUpgradable for T
|
|
|
|
where
|
|
|
|
T: AsyncWriteExt + AsyncReadExt + Send,
|
|
|
|
{
|
|
|
|
async fn upgrade(self, auth: ClientAuthentication) -> EpfClientUpgraded<Self>
|
|
|
|
where
|
|
|
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send,
|
|
|
|
{
|
2023-05-04 00:24:26 +00:00
|
|
|
danger_trace!(target: "EpfClientUpgradable", "upgrade(auth: {:?})", auth);
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let private_key;
|
2023-05-04 00:24:26 +00:00
|
|
|
let public_key;
|
2023-05-03 16:31:57 +00:00
|
|
|
let cert;
|
|
|
|
|
|
|
|
match auth {
|
|
|
|
ClientAuthentication::Cert(cert_d, key) => {
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("----!!!!! CERT AUTHENTICATION !!!!!----");
|
2023-05-03 16:31:57 +00:00
|
|
|
cert = Some(cert_d);
|
2023-05-04 00:24:26 +00:00
|
|
|
private_key = key.clone();
|
|
|
|
public_key = PublicKey::from(&StaticSecret::from(private_key.to_bytes()));
|
2023-05-04 00:38:05 +00:00
|
|
|
}
|
2023-05-03 16:31:57 +00:00
|
|
|
ClientAuthentication::Ephemeral => {
|
|
|
|
cert = None;
|
|
|
|
let private_key_l: [u8; 32] = OsRng.gen();
|
|
|
|
let private_key_real = SigningKey::from(private_key_l);
|
2023-05-04 00:24:26 +00:00
|
|
|
public_key = PublicKey::from(&StaticSecret::from(private_key_real.to_bytes()));
|
|
|
|
private_key = private_key_real;
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EpfClientUpgraded {
|
|
|
|
inner: self,
|
|
|
|
state: EpfClientState::NotStarted,
|
|
|
|
client_random: OsRng.gen(),
|
|
|
|
server_random: [0u8; 16],
|
|
|
|
client_cert: cert.map(|u| *u),
|
|
|
|
server_cert: None,
|
|
|
|
packet_queue: vec![],
|
|
|
|
cipher: None,
|
|
|
|
private_key,
|
|
|
|
public_key,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait EpfClientHandshaker<S: AsyncWriteExt + AsyncReadExt + Unpin> {
|
2023-05-04 00:24:26 +00:00
|
|
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>>;
|
2023-05-04 00:38:05 +00:00
|
|
|
async fn upgrade(self) -> EpfClientStream<S>
|
|
|
|
where
|
|
|
|
Self: Sized;
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-05-04 00:38:05 +00:00
|
|
|
impl<T: AsyncWriteExt + AsyncReadExt + Send + Unpin> EpfClientHandshaker<T>
|
|
|
|
for EpfClientUpgraded<T>
|
|
|
|
{
|
2023-05-04 00:24:26 +00:00
|
|
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>> {
|
2023-05-03 16:31:57 +00:00
|
|
|
match self.state {
|
|
|
|
EpfClientState::NotStarted => (),
|
2023-05-04 00:38:05 +00:00
|
|
|
_ => return Err(EpfHandshakeError::AlreadyTunnelled.into()),
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Step 1: Send Client Hello
|
2023-05-04 00:38:05 +00:00
|
|
|
self.inner
|
|
|
|
.write_all(&encode_packet(
|
|
|
|
PACKET_CLIENT_HELLO,
|
|
|
|
&EpfClientHello {
|
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
client_random: self.client_random,
|
|
|
|
client_certificate: self.client_cert.clone(),
|
|
|
|
client_x25519_public_key: self.public_key.to_bytes(),
|
|
|
|
},
|
|
|
|
)?)
|
|
|
|
.await?;
|
2023-05-04 00:24:26 +00:00
|
|
|
self.inner.flush().await?;
|
|
|
|
|
|
|
|
trace!("---- !!!!! SENT CLIENT HELLO");
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
self.state = EpfClientState::WaitingForServerHello;
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
let server_x25519_key;
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
// Step 2: Wait for Server Hello
|
|
|
|
loop {
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("waiting for server hello");
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let packet = recv_packet(&mut self.inner).await?;
|
|
|
|
|
|
|
|
if packet.packet_id != PACKET_SERVER_HELLO {
|
|
|
|
self.packet_queue.push(packet);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let server_hello: EpfServerHello = rmp_serde::from_slice(&packet.packet_data)?;
|
|
|
|
|
|
|
|
self.server_random = server_hello.server_random;
|
|
|
|
|
|
|
|
if server_hello.protocol_version != PROTOCOL_VERSION {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::UnsupportedProtocolVersion(
|
|
|
|
server_hello.protocol_version as usize,
|
|
|
|
)
|
|
|
|
.into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.server_cert = Some(server_hello.server_certificate);
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
server_x25519_key = server_hello.server_x25519_public_key;
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3: Validate Server Certificate
|
|
|
|
let cert_valid = self.server_cert.as_ref().unwrap().verify(&cert_pool);
|
|
|
|
if let Err(e) = cert_valid {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::InvalidCertificate(e).into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
if let Ok(false) = cert_valid {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::UntrustedCertificate.into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
// Server Cert OK
|
|
|
|
|
|
|
|
// Step 4: Build the cipher
|
2023-05-04 00:24:26 +00:00
|
|
|
|
|
|
|
let private_key = StaticSecret::from(self.private_key.to_bytes());
|
|
|
|
let their_public_key = PublicKey::from(server_x25519_key);
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
assert_ne!(
|
|
|
|
their_public_key.to_bytes(),
|
|
|
|
PublicKey::from(&private_key).to_bytes()
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
danger_trace!(
|
|
|
|
"pr: {}, their pub: {}, my pub: {}",
|
|
|
|
hex::encode(self.private_key.to_bytes()),
|
|
|
|
hex::encode(self.server_cert.as_ref().unwrap().details.public_key),
|
|
|
|
hex::encode(self.private_key.verifying_key().to_bytes())
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
|
|
|
let shared_key = private_key.diffie_hellman(&their_public_key).to_bytes();
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
trace!(
|
|
|
|
"server public key: {:x?}",
|
|
|
|
self.server_cert.as_ref().unwrap().details.public_key
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
danger_trace!("shared key: {}", hex::encode(shared_key));
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
let cc20p1305_key = Key::from(shared_key);
|
|
|
|
let cc20p1305 = XChaCha20Poly1305::new(&cc20p1305_key);
|
|
|
|
self.cipher = Some(cc20p1305);
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: &[0x42],
|
|
|
|
aad: &self.server_random,
|
|
|
|
};
|
|
|
|
|
|
|
|
let nonce = XNonce::from_slice(&self.client_random);
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("encrypting 0x42");
|
|
|
|
|
|
|
|
danger_trace!("aad: {:?} nonce: {:?}", payload.aad, nonce);
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let encrypted_0x42 = match self.cipher.as_ref().unwrap().encrypt(nonce, payload) {
|
|
|
|
Ok(d) => d,
|
2023-05-04 00:38:05 +00:00
|
|
|
Err(_) => return Err(EpfHandshakeError::EncryptionError.into()),
|
2023-05-03 16:31:57 +00:00
|
|
|
};
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
self.inner
|
|
|
|
.write_all(&encode_packet(
|
|
|
|
PACKET_FINISHED,
|
|
|
|
&EpfFinished {
|
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
encrypted_0x42,
|
|
|
|
},
|
|
|
|
)?)
|
|
|
|
.await?;
|
2023-05-04 00:24:26 +00:00
|
|
|
self.inner.flush().await?;
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
self.state = EpfClientState::WaitingForFinished;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let packet = recv_packet(&mut self.inner).await?;
|
|
|
|
|
|
|
|
if packet.packet_id != PACKET_FINISHED {
|
|
|
|
self.packet_queue.push(packet);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let packet_finished: EpfFinished = rmp_serde::from_slice(&packet.packet_data)?;
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("trying to debug 0x42");
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let payload = Payload {
|
|
|
|
msg: &packet_finished.encrypted_0x42,
|
|
|
|
aad: &self.server_random,
|
|
|
|
};
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
danger_trace!(
|
|
|
|
"ciphertext: {:?}, aad: {:?}, nonce: {:?}",
|
|
|
|
packet_finished.encrypted_0x42,
|
|
|
|
payload.aad,
|
|
|
|
nonce
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let hopefully_0x42 = match self.cipher.as_ref().unwrap().decrypt(nonce, payload) {
|
|
|
|
Ok(d) => d,
|
|
|
|
Err(_) => {
|
|
|
|
return Err(EpfHandshakeError::EncryptionError.into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if hopefully_0x42 != vec![0x42] {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::MissingKeyProof.into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.state = EpfClientState::Transport;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
async fn upgrade(self) -> EpfClientStream<T>
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
2023-05-04 00:24:26 +00:00
|
|
|
let aad = self.server_random.clone();
|
|
|
|
let client_cert = self.client_cert.clone();
|
|
|
|
let packet_queue = self.packet_queue.clone();
|
|
|
|
let server_cert = self.server_cert.unwrap().clone();
|
|
|
|
let cipher = self.cipher.unwrap().clone();
|
|
|
|
let private_key = self.private_key.clone();
|
|
|
|
let public_key = self.public_key.clone();
|
|
|
|
let raw_stream = self.inner;
|
2023-05-03 16:31:57 +00:00
|
|
|
EpfClientStream {
|
2023-05-04 00:24:26 +00:00
|
|
|
raw_stream,
|
|
|
|
aad,
|
|
|
|
client_cert,
|
|
|
|
packet_queue,
|
|
|
|
server_cert,
|
|
|
|
cipher,
|
|
|
|
private_key,
|
|
|
|
public_key,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
pub struct EpfClientStream<S: AsyncReadExt + AsyncWriteExt + Unpin> {
|
2023-05-03 16:31:57 +00:00
|
|
|
raw_stream: S,
|
|
|
|
aad: [u8; 16],
|
|
|
|
client_cert: Option<EPFCertificate>,
|
|
|
|
packet_queue: Vec<EpfMessage>,
|
|
|
|
server_cert: EPFCertificate,
|
|
|
|
cipher: XChaCha20Poly1305,
|
|
|
|
private_key: EpfPrivateKey,
|
2023-05-04 00:38:05 +00:00
|
|
|
public_key: PublicKey,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait EpfStreamOps {
|
|
|
|
async fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>>;
|
|
|
|
async fn read(&mut self) -> Result<Vec<u8>, Box<dyn Error>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-05-04 00:24:26 +00:00
|
|
|
impl<S: AsyncReadExt + AsyncWriteExt + Unpin + Send> EpfStreamOps for EpfClientStream<S> {
|
2023-05-03 16:31:57 +00:00
|
|
|
async fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>> {
|
|
|
|
let nonce = XChaCha20Poly1305::generate_nonce(OsRng);
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: data,
|
|
|
|
aad: &self.aad,
|
|
|
|
};
|
|
|
|
|
|
|
|
let ciphertext = match self.cipher.encrypt(&nonce, payload) {
|
|
|
|
Ok(c) => c,
|
2023-05-04 00:38:05 +00:00
|
|
|
Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Encryption error").into()),
|
2023-05-03 16:31:57 +00:00
|
|
|
};
|
|
|
|
let application_data = EpfApplicationData {
|
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
encrypted_application_data: ciphertext,
|
|
|
|
nonce: nonce.try_into().unwrap(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let packet = encode_packet(PACKET_APPLICATION_DATA, &application_data)?;
|
|
|
|
|
|
|
|
self.raw_stream.write_all(&packet).await?;
|
2023-05-04 00:24:26 +00:00
|
|
|
self.raw_stream.flush().await?;
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn read(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
|
|
loop {
|
|
|
|
let packet = recv_packet(&mut self.raw_stream).await?;
|
|
|
|
|
|
|
|
if packet.packet_id != PACKET_APPLICATION_DATA {
|
|
|
|
self.packet_queue.push(packet);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let app_data: EpfApplicationData = rmp_serde::from_slice(&packet.packet_data)?;
|
|
|
|
|
|
|
|
let nonce = XNonce::from_slice(&app_data.nonce);
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: &app_data.encrypted_application_data,
|
|
|
|
aad: &self.aad,
|
|
|
|
};
|
|
|
|
|
|
|
|
let plaintext = match self.cipher.decrypt(nonce, payload) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => {
|
|
|
|
return Err(io::Error::new(io::ErrorKind::Other, "Decryption error").into())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return Ok(plaintext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///// SERVER /////
|
|
|
|
|
|
|
|
pub struct EpfServerUpgraded<T: AsyncWriteExt + AsyncReadExt> {
|
|
|
|
inner: T,
|
|
|
|
state: EpfServerState,
|
|
|
|
client_random: [u8; 24],
|
|
|
|
server_random: [u8; 16],
|
|
|
|
client_cert: Option<EPFCertificate>,
|
|
|
|
packet_queue: Vec<EpfMessage>,
|
|
|
|
cipher: Option<XChaCha20Poly1305>,
|
|
|
|
cert: EPFCertificate,
|
|
|
|
private_key: EpfPrivateKey,
|
2023-05-04 00:38:05 +00:00
|
|
|
public_key: EpfPublicKey,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait EpfServerUpgradable {
|
2023-05-04 00:38:05 +00:00
|
|
|
async fn upgrade(
|
|
|
|
self,
|
|
|
|
cert: EPFCertificate,
|
|
|
|
private_key: EpfPrivateKey,
|
|
|
|
) -> EpfServerUpgraded<Self>
|
|
|
|
where
|
|
|
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send;
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-05-04 00:38:05 +00:00
|
|
|
impl<T: ?Sized> EpfServerUpgradable for T
|
|
|
|
where
|
|
|
|
T: AsyncWriteExt + AsyncReadExt + Send,
|
|
|
|
{
|
|
|
|
async fn upgrade(
|
|
|
|
self,
|
|
|
|
cert: EPFCertificate,
|
|
|
|
private_key: EpfPrivateKey,
|
|
|
|
) -> EpfServerUpgraded<Self>
|
|
|
|
where
|
|
|
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send,
|
|
|
|
{
|
2023-05-03 16:31:57 +00:00
|
|
|
EpfServerUpgraded {
|
|
|
|
inner: self,
|
|
|
|
state: EpfServerState::WaitingForClientHello,
|
|
|
|
server_random: OsRng.gen(),
|
|
|
|
client_random: [0u8; 24],
|
|
|
|
cert,
|
|
|
|
client_cert: None,
|
|
|
|
packet_queue: vec![],
|
|
|
|
cipher: None,
|
2023-05-04 00:24:26 +00:00
|
|
|
private_key: private_key.clone(),
|
|
|
|
public_key: private_key.verifying_key(),
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait EpfServerHandshaker<S: AsyncWriteExt + AsyncReadExt + Unpin> {
|
2023-05-04 00:24:26 +00:00
|
|
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>>;
|
2023-05-04 00:38:05 +00:00
|
|
|
async fn upgrade(self) -> EpfServerStream<S>
|
|
|
|
where
|
|
|
|
Self: Sized;
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-05-04 00:38:05 +00:00
|
|
|
impl<T: AsyncWriteExt + AsyncReadExt + Send + Unpin> EpfServerHandshaker<T>
|
|
|
|
for EpfServerUpgraded<T>
|
|
|
|
{
|
2023-05-04 00:24:26 +00:00
|
|
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>> {
|
2023-05-03 16:31:57 +00:00
|
|
|
match self.state {
|
|
|
|
EpfServerState::WaitingForClientHello => (),
|
2023-05-04 00:38:05 +00:00
|
|
|
_ => return Err(EpfHandshakeError::AlreadyTunnelled.into()),
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let client_public_key;
|
|
|
|
|
|
|
|
// Step 1: Wait for Client Hello
|
|
|
|
loop {
|
|
|
|
let packet = recv_packet(&mut self.inner).await?;
|
|
|
|
|
|
|
|
if packet.packet_id != PACKET_CLIENT_HELLO {
|
|
|
|
self.packet_queue.push(packet);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("got client hello");
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let client_hello: EpfClientHello = rmp_serde::from_slice(&packet.packet_data)?;
|
|
|
|
|
|
|
|
self.client_random = client_hello.client_random;
|
|
|
|
|
|
|
|
if client_hello.protocol_version != PROTOCOL_VERSION {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::UnsupportedProtocolVersion(
|
|
|
|
client_hello.protocol_version as usize,
|
|
|
|
)
|
|
|
|
.into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.client_cert = client_hello.client_certificate;
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
client_public_key = client_hello.client_x25519_public_key;
|
|
|
|
|
|
|
|
trace!("exiting loop");
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 2: Validate Client Certificate (if present)
|
|
|
|
if let Some(client_cert) = &self.client_cert {
|
|
|
|
let cert_valid = client_cert.verify(&cert_pool);
|
|
|
|
if let Err(e) = cert_valid {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::InvalidCertificate(e).into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
if let Ok(false) = cert_valid {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::UntrustedCertificate.into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Client Cert OK (if present)
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("client cert okay");
|
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
// Step 3: Send Server Hello
|
2023-05-04 00:38:05 +00:00
|
|
|
self.inner
|
|
|
|
.write_all(&encode_packet(
|
|
|
|
PACKET_SERVER_HELLO,
|
|
|
|
&EpfServerHello {
|
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
server_certificate: self.cert.clone(),
|
|
|
|
server_random: self.server_random,
|
|
|
|
server_x25519_public_key: PublicKey::from(&StaticSecret::from(
|
|
|
|
self.private_key.to_bytes(),
|
|
|
|
))
|
|
|
|
.to_bytes(),
|
|
|
|
},
|
|
|
|
)?)
|
|
|
|
.await?;
|
2023-05-04 00:24:26 +00:00
|
|
|
self.inner.flush().await?;
|
|
|
|
|
|
|
|
trace!("sent server hello");
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
self.state = EpfServerState::WaitingForFinished;
|
|
|
|
|
|
|
|
// Step 4: Build the cipher
|
2023-05-04 00:24:26 +00:00
|
|
|
let private_key = StaticSecret::from(self.private_key.to_bytes());
|
|
|
|
let their_public_key = PublicKey::from(client_public_key);
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
assert_ne!(
|
|
|
|
their_public_key.to_bytes(),
|
|
|
|
PublicKey::from(&private_key).to_bytes()
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
danger_trace!(
|
|
|
|
"pr: {}, their pub: {}, my pub: {}",
|
|
|
|
hex::encode(self.private_key.to_bytes()),
|
|
|
|
hex::encode(client_public_key),
|
|
|
|
hex::encode(self.private_key.verifying_key().to_bytes())
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
|
|
|
let shared_key = private_key.diffie_hellman(&their_public_key).to_bytes();
|
|
|
|
|
|
|
|
trace!("client public key: {:x?}", client_public_key);
|
|
|
|
danger_trace!("shared key: {}", hex::encode(shared_key));
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
let cc20p1305_key = Key::from(shared_key);
|
|
|
|
let cc20p1305 = XChaCha20Poly1305::new(&cc20p1305_key);
|
|
|
|
self.cipher = Some(cc20p1305);
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: &[0x42],
|
|
|
|
aad: &self.server_random,
|
|
|
|
};
|
|
|
|
|
|
|
|
let nonce = XNonce::from_slice(&self.client_random);
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let packet = recv_packet(&mut self.inner).await?;
|
|
|
|
|
|
|
|
if packet.packet_id != PACKET_FINISHED {
|
|
|
|
self.packet_queue.push(packet);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let packet_finished: EpfFinished = rmp_serde::from_slice(&packet.packet_data)?;
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: &packet_finished.encrypted_0x42,
|
|
|
|
aad: &self.server_random,
|
|
|
|
};
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
trace!("trying to decrypt 0x42");
|
2023-05-04 00:38:05 +00:00
|
|
|
danger_trace!(
|
|
|
|
"ciphertext: {:?}, nonce: {:?}, aad: {:?}",
|
|
|
|
payload.msg,
|
|
|
|
nonce,
|
|
|
|
payload.aad
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
2023-05-03 16:31:57 +00:00
|
|
|
let hopefully_0x42 = match self.cipher.as_ref().unwrap().decrypt(nonce, payload) {
|
|
|
|
Ok(d) => d,
|
|
|
|
Err(_) => {
|
|
|
|
return Err(EpfHandshakeError::EncryptionError.into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if hopefully_0x42 != vec![0x42] {
|
2023-05-04 00:38:05 +00:00
|
|
|
return Err(EpfHandshakeError::MissingKeyProof.into());
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let encrypted_0x42 = match self.cipher.as_ref().unwrap().encrypt(nonce, payload) {
|
|
|
|
Ok(d) => d,
|
2023-05-04 00:38:05 +00:00
|
|
|
Err(_) => return Err(EpfHandshakeError::EncryptionError.into()),
|
2023-05-03 16:31:57 +00:00
|
|
|
};
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
self.inner
|
|
|
|
.write_all(&encode_packet(
|
|
|
|
PACKET_FINISHED,
|
|
|
|
&EpfFinished {
|
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
encrypted_0x42,
|
|
|
|
},
|
|
|
|
)?)
|
|
|
|
.await?;
|
2023-05-04 00:24:26 +00:00
|
|
|
self.inner.flush().await?;
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
self.state = EpfServerState::WaitingForFinished;
|
|
|
|
|
|
|
|
self.state = EpfServerState::Transport;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
async fn upgrade(self) -> EpfServerStream<T>
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
2023-05-03 16:31:57 +00:00
|
|
|
EpfServerStream {
|
|
|
|
aad: self.server_random,
|
|
|
|
server_cert: self.cert,
|
|
|
|
packet_queue: self.packet_queue,
|
|
|
|
client_cert: self.client_cert,
|
|
|
|
cipher: self.cipher.unwrap(),
|
|
|
|
private_key: self.private_key,
|
|
|
|
public_key: self.public_key,
|
2023-05-04 00:38:05 +00:00
|
|
|
raw_stream: self.inner,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-04 00:24:26 +00:00
|
|
|
pub struct EpfServerStream<S: AsyncReadExt + AsyncWriteExt + Unpin> {
|
2023-05-03 16:31:57 +00:00
|
|
|
raw_stream: S,
|
|
|
|
aad: [u8; 16],
|
|
|
|
client_cert: Option<EPFCertificate>,
|
|
|
|
packet_queue: Vec<EpfMessage>,
|
|
|
|
server_cert: EPFCertificate,
|
|
|
|
cipher: XChaCha20Poly1305,
|
|
|
|
private_key: EpfPrivateKey,
|
2023-05-04 00:38:05 +00:00
|
|
|
public_key: EpfPublicKey,
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2023-05-04 00:24:26 +00:00
|
|
|
impl<S: AsyncReadExt + AsyncWriteExt + Unpin + Send> EpfStreamOps for EpfServerStream<S> {
|
2023-05-03 16:31:57 +00:00
|
|
|
async fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>> {
|
|
|
|
let nonce = XChaCha20Poly1305::generate_nonce(OsRng);
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: data,
|
|
|
|
aad: &self.aad,
|
|
|
|
};
|
|
|
|
|
|
|
|
let ciphertext = match self.cipher.encrypt(&nonce, payload) {
|
|
|
|
Ok(c) => c,
|
2023-05-04 00:38:05 +00:00
|
|
|
Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Encryption error").into()),
|
2023-05-03 16:31:57 +00:00
|
|
|
};
|
|
|
|
let application_data = EpfApplicationData {
|
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
encrypted_application_data: ciphertext,
|
|
|
|
nonce: nonce.try_into().unwrap(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let packet = encode_packet(PACKET_APPLICATION_DATA, &application_data)?;
|
|
|
|
|
|
|
|
self.raw_stream.write_all(&packet).await?;
|
2023-05-04 00:24:26 +00:00
|
|
|
self.raw_stream.flush().await?;
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn read(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
|
|
loop {
|
|
|
|
let packet = recv_packet(&mut self.raw_stream).await?;
|
|
|
|
|
|
|
|
if packet.packet_id != PACKET_APPLICATION_DATA {
|
|
|
|
self.packet_queue.push(packet);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let app_data: EpfApplicationData = rmp_serde::from_slice(&packet.packet_data)?;
|
|
|
|
|
|
|
|
let nonce = XNonce::from_slice(&app_data.nonce);
|
|
|
|
|
|
|
|
let payload = Payload {
|
|
|
|
msg: &app_data.encrypted_application_data,
|
|
|
|
aad: &self.aad,
|
|
|
|
};
|
|
|
|
|
|
|
|
let plaintext = match self.cipher.decrypt(nonce, payload) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => {
|
|
|
|
return Err(io::Error::new(io::ErrorKind::Other, "Decryption error").into())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return Ok(plaintext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-05-04 00:38:05 +00:00
|
|
|
use crate::ca_pool::{EpfCaPool, EpfCaPoolOps};
|
|
|
|
use crate::handshake_stream::{
|
|
|
|
ClientAuthentication, EpfClientHandshaker, EpfClientUpgradable, EpfClientUpgraded,
|
|
|
|
EpfServerHandshaker, EpfServerUpgradable, EpfServerUpgraded, EpfStreamOps,
|
|
|
|
};
|
|
|
|
use crate::pki::{EPFCertificate, EPFCertificateDetails, EpfPkiCertificateOps};
|
2023-05-04 00:24:26 +00:00
|
|
|
use ed25519_dalek::{SecretKey, SigningKey};
|
|
|
|
use log::{debug, trace};
|
|
|
|
use rand::rngs::OsRng;
|
2023-05-04 00:38:05 +00:00
|
|
|
use std::net::SocketAddr;
|
|
|
|
use std::str::FromStr;
|
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
2023-05-04 00:24:26 +00:00
|
|
|
use tcp_test::channel;
|
|
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
|
|
use tokio::join;
|
|
|
|
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
|
|
|
use x25519_dalek::{PublicKey, StaticSecret};
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
pub async fn stream_test() {
|
|
|
|
simple_logger::init().unwrap();
|
|
|
|
|
|
|
|
let tcp_listener = TcpListener::bind("0.0.0.0:36116").await.unwrap();
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
let tcp_client_future = TcpSocket::new_v4()
|
|
|
|
.unwrap()
|
|
|
|
.connect(SocketAddr::from_str("127.0.0.1:36116").unwrap());
|
2023-05-04 00:24:26 +00:00
|
|
|
|
|
|
|
let (a, b) = join![tcp_listener.accept(), tcp_client_future];
|
|
|
|
|
|
|
|
let (s, _) = a.unwrap();
|
|
|
|
let c = b.unwrap();
|
|
|
|
|
|
|
|
let server_private_key = SigningKey::from([1u8; 32]);
|
|
|
|
let client_private_key = SigningKey::from([2u8; 32]);
|
|
|
|
|
|
|
|
let mut server_cert = EPFCertificate {
|
|
|
|
details: EPFCertificateDetails {
|
|
|
|
name: "Testing Server Certificate".to_string(),
|
|
|
|
not_before: 0,
|
2023-05-04 00:38:05 +00:00
|
|
|
not_after: SystemTime::now()
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
.unwrap()
|
|
|
|
.as_secs()
|
|
|
|
+ 30,
|
2023-05-04 00:24:26 +00:00
|
|
|
public_key: server_private_key.verifying_key().to_bytes(),
|
|
|
|
issuer_public_key: [0u8; 32],
|
|
|
|
claims: Default::default(),
|
|
|
|
},
|
|
|
|
fingerprint: "".to_string(),
|
|
|
|
signature: [0u8; 64],
|
|
|
|
};
|
|
|
|
server_cert.sign(&server_private_key).unwrap();
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
debug!(
|
|
|
|
"{}",
|
|
|
|
hex::encode(server_private_key.verifying_key().to_bytes())
|
|
|
|
);
|
2023-05-04 00:24:26 +00:00
|
|
|
|
|
|
|
let mut client_cert = EPFCertificate {
|
|
|
|
details: EPFCertificateDetails {
|
|
|
|
name: "Testing Client Certificate".to_string(),
|
|
|
|
not_before: 0,
|
2023-05-04 00:38:05 +00:00
|
|
|
not_after: SystemTime::now()
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
.unwrap()
|
|
|
|
.as_secs()
|
|
|
|
+ 30,
|
2023-05-04 00:24:26 +00:00
|
|
|
public_key: client_private_key.verifying_key().to_bytes(),
|
|
|
|
issuer_public_key: [0u8; 32],
|
|
|
|
claims: Default::default(),
|
|
|
|
},
|
|
|
|
fingerprint: "".to_string(),
|
|
|
|
signature: [0u8; 64],
|
|
|
|
};
|
|
|
|
client_cert.sign(&client_private_key).unwrap();
|
|
|
|
|
|
|
|
let mut cert_pool = EpfCaPool::new();
|
|
|
|
let mut cert_pool_2 = EpfCaPool::new();
|
|
|
|
cert_pool.insert(&server_cert);
|
|
|
|
cert_pool.insert(&client_cert);
|
|
|
|
cert_pool_2.insert(&client_cert);
|
|
|
|
cert_pool_2.insert(&server_cert);
|
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
let mut c: EpfClientUpgraded<TcpStream> = EpfClientUpgradable::upgrade(
|
|
|
|
c,
|
|
|
|
ClientAuthentication::Cert(Box::new(client_cert), client_private_key),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
let mut s: EpfServerUpgraded<TcpStream> =
|
|
|
|
EpfServerUpgradable::upgrade(s, server_cert, server_private_key).await;
|
2023-05-04 00:24:26 +00:00
|
|
|
|
|
|
|
let server_handshake_accept_task = tokio::spawn(async move {
|
|
|
|
trace!("starting server handshake listener");
|
|
|
|
s.handshake(cert_pool_2).await.unwrap();
|
|
|
|
let mut upgraded = s.upgrade().await;
|
|
|
|
assert_eq!(upgraded.read().await.unwrap(), vec![0x42, 0x42])
|
|
|
|
});
|
|
|
|
|
|
|
|
let client_handshake_send_task = tokio::spawn(async move {
|
|
|
|
trace!("starting client handshake sender");
|
|
|
|
c.handshake(cert_pool).await.unwrap();
|
|
|
|
let mut upgraded = EpfClientHandshaker::upgrade(c).await;
|
|
|
|
upgraded.write(&[0x42, 0x42]).await.unwrap();
|
|
|
|
});
|
|
|
|
|
|
|
|
let (a, b) = join![server_handshake_accept_task, client_handshake_send_task];
|
|
|
|
a.unwrap();
|
|
|
|
b.unwrap();
|
|
|
|
}
|
2023-05-03 16:31:57 +00:00
|
|
|
|
|
|
|
#[test]
|
2023-05-04 00:24:26 +00:00
|
|
|
pub fn x25519_sanity_check() {
|
|
|
|
let bob_key = StaticSecret::from([1u8; 32]);
|
|
|
|
let bob_pub = PublicKey::from(&bob_key);
|
|
|
|
|
|
|
|
let alice_key = StaticSecret::from([2u8; 32]);
|
|
|
|
let alice_pub = PublicKey::from(&alice_key);
|
|
|
|
|
|
|
|
let ss_1 = bob_key.diffie_hellman(&alice_pub);
|
|
|
|
let ss_2 = alice_key.diffie_hellman(&bob_pub);
|
|
|
|
|
|
|
|
assert_eq!(ss_1.to_bytes(), ss_2.to_bytes());
|
2023-05-03 16:31:57 +00:00
|
|
|
|
2023-05-04 00:38:05 +00:00
|
|
|
println!(
|
|
|
|
"SS: {}, B_p: {}, A_p: {}",
|
|
|
|
hex::encode(ss_1.to_bytes()),
|
|
|
|
hex::encode(bob_pub.to_bytes()),
|
|
|
|
hex::encode(alice_pub.to_bytes())
|
|
|
|
);
|
2023-05-03 16:31:57 +00:00
|
|
|
}
|
2023-05-04 00:38:05 +00:00
|
|
|
}
|