use crate::crypto::{CipherId, Fek, Kek, SecretKey, ZERO_NONCE};
use crate::error::{Error, Result};
use crate::header::{SLOT_AREA_LEN, SLOT_COUNT, SLOT_LEN};
use crate::util::Writer;
pub fn build_slot_area(
keks: &[Kek],
fek: &Fek,
file_uuid: &[u8; 16],
cipher: CipherId,
) -> Result<Vec<u8>> {
if keks.len() > SLOT_COUNT {
return Err(Error::TooManySlots { got: keks.len(), max: SLOT_COUNT });
}
let mut w = Writer::with_capacity(SLOT_AREA_LEN);
for (i, kek) in keks.iter().enumerate() {
let idx = u16::try_from(i).map_err(|_| Error::TooManySlots {
got: keks.len(),
max: SLOT_COUNT,
})?;
let wrap_key = kek.derive_slot_key(file_uuid, idx);
let wrapped = cipher.encrypt(&wrap_key, &ZERO_NONCE, fek.as_key().as_bytes(), &[])?;
debug_assert_eq!(wrapped.len(), SLOT_LEN, "包裹后的 slot 必须是 48 字节");
w.bytes(&wrapped);
}
let used = keks.len().saturating_mul(SLOT_LEN);
w.random(SLOT_AREA_LEN.saturating_sub(used));
let out = w.into_vec();
debug_assert_eq!(out.len(), SLOT_AREA_LEN, "slot 区必须恰好 384 字节");
Ok(out)
}
pub fn unwrap_fek(
slot_area: &[u8],
keks: &[Kek],
file_uuid: &[u8; 16],
cipher: CipherId,
) -> Result<(Fek, usize, u16)> {
if slot_area.len() < SLOT_AREA_LEN {
return Err(Error::Truncated {
context: "key slot area",
need: SLOT_AREA_LEN,
got: slot_area.len(),
});
}
for (ki, kek) in keks.iter().enumerate() {
for si in 0..SLOT_COUNT {
let start = si.saturating_mul(SLOT_LEN);
let end = start.saturating_add(SLOT_LEN);
let Some(slot) = slot_area.get(start..end) else { continue };
let idx = match u16::try_from(si) {
Ok(v) => v,
Err(_) => continue,
};
let wrap_key = kek.derive_slot_key(file_uuid, idx);
if let Some(pt) = cipher.decrypt(&wrap_key, &ZERO_NONCE, slot, &[])? {
if pt.len() != crate::crypto::KEY_LEN {
continue;
}
let mut raw = [0u8; crate::crypto::KEY_LEN];
let Some(src) = pt.get(..crate::crypto::KEY_LEN) else { continue };
raw.copy_from_slice(src);
return Ok((Fek::from_key(SecretKey::from_bytes(raw)), ki, idx));
}
}
}
Err(Error::NoMatchingSlot)
}