redesigned comms & crypto modules
This commit is contained in:
parent
6b3f466b3c
commit
a592528fee
59
src/comms.rs
59
src/comms.rs
@ -1,59 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use aes_gcm::{aead::consts::U12, aes::Aes256, AesGcm};
|
|
||||||
use base64::{engine::general_purpose, Engine};
|
|
||||||
use rand::rngs::OsRng;
|
|
||||||
use tokio::{
|
|
||||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter},
|
|
||||||
net::tcp::{ReadHalf, WriteHalf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::crypto;
|
|
||||||
|
|
||||||
pub async fn send(
|
|
||||||
writer: &mut BufWriter<WriteHalf<'_>>,
|
|
||||||
cipher: Option<&mut AesGcm<Aes256, U12>>,
|
|
||||||
rng: Option<&mut OsRng>,
|
|
||||||
data: &Vec<u8>,
|
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
|
||||||
let enc: Vec<u8>;
|
|
||||||
|
|
||||||
if let (Some(cipher), Some(rng)) = (cipher, rng) {
|
|
||||||
enc = crypto::aes_encrypt(data, cipher, rng)?;
|
|
||||||
} else {
|
|
||||||
enc = data.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut encoded = general_purpose::STANDARD_NO_PAD
|
|
||||||
.encode(enc)
|
|
||||||
.as_bytes()
|
|
||||||
.to_vec();
|
|
||||||
encoded.push(b':');
|
|
||||||
writer.write_all(&encoded).await?;
|
|
||||||
writer.flush().await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn recv(
|
|
||||||
reader: &mut BufReader<ReadHalf<'_>>,
|
|
||||||
cipher: Option<&mut AesGcm<Aes256, U12>>,
|
|
||||||
) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let n = reader.read_until(b':', &mut buf).await?;
|
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
return Err("Received 0 bytes from the socket".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.pop();
|
|
||||||
buf = general_purpose::STANDARD_NO_PAD.decode(&buf)?.to_vec();
|
|
||||||
|
|
||||||
if let Some(cipher) = cipher {
|
|
||||||
buf = crypto::aes_decrypt(&buf, cipher)?;
|
|
||||||
} else {
|
|
||||||
buf = buf.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
105
src/crypto.rs
Executable file → Normal file
105
src/crypto.rs
Executable file → Normal file
@ -5,21 +5,34 @@ use aes_gcm::{
|
|||||||
aes::Aes256,
|
aes::Aes256,
|
||||||
Aes256Gcm, AesGcm, KeyInit, Nonce,
|
Aes256Gcm, AesGcm, KeyInit, Nonce,
|
||||||
};
|
};
|
||||||
use rand::{distributions::Alphanumeric, rngs::OsRng, Rng, RngCore};
|
use rand::{rngs::OsRng, RngCore};
|
||||||
use tokio::{
|
|
||||||
io::{BufReader, BufWriter},
|
|
||||||
net::tcp::{ReadHalf, WriteHalf},
|
|
||||||
};
|
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
|
use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
|
||||||
|
|
||||||
use crate::comms;
|
use crate::sockets::SocketHandler;
|
||||||
|
|
||||||
const AES_NONCE_SIZE: usize = 12;
|
const AES_NONCE_SIZE: usize = 12;
|
||||||
const DH_PBK_SIZE: usize = 32;
|
const DH_PBK_SIZE: usize = 32;
|
||||||
|
|
||||||
async fn edh(
|
#[derive(Clone)]
|
||||||
reader: &mut BufReader<ReadHalf<'_>>,
|
pub struct Crypto {
|
||||||
writer: &mut BufWriter<WriteHalf<'_>>,
|
cipher: AesGcm<Aes256, U12>,
|
||||||
|
rng: OsRng,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crypto {
|
||||||
|
pub async fn new(
|
||||||
|
handler: &mut SocketHandler<'_>,
|
||||||
|
go_first: bool,
|
||||||
|
) -> Result<Self, Box<dyn Error + Sync + Send>> {
|
||||||
|
let secret = Self::ecdh(handler, go_first).await?;
|
||||||
|
let cipher = Aes256Gcm::new(secret.as_bytes().into());
|
||||||
|
let rng = OsRng;
|
||||||
|
|
||||||
|
Ok(Self { cipher, rng })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ecdh(
|
||||||
|
handler: &mut SocketHandler<'_>,
|
||||||
go_first: bool,
|
go_first: bool,
|
||||||
) -> Result<SharedSecret, Box<dyn Error + Send + Sync>> {
|
) -> Result<SharedSecret, Box<dyn Error + Send + Sync>> {
|
||||||
let buf: Vec<u8>;
|
let buf: Vec<u8>;
|
||||||
@ -28,11 +41,11 @@ async fn edh(
|
|||||||
let msg = own_pbk.as_bytes().to_vec();
|
let msg = own_pbk.as_bytes().to_vec();
|
||||||
|
|
||||||
if go_first {
|
if go_first {
|
||||||
comms::send(writer, None, None, &msg).await?;
|
handler.send(&msg).await?;
|
||||||
buf = comms::recv(reader, None).await?;
|
buf = handler.recv().await?;
|
||||||
} else {
|
} else {
|
||||||
buf = comms::recv(reader, None).await?;
|
buf = handler.recv().await?;
|
||||||
comms::send(writer, None, None, &msg).await?;
|
handler.send(&msg).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let slice: [u8; DH_PBK_SIZE] = buf[..DH_PBK_SIZE].try_into()?;
|
let slice: [u8; DH_PBK_SIZE] = buf[..DH_PBK_SIZE].try_into()?;
|
||||||
@ -41,50 +54,37 @@ async fn edh(
|
|||||||
Ok(own_sec.diffie_hellman(&recv_pbk))
|
Ok(own_sec.diffie_hellman(&recv_pbk))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn aes_cipher(
|
fn nonce(&self) -> Nonce<U12> {
|
||||||
reader: &mut BufReader<ReadHalf<'_>>,
|
|
||||||
writer: &mut BufWriter<WriteHalf<'_>>,
|
|
||||||
go_first: bool,
|
|
||||||
) -> Result<AesGcm<Aes256, U12>, Box<dyn Error + Sync + Send>> {
|
|
||||||
let secret = edh(reader, writer, go_first).await?;
|
|
||||||
Ok(Aes256Gcm::new(secret.as_bytes().into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_nonce(rng: &mut impl RngCore) -> Nonce<U12> {
|
|
||||||
let mut nonce = Nonce::default();
|
let mut nonce = Nonce::default();
|
||||||
rng.fill_bytes(&mut nonce);
|
self.rng.fill_bytes(&mut nonce);
|
||||||
|
|
||||||
nonce
|
nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_encrypt(
|
pub async fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
||||||
data: &Vec<u8>,
|
let nonce = self.nonce();
|
||||||
cipher: &mut AesGcm<Aes256, U12>,
|
let encrypted = match self.cipher.encrypt(&nonce, data.as_ref()) {
|
||||||
rng: &mut OsRng,
|
|
||||||
) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
|
||||||
let nonce = generate_nonce(rng);
|
|
||||||
let encrypted = match cipher.encrypt(&nonce, data.as_ref()) {
|
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return Err("AES encryption failed".into()),
|
Err(e) => return Err(format!("Encryption failed: {}", e).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut data = nonce.to_vec();
|
let mut data = nonce.to_vec();
|
||||||
data.extend_from_slice(&encrypted);
|
data.extend_from_slice(&encrypted);
|
||||||
|
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_decrypt(
|
pub async fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
||||||
data: &[u8],
|
|
||||||
cipher: &mut AesGcm<Aes256, U12>,
|
|
||||||
) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
|
||||||
let (nonce_bytes, data) = data.split_at(AES_NONCE_SIZE);
|
let (nonce_bytes, data) = data.split_at(AES_NONCE_SIZE);
|
||||||
let decrypted = match cipher.decrypt(Nonce::from_slice(nonce_bytes), data.as_ref()) {
|
let nonce = Nonce::from_slice(nonce_bytes);
|
||||||
|
let decrypted = match self.cipher.decrypt(nonce, data.as_ref()) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return Err("AES decryption failed".into()),
|
Err(e) => return Err(format!("Decryption failed: {}", e).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(decrypted)
|
Ok(decrypted)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_hash(path: &Path) -> Result<String, Box<dyn Error + Send + Sync>> {
|
pub fn try_hash(path: &Path) -> Result<String, Box<dyn Error + Send + Sync>> {
|
||||||
let hash = sha256::try_digest(path)?;
|
let hash = sha256::try_digest(path)?;
|
||||||
@ -92,31 +92,4 @@ pub fn try_hash(path: &Path) -> Result<String, Box<dyn Error + Send + Sync>> {
|
|||||||
Ok(hash)
|
Ok(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keygen() -> String {
|
// TODO: unit test if deemed necessary
|
||||||
rand::thread_rng()
|
|
||||||
.sample_iter(&Alphanumeric)
|
|
||||||
.take(8)
|
|
||||||
.map(char::from)
|
|
||||||
.collect::<String>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn aes_implementations() {
|
|
||||||
use aes_gcm::aead;
|
|
||||||
|
|
||||||
let mut gen_rng = aead::OsRng;
|
|
||||||
let key = Aes256Gcm::generate_key(&mut gen_rng);
|
|
||||||
let mut cipher = Aes256Gcm::new(&key);
|
|
||||||
|
|
||||||
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
let mut aes_rng = OsRng;
|
|
||||||
let enc = aes_encrypt(&data, &mut cipher, &mut aes_rng).unwrap();
|
|
||||||
let dec = aes_decrypt(&enc, &mut cipher).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(data, dec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod comms;
|
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
pub mod listener;
|
pub mod listener;
|
||||||
pub mod parsers;
|
pub mod parsers;
|
||||||
|
pub mod sockets;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
80
src/sockets.rs
Normal file
80
src/sockets.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use base64::{engine::general_purpose, Engine};
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter},
|
||||||
|
net::{
|
||||||
|
tcp::{ReadHalf, WriteHalf},
|
||||||
|
TcpStream,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::crypto::Crypto;
|
||||||
|
|
||||||
|
pub struct SocketHandler<'a> {
|
||||||
|
writer: BufWriter<WriteHalf<'a>>,
|
||||||
|
reader: BufReader<ReadHalf<'a>>,
|
||||||
|
crypto: Option<Crypto>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SocketHandler<'a> {
|
||||||
|
pub fn new(socket: &'a mut TcpStream) -> Self {
|
||||||
|
let (reader, writer) = socket.split();
|
||||||
|
let mut reader = BufReader::new(reader);
|
||||||
|
let mut writer = BufWriter::new(writer);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
writer,
|
||||||
|
reader,
|
||||||
|
crypto: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_crypto(&self, crypto: Crypto) {
|
||||||
|
// setting up AES cipher requires DH key exchange in plaintext,
|
||||||
|
// meaning crypto can't be initialized at the same time as the socket handler
|
||||||
|
self.crypto = Some(crypto);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sender(&mut self, data: &[u8]) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
let data = match &self.crypto {
|
||||||
|
Some(c) => c.encrypt(data).await?,
|
||||||
|
None => data.to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.send(&data).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(&mut self, data: &[u8]) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
self.writer.write_all(data).await?;
|
||||||
|
self.writer.flush().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn receiver(&mut self) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
||||||
|
let mut buf = self.recv().await?;
|
||||||
|
buf.pop();
|
||||||
|
buf = general_purpose::STANDARD_NO_PAD.decode(&buf)?.to_vec();
|
||||||
|
|
||||||
|
let data = match &self.crypto {
|
||||||
|
Some(c) => c.decrypt(&buf).await?,
|
||||||
|
None => buf,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv(&mut self) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let n = self.reader.read_until(b':', &mut buf).await?;
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return Err("Received 0 bytes from the socket".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user