1use aes::cipher::KeyInit;
5use aes::Aes256;
6use serpent::Serpent;
7use twofish::Twofish;
8use xts_mode::Xts128;
9
10use crate::error::{Result, VeraError};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Prf {
15 Sha512,
17 Sha256,
19 Whirlpool,
21 Streebog,
23 Ripemd160,
25}
26
27impl Prf {
28 #[must_use]
30 pub fn all() -> [Prf; 5] {
31 [
32 Prf::Sha512,
33 Prf::Sha256,
34 Prf::Whirlpool,
35 Prf::Streebog,
36 Prf::Ripemd160,
37 ]
38 }
39
40 #[must_use]
42 pub fn name(self) -> &'static str {
43 match self {
44 Prf::Sha512 => "sha512",
45 Prf::Sha256 => "sha256",
46 Prf::Whirlpool => "whirlpool",
47 Prf::Streebog => "streebog",
48 Prf::Ripemd160 => "ripemd160",
49 }
50 }
51
52 #[must_use]
54 pub fn iterations(self) -> u32 {
55 match self {
56 Prf::Ripemd160 => 655_331,
57 _ => 500_000,
58 }
59 }
60
61 #[must_use]
65 pub fn iterations_pim(self, pim: u32) -> u32 {
66 if pim == 0 {
67 return self.iterations();
68 }
69 match self {
70 Prf::Ripemd160 => pim.saturating_mul(2048),
71 _ => 15_000u32.saturating_add(pim.saturating_mul(1000)),
72 }
73 }
74
75 pub fn derive(self, password: &[u8], salt: &[u8], iterations: u32, out_len: usize) -> Vec<u8> {
77 let mut out = vec![0u8; out_len];
78 let it = iterations.max(1);
79 match self {
80 Prf::Sha512 => pbkdf2::pbkdf2_hmac::<sha2::Sha512>(password, salt, it, &mut out),
81 Prf::Sha256 => pbkdf2::pbkdf2_hmac::<sha2::Sha256>(password, salt, it, &mut out),
82 Prf::Whirlpool => {
83 pbkdf2::pbkdf2_hmac::<whirlpool::Whirlpool>(password, salt, it, &mut out);
84 }
85 Prf::Streebog => {
86 pbkdf2::pbkdf2_hmac::<streebog::Streebog512>(password, salt, it, &mut out);
87 }
88 Prf::Ripemd160 => {
89 pbkdf2::pbkdf2_hmac::<ripemd::Ripemd160>(password, salt, it, &mut out);
90 }
91 }
92 out
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum Cipher {
100 Aes,
102 Serpent,
104 Twofish,
106}
107
108impl Cipher {
109 #[must_use]
111 pub fn all() -> [Cipher; 3] {
112 [Cipher::Aes, Cipher::Serpent, Cipher::Twofish]
113 }
114
115 #[must_use]
117 pub fn name(self) -> &'static str {
118 match self {
119 Cipher::Aes => "aes",
120 Cipher::Serpent => "serpent",
121 Cipher::Twofish => "twofish",
122 }
123 }
124
125 #[must_use]
127 pub fn key_len(self) -> usize {
128 64
129 }
130}
131
132pub fn xts_decrypt(
138 cipher: Cipher,
139 key: &[u8],
140 buffer: &mut [u8],
141 unit_size: usize,
142 base_unit: u128,
143) -> Result<()> {
144 if key.len() != 64 {
145 return Err(VeraError::Crypto {
146 what: "xts key must be 64 bytes",
147 });
148 }
149 let (k1, k2) = key.split_at(32);
150 match cipher {
151 Cipher::Aes => decrypt_units(
152 &Xts128::new(Aes256::new(k1.into()), Aes256::new(k2.into())),
153 buffer,
154 unit_size,
155 base_unit,
156 ),
157 Cipher::Serpent => {
158 let c1 = Serpent::new_from_slice(k1).map_err(|_| VeraError::Crypto {
163 what: "serpent 256-bit key",
164 })?; let c2 = Serpent::new_from_slice(k2).map_err(|_| VeraError::Crypto {
166 what: "serpent 256-bit key",
167 })?; decrypt_units(&Xts128::new(c1, c2), buffer, unit_size, base_unit);
169 }
170 Cipher::Twofish => decrypt_units(
171 &Xts128::new(Twofish::new(k1.into()), Twofish::new(k2.into())),
172 buffer,
173 unit_size,
174 base_unit,
175 ),
176 }
177 Ok(())
178}
179
180fn decrypt_units<C>(xts: &Xts128<C>, buffer: &mut [u8], unit_size: usize, base: u128)
181where
182 C: aes::cipher::BlockCipher + aes::cipher::BlockEncrypt + aes::cipher::BlockDecrypt,
183{
184 for (u, chunk) in buffer.chunks_mut(unit_size).enumerate() {
185 if chunk.len() < 16 {
186 continue; }
188 let tweak = (base + u as u128).to_le_bytes();
189 xts.decrypt_sector(chunk, tweak);
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn prf_iterations_and_names() {
199 assert_eq!(Prf::Sha512.iterations(), 500_000);
200 assert_eq!(Prf::Ripemd160.iterations(), 655_331);
201 assert_eq!(Prf::Sha512.iterations_pim(0), 500_000);
202 assert_eq!(Prf::Sha512.iterations_pim(10), 25_000);
203 assert_eq!(Prf::Ripemd160.iterations_pim(10), 20_480);
204 assert_eq!(Prf::all().len(), 5);
205 assert_eq!(
206 Cipher::all().map(Cipher::name),
207 ["aes", "serpent", "twofish"]
208 );
209 }
210
211 #[test]
212 fn every_prf_has_a_name_and_derives() {
213 assert_eq!(Prf::Sha256.name(), "sha256");
215 assert_eq!(Prf::Whirlpool.name(), "whirlpool");
216 assert_eq!(Prf::Streebog.name(), "streebog");
217 assert_eq!(Prf::Ripemd160.name(), "ripemd160");
218 for prf in Prf::all() {
220 let k = prf.derive(b"password", b"salt", 1, 64);
221 assert_eq!(k.len(), 64, "prf {} derive length", prf.name());
222 }
223 }
224
225 #[test]
226 fn cipher_key_len_is_two_256bit_subkeys() {
227 assert_eq!(Cipher::Aes.key_len(), 64);
228 assert_eq!(Cipher::Twofish.key_len(), 64);
229 }
230
231 #[test]
232 fn pbkdf2_sha512_matches_python() {
233 let k = Prf::Sha512.derive(b"password", b"salt", 1, 32);
235 assert_eq!(
236 hex(&k),
237 "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252"
238 );
239 }
240
241 #[test]
242 fn xts_roundtrips_for_each_cipher() {
243 for cipher in Cipher::all() {
244 let key = [0x24u8; 64];
245 let mut buf = vec![0u8; 512];
246 for (i, b) in buf.iter_mut().enumerate() {
247 *b = (i as u8) ^ 0x3c;
248 }
249 let plain = buf.clone();
250 let (k1, k2) = key.split_at(32);
252 match cipher {
253 Cipher::Aes => encrypt_one(
254 &Xts128::new(Aes256::new(k1.into()), Aes256::new(k2.into())),
255 &mut buf,
256 256,
257 ),
258 Cipher::Serpent => encrypt_one(
259 &Xts128::new(
260 Serpent::new_from_slice(k1).unwrap(),
261 Serpent::new_from_slice(k2).unwrap(),
262 ),
263 &mut buf,
264 256,
265 ),
266 Cipher::Twofish => encrypt_one(
267 &Xts128::new(Twofish::new(k1.into()), Twofish::new(k2.into())),
268 &mut buf,
269 256,
270 ),
271 }
272 xts_decrypt(cipher, &key, &mut buf, 512, 256).unwrap();
273 assert_eq!(buf, plain, "cipher {}", cipher.name());
274 }
275 }
276
277 #[test]
278 fn xts_rejects_bad_key_len() {
279 let mut b = [0u8; 512];
280 assert!(matches!(
281 xts_decrypt(Cipher::Aes, &[0u8; 48], &mut b, 512, 0),
282 Err(VeraError::Crypto { .. })
283 ));
284 }
285
286 fn encrypt_one<C>(xts: &Xts128<C>, buf: &mut [u8], unit: u128)
287 where
288 C: aes::cipher::BlockCipher + aes::cipher::BlockEncrypt + aes::cipher::BlockDecrypt,
289 {
290 xts.encrypt_sector(buf, unit.to_le_bytes());
291 }
292
293 fn hex(b: &[u8]) -> String {
294 use std::fmt::Write;
295 b.iter().fold(String::new(), |mut s, x| {
296 let _ = write!(s, "{x:02x}");
297 s
298 })
299 }
300}