ipcrypt_rs/
deterministic.rs

1use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
2use aes::Aes128;
3use aes::Block;
4use std::net::IpAddr;
5
6use crate::common::{bytes_to_ip, ip_to_bytes};
7
8/// A structure representing the IPCrypt context for deterministic mode.
9pub struct Ipcrypt {
10    cipher: Aes128,
11}
12
13impl Ipcrypt {
14    /// The number of bytes required for the encryption key.
15    pub const KEY_BYTES: usize = 16;
16
17    /// Generates a new random key for encryption.
18    #[cfg(feature = "random")]
19    pub fn generate_key() -> [u8; Self::KEY_BYTES] {
20        rand::random()
21    }
22
23    /// Creates a new Ipcrypt instance with the given key.
24    ///
25    /// # Arguments
26    ///
27    /// * `key` - A 16-byte array containing the encryption key.
28    pub fn new(key: [u8; Self::KEY_BYTES]) -> Self {
29        let cipher = Aes128::new_from_slice(&key).expect("key length is guaranteed to be correct");
30        Self { cipher }
31    }
32
33    /// Creates a new Ipcrypt instance with a random key.
34    #[cfg(feature = "random")]
35    pub fn new_random() -> Self {
36        Self::new(Self::generate_key())
37    }
38
39    /// Encrypts a 16-byte IP address in place.
40    pub fn encrypt_ip16(&self, ip: &mut [u8; 16]) {
41        let mut block = Block::from(*ip);
42        self.cipher.encrypt_block(&mut block);
43        *ip = block.into();
44    }
45
46    /// Decrypts a 16-byte IP address in place.
47    pub fn decrypt_ip16(&self, ip: &mut [u8; 16]) {
48        let mut block = Block::from(*ip);
49        self.cipher.decrypt_block(&mut block);
50        *ip = block.into();
51    }
52
53    /// Encrypts an IP address.
54    ///
55    /// # Arguments
56    ///
57    /// * `ip` - The IP address to encrypt
58    ///
59    /// # Returns
60    /// The encrypted IP address
61    pub fn encrypt_ipaddr(&self, ip: IpAddr) -> IpAddr {
62        let mut bytes = ip_to_bytes(ip);
63        self.encrypt_ip16(&mut bytes);
64        bytes_to_ip(bytes)
65    }
66
67    /// Decrypts an IP address.
68    ///
69    /// # Arguments
70    ///
71    /// * `encrypted` - The encrypted IP address
72    ///
73    /// # Returns
74    /// The decrypted IP address
75    pub fn decrypt_ipaddr(&self, encrypted: IpAddr) -> IpAddr {
76        let mut bytes = ip_to_bytes(encrypted);
77        self.decrypt_ip16(&mut bytes);
78        bytes_to_ip(bytes)
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use ct_codecs::{Decoder as _, Hex};
86    use std::str::FromStr;
87
88    #[test]
89    fn test_deterministic_vectors() {
90        let test_vectors = vec![
91            (
92                // Test vector 1
93                "0123456789abcdeffedcba9876543210",
94                "0.0.0.0",
95                "bde9:6789:d353:824c:d7c6:f58a:6bd2:26eb",
96            ),
97            (
98                // Test vector 2
99                "1032547698badcfeefcdab8967452301",
100                "255.255.255.255",
101                "aed2:92f6:ea23:58c3:48fd:8b8:74e8:45d8",
102            ),
103            (
104                // Test vector 3
105                "2b7e151628aed2a6abf7158809cf4f3c",
106                "192.0.2.1",
107                "1dbd:c1b9:fff1:7586:7d0b:67b4:e76e:4777",
108            ),
109        ];
110
111        for (key_hex, input_ip, expected_output) in test_vectors {
112            // Parse key using constant-time hex decoder
113            let key_vec = Hex::decode_to_vec(key_hex.as_bytes(), None).unwrap();
114            let mut key = [0u8; Ipcrypt::KEY_BYTES];
115            key.copy_from_slice(&key_vec);
116
117            // Create Ipcrypt instance
118            let ipcrypt = Ipcrypt::new(key);
119
120            // Parse input IP
121            let ip = IpAddr::from_str(input_ip).unwrap();
122
123            // Encrypt
124            let encrypted = ipcrypt.encrypt_ipaddr(ip);
125            assert_eq!(encrypted.to_string(), expected_output);
126
127            // Decrypt
128            let decrypted = ipcrypt.decrypt_ipaddr(encrypted);
129            assert_eq!(decrypted, ip);
130        }
131    }
132
133    #[test]
134    #[cfg(feature = "random")]
135    fn test_random_key() {
136        let ipcrypt = Ipcrypt::new_random();
137        let ip = IpAddr::from_str("192.0.2.1").unwrap();
138        let encrypted = ipcrypt.encrypt_ipaddr(ip);
139        let decrypted = ipcrypt.decrypt_ipaddr(encrypted);
140        assert_eq!(ip, decrypted);
141    }
142}