1use alloc::vec;
17
18use crate::evictor::{BranchSelector, EvictionStrategy, EvictorCreator};
19use aligned_cmov::{
20 subtle::{Choice, ConstantTimeEq, ConstantTimeLess},
21 typenum::{PartialDiv, Prod, Unsigned, U16, U64, U8},
22 A64Bytes, A8Bytes, ArrayLength, AsAlignedChunks, AsNeSlice, CMov,
23};
24use alloc::{boxed::Box, vec::Vec};
25use balanced_tree_index::TreeIndex;
26use core::{marker::PhantomData, ops::Mul};
27use mc_oblivious_traits::{
28 log2_ceil, ORAMStorage, ORAMStorageCreator, PositionMap, PositionMapCreator, ORAM,
29};
30use rand_core::{CryptoRng, RngCore};
31
32pub(crate) type MetaSize = U16;
39
40pub(crate) fn meta_leaf_num(src: &A8Bytes<MetaSize>) -> &u64 {
62 &src.as_ne_u64_slice()[0]
63}
64pub(crate) fn meta_leaf_num_mut(src: &mut A8Bytes<MetaSize>) -> &mut u64 {
66 &mut src.as_mut_ne_u64_slice()[0]
67}
68pub(crate) fn meta_block_num(src: &A8Bytes<MetaSize>) -> &u64 {
70 &src.as_ne_u64_slice()[1]
71}
72pub(crate) fn meta_block_num_mut(src: &mut A8Bytes<MetaSize>) -> &mut u64 {
74 &mut src.as_mut_ne_u64_slice()[1]
75}
76pub(crate) fn meta_is_vacant(src: &A8Bytes<MetaSize>) -> Choice {
78 meta_leaf_num(src).ct_eq(&0)
79}
80pub(crate) fn meta_set_vacant(condition: Choice, src: &mut A8Bytes<MetaSize>) {
82 meta_leaf_num_mut(src).cmov(condition, &0);
83}
84
85pub struct PathORAM<ValueSize, Z, StorageType, RngType, EvictorType>
87where
88 ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
89 Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
90 RngType: RngCore + CryptoRng + Send + Sync + 'static,
91 StorageType: ORAMStorage<Prod<Z, ValueSize>, Prod<Z, MetaSize>> + Send + Sync + 'static,
92 EvictorType: EvictionStrategy<ValueSize, Z> + BranchSelector + Send + Sync + 'static,
93 Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
94 Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
95{
96 height: u32,
98 storage: StorageType,
100 pos: Box<dyn PositionMap + Send + Sync + 'static>,
102 rng: RngType,
104 stash_data: Vec<A64Bytes<ValueSize>>,
106 stash_meta: Vec<A8Bytes<MetaSize>>,
108 branch: BranchCheckout<ValueSize, Z>,
110 evictor: EvictorType,
112}
113
114impl<ValueSize, Z, StorageType, RngType, EvictorType>
115 PathORAM<ValueSize, Z, StorageType, RngType, EvictorType>
116where
117 ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
118 Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
119 RngType: RngCore + CryptoRng + Send + Sync + 'static,
120 StorageType: ORAMStorage<Prod<Z, ValueSize>, Prod<Z, MetaSize>> + Send + Sync + 'static,
121 EvictorType: EvictionStrategy<ValueSize, Z> + BranchSelector + Send + Sync + 'static,
122 Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
123 Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
124{
125 pub fn new<
132 PMC: PositionMapCreator<RngType>,
133 SC: ORAMStorageCreator<Prod<Z, ValueSize>, Prod<Z, MetaSize>, Output = StorageType>,
134 F: FnMut() -> RngType + 'static,
135 EVC: EvictorCreator<ValueSize, Z, Output = EvictorType>,
136 >(
137 size: u64,
138 stash_size: usize,
139 rng_maker: &mut F,
140 evictor_factory: EVC,
141 ) -> Self {
142 assert!(size != 0, "size cannot be zero");
143 assert!(size & (size - 1) == 0, "size must be a power of two");
144 let height = log2_ceil(size).saturating_sub(log2_ceil(Z::U64));
146 let mut rng = rng_maker();
150 let evictor = evictor_factory.create(height);
151 let storage = SC::create(2u64 << height, &mut rng).expect("Storage failed");
152 let pos = PMC::create(size, height, stash_size, rng_maker);
153 Self {
154 height,
155 storage,
156 pos,
157 rng,
158 stash_data: vec![Default::default(); stash_size],
159 stash_meta: vec![Default::default(); stash_size],
160 branch: Default::default(),
161 evictor,
162 }
163 }
164}
165
166impl<ValueSize, Z, StorageType, RngType, EvictorType> ORAM<ValueSize>
167 for PathORAM<ValueSize, Z, StorageType, RngType, EvictorType>
168where
169 ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
170 Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
171 RngType: RngCore + CryptoRng + Send + Sync + 'static,
172 StorageType: ORAMStorage<Prod<Z, ValueSize>, Prod<Z, MetaSize>> + Send + Sync + 'static,
173 EvictorType: EvictionStrategy<ValueSize, Z> + BranchSelector + Send + Sync + 'static,
174 Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
175 Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
176{
177 fn len(&self) -> u64 {
178 self.pos.len()
179 }
180
181 fn stash_size(&self) -> usize {
182 let mut stash_count = 0u64;
183 for idx in 0..self.stash_data.len() {
184 let stash_count_incremented = stash_count + 1;
185 stash_count.cmov(
186 !meta_is_vacant(&self.stash_meta[idx]),
187 &stash_count_incremented,
188 );
189 }
190 stash_count as usize
191 }
192
193 fn access<T, F: FnOnce(&mut A64Bytes<ValueSize>) -> T>(&mut self, key: u64, f: F) -> T {
195 let result: T;
196 let new_pos = 1u64.random_child_at_height(self.height, &mut self.rng);
198 let current_pos = self.pos.write(&key, &new_pos);
200 debug_assert!(current_pos != 0, "position map told us the item is at 0");
201 debug_assert!(self.branch.leaf == 0);
205 self.branch.checkout(&mut self.storage, current_pos);
206
207 {
210 debug_assert!(self.branch.leaf == current_pos);
211 let mut meta = A8Bytes::<MetaSize>::default();
212 let mut data = A64Bytes::<ValueSize>::default();
213
214 self.branch
215 .ct_find_and_remove(1.into(), &key, &mut data, &mut meta);
216 details::ct_find_and_remove(
217 1.into(),
218 &key,
219 &mut data,
220 &mut meta,
221 &mut self.stash_data,
222 &mut self.stash_meta,
223 );
224 debug_assert!(
225 meta_block_num(&meta) == &key || meta_is_vacant(&meta).into(),
226 "Hmm, we didn't find the expected item something else"
227 );
228 debug_assert!(self.branch.leaf == current_pos);
229
230 result = f(&mut data);
232
233 *meta_block_num_mut(&mut meta) = key;
235 *meta_leaf_num_mut(&mut meta) = new_pos;
237
238 details::ct_insert(
240 1.into(),
241 &data,
242 &mut meta,
243 &mut self.stash_data,
244 &mut self.stash_meta,
245 );
246 assert!(bool::from(meta_is_vacant(&meta)), "Stash overflow!");
247 }
248
249 self.evictor.evict_from_stash_to_branch(
251 &mut self.stash_data,
252 &mut self.stash_meta,
253 &mut self.branch,
254 );
255
256 debug_assert!(self.branch.leaf == current_pos);
257 self.branch.checkin(&mut self.storage);
258 debug_assert!(self.branch.leaf == 0);
259
260 for _ in 0..self.evictor.get_number_of_additional_branches_to_evict() {
261 let leaf = self.evictor.get_next_branch_to_evict();
262 debug_assert!(leaf != 0);
263 self.branch.checkout(&mut self.storage, leaf);
264 self.evictor.evict_from_stash_to_branch(
265 &mut self.stash_data,
266 &mut self.stash_meta,
267 &mut self.branch,
268 );
269 self.branch.checkin(&mut self.storage);
270 debug_assert!(self.branch.leaf == 0);
271 }
272
273 result
274 }
275}
276
277pub struct BranchCheckout<ValueSize, Z>
285where
286 ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
287 Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
288 Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
289 Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
290{
291 pub(crate) leaf: u64,
294 pub(crate) data: Vec<A64Bytes<Prod<Z, ValueSize>>>,
296 pub(crate) meta: Vec<A8Bytes<Prod<Z, MetaSize>>>,
298 _value_size: PhantomData<fn() -> ValueSize>,
300}
301
302impl<ValueSize, Z> Default for BranchCheckout<ValueSize, Z>
303where
304 ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
305 Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
306 Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
307 Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
308{
309 fn default() -> Self {
310 Self {
311 leaf: 0,
312 data: Default::default(),
313 meta: Default::default(),
314 _value_size: Default::default(),
315 }
316 }
317}
318
319impl<ValueSize, Z> BranchCheckout<ValueSize, Z>
320where
321 ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
322 Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
323 Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
324 Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
325{
326 pub fn ct_find_and_remove(
328 &mut self,
329 condition: Choice,
330 query: &u64,
331 dest_data: &mut A64Bytes<ValueSize>,
332 dest_meta: &mut A8Bytes<MetaSize>,
333 ) {
334 debug_assert!(self.data.len() == self.meta.len());
335 for idx in 0..self.data.len() {
336 let bucket_data: &mut [A64Bytes<ValueSize>] = self.data[idx].as_mut_aligned_chunks();
337 let bucket_meta: &mut [A8Bytes<MetaSize>] = self.meta[idx].as_mut_aligned_chunks();
338 debug_assert!(bucket_data.len() == Z::USIZE);
339 debug_assert!(bucket_meta.len() == Z::USIZE);
340
341 details::ct_find_and_remove(
342 condition,
343 query,
344 dest_data,
345 dest_meta,
346 bucket_data,
347 bucket_meta,
348 );
349 }
350 }
351
352 pub fn ct_insert(
355 &mut self,
356 mut condition: Choice,
357 src_data: &A64Bytes<ValueSize>,
358 src_meta: &mut A8Bytes<MetaSize>,
359 ) {
360 condition &= !meta_is_vacant(src_meta);
361 let lowest_height_legal_index = self.lowest_height_legal_index(*meta_leaf_num(src_meta));
362 Self::insert_into_branch_suffix(
363 condition,
364 src_data,
365 src_meta,
366 lowest_height_legal_index,
367 &mut self.data,
368 &mut self.meta,
369 );
370 }
371
372 pub fn pack(&mut self) {
375 debug_assert!(self.leaf != 0);
376 debug_assert!(self.data.len() == self.meta.len());
377 let data_len = self.data.len();
378 for bucket_num in 1..self.data.len() {
379 let (lower_data, upper_data) = self.data.split_at_mut(bucket_num);
380 let (lower_meta, upper_meta) = self.meta.split_at_mut(bucket_num);
381
382 let bucket_data: &mut [A64Bytes<ValueSize>] = upper_data[0].as_mut_aligned_chunks();
383 let bucket_meta: &mut [A8Bytes<MetaSize>] = upper_meta[0].as_mut_aligned_chunks();
384
385 debug_assert!(bucket_data.len() == bucket_meta.len());
386 for idx in 0..bucket_data.len() {
387 let src_data: &mut A64Bytes<ValueSize> = &mut bucket_data[idx];
388 let src_meta: &mut A8Bytes<MetaSize> = &mut bucket_meta[idx];
389
390 let lowest_height_legal_index = Self::lowest_height_legal_index_impl(
393 *meta_leaf_num(src_meta),
394 self.leaf,
395 data_len,
396 );
397 Self::insert_into_branch_suffix(
398 1.into(),
399 src_data,
400 src_meta,
401 lowest_height_legal_index,
402 lower_data,
403 lower_meta,
404 );
405 }
406 }
407 debug_assert!(self.leaf != 0);
408 }
409
410 pub fn checkout(
412 &mut self,
413 storage: &mut impl ORAMStorage<Prod<Z, ValueSize>, Prod<Z, MetaSize>>,
414 leaf: u64,
415 ) {
416 debug_assert!(self.leaf == 0);
417 self.data
418 .resize_with(leaf.height() as usize + 1, Default::default);
419 self.meta
420 .resize_with(leaf.height() as usize + 1, Default::default);
421 storage.checkout(leaf, &mut self.data, &mut self.meta);
422 self.leaf = leaf;
423 }
424
425 pub fn checkin(
427 &mut self,
428 storage: &mut impl ORAMStorage<Prod<Z, ValueSize>, Prod<Z, MetaSize>>,
429 ) {
430 debug_assert!(self.leaf != 0);
431 storage.checkin(self.leaf, &mut self.data, &mut self.meta);
432 self.leaf = 0;
433 }
434
435 pub(crate) fn lowest_height_legal_index(&self, query: u64) -> usize {
443 Self::lowest_height_legal_index_impl(query, self.leaf, self.data.len())
444 }
445
446 pub(crate) fn lowest_height_legal_index_impl(
451 mut query: u64,
452 leaf: u64,
453 data_len: usize,
454 ) -> usize {
455 query.cmov(query.ct_eq(&0), &1);
457 debug_assert!(
458 leaf != 0,
459 "this should not be called when there is not currently a checkout"
460 );
461
462 let common_ancestor_height = leaf.common_ancestor_height(&query) as usize;
463 debug_assert!(data_len > common_ancestor_height);
464 data_len - 1 - common_ancestor_height
465 }
466
467 pub(crate) fn insert_into_branch_suffix(
473 condition: Choice,
474 src_data: &A64Bytes<ValueSize>,
475 src_meta: &mut A8Bytes<MetaSize>,
476 insert_after_index: usize,
477 dest_data: &mut [A64Bytes<Prod<Z, ValueSize>>],
478 dest_meta: &mut [A8Bytes<Prod<Z, MetaSize>>],
479 ) {
480 debug_assert!(dest_data.len() == dest_meta.len());
481 for idx in 0..dest_data.len() {
482 details::ct_insert::<ValueSize>(
483 condition & !(idx as u64).ct_lt(&(insert_after_index as u64)),
484 src_data,
485 src_meta,
486 dest_data[idx].as_mut_aligned_chunks(),
487 dest_meta[idx].as_mut_aligned_chunks(),
488 )
489 }
490 }
491}
492
493pub(crate) mod details {
495 use super::*;
496
497 pub fn ct_find_and_remove<ValueSize: ArrayLength<u8>>(
513 mut condition: Choice,
514 query: &u64,
515 dest_data: &mut A64Bytes<ValueSize>,
516 dest_meta: &mut A8Bytes<MetaSize>,
517 src_data: &mut [A64Bytes<ValueSize>],
518 src_meta: &mut [A8Bytes<MetaSize>],
519 ) {
520 debug_assert!(src_data.len() == src_meta.len());
521 for idx in 0..src_meta.len() {
522 let test = condition
525 & (query.ct_eq(meta_block_num(&src_meta[idx])))
526 & !meta_is_vacant(&src_meta[idx]);
527 dest_meta.cmov(test, &src_meta[idx]);
528 dest_data.cmov(test, &src_data[idx]);
529 meta_set_vacant(test, &mut src_meta[idx]);
531 condition &= !test;
532 }
533 }
534
535 pub fn ct_insert<ValueSize: ArrayLength<u8>>(
550 mut condition: Choice,
551 src_data: &A64Bytes<ValueSize>,
552 src_meta: &mut A8Bytes<MetaSize>,
553 dest_data: &mut [A64Bytes<ValueSize>],
554 dest_meta: &mut [A8Bytes<MetaSize>],
555 ) {
556 debug_assert!(dest_data.len() == dest_meta.len());
557 condition &= !meta_is_vacant(src_meta);
558 for idx in 0..dest_meta.len() {
559 let test = condition & meta_is_vacant(&dest_meta[idx]);
562 dest_meta[idx].cmov(test, src_meta);
563 dest_data[idx].cmov(test, src_data);
564 meta_set_vacant(test, src_meta);
565 condition &= !test;
566 }
567 }
568}