use aes::{
cipher::{generic_array::GenericArray, BlockEncrypt},
Aes128,
};
use rand::RngCore;
use sha2::{Digest, Sha256, Sha512};
use hmac::Hmac;
type HmacSha256 = Hmac<Sha256>;
const BLOCKSIZE: usize = 16;
pub const KEY_SIZE: usize = 16;
pub const SALT_SIZE: usize = 12;
pub fn salt<T>(rng: &mut T) -> [u8; SALT_SIZE]
where
T: RngCore,
{
let mut bytes = [0; SALT_SIZE];
rng.fill_bytes(&mut bytes);
bytes
}
#[inline]
pub fn decrypt(bytes: &mut [u8], key: &[u8; KEY_SIZE], salt: &[u8; SALT_SIZE]) {
encrypt(bytes, key, salt)
}
pub fn encrypt(bytes: &mut [u8], key: &[u8; KEY_SIZE], salt: &[u8; SALT_SIZE]) {
use aes::cipher::KeyInit;
let key = GenericArray::from_slice(key);
let cipher = Aes128::new(key);
let bytes_len = bytes.len();
let block_count = (bytes_len.max(1) - 1) / BLOCKSIZE + 1;
let mut blocks = Vec::with_capacity(block_count);
let mut block = Vec::<u8>::with_capacity(BLOCKSIZE);
for i in 1..=block_count {
block.clear();
let bytes = (i as u32).to_be_bytes();
block.extend_from_slice(&bytes);
block.extend_from_slice(salt);
blocks.push(GenericArray::clone_from_slice(&block));
}
cipher.encrypt_blocks(&mut blocks);
let mut offset = 0;
'outer: for block in blocks {
for b in block {
if offset < bytes_len {
bytes[offset] ^= b;
offset += 1;
} else {
break 'outer;
}
}
}
}
pub fn sign(bytes: &[u8], key: &[u8]) -> Vec<u8> {
use hmac::Mac;
let mut mac = HmacSha256::new_from_slice(key).unwrap();
mac.update(bytes);
mac.finalize().into_bytes().to_vec()
}
pub fn verify(bytes: &[u8], key: &[u8], compare_bytes: &[u8]) -> bool {
use hmac::Mac;
let mut mac = HmacSha256::new_from_slice(key).unwrap();
mac.update(bytes);
mac.verify_slice(compare_bytes).is_ok()
}
pub fn hash(bytes: &[u8], salt: &[u8; SALT_SIZE]) -> Vec<u8> {
let mut hasher = Sha512::new();
hasher.update(salt);
hasher.update(bytes);
let mut hash = salt.to_vec();
hash.extend(hasher.finalize());
hash
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt() {
let bytes = b"The quick brown fox jumps over the lazy dog.";
let key = [0_u8; KEY_SIZE];
let salt = [0_u8; SALT_SIZE];
let mut encrypted_bytes = *bytes;
encrypt(&mut encrypted_bytes, &key, &salt);
assert_eq!(hex::encode(
encrypted_bytes
), "bafc2e0f979c95ebeb6202ffc61631558b5b052e6b5d836a00b2cea50fc5aadd461c7ee09b333a0761a0796e");
let mut decrypted_bytes = encrypted_bytes;
decrypt(&mut decrypted_bytes, &key, &salt);
assert_eq!(&decrypted_bytes, bytes);
}
#[test]
fn test_sign() {
let bytes = b"The quick brown fox jumps over the lazy dog.";
let key = b"changeme";
let hashed = sign(bytes, key);
assert_eq!(
hex::encode(hashed.clone()),
"ed31e94c161aea6ff2300c72b17741f71b616463f294dac0542324bbdbf8a2de"
);
assert!(verify(bytes, key, &hashed));
assert!(!verify(bytes, key, b"rubbish"));
}
#[test]
fn test_hash() {
let bytes = b"The quick brown fox jumps over the lazy dog.";
let salt = [0_u8; SALT_SIZE];
let hashed = hash(bytes, &salt);
assert_eq!(hex::encode(
hashed
), "00000000000000000000000027cbc61af1b821ec863e77076df08016f41eb583d668426520b8ce5c85c150ada36f89061955c1bd12340764ee471f370528326dfa060a8c98978fb25774b35d");
}
}