use crate::{Address, Oram, OramBlock, OramError};
use rand::{CryptoRng, RngCore};
use subtle::{ConstantTimeEq, ConstantTimeLess};
#[derive(Debug)]
pub struct LinearTimeOram<V: OramBlock> {
pub physical_memory: Vec<V>,
}
impl<V: OramBlock> LinearTimeOram<V> {
pub fn new(block_capacity: Address) -> Result<Self, OramError> {
log::info!("LinearTimeOram::new(capacity = {})", block_capacity,);
let mut physical_memory = Vec::new();
physical_memory.resize(usize::try_from(block_capacity)?, V::default());
Ok(Self { physical_memory })
}
}
impl<V: OramBlock> Oram for LinearTimeOram<V> {
type V = V;
fn access<R: RngCore + CryptoRng, F: Fn(&V) -> V>(
&mut self,
index: Address,
callback: F,
_: &mut R,
) -> Result<V, OramError> {
let index_in_bounds: bool = index.ct_lt(&self.block_capacity()?).into();
if !index_in_bounds {
return Err(OramError::AddressOutOfBoundsError {
attempted: index,
capacity: self.block_capacity()?,
});
}
let mut result = V::default();
for i in 0..self.physical_memory.len() {
let entry = &self.physical_memory[i];
let is_requested_index = (u64::try_from(i)?).ct_eq(&index);
result.conditional_assign(entry, is_requested_index);
let potential_new_value = callback(entry);
self.physical_memory[i].conditional_assign(&potential_new_value, is_requested_index);
}
Ok(result)
}
fn block_capacity(&self) -> Result<Address, OramError> {
Ok(u64::try_from(self.physical_memory.len())?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{bucket::BlockValue, test_utils::*};
#[test]
fn linear_time_oram_correctness_random_workload() {
let mut oram = LinearTimeOram::<BlockValue<1>>::new(64).unwrap();
random_workload(&mut oram, 1000);
}
#[test]
fn linear_time_oram_correctness_linear_workload() {
let mut oram = LinearTimeOram::<BlockValue<1>>::new(64).unwrap();
linear_workload(&mut oram, 1000);
}
}