contego/src/crypto.rs

96 lines
2.6 KiB
Rust

use std::{error::Error, path::Path};
use aes_gcm::{
aead::{consts::U12, Aead},
aes::Aes256,
Aes256Gcm, AesGcm, KeyInit, Nonce,
};
use rand::{rngs::OsRng, RngCore};
use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
use crate::sockets::SocketHandler;
const AES_NONCE_SIZE: usize = 12;
const DH_PBK_SIZE: usize = 32;
#[derive(Clone)]
pub struct Crypto {
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,
) -> Result<SharedSecret, Box<dyn Error + Send + Sync>> {
let buf: Vec<u8>;
let own_sec = EphemeralSecret::new(OsRng);
let own_pbk = PublicKey::from(&own_sec);
let msg = own_pbk.as_bytes().to_vec();
if go_first {
handler.send_raw(&msg).await?;
buf = handler.recv_raw().await?;
} else {
buf = handler.recv_raw().await?;
handler.send_raw(&msg).await?;
}
let slice: [u8; DH_PBK_SIZE] = buf[..DH_PBK_SIZE].try_into()?;
let recv_pbk = PublicKey::from(slice);
Ok(own_sec.diffie_hellman(&recv_pbk))
}
fn nonce(&self) -> Nonce<U12> {
let mut nonce = Nonce::default();
self.rng.fill_bytes(&mut nonce);
nonce
}
pub async fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
let nonce = self.nonce();
let encrypted = match self.cipher.encrypt(&nonce, data.as_ref()) {
Ok(data) => data,
Err(e) => return Err(format!("Encryption failed: {}", e).into()),
};
let mut data = nonce.to_vec();
data.extend_from_slice(&encrypted);
Ok(data)
}
pub async fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
let (nonce_bytes, data) = data.split_at(AES_NONCE_SIZE);
let nonce = Nonce::from_slice(nonce_bytes);
let decrypted = match self.cipher.decrypt(nonce, data.as_ref()) {
Ok(data) => data,
Err(e) => return Err(format!("Decryption failed: {}", e).into()),
};
Ok(decrypted)
}
}
pub fn try_hash(path: &Path) -> Result<String, Box<dyn Error + Send + Sync>> {
let hash = sha256::try_digest(path)?;
Ok(hash)
}
// TODO: unit test if deemed necessary