use aes::{
self,
cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit},
Aes256,
};
use base64::{
alphabet,
engine::{self, general_purpose::STANDARD},
Engine as _,
};
use cbc::{Decryptor, Encryptor};
use rand;
use sha1::{Digest, Sha1};
use std::error::Error;
pub fn generate_signature(mut inputs: Vec<&str>) -> String {
inputs.sort_unstable();
let digest = Sha1::digest(inputs.concat().as_bytes());
base16ct::lower::encode_string(&digest)
}
#[derive(PartialEq, Debug)]
pub struct CryptoSource {
pub text: String,
pub receive_id: String,
}
#[derive(Clone)]
pub struct CryptoAgent {
key: [u8; 32],
nonce: [u8; 16],
}
impl CryptoAgent {
pub fn new(key: &str) -> Self {
let config = engine::GeneralPurposeConfig::new()
.with_decode_allow_trailing_bits(true)
.with_decode_padding_mode(engine::DecodePaddingMode::RequireNone);
let key_as_vec = engine::GeneralPurpose::new(&alphabet::STANDARD, config)
.decode(key)
.unwrap();
let key = <[u8; 32]>::try_from(key_as_vec).unwrap();
let nonce = <[u8; 16]>::try_from(&key[..16]).unwrap();
Self { key, nonce }
}
pub fn encrypt(&self, input: &CryptoSource) -> String {
let mut block: Vec<u8> = Vec::new();
block.extend(rand::random::<[u8; 16]>());
block.extend((input.text.len() as u32).to_be_bytes());
block.extend(input.text.as_bytes());
block.extend(input.receive_id.as_bytes());
let cipher_bytes = Encryptor::<Aes256>::new(&self.key.into(), &self.nonce.into())
.encrypt_padded_vec_mut::<Pkcs7>(&block);
STANDARD.encode(&cipher_bytes)
}
pub fn decrypt(&self, encoded: &str) -> Result<CryptoSource, Box<dyn Error>> {
let cipher_bytes = STANDARD.decode(encoded).unwrap();
let block = Decryptor::<Aes256>::new(&self.key.into(), &self.nonce.into())
.decrypt_padded_vec_mut::<Pkcs7>(&cipher_bytes)
.map_err(|e| format!("Decryption error: {}", e.to_string()))?;
let buf = block.as_slice();
let msg_len: usize = u32::from_be_bytes(buf[16..20].try_into().unwrap()) as usize;
let text = String::from_utf8(buf[20..20 + msg_len].to_vec())?;
let receive_id = String::from_utf8(buf[20 + msg_len..].to_vec())?;
Ok(CryptoSource { text, receive_id })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signature() {
assert_eq!(
generate_signature(vec!["0", "c", "a", "b"]),
"a8addbc99f8b3f51d2adbceb605d650b9a8940e2",
);
}
#[test]
fn test_encrypt_decrypt() {
let key = "cGCVnNJRgRu6wDgo7gxG2diBovGnRQq1Tqy4Rm4V4qF";
let agent = CryptoAgent::new(key);
let source = CryptoSource {
text: "abcd".to_string(),
receive_id: "xyz".to_string(),
};
let enc = agent.encrypt(&source);
let dec = agent.decrypt(enc.as_str()).unwrap();
assert_eq!(source, dec);
}
}