use aes_kw::KekAes128;
use base64::engine::general_purpose::STANDARD as B64;
use base64::Engine;
use hmac::Hmac;
use pbkdf2::pbkdf2;
use sha2::{Digest, Sha256};
pub const BLOCK: usize = 4096;
pub const PASSWORD: &str = "synthetic-pass";
pub const SALT: [u8; 16] = [0xAB; 16];
pub const ITERS: u32 = 1000;
pub const KEK: [u8; 16] = [0x10; 16];
pub const VMK: [u8; 16] = [0x20; 16];
pub const KEY1: [u8; 16] = [0x30; 16]; pub const PV_ID: [u8; 16] = [0x40; 16]; pub const FAMILY: [u8; 16] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
];
pub const LV_PHYS_BLOCK: u64 = 16; pub const LV_BLOCKS: u32 = 4;
fn wrap(kek: &[u8; 16], key: &[u8; 16]) -> [u8; 24] {
let k = KekAes128::from(*kek);
let mut out = [0u8; 24];
k.wrap(key, &mut out).unwrap();
out
}
fn set_block_type(block: &mut [u8], t: u16) {
block[10..12].copy_from_slice(&t.to_le_bytes());
}
fn build_decrypted_metadata() -> Vec<u8> {
let passphrase_key = {
let mut pk = [0u8; 16];
pbkdf2::<Hmac<Sha256>>(PASSWORD.as_bytes(), &SALT, ITERS, &mut pk).unwrap();
pk
};
let wrapped_kek = wrap(&passphrase_key, &KEK);
let wrapped_vmk = wrap(&KEK, &VMK);
let mut pw = vec![0u8; 284];
pw[8..24].copy_from_slice(&SALT);
pw[32..56].copy_from_slice(&wrapped_kek);
pw[168..172].copy_from_slice(&ITERS.to_le_bytes());
let mut kekw = vec![0u8; 256];
kekw[8..32].copy_from_slice(&wrapped_vmk);
let plist = format!(
"<dict ID=\"0\"><key>CryptoUsers</key><array ID=\"2\"><dict ID=\"3\">\
<key>PassphraseWrappedKEKStruct</key><data ID=\"4\">{}</data>\
<key>UserType</key><integer size=\"32\" ID=\"5\">0x10000001</integer>\
<key>BlockAlgorithm</key><string ID=\"6\">AES-XTS</string>\
<key>KEKWrappedVolumeKeyStruct</key><data ID=\"7\">{}</data>\
</dict></array>\
<key>ConversionStatus</key><string ID=\"8\">Complete</string></dict>",
B64.encode(&pw),
B64.encode(&kekw),
);
let mut meta = vec![0u8; BLOCK * 3];
set_block_type(&mut meta[0..BLOCK], 0x0013);
let body = plist.as_bytes();
meta[64..64 + body.len()].copy_from_slice(body);
set_block_type(&mut meta[BLOCK..2 * BLOCK], 0x001a);
let fam_uuid = uuid::Uuid::from_bytes(FAMILY).hyphenated().to_string();
let lv_plist = format!(
"<dict ID=\"0\">\
<key>com.apple.corestorage.lv.familyUUID</key><string ID=\"1\">{fam_uuid}</string>\
<key>com.apple.corestorage.lv.name</key><string ID=\"2\">SynthLV</string>\
<key>com.apple.corestorage.lv.uuid</key><string ID=\"3\">00000000-0000-0000-0000-000000000001</string>\
<key>com.apple.corestorage.lv.size</key><integer size=\"64\" ID=\"4\">0x4000</integer></dict>"
);
let lb = lv_plist.as_bytes();
meta[BLOCK + 64..BLOCK + 64 + lb.len()].copy_from_slice(lb);
let base = 2 * BLOCK;
set_block_type(&mut meta[base..base + BLOCK], 0x0305);
let payload = base + 64;
meta[payload..payload + 4].copy_from_slice(&1u32.to_le_bytes());
let entry = payload + 8;
meta[entry + 8..entry + 16].copy_from_slice(&0u64.to_le_bytes()); meta[entry + 16..entry + 20].copy_from_slice(&LV_BLOCKS.to_le_bytes());
let phys = LV_PHYS_BLOCK | (0x99u64 << 48);
meta[entry + 32..entry + 40].copy_from_slice(&phys.to_le_bytes());
meta
}
#[must_use]
pub fn build_image() -> Vec<u8> {
let enc_meta_block: u64 = 8;
let plaintext_meta_block: u64 = 1;
let plain_meta = build_decrypted_metadata();
let mut enc_meta = plain_meta.clone();
crate::xts::encrypt_units(&mut enc_meta, &KEY1, &PV_ID, 8192, 0);
let meta_size = BLOCK as u32; let mut region = vec![0u8; BLOCK];
set_block_type(&mut region, 0x0011);
region[64..68].copy_from_slice(&meta_size.to_le_bytes()); let desc_off = 200u32;
region[64 + 156..64 + 160].copy_from_slice(&desc_off.to_le_bytes());
let d = desc_off as usize;
region[d + 8..d + 16].copy_from_slice(&(plain_meta.len() as u64 / BLOCK as u64).to_le_bytes()); region[d + 32..d + 40].copy_from_slice(&enc_meta_block.to_le_bytes());
let tweak_key = {
let mut h = Sha256::new();
h.update(VMK);
h.update(FAMILY);
let dg = h.finalize();
let mut tk = [0u8; 16];
tk.copy_from_slice(&dg[..16]);
tk
};
let lv_len = (LV_BLOCKS as usize) * BLOCK;
let lv_plain: Vec<u8> = (0..lv_len).map(|i| (i & 0xff) as u8).collect();
let mut lv_cipher = lv_plain.clone();
crate::xts::encrypt_units(&mut lv_cipher, &VMK, &tweak_key, 512, 0);
let lv_block: u64 = LV_PHYS_BLOCK;
let total = (lv_block as usize + LV_BLOCKS as usize) * BLOCK;
let mut image = vec![0u8; total];
image[88] = b'C';
image[89] = b'S';
image[48..52].copy_from_slice(&512u32.to_le_bytes());
image[96..100].copy_from_slice(&(BLOCK as u32).to_le_bytes());
image[64..72].copy_from_slice(&(total as u64).to_le_bytes());
image[172..176].copy_from_slice(&2u32.to_le_bytes());
image[104..112].copy_from_slice(&plaintext_meta_block.to_le_bytes());
image[176..192].copy_from_slice(&KEY1);
image[304..320].copy_from_slice(&PV_ID);
let po = plaintext_meta_block as usize * BLOCK;
image[po..po + region.len()].copy_from_slice(®ion);
let eo = enc_meta_block as usize * BLOCK;
image[eo..eo + enc_meta.len()].copy_from_slice(&enc_meta);
let lo = lv_block as usize * BLOCK;
image[lo..lo + lv_cipher.len()].copy_from_slice(&lv_cipher);
image
}