1#![no_std]
25#![deny(unsafe_code)]
26#![deny(missing_docs)]
27
28extern crate alloc;
29
30use aligned_cmov::{
31 subtle::{Choice, ConstantTimeEq, ConstantTimeGreater},
32 typenum, A64Bytes, A8Bytes, ArrayLength, AsAlignedChunks, CMov,
33};
34use alloc::vec::Vec;
35use core::{
36 hash::{BuildHasher, Hash, Hasher},
37 marker::PhantomData,
38 ops::{Add, Sub},
39};
40use generic_array::sequence::Split;
41use mc_oblivious_traits::{
42 log2_ceil, OMapCreator, ORAMCreator, ObliviousHashMap, OMAP_FOUND, OMAP_INVALID_KEY,
43 OMAP_NOT_FOUND, OMAP_OVERFLOW, ORAM,
44};
45use rand_core::{CryptoRng, RngCore};
46use typenum::{PartialDiv, Sum, U8};
47
48mod build_hasher;
49use build_hasher::SipBuildHasher;
50
51const MAX_EVICTION_RETRIES: usize = 6;
57
58pub struct CuckooHashTable<KeySize, ValueSize, BlockSize, RngType, O>
64where
65 KeySize: ArrayLength<u8> + Add<ValueSize> + PartialDiv<U8> + 'static,
66 ValueSize: ArrayLength<u8> + PartialDiv<U8>,
67 BlockSize: ArrayLength<u8> + PartialDiv<U8>,
68 RngType: RngCore + CryptoRng + Send + Sync + 'static,
69 O: ORAM<BlockSize> + Send + Sync + 'static,
70 Sum<KeySize, ValueSize>: ArrayLength<u8> + Sub<KeySize, Output = ValueSize> + PartialDiv<U8>,
71{
72 num_items: u64,
74 num_buckets: u64,
76 hash1: SipBuildHasher,
78 hash2: SipBuildHasher,
80 oram1: O,
82 oram2: O,
84 rng: RngType,
86 _key_size: PhantomData<fn() -> KeySize>,
88 _value_size: PhantomData<fn() -> ValueSize>,
89 _block_size: PhantomData<fn() -> BlockSize>,
90}
91
92impl<KeySize, ValueSize, BlockSize, RngType, O>
93 CuckooHashTable<KeySize, ValueSize, BlockSize, RngType, O>
94where
95 KeySize: ArrayLength<u8> + Add<ValueSize> + PartialDiv<U8> + 'static,
96 ValueSize: ArrayLength<u8> + PartialDiv<U8>,
97 BlockSize: ArrayLength<u8> + PartialDiv<U8>,
98 RngType: RngCore + CryptoRng + Send + Sync + 'static,
99 O: ORAM<BlockSize> + Send + Sync + 'static,
100 Sum<KeySize, ValueSize>: ArrayLength<u8> + Sub<KeySize, Output = ValueSize> + PartialDiv<U8>,
101{
102 pub fn new<OC, M>(desired_capacity: u64, stash_size: usize, mut maker: M) -> Self
105 where
106 OC: ORAMCreator<BlockSize, RngType, Output = O>,
107 M: 'static + FnMut() -> RngType,
108 {
109 assert!(Self::BUCKET_CAPACITY > 0, "Block size is insufficient to store even one item. For good performance it should be enough to store several items.");
110 let num_buckets = 1u64 << log2_ceil((desired_capacity / Self::BUCKET_CAPACITY) / 2);
115 debug_assert!(
116 num_buckets & (num_buckets - 1) == 0,
117 "num_buckets must be a power of two"
118 );
119
120 let oram1 = OC::create(num_buckets, stash_size, &mut maker);
121 debug_assert!(num_buckets <= oram1.len(), "unexpected oram capacity");
122 let oram2 = OC::create(num_buckets, stash_size, &mut maker);
123 debug_assert!(num_buckets <= oram2.len(), "unexpected oram capacity");
124 debug_assert!(
125 oram1.len() == oram2.len(),
126 "Orams didn't have the same length, not expected"
127 );
128
129 let mut rng = maker();
130 let hash1 = SipBuildHasher::from_rng(&mut rng);
131 let hash2 = SipBuildHasher::from_rng(&mut rng);
132
133 Self {
134 num_items: 0,
135 num_buckets,
136 hash1,
137 hash2,
138 oram1,
139 oram2,
140 rng,
141 _key_size: Default::default(),
142 _value_size: Default::default(),
143 _block_size: Default::default(),
144 }
145 }
146
147 fn hash_query(&self, query: &A8Bytes<KeySize>) -> [u64; 2] {
148 let result1 = {
149 let mut hasher = self.hash1.build_hasher();
150 query.as_slice().hash(&mut hasher);
151 hasher.finish() & (self.num_buckets - 1)
152 };
153
154 let result2 = {
155 let mut hasher = self.hash2.build_hasher();
156 query.as_slice().hash(&mut hasher);
157 hasher.finish() & (self.num_buckets - 1)
158 };
159
160 [result1, result2]
161 }
162
163 fn count_before_insert(query: &A8Bytes<KeySize>, block: &A64Bytes<BlockSize>) -> (Choice, u32) {
167 let mut found = Choice::from(0);
168 let mut empty_count = 0u32;
169
170 let pairs: &[A8Bytes<Sum<KeySize, ValueSize>>] = block.as_aligned_chunks();
171 for pair in pairs {
172 let (key, _): (&A8Bytes<KeySize>, &A8Bytes<ValueSize>) = pair.split();
173 found |= key.ct_eq(query);
174 empty_count += key.ct_eq(&A8Bytes::<KeySize>::default()).unwrap_u8() as u32;
175 }
176
177 (found, empty_count)
178 }
179
180 fn insert_to_block(
187 condition: Choice,
188 query: &A8Bytes<KeySize>,
189 new_value: &A8Bytes<ValueSize>,
190 block: &mut A64Bytes<BlockSize>,
191 ) {
192 let mut key_buf = query.clone();
194 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] = block.as_mut_aligned_chunks();
195 for pair in pairs {
196 let (key, value): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) =
197 <&mut A8Bytes<Sum<KeySize, ValueSize>> as Split<u8, KeySize>>::split(pair);
198 let test = condition & (key.ct_eq(query) | key.ct_eq(&A8Bytes::<KeySize>::default()));
199 key.cmov(test, &key_buf);
200 key_buf.cmov(test, &A8Bytes::default());
203 value.cmov(test, new_value);
204 }
205 }
206
207 const BUCKET_CAPACITY: u64 = (BlockSize::U64 / (KeySize::U64 + ValueSize::U64));
208}
209
210impl<KeySize, ValueSize, BlockSize, RngType, O> ObliviousHashMap<KeySize, ValueSize>
211 for CuckooHashTable<KeySize, ValueSize, BlockSize, RngType, O>
212where
213 KeySize: ArrayLength<u8> + Add<ValueSize> + PartialDiv<U8> + 'static,
214 ValueSize: ArrayLength<u8> + PartialDiv<U8>,
215 BlockSize: ArrayLength<u8> + PartialDiv<U8>,
216 RngType: RngCore + CryptoRng + Send + Sync + 'static,
217 O: ORAM<BlockSize> + Send + Sync + 'static,
218 Sum<KeySize, ValueSize>: ArrayLength<u8> + Sub<KeySize, Output = ValueSize> + PartialDiv<U8>,
219{
220 fn len(&self) -> u64 {
221 self.num_items
222 }
223 fn capacity(&self) -> u64 {
224 2 * self.num_buckets * Self::BUCKET_CAPACITY
225 }
226
227 fn read(&mut self, query: &A8Bytes<KeySize>, output: &mut A8Bytes<ValueSize>) -> u32 {
237 if bool::from(query.ct_eq(&A8Bytes::<KeySize>::default())) {
239 return OMAP_INVALID_KEY;
240 }
241
242 let mut result_code = OMAP_NOT_FOUND;
243 let hashes = self.hash_query(query);
244 self.oram1.access(hashes[0], |block| {
245 let pairs: &[A8Bytes<Sum<KeySize, ValueSize>>] = block.as_aligned_chunks();
246 for pair in pairs {
247 let (key, value): (&A8Bytes<KeySize>, &A8Bytes<ValueSize>) = pair.split();
248 let test = query.ct_eq(key);
249 result_code.cmov(test, &OMAP_FOUND);
250 output.cmov(test, value);
251 }
252 });
253 self.oram2.access(hashes[1], |block| {
254 let pairs: &[A8Bytes<Sum<KeySize, ValueSize>>] = block.as_aligned_chunks();
255 for pair in pairs {
256 let (key, value): (&A8Bytes<KeySize>, &A8Bytes<ValueSize>) = pair.split();
257 let test = query.ct_eq(key);
258 result_code.cmov(test, &OMAP_FOUND);
259 output.cmov(test, value);
260 }
261 });
262 result_code
263 }
264
265 fn access_and_remove<F: FnOnce(u32, &mut A8Bytes<ValueSize>) -> Choice>(
278 &mut self,
279 query: &A8Bytes<KeySize>,
280 f: F,
281 ) {
282 let mut callback_buffer = A8Bytes::<ValueSize>::default();
283 let query_is_valid = !query.ct_eq(&A8Bytes::<KeySize>::default());
287
288 let hashes = self.hash_query(query);
289 let oram1 = &mut self.oram1;
290 let oram2 = &mut self.oram2;
291
292 oram1.access(hashes[0], |block1| {
293 oram2.access(hashes[1], |block2| {
294 let mut result_code = OMAP_INVALID_KEY;
297 result_code.cmov(query_is_valid, &OMAP_NOT_FOUND);
298 for block in &[&block1, &block2] {
299 let pairs: &[A8Bytes<Sum<KeySize, ValueSize>>] = block.as_aligned_chunks();
300 for pair in pairs {
301 let (key, val): (&A8Bytes<KeySize>, &A8Bytes<ValueSize>) = pair.split();
302 let test = query.ct_eq(key) & query_is_valid;
304 callback_buffer.cmov(test, val);
305 debug_assert!(
306 result_code != OMAP_FOUND || bool::from(!test),
307 "key should not be found twice"
308 );
309 result_code.cmov(test, &OMAP_FOUND);
310 }
311 }
312 let should_delete = f(result_code, &mut callback_buffer);
313
314 for block in &mut [block1, block2] {
315 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] =
316 block.as_mut_aligned_chunks();
317 for pair in pairs {
318 let (key, val): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) =
319 pair.split();
320 let test = query.ct_eq(key) & query_is_valid;
322 val.cmov(test, &callback_buffer);
323 key.cmov(test & should_delete, &Default::default());
325 }
326 }
327 });
328 });
329 }
330
331 fn remove(&mut self, query: &A8Bytes<KeySize>) -> u32 {
332 if bool::from(query.ct_eq(&A8Bytes::<KeySize>::default())) {
334 return OMAP_INVALID_KEY;
335 }
336 let mut result_code = OMAP_NOT_FOUND;
337 let hashes = self.hash_query(query);
338 self.oram1.access(hashes[0], |block| {
339 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] = block.as_mut_aligned_chunks();
340 for pair in pairs {
341 let (key, _): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) = pair.split();
342 let test = query.as_slice().ct_eq(key.as_slice());
343 key.cmov(test, &Default::default());
344 result_code.cmov(test, &OMAP_FOUND);
345 }
346 });
347 self.oram2.access(hashes[1], |block| {
348 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] = block.as_mut_aligned_chunks();
349 for pair in pairs {
350 let (key, _): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) = pair.split();
351 let test = query.as_slice().ct_eq(key.as_slice());
352 key.cmov(test, &Default::default());
353 result_code.cmov(test, &OMAP_FOUND);
354 }
355 });
356 result_code
357 }
358
359 fn vartime_write_extended(
396 &mut self,
397 query: &A8Bytes<KeySize>,
398 new_value: &A8Bytes<ValueSize>,
399 allow_overwrite: Choice,
400 allow_sideeffects_and_eviction: Choice,
401 ) -> u32 {
402 if bool::from(query.ct_eq(&A8Bytes::<KeySize>::default())) {
404 return OMAP_INVALID_KEY;
405 }
406
407 let mut result_code = OMAP_NOT_FOUND;
409
410 let mut evicted_key = A8Bytes::<KeySize>::default();
414 let mut evicted_val = A8Bytes::<ValueSize>::default();
415
416 let mut eviction_retries = MAX_EVICTION_RETRIES;
418 let mut eviction_from = Vec::<u64>::with_capacity(eviction_retries);
421 let mut eviction_indices = Vec::<usize>::with_capacity(eviction_retries);
424
425 let hashes = self.hash_query(query);
427
428 let rng = &mut self.rng;
430 let oram1 = &mut self.oram1;
431 let oram2 = &mut self.oram2;
432 oram1.access(hashes[0], |block1| {
433 oram2.access(hashes[1], |block2| {
434 let (block1_found, block1_empty_count) = Self::count_before_insert(query, block1);
438 let (block2_found, block2_empty_count) = Self::count_before_insert(query, block2);
439 debug_assert!(
440 !bool::from(block1_found & block2_found),
441 "key should not be found twice"
442 );
443
444 let found = block1_found | block2_found;
445 result_code.cmov(found, &OMAP_FOUND);
446
447 {
449 let condition = allow_sideeffects_and_eviction & (allow_overwrite | !found);
452 let write_to_block1 = !block2_found
459 & (block1_found | block1_empty_count.ct_gt(&block2_empty_count));
460
461 Self::insert_to_block(condition & write_to_block1, query, new_value, block1);
462 Self::insert_to_block(condition & !write_to_block1, query, new_value, block2);
463 }
464
465 if bool::from(
472 !found
473 & block1_empty_count.ct_eq(&0)
474 & block2_empty_count.ct_eq(&0)
475 & allow_sideeffects_and_eviction,
476 ) {
477 result_code = OMAP_OVERFLOW;
478
479 let random = rng.next_u32();
480 let index = (random % (Self::BUCKET_CAPACITY as u32)) as usize;
482
483 eviction_from.push(hashes[0]);
484 eviction_indices.push(index);
485
486 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] =
487 block1.as_mut_aligned_chunks();
488 let pair = &mut pairs[index];
489 let (key, val): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) = pair.split();
490 evicted_key = key.clone();
491 evicted_val = val.clone();
492 debug_assert!(bool::from(allow_sideeffects_and_eviction));
495 *key = query.clone();
496 *val = new_value.clone();
497 }
498 });
499 });
500
501 while result_code == OMAP_OVERFLOW {
503 if eviction_retries > 0 {
504 let last_evicted_from = eviction_from[eviction_from.len() - 1];
505
506 let next_oram = eviction_from.len() % 2;
510
511 let hashes = self.hash_query(&evicted_key);
512 debug_assert!(hashes[1 - next_oram] == last_evicted_from);
514 let dest = hashes[next_oram];
515
516 let rng = &mut self.rng;
518 let oram = if next_oram == 0 {
522 &mut self.oram1
523 } else {
524 &mut self.oram2
525 };
526
527 oram.access(dest, |block| {
528 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] =
529 block.as_mut_aligned_chunks();
530 debug_assert!(pairs.len() == Self::BUCKET_CAPACITY as usize);
531
532 let mut found_vacant = Choice::from(0);
534 for pair in pairs.iter_mut() {
535 let (key, val): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) =
536 pair.split();
537 debug_assert!(
538 key != &evicted_key,
539 "evicted key should not be present anywhere"
540 );
541 let is_vacant = key.ct_eq(&A8Bytes::<KeySize>::default());
542 debug_assert!(bool::from(allow_sideeffects_and_eviction));
545 let cond = !found_vacant & is_vacant;
546 key.cmov(cond, &evicted_key);
547 val.cmov(cond, &evicted_val);
548 found_vacant |= is_vacant;
549 }
550
551 if bool::from(found_vacant) {
554 result_code = OMAP_NOT_FOUND;
556 } else {
557 let index = (rng.next_u32() % (Self::BUCKET_CAPACITY as u32)) as usize;
559
560 let pair = &mut pairs[index];
561 let (key, val): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) =
562 pair.split();
563
564 debug_assert!(bool::from(allow_sideeffects_and_eviction));
568 core::mem::swap(key, &mut evicted_key);
569 core::mem::swap(val, &mut evicted_val);
570
571 eviction_from.push(dest);
572 eviction_indices.push(index);
573 }
574 });
575
576 eviction_retries = eviction_retries.wrapping_sub(1);
577 } else {
578 debug_assert!(eviction_from.len() == eviction_indices.len());
580 while !eviction_indices.is_empty() {
581 let next_oram = eviction_from.len() % 2;
585
586 let evicted_index = eviction_indices.pop().unwrap();
591 let evicted_from = eviction_from.pop().unwrap();
592 debug_assert!(
593 self.hash_query(&evicted_key)[1 - next_oram] == evicted_from,
594 "The evicted key doesn't hash to the spot we thought we evicted it from"
595 );
596 let oram = if next_oram == 0 {
600 &mut self.oram2
601 } else {
602 &mut self.oram1
603 };
604
605 oram.access(evicted_from, |block| {
606 let pairs: &mut [A8Bytes<Sum<KeySize, ValueSize>>] =
607 block.as_mut_aligned_chunks();
608 let pair = &mut pairs[evicted_index];
609 let (key, val): (&mut A8Bytes<KeySize>, &mut A8Bytes<ValueSize>) =
610 pair.split();
611
612 debug_assert!(bool::from(allow_sideeffects_and_eviction));
615 core::mem::swap(key, &mut evicted_key);
616 core::mem::swap(val, &mut evicted_val);
617 })
618 }
619
620 debug_assert!(&evicted_key == query, "After rolling back evictions, we didn't end up with the initially inserted item coming back");
621 return OMAP_OVERFLOW;
626 }
627 }
628
629 self.num_items += (result_code.ct_eq(&OMAP_NOT_FOUND) & allow_sideeffects_and_eviction)
631 .unwrap_u8() as u64;
632 result_code
633 }
634}
635
636pub struct CuckooHashTableCreator<BlockSize, RngType, OC>
639where
640 BlockSize: ArrayLength<u8>,
641 RngType: RngCore + CryptoRng + Send + Sync + 'static,
642 OC: ORAMCreator<BlockSize, RngType>,
643 OC::Output: ORAM<BlockSize> + Send + Sync + 'static,
644{
645 _block_size: PhantomData<fn() -> BlockSize>,
646 _rng_type: PhantomData<fn() -> RngType>,
647 _oc: PhantomData<fn() -> OC>,
648}
649
650impl<KeySize, ValueSize, BlockSize, RngType, OC> OMapCreator<KeySize, ValueSize, RngType>
651 for CuckooHashTableCreator<BlockSize, RngType, OC>
652where
653 KeySize: ArrayLength<u8> + Add<ValueSize> + PartialDiv<U8> + 'static,
654 ValueSize: ArrayLength<u8> + PartialDiv<U8> + 'static,
655 BlockSize: ArrayLength<u8> + PartialDiv<U8> + 'static,
656 RngType: RngCore + CryptoRng + Send + Sync + 'static,
657 OC: ORAMCreator<BlockSize, RngType>,
658 OC::Output: ORAM<BlockSize> + Send + Sync + 'static,
659 Sum<KeySize, ValueSize>: ArrayLength<u8> + Sub<KeySize, Output = ValueSize> + PartialDiv<U8>,
660{
661 type Output = CuckooHashTable<KeySize, ValueSize, BlockSize, RngType, OC::Output>;
662
663 fn create<M: 'static + FnMut() -> RngType>(
664 size: u64,
665 stash_size: usize,
666 rng_maker: M,
667 ) -> Self::Output {
668 Self::Output::new::<OC, M>(size, stash_size, rng_maker)
669 }
670}
671
672#[cfg(test)]
673mod testing {
674 use super::*;
675 use mc_oblivious_ram::PathORAM4096Z4Creator;
676 use mc_oblivious_traits::{
677 rng_maker, testing, HeapORAMStorageCreator, OMapCreator, OMAP_FOUND, OMAP_NOT_FOUND,
678 };
679 use test_helper::{run_with_several_seeds, RngType};
680 use typenum::{U1024, U8};
681
682 extern crate std;
683
684 const STASH_SIZE: usize = 16;
685
686 type ORAMCreatorZ4 = PathORAM4096Z4Creator<RngType, HeapORAMStorageCreator>;
687 type CuckooCreatorZ4 = CuckooHashTableCreator<U1024, RngType, ORAMCreatorZ4>;
688
689 fn a8_8<N: ArrayLength<u8>>(src: u8) -> A8Bytes<N> {
692 let mut result = A8Bytes::<N>::default();
693 for byte in result.as_mut_slice() {
694 *byte = src;
695 }
696 result
697 }
698
699 #[test]
700 fn sanity_check_omap_z4_4() {
701 run_with_several_seeds(|rng| {
702 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
704 4,
705 STASH_SIZE,
706 rng_maker(rng),
707 );
708
709 let mut temp = A8Bytes::<U8>::default();
710
711 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(1), &mut temp));
712 assert_eq!(
713 OMAP_NOT_FOUND,
714 omap.vartime_write(&a8_8(1), &a8_8(2), 0.into())
715 );
716 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
717 assert_eq!(&temp, &a8_8(2));
718 assert_eq!(OMAP_FOUND, omap.vartime_write(&a8_8(1), &a8_8(3), 1.into()));
719 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
720 assert_eq!(&temp, &a8_8(3));
721
722 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(2), &mut temp));
723 assert_eq!(
724 &temp,
725 &a8_8(3),
726 "omap.read must not modify the output on not_found"
727 );
728 assert_eq!(
729 OMAP_NOT_FOUND,
730 omap.vartime_write(&a8_8(2), &a8_8(20), 1.into())
731 );
732 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
733 assert_eq!(&temp, &a8_8(20));
734 assert_eq!(
735 OMAP_FOUND,
736 omap.vartime_write(&a8_8(2), &a8_8(30), 0.into())
737 );
738 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
739 assert_eq!(
740 &temp,
741 &a8_8(20),
742 "omap.write must not modify when overwrite is disallowed"
743 );
744 assert_eq!(
745 OMAP_FOUND,
746 omap.vartime_write(&a8_8(2), &a8_8(30), 1.into())
747 );
748 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
749 assert_eq!(&temp, &a8_8(30));
750 })
751 }
752
753 #[test]
754 fn sanity_check_omap_z4_256() {
755 run_with_several_seeds(|rng| {
756 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
758 256,
759 STASH_SIZE,
760 rng_maker(rng),
761 );
762
763 let mut temp = A8Bytes::<U8>::default();
764
765 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(1), &mut temp));
766 assert_eq!(
767 OMAP_NOT_FOUND,
768 omap.vartime_write(&a8_8(1), &a8_8(2), 0.into())
769 );
770 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
771 assert_eq!(&temp, &a8_8(2));
772 assert_eq!(OMAP_FOUND, omap.vartime_write(&a8_8(1), &a8_8(3), 1.into()));
773 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
774 assert_eq!(&temp, &a8_8(3));
775
776 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(2), &mut temp));
777 assert_eq!(
778 &temp,
779 &a8_8(3),
780 "omap.read must not modify the output on not_found"
781 );
782 assert_eq!(
783 OMAP_NOT_FOUND,
784 omap.vartime_write(&a8_8(2), &a8_8(20), 1.into())
785 );
786 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
787 assert_eq!(&temp, &a8_8(20));
788 assert_eq!(
789 OMAP_FOUND,
790 omap.vartime_write(&a8_8(2), &a8_8(30), 0.into())
791 );
792 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
793 assert_eq!(
794 &temp,
795 &a8_8(20),
796 "omap.write must not modify when overwrite is disallowed"
797 );
798 assert_eq!(
799 OMAP_FOUND,
800 omap.vartime_write(&a8_8(2), &a8_8(30), 1.into())
801 );
802 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
803 assert_eq!(&temp, &a8_8(30));
804 })
805 }
806
807 #[test]
808 fn sanity_check_omap_access_api_z4_256() {
809 run_with_several_seeds(|rng| {
810 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
812 256,
813 STASH_SIZE,
814 rng_maker(rng),
815 );
816
817 omap.access(&a8_8(1), |code, _buffer| {
818 assert_eq!(code, OMAP_NOT_FOUND);
819 });
820
821 assert_eq!(
822 OMAP_NOT_FOUND,
823 omap.vartime_write(&a8_8(1), &a8_8(2), 0.into())
824 );
825
826 omap.access(&a8_8(1), |code, buffer| {
827 assert_eq!(code, OMAP_FOUND);
828 assert_eq!(buffer, &a8_8(2));
829 });
830
831 assert_eq!(OMAP_FOUND, omap.vartime_write(&a8_8(1), &a8_8(3), 1.into()));
832
833 omap.access(&a8_8(1), |code, buffer| {
834 assert_eq!(code, OMAP_FOUND);
835 assert_eq!(buffer, &a8_8(3));
836 });
837
838 omap.access(&a8_8(2), |code, _buffer| {
839 assert_eq!(code, OMAP_NOT_FOUND);
840 });
841
842 assert_eq!(
843 OMAP_NOT_FOUND,
844 omap.vartime_write(&a8_8(2), &a8_8(20), 1.into())
845 );
846
847 omap.access(&a8_8(2), |code, buffer| {
848 assert_eq!(code, OMAP_FOUND);
849 assert_eq!(buffer, &a8_8(20));
850 });
851
852 assert_eq!(
853 OMAP_FOUND,
854 omap.vartime_write(&a8_8(2), &a8_8(30), 0.into())
855 );
856
857 omap.access_and_remove(&a8_8(2), |code, buffer| {
858 assert_eq!(code, OMAP_FOUND);
859 assert_eq!(
860 buffer,
861 &a8_8(20),
862 "omap.write must not modify when overwrite is disallowed"
863 );
864 Choice::from(0)
865 });
866
867 omap.access_and_remove(&a8_8(2), |code, buffer| {
868 assert_eq!(code, OMAP_FOUND);
869 assert_eq!(
870 buffer,
871 &a8_8(20),
872 "omap.access_and_remove should not delete when delete is false"
873 );
874 Choice::from(0)
875 });
876
877 assert_eq!(
878 OMAP_FOUND,
879 omap.vartime_write(&a8_8(2), &a8_8(30), 1.into())
880 );
881
882 omap.access_and_remove(&a8_8(2), |code, buffer| {
883 assert_eq!(code, OMAP_FOUND);
884 assert_eq!(
885 buffer,
886 &a8_8(30),
887 "omap.write must modify when overwrite is allowed"
888 );
889 Choice::from(1)
890 });
891
892 omap.access(&a8_8(2), |code, buffer| {
893 assert_eq!(
894 code, OMAP_NOT_FOUND,
895 "omap.access_and_remove should delete when delete is true"
896 );
897 assert_eq!(
898 buffer,
899 &a8_8(0),
900 "when data is not present, access should give us all zeroes buffer"
901 );
902 });
903 })
904 }
905
906 #[test]
907 fn sanity_check_omap_z4_524288() {
908 run_with_several_seeds(|rng| {
909 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
911 524288,
912 STASH_SIZE,
913 rng_maker(rng),
914 );
915
916 let mut temp = A8Bytes::<U8>::default();
917
918 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(1), &mut temp));
919 assert_eq!(
920 OMAP_NOT_FOUND,
921 omap.vartime_write(&a8_8(1), &a8_8(2), 0.into())
922 );
923 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
924 assert_eq!(&temp, &a8_8(2));
925 assert_eq!(OMAP_FOUND, omap.vartime_write(&a8_8(1), &a8_8(3), 1.into()));
926 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
927 assert_eq!(&temp, &a8_8(3));
928
929 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(2), &mut temp));
930 assert_eq!(
931 &temp,
932 &a8_8(3),
933 "omap.read must not modify the output on not_found"
934 );
935 assert_eq!(
936 OMAP_NOT_FOUND,
937 omap.vartime_write(&a8_8(2), &a8_8(20), 1.into())
938 );
939 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
940 assert_eq!(&temp, &a8_8(20));
941 assert_eq!(
942 OMAP_FOUND,
943 omap.vartime_write(&a8_8(2), &a8_8(30), 0.into())
944 );
945 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
946 assert_eq!(
947 &temp,
948 &a8_8(20),
949 "omap.write must not modify when overwrite is disallowed"
950 );
951 assert_eq!(
952 OMAP_FOUND,
953 omap.vartime_write(&a8_8(2), &a8_8(30), 1.into())
954 );
955 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
956 assert_eq!(&temp, &a8_8(30));
957 })
958 }
959
960 #[test]
961 fn sanity_check_omap_z4_2097152() {
962 run_with_several_seeds(|rng| {
963 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
965 2097152,
966 STASH_SIZE,
967 rng_maker(rng),
968 );
969
970 let mut temp = A8Bytes::<U8>::default();
971
972 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(1), &mut temp));
973 assert_eq!(
974 OMAP_NOT_FOUND,
975 omap.vartime_write(&a8_8(1), &a8_8(2), 1.into())
976 );
977 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
978 assert_eq!(&temp, &a8_8(2));
979 assert_eq!(OMAP_FOUND, omap.vartime_write(&a8_8(1), &a8_8(3), 1.into()));
980 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
981 assert_eq!(&temp, &a8_8(3));
982
983 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(2), &mut temp));
984 assert_eq!(
985 &temp,
986 &a8_8(3),
987 "omap.read must not modify the output on not_found"
988 );
989 assert_eq!(
990 OMAP_NOT_FOUND,
991 omap.vartime_write(&a8_8(2), &a8_8(20), 1.into())
992 );
993 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
994 assert_eq!(&temp, &a8_8(20));
995 assert_eq!(
996 OMAP_FOUND,
997 omap.vartime_write(&a8_8(2), &a8_8(30), 0.into())
998 );
999 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
1000 assert_eq!(
1001 &temp,
1002 &a8_8(20),
1003 "omap.write must not modify when overwrite is disallowed"
1004 );
1005 assert_eq!(
1006 OMAP_FOUND,
1007 omap.vartime_write(&a8_8(2), &a8_8(30), 1.into())
1008 );
1009 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
1010 assert_eq!(&temp, &a8_8(30));
1011 })
1012 }
1013
1014 #[test]
1015 #[cfg(not(debug_assertions))]
1016 fn sanity_check_omap_z4_262144() {
1017 run_with_several_seeds(|rng| {
1018 use typenum::{U16, U280};
1019 let mut omap = <CuckooCreatorZ4 as OMapCreator<U16, U280, RngType>>::create(
1021 262144,
1022 STASH_SIZE,
1023 rng_maker(rng),
1024 );
1025
1026 let mut temp = A8Bytes::<U280>::default();
1027
1028 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(1), &mut temp));
1029 assert_eq!(
1030 OMAP_NOT_FOUND,
1031 omap.vartime_write(&a8_8(1), &a8_8(2), 1.into())
1032 );
1033 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
1034 assert_eq!(&temp, &a8_8(2));
1035 assert_eq!(OMAP_FOUND, omap.vartime_write(&a8_8(1), &a8_8(3), 1.into()));
1036 assert_eq!(OMAP_FOUND, omap.read(&a8_8(1), &mut temp));
1037 assert_eq!(&temp, &a8_8(3));
1038
1039 assert_eq!(OMAP_NOT_FOUND, omap.read(&a8_8(2), &mut temp));
1040 assert_eq!(
1041 &temp,
1042 &a8_8(3),
1043 "omap.read must not modify the output on not_found"
1044 );
1045 assert_eq!(
1046 OMAP_NOT_FOUND,
1047 omap.vartime_write(&a8_8(2), &a8_8(20), 1.into())
1048 );
1049 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
1050 assert_eq!(&temp, &a8_8(20));
1051 assert_eq!(
1052 OMAP_FOUND,
1053 omap.vartime_write(&a8_8(2), &a8_8(30), 0.into())
1054 );
1055 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
1056 assert_eq!(
1057 &temp,
1058 &a8_8(20),
1059 "omap.write must not modify when overwrite is disallowed"
1060 );
1061 assert_eq!(
1062 OMAP_FOUND,
1063 omap.vartime_write(&a8_8(2), &a8_8(30), 1.into())
1064 );
1065 assert_eq!(OMAP_FOUND, omap.read(&a8_8(2), &mut temp));
1066 assert_eq!(&temp, &a8_8(30));
1067 })
1068 }
1069
1070 #[test]
1072 fn exercise_omap_two_choice_path_oram_z4_256() {
1073 run_with_several_seeds(|rng| {
1074 let mut maker = rng_maker(rng);
1075 let mut rng = maker();
1076 let mut omap =
1078 <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(256, STASH_SIZE, maker);
1079 testing::exercise_omap(200, &mut omap, &mut rng);
1080 });
1081
1082 run_with_several_seeds(|rng| {
1083 let mut maker = rng_maker(rng);
1084 let mut rng = maker();
1085 let mut omap =
1086 <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(256, STASH_SIZE, maker);
1087 testing::exercise_omap_counter_table(200, &mut omap, &mut rng);
1088 });
1089 }
1090
1091 #[test]
1093 fn exercise_omap_two_choice_path_oram_z4_3072() {
1094 run_with_several_seeds(|rng| {
1095 use typenum::{U16, U280};
1096 let mut maker = rng_maker(rng);
1097 let _ = maker();
1098 let mut rng = maker();
1099 let mut omap = <CuckooCreatorZ4 as OMapCreator<U16, U280, RngType>>::create(
1101 3072, STASH_SIZE, maker,
1102 );
1103
1104 testing::exercise_omap(400, &mut omap, &mut rng);
1105 });
1106
1107 run_with_several_seeds(|rng| {
1108 use typenum::U16;
1109 let mut maker = rng_maker(rng);
1110 let _ = maker();
1111 let mut rng = maker();
1112 let mut omap =
1113 <CuckooCreatorZ4 as OMapCreator<U16, U8, RngType>>::create(3072, STASH_SIZE, maker);
1114
1115 testing::exercise_omap_counter_table(400, &mut omap, &mut rng);
1116 });
1117 }
1118
1119 #[test]
1121 #[cfg(not(debug_assertions))]
1122 fn exercise_omap_two_choice_path_oram_z4_65536() {
1123 run_with_several_seeds(|rng| {
1124 let mut maker = rng_maker(rng);
1125 let mut rng = maker();
1126 let mut omap =
1128 <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(65536, STASH_SIZE, maker);
1129
1130 testing::exercise_omap(2000, &mut omap, &mut rng);
1131 });
1132
1133 run_with_several_seeds(|rng| {
1134 let mut maker = rng_maker(rng);
1135 let mut rng = maker();
1136 let mut omap =
1137 <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(65536, STASH_SIZE, maker);
1138
1139 testing::exercise_omap_counter_table(2000, &mut omap, &mut rng);
1140 });
1141 }
1142
1143 #[test]
1145 #[cfg(not(debug_assertions))]
1146 fn exercise_omap_two_choice_path_oram_z4_524288() {
1147 run_with_several_seeds(|rng| {
1148 let mut maker = rng_maker(rng);
1149 let mut rng = maker();
1150 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
1152 524288, STASH_SIZE, maker,
1153 );
1154
1155 testing::exercise_omap(16_000, &mut omap, &mut rng);
1156 });
1157
1158 run_with_several_seeds(|rng| {
1159 let mut maker = rng_maker(rng);
1160 let mut rng = maker();
1161 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
1162 524288, STASH_SIZE, maker,
1163 );
1164
1165 testing::exercise_omap_counter_table(16_000, &mut omap, &mut rng);
1166 });
1167 }
1168
1169 #[test]
1171 #[cfg(not(debug_assertions))]
1172 fn exercise_omap_two_choice_path_oram_z4_2097152() {
1173 run_with_several_seeds(|rng| {
1174 let mut maker = rng_maker(rng);
1175 let mut rng = maker();
1176 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
1178 2097152, STASH_SIZE, maker,
1179 );
1180
1181 testing::exercise_omap(16_000, &mut omap, &mut rng);
1182 });
1183
1184 run_with_several_seeds(|rng| {
1185 let mut maker = rng_maker(rng);
1186 let mut rng = maker();
1187 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U8, RngType>>::create(
1188 2097152, STASH_SIZE, maker,
1189 );
1190
1191 testing::exercise_omap_counter_table(16_000, &mut omap, &mut rng);
1192 });
1193 }
1194
1195 #[test]
1197 #[cfg(not(debug_assertions))]
1198 fn omap_70_capacity_two_choice_path_oram_z4_16384() {
1199 use typenum::U248;
1200 run_with_several_seeds(|rng| {
1201 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U248, RngType>>::create(
1203 16384,
1204 STASH_SIZE,
1205 rng_maker(rng),
1206 );
1207
1208 let mut temp = A8Bytes::<U8>::default();
1209 let val = A8Bytes::<U248>::default();
1210 for idx in 1u64..(omap.capacity() * 7 / 10) {
1211 temp.copy_from_slice(&idx.to_le_bytes());
1212 let result_code = omap.vartime_write(&temp, &val, 0.into());
1213 assert!(OMAP_OVERFLOW != result_code);
1214 assert!(OMAP_NOT_FOUND == result_code);
1215 assert_eq!(omap.len(), idx);
1216 }
1217 });
1218 }
1219
1220 #[test]
1224 #[cfg(not(debug_assertions))]
1225 fn omap_overflow_semantics_two_choice_path_oram_z4_16384() {
1226 use std::println;
1227 use typenum::U248;
1228 run_with_several_seeds(|rng| {
1229 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U248, RngType>>::create(
1231 16384,
1232 STASH_SIZE,
1233 rng_maker(rng),
1234 );
1235
1236 let len = testing::test_omap_overflow(&mut omap);
1237 let cap = omap.capacity();
1238 let fraction = (len as f32) * 100f32 / (cap as f32);
1239 println!("Overflowed at {} / {} = {}%", len, cap, fraction);
1240 })
1241 }
1242
1243 #[test]
1247 #[cfg(not(debug_assertions))]
1248 fn omap_overflow_semantics_two_choice_path_oram_z4_32768() {
1249 use std::println;
1250 use typenum::U248;
1251 run_with_several_seeds(|rng| {
1252 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U248, RngType>>::create(
1254 32768,
1255 STASH_SIZE,
1256 rng_maker(rng),
1257 );
1258
1259 let len = testing::test_omap_overflow(&mut omap);
1260 let cap = omap.capacity();
1261 let fraction = (len as f32) * 100f32 / (cap as f32);
1262 println!("Overflowed at {} / {} = {}%", len, cap, fraction);
1263 })
1264 }
1265
1266 #[test]
1270 #[cfg(not(debug_assertions))]
1271 fn omap_overflow_semantics_two_choice_path_oram_z4_65536() {
1272 use std::println;
1273 use typenum::U248;
1274 let large_stash_size = STASH_SIZE * 2;
1275 run_with_several_seeds(|rng| {
1276 let mut omap = <CuckooCreatorZ4 as OMapCreator<U8, U248, RngType>>::create(
1278 65536,
1279 large_stash_size,
1280 rng_maker(rng),
1281 );
1282
1283 let len = testing::test_omap_overflow(&mut omap);
1284 let cap = omap.capacity();
1285 let fraction = (len as f32) * 100f32 / (cap as f32);
1286 println!("Overflowed at {} / {} = {}%", len, cap, fraction);
1287 })
1288 }
1289}