forked from mobilecoinfoundation/mc-oblivious
-
Notifications
You must be signed in to change notification settings - Fork 0
/
evictor.rs
261 lines (242 loc) · 10.1 KB
/
evictor.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// Copyright (c) 2018-2022 The MobileCoin Foundation
//! Evictor functions for ORAM
//!
//! A module containing different eviction strategies for tree based ORAMs which
//! include path ORAM and circuit ORAM. These strategies will be used for
//! evicting stash elements to the tree ORAM.
use crate::path_oram::{BranchCheckout, MetaSize};
use aligned_cmov::{
typenum::{PartialDiv, Prod, Unsigned, U64, U8},
A64Bytes, A8Bytes, ArrayLength,
};
use balanced_tree_index::TreeIndex;
use core::ops::Mul;
use rand_core::{CryptoRng, RngCore};
/// Selects branches in reverse lexicographic order, where the most significant
/// digit of the branch is always 1, corresponding to the leaf node that
/// represents that branch. Reverse lexicographic ordering only on the
/// `num_bits_to_be_reversed` E.g. for a depth of 3:
/// 100, 110, 101, 111
/// `num_bits_to_be_reversed` corresponds to the number of possible branches
/// that need to be explored, and is 1 less than the number of bits in the leaf
/// node. `iteration` i corresponds to the ith branch in reverse lexicographic
/// order.
fn deterministic_get_next_branch_to_evict(num_bits_to_be_reversed: u32, iteration: u64) -> u64 {
// Return 1 if the number of bits needed is 0. Calculation furtherdown would
// overflow, and shortcutting here does not leak information because the
// number of bits is structural information rather than query specific.
if num_bits_to_be_reversed == 0 {
return 1;
}
// This is the first index at which leafs exist, the most significant digit
// of all leafs is 1.
let leaf_significant_index: u64 = 1 << (num_bits_to_be_reversed);
let test_position: u64 =
((iteration).reverse_bits() >> (64 - num_bits_to_be_reversed)) % leaf_significant_index;
leaf_significant_index + test_position
}
/// An evictor that implements a random branch selection and the path oram
/// eviction strategy
pub struct PathOramRandomEvictor<RngType>
where
RngType: RngCore + CryptoRng + Send + Sync + 'static,
{
rng: RngType,
number_of_additional_branches_to_evict: usize,
branches_evicted: u64,
tree_height: u32,
}
impl<RngType> BranchSelector for PathOramRandomEvictor<RngType>
where
RngType: RngCore + CryptoRng + Send + Sync + 'static,
{
fn get_next_branch_to_evict(&mut self) -> u64 {
self.branches_evicted += 1;
1u64.random_child_at_height(self.tree_height, &mut self.rng)
}
fn get_number_of_additional_branches_to_evict(&self) -> usize {
self.number_of_additional_branches_to_evict
}
}
impl<ValueSize, Z, RngType> EvictionStrategy<ValueSize, Z> for PathOramRandomEvictor<RngType>
where
ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
RngType: RngCore + CryptoRng + Send + Sync + 'static,
{
/// Method that takes a branch and a stash and moves elements from the
/// stash into the branch.
fn evict_from_stash_to_branch(
&self,
stash_data: &mut [A64Bytes<ValueSize>],
stash_meta: &mut [A8Bytes<MetaSize>],
branch: &mut BranchCheckout<ValueSize, Z>,
) {
path_oram_eviction_strategy::<ValueSize, Z>(stash_data, stash_meta, branch);
}
}
/// An evictor that implements a deterministic branch selection in reverse
/// lexicographic order and using the path oram eviction strategy
pub struct PathOramDeterministicEvictor {
number_of_additional_branches_to_evict: usize,
branches_evicted: u64,
tree_height: u32,
tree_breadth: u64,
}
impl PathOramDeterministicEvictor {
/// Create a new deterministic branch selector that will select
/// `number_of_additional_branches_to_evict`: branches per access in
/// excess of branch with accessed element.
/// `tree height`: corresponds to the height of tree
pub fn new(number_of_additional_branches_to_evict: usize, tree_height: u32) -> Self {
Self {
number_of_additional_branches_to_evict,
tree_height,
tree_breadth: 2u64 ^ (tree_height as u64),
branches_evicted: 0,
}
}
}
impl BranchSelector for PathOramDeterministicEvictor {
fn get_next_branch_to_evict(&mut self) -> u64 {
//The height of the root is 0, so the number of bits needed for the leaves is
// just the height
let iteration = self.branches_evicted;
self.branches_evicted = (self.branches_evicted + 1) % self.tree_breadth;
deterministic_get_next_branch_to_evict(self.tree_height, iteration)
}
fn get_number_of_additional_branches_to_evict(&self) -> usize {
self.number_of_additional_branches_to_evict
}
}
impl<ValueSize, Z> EvictionStrategy<ValueSize, Z> for PathOramDeterministicEvictor
where
ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
{
fn evict_from_stash_to_branch(
&self,
stash_data: &mut [A64Bytes<ValueSize>],
stash_meta: &mut [A8Bytes<MetaSize>],
branch: &mut BranchCheckout<ValueSize, Z>,
) {
path_oram_eviction_strategy::<ValueSize, Z>(stash_data, stash_meta, branch);
}
}
/// Eviction algorithm defined in path oram. Packs the branch and greedily
/// tries to evict everything from the stash into the checked out branch
fn path_oram_eviction_strategy<ValueSize, Z>(
stash_data: &mut [A64Bytes<ValueSize>],
stash_meta: &mut [A8Bytes<MetaSize>],
branch: &mut BranchCheckout<ValueSize, Z>,
) where
ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
{
branch.pack();
//Greedily place elements of the stash into the branch as close to the leaf as
// they can go.
for idx in 0..stash_data.len() {
branch.ct_insert(1.into(), &stash_data[idx], &mut stash_meta[idx]);
}
}
pub trait BranchSelector {
/// Returns the leaf index of the next branch to call
/// [EvictionStrategy::evict_from_stash_to_branch] on.
fn get_next_branch_to_evict(&mut self) -> u64;
/// Returns the number of branches to call
/// [EvictionStrategy::evict_from_stash_to_branch] on.
fn get_number_of_additional_branches_to_evict(&self) -> usize;
}
/// Evictor trait conceptually is a mechanism for moving stash elements into
/// the oram.
pub trait EvictionStrategy<ValueSize, Z>
where
ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
{
/// Method that takes a branch and a stash and moves elements from the
/// stash into the branch.
fn evict_from_stash_to_branch(
&self,
stash_data: &mut [A64Bytes<ValueSize>],
stash_meta: &mut [A8Bytes<MetaSize>],
branch: &mut BranchCheckout<ValueSize, Z>,
);
}
/// A factory which creates an Evictor
pub trait EvictorCreator<ValueSize, Z>
where
ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
{
type Output: EvictionStrategy<ValueSize, Z> + BranchSelector + Send + Sync + 'static;
/// Creates an eviction strategy
/// `height`: height of the tree eviction will be called on, impacts branch
/// selection.
fn create(&self, height: u32) -> Self::Output;
}
/// A factory which creates an PathOramDeterministicEvictor that evicts from the
/// stash into an additional `number_of_additional_branches_to_evict` branches
/// in addition to the currently checked out branch in reverse lexicographic
/// order.
pub struct PathOramDeterministicEvictorCreator {
number_of_additional_branches_to_evict: usize,
}
impl PathOramDeterministicEvictorCreator {
/// Create a factory for a deterministic branch selector that will evict
/// `number_of_additional_branches_to_evict` branches per access
pub fn new(number_of_additional_branches_to_evict: usize) -> Self {
Self {
number_of_additional_branches_to_evict,
}
}
}
impl<ValueSize, Z> EvictorCreator<ValueSize, Z> for PathOramDeterministicEvictorCreator
where
ValueSize: ArrayLength<u8> + PartialDiv<U8> + PartialDiv<U64>,
Z: Unsigned + Mul<ValueSize> + Mul<MetaSize>,
Prod<Z, ValueSize>: ArrayLength<u8> + PartialDiv<U8>,
Prod<Z, MetaSize>: ArrayLength<u8> + PartialDiv<U8>,
{
type Output = PathOramDeterministicEvictor;
fn create(&self, height: u32) -> Self::Output {
PathOramDeterministicEvictor::new(self.number_of_additional_branches_to_evict, height)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
// Check that deterministic oram correctly chooses leaf values
fn test_deterministic_oram_get_branches_to_evict() {
let test_branch = deterministic_get_next_branch_to_evict(3, 0);
assert_eq!(test_branch, 8);
let test_branch = deterministic_get_next_branch_to_evict(3, 1);
assert_eq!(test_branch, 12);
let test_branch = deterministic_get_next_branch_to_evict(3, 2);
assert_eq!(test_branch, 10);
let test_branch = deterministic_get_next_branch_to_evict(3, 3);
assert_eq!(test_branch, 14);
let test_branch = deterministic_get_next_branch_to_evict(3, 4);
assert_eq!(test_branch, 9);
let test_branch = deterministic_get_next_branch_to_evict(3, 5);
assert_eq!(test_branch, 13);
let test_branch = deterministic_get_next_branch_to_evict(3, 6);
assert_eq!(test_branch, 11);
let test_branch = deterministic_get_next_branch_to_evict(3, 7);
assert_eq!(test_branch, 15);
let test_branch = deterministic_get_next_branch_to_evict(3, 8);
assert_eq!(test_branch, 8);
}
}