use vck_common::{
types::VolumeState, EncryptedOffset, EncryptedOffsetStore, SectorIo, VckResult, VolumeCipher,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EngineState {
Idle,
Encrypting,
Decrypting,
Paused,
}
impl EngineState {
pub fn as_wire(self) -> i32 {
match self {
EngineState::Idle => 0,
EngineState::Encrypting => 1,
EngineState::Decrypting => 2,
EngineState::Paused => 3,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ProgressSnapshot {
pub encrypted_sector: u64,
pub total_sectors: u64,
pub state: EngineState,
}
pub struct EncryptionEngine {
offset_sector: u64,
encrypted_offset: EncryptedOffset,
state: EngineState,
}
impl EncryptionEngine {
pub fn new(offset_sector: u64, encrypted_offset: EncryptedOffset) -> Self {
Self {
offset_sector,
encrypted_offset,
state: EngineState::Idle,
}
}
pub fn relative(&self, lba: u64) -> Option<u64> {
lba.checked_sub(self.offset_sector)
.filter(|rel| *rel < self.encrypted_offset.total_sectors)
}
pub fn is_encrypted(&self, rel: u64) -> bool {
self.encrypted_offset.is_encrypted(rel)
}
pub fn encrypted_boundary(&self) -> u64 {
self.encrypted_offset.sector
}
pub fn state(&self) -> EngineState {
self.state
}
pub fn snapshot(&self) -> ProgressSnapshot {
ProgressSnapshot {
encrypted_sector: self.encrypted_offset.sector,
total_sectors: self.encrypted_offset.total_sectors,
state: self.state,
}
}
pub fn start_encrypt(&mut self, store: &dyn EncryptedOffsetStore) {
let _ = store.store_state(VolumeState::Encrypt);
if !self.encrypted_offset.is_fully_encrypted() {
self.state = EngineState::Encrypting;
} else {
self.state = EngineState::Idle;
}
}
pub fn start_decrypt(&mut self, store: &dyn EncryptedOffsetStore) {
let _ = store.store_state(VolumeState::Decrypt);
if self.encrypted_offset.sector > 0 {
self.state = EngineState::Decrypting;
} else {
self.state = EngineState::Idle;
}
}
pub fn resume(&mut self, store: &dyn EncryptedOffsetStore) {
match store.load_state().unwrap_or(VolumeState::Encrypt) {
VolumeState::Encrypt => {
self.state = if self.encrypted_offset.is_fully_encrypted() {
EngineState::Idle
} else {
EngineState::Encrypting
};
}
VolumeState::Decrypt => {
self.state = if self.encrypted_offset.sector > 0 {
EngineState::Decrypting
} else {
EngineState::Idle
};
}
}
}
pub fn pause(&mut self) {
if matches!(
self.state,
EngineState::Encrypting | EngineState::Decrypting
) {
self.state = EngineState::Paused;
}
}
pub fn progress_step(
&mut self,
io: &dyn SectorIo,
cipher: &dyn VolumeCipher,
store: &dyn EncryptedOffsetStore,
batch_sectors: u64,
) -> VckResult<bool> {
match self.state {
EngineState::Encrypting => self.encrypt_step(io, cipher, store, batch_sectors),
EngineState::Decrypting => self.decrypt_step(io, cipher, store, batch_sectors),
_ => Ok(false),
}
}
fn encrypt_step(
&mut self,
io: &dyn SectorIo,
cipher: &dyn VolumeCipher,
store: &dyn EncryptedOffsetStore,
batch_sectors: u64,
) -> VckResult<bool> {
if self.encrypted_offset.is_fully_encrypted() {
self.state = EngineState::Idle;
return Ok(false);
}
let sector_size = io.sector_size() as usize;
let start_rel = self.encrypted_offset.sector;
let remaining = self.encrypted_offset.total_sectors - start_rel;
let count = remaining.min(batch_sectors.max(1));
let mut buf = alloc::vec![0u8; count as usize * sector_size];
let abs = self.offset_sector + start_rel;
crate::vck_log!("sweep: enc read abs={} count={}", abs, count);
io.read_sectors(abs, &mut buf).map_err(|e| {
crate::vck_log!("sweep: read err: {}", e);
e
})?;
cipher.encrypt_area(&mut buf, sector_size, start_rel);
crate::vck_log!("sweep: enc write abs={}", abs);
io.write_sectors(abs, &buf).map_err(|e| {
crate::vck_log!("sweep: write err: {}", e);
e
})?;
self.encrypted_offset.sector += count;
crate::vck_log!("sweep: stored boundary={}", self.encrypted_offset.sector);
store.store(&self.encrypted_offset).map_err(|e| {
crate::vck_log!("sweep: store err: {}", e);
e
})?;
store.flush()?;
if self.encrypted_offset.is_fully_encrypted() {
self.state = EngineState::Idle;
Ok(false)
} else {
Ok(true)
}
}
fn decrypt_step(
&mut self,
io: &dyn SectorIo,
cipher: &dyn VolumeCipher,
store: &dyn EncryptedOffsetStore,
batch_sectors: u64,
) -> VckResult<bool> {
if self.encrypted_offset.sector == 0 {
self.state = EngineState::Idle;
return Ok(false);
}
let sector_size = io.sector_size() as usize;
let count = self.encrypted_offset.sector.min(batch_sectors.max(1));
let new_boundary = self.encrypted_offset.sector - count;
let mut buf = alloc::vec![0u8; count as usize * sector_size];
let abs = self.offset_sector + new_boundary;
io.read_sectors(abs, &mut buf)?;
cipher.decrypt_area(&mut buf, sector_size, new_boundary);
io.write_sectors(abs, &buf)?;
self.encrypted_offset.sector = new_boundary;
store.store(&self.encrypted_offset)?;
store.flush()?;
if new_boundary == 0 {
self.state = EngineState::Idle;
Ok(false)
} else {
Ok(true)
}
}
}