#![no_std]
#![deny(unsafe_code)]
use core::fmt::{Debug, Display};
extern crate alloc;
use alloc::vec::Vec;
pub use aligned_cmov::{
cswap, subtle, typenum, A64Bytes, A8Bytes, ArrayLength, CMov, GenericArray,
};
pub use rand_core::{CryptoRng, RngCore};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
mod naive_storage;
pub use naive_storage::{HeapORAMStorage, HeapORAMStorageCreator};
mod linear_scanning;
pub use linear_scanning::LinearScanningORAM;
mod creators;
pub use creators::*;
pub mod testing;
#[allow(clippy::len_without_is_empty)]
pub trait ORAMStorage<BlockSize: ArrayLength<u8>, MetaSize: ArrayLength<u8>> {
fn len(&self) -> u64;
fn checkout(
&mut self,
index: u64,
dest: &mut [A64Bytes<BlockSize>],
dest_meta: &mut [A8Bytes<MetaSize>],
);
fn checkin(
&mut self,
index: u64,
src: &mut [A64Bytes<BlockSize>],
src_meta: &mut [A8Bytes<MetaSize>],
);
}
#[allow(clippy::len_without_is_empty)]
#[allow(clippy::upper_case_acronyms)]
pub trait ORAM<ValueSize: ArrayLength<u8>> {
fn len(&self) -> u64;
fn access<T, F: FnOnce(&mut A64Bytes<ValueSize>) -> T>(&mut self, index: u64, func: F) -> T;
#[inline]
fn read(&mut self, index: u64) -> A64Bytes<ValueSize> {
self.access(index, |val| val.clone())
}
#[inline]
fn write(&mut self, index: u64, new_val: &A64Bytes<ValueSize>) -> A64Bytes<ValueSize> {
self.access(index, |val| {
let retval = val.clone();
*val = new_val.clone();
retval
})
}
}
pub trait ORAMDebug<ValueSize: ArrayLength<u8>> {
fn check_invariants(&self) -> Vec<A64Bytes<ValueSize>>;
}
#[allow(clippy::len_without_is_empty)]
pub trait PositionMap {
fn len(&self) -> u64;
fn write(&mut self, key: &u64, new_val: &u64) -> u64;
}
pub trait ObliviousHashMap<KeySize: ArrayLength<u8>, ValueSize: ArrayLength<u8>> {
fn len(&self) -> u64;
fn capacity(&self) -> u64;
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
fn read(&mut self, key: &A8Bytes<KeySize>, output: &mut A8Bytes<ValueSize>) -> u32;
fn access<F: FnOnce(u32, &mut A8Bytes<ValueSize>)>(
&mut self,
key: &A8Bytes<KeySize>,
callback: F,
);
fn remove(&mut self, key: &A8Bytes<KeySize>) -> u32;
#[inline]
fn vartime_write(
&mut self,
key: &A8Bytes<KeySize>,
value: &A8Bytes<ValueSize>,
allow_overwrite: Choice,
) -> u32 {
self.vartime_write_extended(key, value, allow_overwrite, Choice::from(1))
}
fn vartime_write_extended(
&mut self,
key: &A8Bytes<KeySize>,
value: &A8Bytes<ValueSize>,
allow_overwrite: Choice,
allow_sideeffects_and_eviction: Choice,
) -> u32;
fn access_and_insert<F: FnOnce(u32, &mut A8Bytes<ValueSize>), R: RngCore + CryptoRng>(
&mut self,
key: &A8Bytes<KeySize>,
default_value: &A8Bytes<ValueSize>,
rng: &mut R,
callback: F,
) -> u32 {
let mut buffer = default_value.clone();
let read_code = self.read(key, &mut buffer);
if read_code == OMAP_INVALID_KEY {
return read_code;
}
debug_assert!(
read_code == OMAP_FOUND || read_code == OMAP_NOT_FOUND,
"unexpected status code value: {}",
read_code
);
callback(read_code, &mut buffer);
let mut first_write_key = key.clone();
let mut first_write_val = buffer;
let mut first_write_allow_overwrite = Choice::from(1);
let mut first_write_allow_sideeffects_and_eviction = Choice::from(1);
let mut second_write_key = A8Bytes::<KeySize>::default();
while bool::from(second_write_key.ct_eq(&A8Bytes::<KeySize>::default())) {
rng.fill_bytes(second_write_key.as_mut());
}
let mut second_write_val = default_value.clone();
let mut second_write_allow_overwrite = Choice::from(0);
let mut second_write_allow_sideeffects_and_eviction = read_code.ct_eq(&OMAP_FOUND);
let swap = (rng.next_u32() & 1).ct_eq(&0);
cswap(swap, &mut first_write_key, &mut second_write_key);
cswap(swap, &mut first_write_val, &mut second_write_val);
ConditionallySelectable::conditional_swap(
&mut first_write_allow_overwrite,
&mut second_write_allow_overwrite,
swap,
);
ConditionallySelectable::conditional_swap(
&mut first_write_allow_sideeffects_and_eviction,
&mut second_write_allow_sideeffects_and_eviction,
swap,
);
let first_write_code = self.vartime_write_extended(
&first_write_key,
&first_write_val,
first_write_allow_overwrite,
first_write_allow_sideeffects_and_eviction,
);
debug_assert!(
first_write_code != OMAP_INVALID_KEY,
"unexpected status code value: {}",
first_write_code
);
let second_write_code = self.vartime_write_extended(
&second_write_key,
&second_write_val,
second_write_allow_overwrite,
second_write_allow_sideeffects_and_eviction,
);
debug_assert!(
second_write_code != OMAP_INVALID_KEY,
"unexpected status code value: {}",
second_write_code
);
let mut result = read_code;
result.cmov(first_write_code.ct_eq(&OMAP_OVERFLOW), &OMAP_OVERFLOW);
result.cmov(second_write_code.ct_eq(&OMAP_OVERFLOW), &OMAP_OVERFLOW);
result
}
}
pub const OMAP_FOUND: u32 = 0;
pub const OMAP_NOT_FOUND: u32 = 1;
pub const OMAP_OVERFLOW: u32 = 2;
pub const OMAP_INVALID_KEY: u32 = 3;
#[inline]
pub const fn log2_ceil(arg: u64) -> u32 {
if arg == 0 {
return 0;
}
(!0u64).count_ones() - (arg - 1).leading_zeros()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_log2_ceil() {
assert_eq!(0, log2_ceil(0));
assert_eq!(0, log2_ceil(1));
assert_eq!(1, log2_ceil(2));
assert_eq!(2, log2_ceil(3));
assert_eq!(2, log2_ceil(4));
assert_eq!(3, log2_ceil(5));
assert_eq!(3, log2_ceil(8));
assert_eq!(4, log2_ceil(9));
assert_eq!(4, log2_ceil(16));
assert_eq!(5, log2_ceil(17));
}
}