Skip to main content

veracrypt/
crypto.rs

1//! VeraCrypt key derivation (PBKDF2 over five PRFs) and AES/Serpent/Twofish XTS
2//! decryption. Every primitive is an audited RustCrypto crate.
3
4use aes::cipher::KeyInit;
5use aes::Aes256;
6use serpent::Serpent;
7use twofish::Twofish;
8use xts_mode::Xts128;
9
10use crate::error::{Result, VeraError};
11
12/// A VeraCrypt header PRF (the PBKDF2 hash), with its non-system iteration count.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Prf {
15    /// SHA-512 (500000 iterations).
16    Sha512,
17    /// SHA-256 (500000 iterations).
18    Sha256,
19    /// Whirlpool (500000 iterations).
20    Whirlpool,
21    /// Streebog-512 (500000 iterations).
22    Streebog,
23    /// RIPEMD-160 (655331 iterations, TrueCrypt-compatible).
24    Ripemd160,
25}
26
27impl Prf {
28    /// All PRFs, in VeraCrypt's try order.
29    #[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    /// Human name.
41    #[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    /// Non-system, no-PIM iteration count.
53    #[must_use]
54    pub fn iterations(self) -> u32 {
55        match self {
56            Prf::Ripemd160 => 655_331,
57            _ => 500_000,
58        }
59    }
60
61    /// Iterations for an explicit PIM (personal iterations multiplier). PIM 0 uses
62    /// the default. For SHA-family/Whirlpool/Streebog: `15000 + PIM*1000`;
63    /// RIPEMD-160: `PIM*2048` — matching VeraCrypt's non-system formula.
64    #[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    /// Derive `out_len` bytes with PBKDF2-HMAC using this PRF.
76    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/// A VeraCrypt data cipher (single-cipher; cipher cascades are a future
97/// extension). All are 256-bit keys in XTS mode.
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum Cipher {
100    /// AES-256 (XTS).
101    Aes,
102    /// Serpent-256 (XTS).
103    Serpent,
104    /// Twofish-256 (XTS).
105    Twofish,
106}
107
108impl Cipher {
109    /// All single ciphers, in VeraCrypt's try order.
110    #[must_use]
111    pub fn all() -> [Cipher; 3] {
112        [Cipher::Aes, Cipher::Serpent, Cipher::Twofish]
113    }
114
115    /// Human name.
116    #[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    /// XTS key length in bytes (two 256-bit subkeys = 64).
126    #[must_use]
127    pub fn key_len(self) -> usize {
128        64
129    }
130}
131
132/// The cipher chains VeraCrypt supports, in cryptsetup `tcrypt` **array order**
133/// (which fixes the per-cipher key offsets): the three singles, then the cascades.
134#[must_use]
135pub fn cipher_chains() -> Vec<Vec<Cipher>> {
136    use Cipher::{Aes, Serpent, Twofish};
137    vec![
138        vec![Aes],
139        vec![Serpent],
140        vec![Twofish],
141        vec![Twofish, Aes],          // "twofish-aes"
142        vec![Serpent, Twofish, Aes], // "serpent-twofish-aes"
143        vec![Aes, Serpent],          // "aes-serpent"
144        vec![Aes, Twofish, Serpent], // "aes-twofish-serpent"
145        vec![Serpent, Twofish],      // "serpent-twofish"
146    ]
147}
148
149/// The maximum cascade key length in bytes (three 256-bit XTS ciphers).
150pub const MAX_CHAIN_KEY_LEN: usize = 3 * 64;
151
152/// Decrypt `buffer` in place through a cascade of `ciphers` given in cryptsetup
153/// array order, exactly as `TCRYPT_decrypt_hdr`: cipher at array index `j` of an
154/// `n`-cipher cascade uses XTS key `key[32j..32j+32]` (primary) ++
155/// `key[32(n+j)..32(n+j)+32]` (secondary), and the ciphers are applied in **reverse**
156/// array order (`j = n-1 .. 0`). A single-cipher chain reduces to [`xts_decrypt`].
157///
158/// # Errors
159/// [`VeraError::Crypto`] if `ciphers` is empty or `key` is shorter than `64*n`.
160pub fn xts_decrypt_chain(
161    ciphers: &[Cipher],
162    key: &[u8],
163    buffer: &mut [u8],
164    unit_size: usize,
165    base_unit: u128,
166) -> Result<()> {
167    let n = ciphers.len();
168    if n == 0 || key.len() < 64 * n {
169        return Err(VeraError::Crypto {
170            what: "cascade key too short",
171        });
172    }
173    let mut subkey = [0u8; 64];
174    for j in (0..n).rev() {
175        subkey[..32].copy_from_slice(&key[32 * j..32 * j + 32]);
176        subkey[32..].copy_from_slice(&key[32 * (n + j)..32 * (n + j) + 32]);
177        xts_decrypt(ciphers[j], &subkey, buffer, unit_size, base_unit)?;
178    }
179    Ok(())
180}
181
182/// Decrypt `buffer` in place as XTS with `cipher`, split into `unit_size`-byte
183/// data units; data unit `u` uses tweak `base_unit + u` (little-endian).
184///
185/// # Errors
186/// [`VeraError::Crypto`] if `key` is not 64 bytes.
187pub fn xts_decrypt(
188    cipher: Cipher,
189    key: &[u8],
190    buffer: &mut [u8],
191    unit_size: usize,
192    base_unit: u128,
193) -> Result<()> {
194    if key.len() != 64 {
195        return Err(VeraError::Crypto {
196            what: "xts key must be 64 bytes",
197        });
198    }
199    let (k1, k2) = key.split_at(32);
200    match cipher {
201        Cipher::Aes => decrypt_units(
202            &Xts128::new(Aes256::new(k1.into()), Aes256::new(k2.into())),
203            buffer,
204            unit_size,
205            base_unit,
206        ),
207        Cipher::Serpent => {
208            // The typed Serpent::new() is fixed to 16-byte keys, but new_from_slice
209            // accepts 16..=32 and uses the full 256-bit key schedule (VeraCrypt's).
210            // k1/k2 are exactly 32 bytes here (key.len()==64 checked above), and
211            // new_from_slice accepts 16..=32, so the InvalidLength arm is unreachable.
212            let c1 = Serpent::new_from_slice(k1).map_err(|_| VeraError::Crypto {
213                what: "serpent 256-bit key",
214            })?; // cov:unreachable: k1 is 32 bytes
215            let c2 = Serpent::new_from_slice(k2).map_err(|_| VeraError::Crypto {
216                what: "serpent 256-bit key",
217            })?; // cov:unreachable: k2 is 32 bytes
218            decrypt_units(&Xts128::new(c1, c2), buffer, unit_size, base_unit);
219        }
220        Cipher::Twofish => decrypt_units(
221            &Xts128::new(Twofish::new(k1.into()), Twofish::new(k2.into())),
222            buffer,
223            unit_size,
224            base_unit,
225        ),
226    }
227    Ok(())
228}
229
230fn decrypt_units<C>(xts: &Xts128<C>, buffer: &mut [u8], unit_size: usize, base: u128)
231where
232    C: aes::cipher::BlockCipher + aes::cipher::BlockEncrypt + aes::cipher::BlockDecrypt,
233{
234    for (u, chunk) in buffer.chunks_mut(unit_size).enumerate() {
235        if chunk.len() < 16 {
236            continue; // cov:unreachable: reads are unit-aligned (>= 16)
237        }
238        let tweak = (base + u as u128).to_le_bytes();
239        xts.decrypt_sector(chunk, tweak);
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn prf_iterations_and_names() {
249        assert_eq!(Prf::Sha512.iterations(), 500_000);
250        assert_eq!(Prf::Ripemd160.iterations(), 655_331);
251        assert_eq!(Prf::Sha512.iterations_pim(0), 500_000);
252        assert_eq!(Prf::Sha512.iterations_pim(10), 25_000);
253        assert_eq!(Prf::Ripemd160.iterations_pim(10), 20_480);
254        assert_eq!(Prf::all().len(), 5);
255        assert_eq!(
256            Cipher::all().map(Cipher::name),
257            ["aes", "serpent", "twofish"]
258        );
259    }
260
261    #[test]
262    fn every_prf_has_a_name_and_derives() {
263        // Names for the four non-SHA512 PRFs (SHA-512 is covered by the oracle).
264        assert_eq!(Prf::Sha256.name(), "sha256");
265        assert_eq!(Prf::Whirlpool.name(), "whirlpool");
266        assert_eq!(Prf::Streebog.name(), "streebog");
267        assert_eq!(Prf::Ripemd160.name(), "ripemd160");
268        // Each PRF derives the requested number of bytes (1 iteration for speed).
269        for prf in Prf::all() {
270            let k = prf.derive(b"password", b"salt", 1, 64);
271            assert_eq!(k.len(), 64, "prf {} derive length", prf.name());
272        }
273    }
274
275    #[test]
276    fn cipher_key_len_is_two_256bit_subkeys() {
277        assert_eq!(Cipher::Aes.key_len(), 64);
278        assert_eq!(Cipher::Twofish.key_len(), 64);
279    }
280
281    #[test]
282    fn pbkdf2_sha512_matches_python() {
283        // PBKDF2-HMAC-SHA512("password","salt",1,32) — cross-checked vs Python.
284        let k = Prf::Sha512.derive(b"password", b"salt", 1, 32);
285        assert_eq!(
286            hex(&k),
287            "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252"
288        );
289    }
290
291    #[test]
292    fn xts_roundtrips_for_each_cipher() {
293        for cipher in Cipher::all() {
294            let key = [0x24u8; 64];
295            let mut buf = vec![0u8; 512];
296            for (i, b) in buf.iter_mut().enumerate() {
297                *b = (i as u8) ^ 0x3c;
298            }
299            let plain = buf.clone();
300            // encrypt via the same primitive at unit base 256, then decrypt
301            let (k1, k2) = key.split_at(32);
302            match cipher {
303                Cipher::Aes => encrypt_one(
304                    &Xts128::new(Aes256::new(k1.into()), Aes256::new(k2.into())),
305                    &mut buf,
306                    256,
307                ),
308                Cipher::Serpent => encrypt_one(
309                    &Xts128::new(
310                        Serpent::new_from_slice(k1).unwrap(),
311                        Serpent::new_from_slice(k2).unwrap(),
312                    ),
313                    &mut buf,
314                    256,
315                ),
316                Cipher::Twofish => encrypt_one(
317                    &Xts128::new(Twofish::new(k1.into()), Twofish::new(k2.into())),
318                    &mut buf,
319                    256,
320                ),
321            }
322            xts_decrypt(cipher, &key, &mut buf, 512, 256).unwrap();
323            assert_eq!(buf, plain, "cipher {}", cipher.name());
324        }
325    }
326
327    #[test]
328    fn xts_rejects_bad_key_len() {
329        let mut b = [0u8; 512];
330        assert!(matches!(
331            xts_decrypt(Cipher::Aes, &[0u8; 48], &mut b, 512, 0),
332            Err(VeraError::Crypto { .. })
333        ));
334    }
335
336    #[test]
337    fn cipher_chains_are_the_eight_veracrypt_chains() {
338        let chains = cipher_chains();
339        assert_eq!(chains.len(), 8);
340        // Three singles, then the five multi-cipher cascades in cryptsetup order.
341        assert_eq!(chains[0], vec![Cipher::Aes]);
342        assert_eq!(chains[3], vec![Cipher::Twofish, Cipher::Aes]);
343        assert_eq!(
344            chains[6],
345            vec![Cipher::Aes, Cipher::Twofish, Cipher::Serpent]
346        );
347        assert!(chains.iter().all(|c| c.len() <= 3));
348    }
349
350    #[test]
351    fn xts_decrypt_chain_rejects_empty_and_short_key() {
352        let mut b = [0u8; 512];
353        // Empty chain.
354        assert!(matches!(
355            xts_decrypt_chain(&[], &[0u8; 64], &mut b, 512, 0),
356            Err(VeraError::Crypto { .. })
357        ));
358        // Two ciphers need 128 bytes; 100 is short.
359        assert!(matches!(
360            xts_decrypt_chain(&[Cipher::Aes, Cipher::Twofish], &[0u8; 100], &mut b, 512, 0),
361            Err(VeraError::Crypto { .. })
362        ));
363    }
364
365    #[test]
366    fn xts_decrypt_chain_roundtrips_a_three_cipher_cascade() {
367        // Prove the cryptsetup key layout + reverse-apply order: encrypt forward
368        // (e[0]..e[n-1]), decrypt via the chain, recover the plaintext. A
369        // three-cipher chain exercises all three cipher arms in one round-trip.
370        let ciphers = [Cipher::Aes, Cipher::Twofish, Cipher::Serpent];
371        let n = ciphers.len();
372        let mut key = [0u8; 192];
373        for (i, b) in key.iter_mut().enumerate() {
374            *b = (i as u8).wrapping_mul(7) ^ 0x5a;
375        }
376        let mut buf = vec![0u8; 512];
377        for (i, b) in buf.iter_mut().enumerate() {
378            *b = (i as u8) ^ 0x91;
379        }
380        let plain = buf.clone();
381        // Encrypt forward: cipher j uses subkey key[32j..]||key[32(n+j)..].
382        for j in 0..n {
383            let mut k1 = [0u8; 32];
384            let mut k2 = [0u8; 32];
385            k1.copy_from_slice(&key[32 * j..32 * j + 32]);
386            k2.copy_from_slice(&key[32 * (n + j)..32 * (n + j) + 32]);
387            match ciphers[j] {
388                Cipher::Aes => encrypt_one(
389                    &Xts128::new(Aes256::new((&k1).into()), Aes256::new((&k2).into())),
390                    &mut buf,
391                    256,
392                ),
393                Cipher::Twofish => encrypt_one(
394                    &Xts128::new(Twofish::new((&k1).into()), Twofish::new((&k2).into())),
395                    &mut buf,
396                    256,
397                ),
398                Cipher::Serpent => encrypt_one(
399                    &Xts128::new(
400                        Serpent::new_from_slice(&k1).unwrap(),
401                        Serpent::new_from_slice(&k2).unwrap(),
402                    ),
403                    &mut buf,
404                    256,
405                ),
406            }
407        }
408        assert_ne!(buf, plain, "cascade must actually encrypt");
409        xts_decrypt_chain(&ciphers, &key, &mut buf, 512, 256).unwrap();
410        assert_eq!(buf, plain, "two-cipher cascade round-trip");
411    }
412
413    fn encrypt_one<C>(xts: &Xts128<C>, buf: &mut [u8], unit: u128)
414    where
415        C: aes::cipher::BlockCipher + aes::cipher::BlockEncrypt + aes::cipher::BlockDecrypt,
416    {
417        xts.encrypt_sector(buf, unit.to_le_bytes());
418    }
419
420    fn hex(b: &[u8]) -> String {
421        use std::fmt::Write;
422        b.iter().fold(String::new(), |mut s, x| {
423            let _ = write!(s, "{x:02x}");
424            s
425        })
426    }
427}