use crate::error::Result;
use aes_gcm::aead::{Aead, KeyInit, OsRng};
use aes_gcm::{AeadCore, Aes256Gcm, Nonce};
use sha2::{Digest, Sha256};
use std::io;
use std::sync::Arc;
use x25519_dalek::{EphemeralSecret, PublicKey};
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const AES_KEY_SIZE: usize = 32;
pub const AES_NONCE_SIZE: usize = 12;
pub async fn dh_key_pair() -> (PublicKey, EphemeralSecret) {
tokio::task::spawn_blocking(move || {
let secret_key = EphemeralSecret::random_from_rng(OsRng);
let public_key = PublicKey::from(&secret_key);
(public_key, secret_key)
})
.await
.unwrap()
}
pub async fn dh_shared_key(
this_secret_key: EphemeralSecret,
other_public_key: impl Into<PublicKey> + Send,
) -> [u8; AES_KEY_SIZE] {
let other_public_key = other_public_key.into();
tokio::task::spawn_blocking(move || {
let shared_secret = this_secret_key.diffie_hellman(&other_public_key);
let hashed_key = Sha256::digest(shared_secret);
hashed_key.as_slice().try_into().unwrap()
})
.await
.unwrap()
}
pub async fn aes_encrypt(key: [u8; AES_KEY_SIZE], plaintext: Arc<[u8]>) -> Result<Vec<u8>> {
tokio::task::spawn_blocking(move || {
let cipher = Aes256Gcm::new_from_slice(&key).unwrap();
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, plaintext.as_ref())?;
let mut ciphertext_with_nonce = nonce.to_vec();
ciphertext_with_nonce.extend(ciphertext);
Ok(ciphertext_with_nonce)
})
.await
.unwrap()
}
pub async fn aes_decrypt(
key: [u8; AES_KEY_SIZE],
ciphertext_with_nonce: Arc<[u8]>,
) -> Result<Vec<u8>> {
tokio::task::spawn_blocking(move || {
let cipher = Aes256Gcm::new_from_slice(&key).unwrap();
let (nonce_slice, ciphertext) = ciphertext_with_nonce.split_at(AES_NONCE_SIZE);
let nonce_slice_sized: [u8; AES_NONCE_SIZE] = nonce_slice
.try_into()
.map_err(|_| io::Error::new(io::ErrorKind::UnexpectedEof, "incorrect nonce length"))?;
let nonce = Nonce::from(nonce_slice_sized);
let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
Ok(plaintext)
})
.await
.unwrap()
}