use aes::cipher::block_padding::NoPadding;
use aes::cipher::{BlockDecryptMut, BlockEncrypt, KeyInit, KeyIvInit};
use aes::Aes128;
use ccm::aead::generic_array::GenericArray;
use ccm::aead::AeadInPlace;
use ccm::consts::{U12, U16};
use ccm::{Ccm, KeyInit as CcmKeyInit};
use sha2::{Digest, Sha256};
const STRETCH_ITERATIONS: u64 = 0x0010_0000;
type BdeCcm = Ccm<aes::Aes256, U16, U12>;
#[must_use]
pub fn password_hash(password: &str) -> [u8; 32] {
let utf16: Vec<u8> = password.encode_utf16().flat_map(u16::to_le_bytes).collect();
let first = Sha256::digest(&utf16);
let second = Sha256::digest(first);
second.into()
}
#[must_use]
pub fn stretch_key_n(password_hash: &[u8; 32], salt: &[u8; 16], iterations: u64) -> [u8; 32] {
let mut buf = [0u8; 88];
buf[32..64].copy_from_slice(password_hash);
buf[64..80].copy_from_slice(salt);
for count in 0..iterations {
buf[80..88].copy_from_slice(&count.to_le_bytes());
let digest = Sha256::digest(buf);
buf[0..32].copy_from_slice(&digest);
}
let mut out = [0u8; 32];
out.copy_from_slice(&buf[0..32]);
out
}
#[must_use]
pub fn stretch_key(password_hash: &[u8; 32], salt: &[u8; 16]) -> [u8; 32] {
stretch_key_n(password_hash, salt, STRETCH_ITERATIONS)
}
#[must_use]
pub fn aes_ccm_unwrap(key: &[u8; 32], value_data: &[u8]) -> Option<Vec<u8>> {
let nonce = value_data.get(0..12)?;
let tag = value_data.get(12..28)?;
let mut buffer = value_data.get(28..)?.to_vec();
let cipher = <BdeCcm as CcmKeyInit>::new(GenericArray::from_slice(key));
cipher
.decrypt_in_place_detached(
GenericArray::from_slice(nonce),
&[],
&mut buffer,
GenericArray::from_slice(tag),
)
.ok()?;
Some(buffer)
}
#[cfg(test)]
#[must_use]
pub fn aes_ccm_wrap(key: &[u8; 32], nonce: &[u8; 12], plaintext: &[u8]) -> Vec<u8> {
let cipher = <BdeCcm as CcmKeyInit>::new(GenericArray::from_slice(key));
let mut buffer = plaintext.to_vec();
let tag = cipher
.encrypt_in_place_detached(GenericArray::from_slice(nonce), &[], &mut buffer)
.unwrap();
let mut out = Vec::with_capacity(12 + 16 + buffer.len());
out.extend_from_slice(nonce);
out.extend_from_slice(&tag);
out.extend_from_slice(&buffer);
out
}
fn to_words(sector: &[u8]) -> Vec<u32> {
sector
.chunks_exact(4)
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect()
}
fn from_words(words: &[u32], out: &mut [u8]) {
for (i, w) in words.iter().enumerate() {
if let Some(slot) = out.get_mut(i * 4..i * 4 + 4) {
slot.copy_from_slice(&w.to_le_bytes());
}
}
}
const RA: [u32; 4] = [9, 0, 13, 0];
const RB: [u32; 4] = [0, 10, 0, 25];
pub fn diffuser_a_decrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..5 {
for i in 0..n {
let a = d[(i + n - 2) % n];
let b = d[(i + n - 5) % n].rotate_left(RA[i % 4]);
d[i] = d[i].wrapping_add(a ^ b);
}
}
from_words(&d, sector);
}
pub fn diffuser_b_decrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..3 {
for i in 0..n {
let a = d[(i + 2) % n];
let b = d[(i + 5) % n].rotate_left(RB[i % 4]);
d[i] = d[i].wrapping_add(a ^ b);
}
}
from_words(&d, sector);
}
#[cfg(test)]
pub fn diffuser_a_encrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..5 {
for i in (0..n).rev() {
let a = d[(i + n - 2) % n];
let b = d[(i + n - 5) % n].rotate_left(RA[i % 4]);
d[i] = d[i].wrapping_sub(a ^ b);
}
}
from_words(&d, sector);
}
#[cfg(test)]
pub fn diffuser_b_encrypt(sector: &mut [u8]) {
let mut d = to_words(sector);
let n = d.len();
if n == 0 {
return;
}
for _ in 0..3 {
for i in (0..n).rev() {
let a = d[(i + 2) % n];
let b = d[(i + 5) % n].rotate_left(RB[i % 4]);
d[i] = d[i].wrapping_sub(a ^ b);
}
}
from_words(&d, sector);
}
pub struct SectorCipher {
fvek: [u8; 16],
tweak: Aes128,
fvek_ecb: Aes128,
}
impl SectorCipher {
#[must_use]
pub fn new(fvek: [u8; 16], tweak: [u8; 16]) -> SectorCipher {
SectorCipher {
fvek,
tweak: Aes128::new(GenericArray::from_slice(&tweak)),
fvek_ecb: Aes128::new(GenericArray::from_slice(&fvek)),
}
}
fn ecb(cipher: &Aes128, input: &[u8; 16]) -> [u8; 16] {
let mut block = GenericArray::clone_from_slice(input);
cipher.encrypt_block(&mut block);
block.into()
}
fn sector_key(&self, byte_offset: u64) -> [u8; 32] {
let mut iv = [0u8; 16];
iv[0..8].copy_from_slice(&byte_offset.to_le_bytes());
let lower = Self::ecb(&self.tweak, &iv);
iv[15] = 0x80;
let upper = Self::ecb(&self.tweak, &iv);
let mut key = [0u8; 32];
key[0..16].copy_from_slice(&lower);
key[16..32].copy_from_slice(&upper);
key
}
fn cbc_iv(&self, byte_offset: u64) -> [u8; 16] {
let mut iv = [0u8; 16];
iv[0..8].copy_from_slice(&byte_offset.to_le_bytes());
Self::ecb(&self.fvek_ecb, &iv)
}
#[must_use]
pub fn decrypt_sector(&self, cipher: &[u8], byte_offset: u64) -> Vec<u8> {
let sector_key = self.sector_key(byte_offset);
let iv = self.cbc_iv(byte_offset);
let mut buf = cipher.to_vec();
let dec = cbc::Decryptor::<Aes128>::new(
GenericArray::from_slice(&self.fvek),
GenericArray::from_slice(&iv),
);
let len = buf.len() - (buf.len() % 16);
#[allow(clippy::single_match)]
match dec.decrypt_padded_mut::<NoPadding>(&mut buf[..len]) {
Ok(plain) => {
let plain_len = plain.len();
diffuser_b_decrypt(&mut buf[..plain_len]);
diffuser_a_decrypt(&mut buf[..plain_len]);
for (i, b) in buf[..plain_len].iter_mut().enumerate() {
*b ^= sector_key[i % 32];
}
}
Err(_) => {} }
buf
}
#[cfg(test)]
#[must_use]
pub fn encrypt_sector(&self, plain: &[u8], byte_offset: u64) -> Vec<u8> {
use aes::cipher::BlockEncryptMut;
let sector_key = self.sector_key(byte_offset);
let iv = self.cbc_iv(byte_offset);
let mut buf = plain.to_vec();
for (i, b) in buf.iter_mut().enumerate() {
*b ^= sector_key[i % 32];
}
diffuser_a_encrypt(&mut buf);
diffuser_b_encrypt(&mut buf);
let enc = cbc::Encryptor::<Aes128>::new(
GenericArray::from_slice(&self.fvek),
GenericArray::from_slice(&iv),
);
let len = buf.len() - (buf.len() % 16);
let out = enc
.encrypt_padded_mut::<NoPadding>(&mut buf[..len], len)
.unwrap()
.to_vec();
out
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(bytes: &[u8]) -> String {
use std::fmt::Write;
bytes.iter().fold(String::new(), |mut s, b| {
let _ = write!(s, "{b:02x}");
s
})
}
#[test]
fn password_hash_matches_independent_python() {
assert_eq!(
hex(&password_hash("bde-TEST")),
"f5acb5bd3c4e31c5c988128fcfc50717da18ca4f7dbaa8bf21e7525bd431ee3f"
);
}
#[test]
fn stretch_two_iterations_matches_independent_python() {
let salt: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let ph = password_hash("bde-TEST");
assert_eq!(
hex(&stretch_key_n(&ph, &salt, 2)),
"0660651b876f7d3777e292fb96671e57d48a4b34d7e17c3e01f20d6ba6af42e4"
);
}
fn sample_sector() -> Vec<u8> {
(0..512u32)
.map(|i| (i.wrapping_mul(31) ^ 0xA5) as u8)
.collect()
}
#[test]
fn diffuser_a_roundtrip() {
let orig = sample_sector();
let mut buf = orig.clone();
diffuser_a_encrypt(&mut buf);
assert_ne!(buf, orig);
diffuser_a_decrypt(&mut buf);
assert_eq!(buf, orig);
}
#[test]
fn diffuser_b_roundtrip() {
let orig = sample_sector();
let mut buf = orig.clone();
diffuser_b_encrypt(&mut buf);
assert_ne!(buf, orig);
diffuser_b_decrypt(&mut buf);
assert_eq!(buf, orig);
}
#[test]
fn diffuser_empty_and_short_no_panic() {
let mut empty: [u8; 0] = [];
diffuser_a_decrypt(&mut empty);
diffuser_b_decrypt(&mut empty);
diffuser_a_encrypt(&mut empty);
diffuser_b_encrypt(&mut empty);
let mut three = [1u8, 2, 3];
diffuser_a_decrypt(&mut three); diffuser_b_encrypt(&mut three);
}
#[test]
fn ccm_unwrap_roundtrip_ms_layout() {
let key = [7u8; 32];
let nonce = [3u8; 12];
let plaintext = b"volume master key container payload!!".to_vec();
let cipher = <BdeCcm as CcmKeyInit>::new(GenericArray::from_slice(&key));
let mut buf = plaintext.clone();
let tag = cipher
.encrypt_in_place_detached(GenericArray::from_slice(&nonce), &[], &mut buf)
.unwrap();
let mut value_data = Vec::new();
value_data.extend_from_slice(&nonce);
value_data.extend_from_slice(&tag); value_data.extend_from_slice(&buf);
assert_eq!(aes_ccm_unwrap(&key, &value_data).unwrap(), plaintext);
assert!(aes_ccm_unwrap(&[8u8; 32], &value_data).is_none());
assert!(aes_ccm_unwrap(&key, &value_data[..20]).is_none());
}
#[test]
fn sector_cipher_roundtrip() {
let cipher = SectorCipher::new([0x11; 16], [0x22; 16]);
let plain = sample_sector();
let off = 0x0211_0800u64;
let ct = cipher.encrypt_sector(&plain, off);
assert_ne!(ct, plain);
assert_eq!(cipher.decrypt_sector(&ct, off), plain);
}
}