use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
use aes::Aes128;
use aes::Block;
use std::net::IpAddr;
use crate::common::{bytes_to_ip, ip_to_bytes};
pub struct Ipcrypt {
cipher: Aes128,
}
impl Ipcrypt {
pub const KEY_BYTES: usize = 16;
#[cfg(feature = "random")]
pub fn generate_key() -> [u8; Self::KEY_BYTES] {
rand::random()
}
pub fn new(key: [u8; Self::KEY_BYTES]) -> Self {
let cipher = Aes128::new_from_slice(&key).expect("key length is guaranteed to be correct");
Self { cipher }
}
#[cfg(feature = "random")]
pub fn new_random() -> Self {
Self::new(Self::generate_key())
}
pub fn encrypt_ip16(&self, ip: &mut [u8; 16]) {
let mut block = Block::from(*ip);
self.cipher.encrypt_block(&mut block);
*ip = block.into();
}
pub fn decrypt_ip16(&self, ip: &mut [u8; 16]) {
let mut block = Block::from(*ip);
self.cipher.decrypt_block(&mut block);
*ip = block.into();
}
pub fn encrypt_ipaddr(&self, ip: IpAddr) -> IpAddr {
let mut bytes = ip_to_bytes(ip);
self.encrypt_ip16(&mut bytes);
bytes_to_ip(bytes)
}
pub fn decrypt_ipaddr(&self, encrypted: IpAddr) -> IpAddr {
let mut bytes = ip_to_bytes(encrypted);
self.decrypt_ip16(&mut bytes);
bytes_to_ip(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ct_codecs::{Decoder as _, Hex};
use std::str::FromStr;
#[test]
fn test_deterministic_vectors() {
let test_vectors = vec![
(
"0123456789abcdeffedcba9876543210",
"0.0.0.0",
"bde9:6789:d353:824c:d7c6:f58a:6bd2:26eb",
),
(
"1032547698badcfeefcdab8967452301",
"255.255.255.255",
"aed2:92f6:ea23:58c3:48fd:8b8:74e8:45d8",
),
(
"2b7e151628aed2a6abf7158809cf4f3c",
"192.0.2.1",
"1dbd:c1b9:fff1:7586:7d0b:67b4:e76e:4777",
),
];
for (key_hex, input_ip, expected_output) in test_vectors {
let key_vec = Hex::decode_to_vec(key_hex.as_bytes(), None).unwrap();
let mut key = [0u8; Ipcrypt::KEY_BYTES];
key.copy_from_slice(&key_vec);
let ipcrypt = Ipcrypt::new(key);
let ip = IpAddr::from_str(input_ip).unwrap();
let encrypted = ipcrypt.encrypt_ipaddr(ip);
assert_eq!(encrypted.to_string(), expected_output);
let decrypted = ipcrypt.decrypt_ipaddr(encrypted);
assert_eq!(decrypted, ip);
}
}
#[test]
#[cfg(feature = "random")]
fn test_random_key() {
let ipcrypt = Ipcrypt::new_random();
let ip = IpAddr::from_str("192.0.2.1").unwrap();
let encrypted = ipcrypt.encrypt_ipaddr(ip);
let decrypted = ipcrypt.decrypt_ipaddr(encrypted);
assert_eq!(ip, decrypted);
}
}