use aes_gcm_siv::aead::{generic_array::GenericArray, Aead, NewAead};
use aes_gcm_siv::Aes256GcmSiv;
use rand::distributions::Alphanumeric;
use rand::rngs::OsRng;
use rand::Rng;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256, Sha512};
use std::fs::File;
use std::io::prelude::*;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Cipher {
len: usize,
rand_string: String,
ciphertext: Vec<u8>,
}
pub fn read_file(path: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut f = File::open(path)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
Ok(buffer)
}
pub fn save_file(data: Vec<u8>, path: &str) -> std::io::Result<()> {
let mut file = File::create(path)?;
file.write_all(&data)?;
Ok(())
}
pub fn create_key(path: &str) -> std::io::Result<()> {
let key: String = OsRng
.sample_iter(&Alphanumeric)
.take(32)
.collect::<String>();
let mut file = File::create(path)?;
file.write_all(&key.as_bytes())?;
Ok(())
}
pub fn encrypt_file(cleartext: Vec<u8>, key: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let key = GenericArray::clone_from_slice(key.as_bytes());
let aead = Aes256GcmSiv::new(key);
let rand_string: String = OsRng
.sample_iter(&Alphanumeric)
.take(12)
.collect::<String>();
let nonce = GenericArray::from_slice(rand_string.as_bytes());
let ciphertext: Vec<u8> = aead
.encrypt(nonce, cleartext.as_ref())
.expect("encryption failure!");
let ciphertext_to_send = Cipher {
len: ciphertext.len(),
rand_string,
ciphertext,
};
let encoded: Vec<u8> = bincode::serialize(&ciphertext_to_send).unwrap();
Ok(encoded)
}
pub fn decrypt_file(enc: Vec<u8>, key: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let key = GenericArray::clone_from_slice(key.as_bytes());
let aead = Aes256GcmSiv::new(key);
let decoded: Cipher = bincode::deserialize(&enc[..]).unwrap();
let (ciphertext2, len_ciphertext, rand_string2) =
(decoded.ciphertext, decoded.len, decoded.rand_string);
if ciphertext2.len() != len_ciphertext {
panic!("length of received ciphertext not ok")
};
let nonce = GenericArray::from_slice(rand_string2.as_bytes());
let plaintext: Vec<u8> = aead
.decrypt(nonce, ciphertext2.as_ref())
.expect("decryption failure!");
Ok(plaintext)
}
pub fn get_blake3_hash(path: &str) -> Result<blake3::Hash, Box<dyn std::error::Error>> {
let data = read_file(path)?;
let hash = blake3::hash(&data);
Ok(hash)
}
pub fn get_sha256_hash(path: &str) -> Result<String, Box<dyn std::error::Error>> {
let data = read_file(path)?;
let mut hasher = Sha256::new();
hasher.input(data);
let hash = hasher.result();
Ok(format!("{:?}", hash))
}
pub fn get_sha512_hash(path: &str) -> Result<String, Box<dyn std::error::Error>> {
let data = read_file(path)?;
let mut hasher = Sha512::new();
hasher.input(data);
let hash = hasher.result();
Ok(format!("{:?}", hash))
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::remove_file;
#[test]
fn test_save_read_file() {
let content: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let path: &str = "test_abcdefg.file";
save_file(content.clone(), &path).unwrap();
let content_read: Vec<u8> = read_file(&path).unwrap();
remove_file(&path).unwrap(); assert_eq!(content, content_read);
}
#[test]
fn test_encryt_decrypt() {
let text = b"This a test";
let key: &str = "an example very very secret key.";
let text_vec = text.to_vec();
let ciphertext = encrypt_file(text_vec, key).unwrap();
let plaintext = decrypt_file(ciphertext, key).unwrap();
assert_eq!(format!("{:?}", text), format!("{:?}", plaintext));
}
#[test]
fn test_hash_blake3() {
let filename = "cargo.toml";
let hash1 = get_blake3_hash(&filename).unwrap();
let hash2 = get_blake3_hash(&filename).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_hash_sha256() {
let filename = "cargo.toml";
let hash1 = get_sha256_hash(&filename).unwrap();
let hash2 = get_sha256_hash(&filename).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_hash_sha512() {
let filename = "cargo.toml";
let hash1 = get_sha512_hash(&filename).unwrap();
let hash2 = get_sha512_hash(&filename).unwrap();
assert_eq!(hash1, hash2);
}
}