use crate::aliases::SpanBuffer;
use crate::error::AescryptError;
use secure_gate::{RevealSecret, RevealSecretMut};
use std::io::Read;
#[inline(always)]
pub fn read_exact_span<R, const N: usize>(reader: &mut R) -> Result<SpanBuffer<N>, AescryptError>
where
R: Read,
{
let mut buf = SpanBuffer::new([0u8; N]);
buf.with_secret_mut(|b| reader.read_exact(b))
.map_err(AescryptError::Io)?;
Ok(buf)
}
#[inline(always)]
pub fn read_file_version<R>(reader: &mut R) -> Result<(u8, u8), AescryptError>
where
R: Read,
{
let header = read_exact_span::<_, 4>(reader)?;
let is_aes = header.with_secret(|h| &h[..3] == b"AES");
if !is_aes {
return Err(AescryptError::Header(
"invalid magic header (expected 'AES')".into(),
));
}
let version = header.with_secret(|h| h[3]);
if version > 3 {
return Err(AescryptError::UnsupportedVersion(version));
}
let modulo_or_reserved = read_exact_span::<_, 1>(reader)?.with_secret(|b| b[0]);
if version >= 1 && modulo_or_reserved != 0x00 {
return Err(AescryptError::Header(
"invalid header: reserved byte must be 0x00 for v1–v3".into(),
));
}
Ok((version, modulo_or_reserved))
}
const MAX_EXTENSIONS: usize = 256;
#[inline(always)]
pub fn consume_all_extensions<R>(reader: &mut R, version: u8) -> Result<(), AescryptError>
where
R: Read,
{
if version < 2 {
return Ok(());
}
let mut count = 0usize;
loop {
if count >= MAX_EXTENSIONS {
return Err(AescryptError::Header(
"too many extensions (limit: 256)".into(),
));
}
let len_bytes = read_exact_span::<_, 2>(reader)?;
let len = len_bytes.with_secret(|lb| u16::from_be_bytes(*lb));
if len == 0 {
break; }
let mut discard = [0u8; 256]; let mut remaining = len as usize;
while remaining > 0 {
let to_read = remaining.min(discard.len());
reader
.read_exact(&mut discard[..to_read])
.map_err(AescryptError::Io)?;
remaining -= to_read;
}
count += 1;
}
Ok(())
}
#[inline(always)]
pub fn read_kdf_iterations<R>(reader: &mut R, version: u8) -> Result<u32, AescryptError>
where
R: Read,
{
if version < 3 {
return Ok(0);
}
let iter_bytes = read_exact_span::<_, 4>(reader)?;
let iterations = iter_bytes.with_secret(|ib| u32::from_be_bytes(*ib));
if iterations == 0 {
return Err(AescryptError::Header(
"KDF iterations cannot be zero".into(),
));
}
if iterations > 5_000_000 {
return Err(AescryptError::Header(
"KDF iterations unreasonably high (>5M)".into(),
));
}
Ok(iterations)
}