Skip to content

Commit

Permalink
update cuckoo hash
Browse files Browse the repository at this point in the history
  • Loading branch information
xiangxiecrypto committed Jan 17, 2024
1 parent 868ecc5 commit 8125e9e
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 101 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
//! Utils for the implementation of Ferret.
//! Implementation of Cuckoo hash.
use std::sync::Arc;

use mpz_core::{aes::AesEncryptor, Block};

use super::{CUCKOO_HASH_NUM, CUCKOO_TRIAL_NUM};

/// Errors that can occur when inserting Cuckoo hash.
/// Cuckoo hash insertion error
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum CuckooHashError {
#[error("invalid Cuckoo hash state: expected {0}")]
CuckooHashLoop(String),
}
#[error("insertion loops")]
pub struct CuckooHashError;

/// Errors that can occur when handling Buckets.
#[derive(Debug, thiserror::Error)]
Expand All @@ -26,32 +25,21 @@ pub enum BucketError {
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Item {
/// Value in the table.
pub value: u32,
pub(crate) value: u32,
/// The hash index during the insertion.
pub hash_index: usize,
pub(crate) hash_index: usize,
}

/// Implementation of Cuckoo hash. See [here](https://eprint.iacr.org/2019/1084.pdf) for reference.
pub struct CuckooHash<'a> {
/// The table contains the elements.
pub table: Vec<Option<Item>>,
/// The length of the table.
pub m: usize,
// The hash functions.
hashes: &'a [AesEncryptor; CUCKOO_HASH_NUM],
pub struct CuckooHash {
hashes: Arc<[AesEncryptor; CUCKOO_HASH_NUM]>,
}

impl<'a> CuckooHash<'a> {
impl CuckooHash {
/// Creates a new instance.
#[inline]
pub fn new(hashes: &'a [AesEncryptor; CUCKOO_HASH_NUM]) -> Self {
let table = Vec::default();

Self {
table,
m: 0,
hashes,
}
pub fn new(hashes: Arc<[AesEncryptor; CUCKOO_HASH_NUM]>) -> Self {
Self { hashes }
}

/// Insert elements into a Cuckoo hash table.
Expand All @@ -60,23 +48,22 @@ impl<'a> CuckooHash<'a> {
///
/// * `alphas` - A u32 vector being inserted.
#[inline]
pub fn insert(&mut self, alphas: &[u32]) -> Result<(), CuckooHashError> {
pub fn insert(&self, alphas: &[u32]) -> Result<Vec<Option<Item>>, CuckooHashError> {
// Always sets m = 1.5 * t. t is the length of `alphas`.
self.m = compute_table_length(alphas.len() as u32);
let m = compute_table_length(alphas.len() as u32);

// Allocates table.
self.table = vec![None; self.m];

let mut table = vec![None; m];
// Inserts each alpha.
for &value in alphas {
self.hash(value)?
self.hash(&mut table, value)?
}
Ok(())
Ok(table)
}

// Hash an element to a position with the current hash function.
#[inline]
fn hash(&mut self, value: u32) -> Result<(), CuckooHashError> {
fn hash(&self, table: &mut Vec<Option<Item>>, value: u32) -> Result<(), CuckooHashError> {
// The item consists of the value and hash index, starting from 0.
let mut item = Item {
value,
Expand All @@ -85,10 +72,10 @@ impl<'a> CuckooHash<'a> {

for _ in 0..CUCKOO_TRIAL_NUM {
// Computes the position of the value.
let pos = hash_to_index(&self.hashes[item.hash_index], self.m, item.value);
let pos = hash_to_index(&self.hashes[item.hash_index], table.len(), item.value);

// Inserts the value to position `pos`.
let opt_item = self.table[pos].replace(item);
let opt_item = table[pos].replace(item);

// If position `pos` is not empty before the above insertion, iteratively inserts the obtained value.
if let Some(x) = opt_item {
Expand All @@ -99,31 +86,23 @@ impl<'a> CuckooHash<'a> {
return Ok(());
}
}
Err(CuckooHashError::CuckooHashLoop(
"insertion loops".to_string(),
))
Err(CuckooHashError)
}
}

/// Implementation of Bucket. See step 3 in Figure 7
pub struct Bucket<'a> {
pub struct Bucket {
// The hash functions.
hashes: &'a [AesEncryptor; CUCKOO_HASH_NUM],
hashes: Arc<[AesEncryptor; CUCKOO_HASH_NUM]>,
// The number of buckets.
m: usize,
/// The buckets contain all the elements.
pub buckets: Vec<Vec<Item>>,
}

impl<'a> Bucket<'a> {
impl Bucket {
/// Creates a new instance.
#[inline]
pub fn new(hashes: &'a [AesEncryptor; CUCKOO_HASH_NUM], m: usize) -> Self {
Self {
hashes,
m,
buckets: Vec::default(),
}
pub fn new(hashes: Arc<[AesEncryptor; CUCKOO_HASH_NUM]>, m: usize) -> Self {
Self { hashes, m }
}

/// Inserts the input vector [0..n-1] into buckets.
Expand All @@ -132,21 +111,23 @@ impl<'a> Bucket<'a> {
///
/// * `n` - The length of the vector [0..n-1].
#[inline]
pub fn insert(&mut self, n: u32) {
self.buckets = vec![Vec::default(); self.m];
pub fn insert(&self, n: u32) -> Vec<Vec<Item>> {
let mut buckets = vec![Vec::default(); self.m];
// NOTE: the sorted step in Step 3.c can be removed.
for i in 0..n {
for (index, hash) in self.hashes.iter().enumerate() {
let pos = hash_to_index(hash, self.m, i);
self.buckets[pos].push(Item {
buckets[pos].push(Item {
value: i,
hash_index: index,
});
}
}
buckets
}
}

// Always sets m = 1.5 * t. t is the length of `alphas`.
// Always sets m = 1.5 * t. t is the length of `alphas`. See Section 7.1 Parameter Selection.
#[inline(always)]
pub(crate) fn compute_table_length(t: u32) -> usize {
(1.5 * (t as f32)).ceil() as usize
Expand All @@ -170,7 +151,8 @@ pub(crate) fn find_pos(bucket: &[Item], item: &Item) -> Result<usize, BucketErro

#[cfg(test)]
mod tests {
use crate::ferret::utils::find_pos;
use crate::ferret::cuckoo::find_pos;
use std::sync::Arc;

use super::{Bucket, CuckooHash};
use mpz_core::{aes::AesEncryptor, prg::Prg};
Expand All @@ -179,28 +161,28 @@ mod tests {
fn cockoo_hash_bucket_test() {
let mut prg = Prg::new();
const NUM: usize = 50;
let hashes = std::array::from_fn(|_| AesEncryptor::new(prg.random_block()));
let mut cuckoo = CuckooHash::new(&hashes);
let hashes = Arc::new(std::array::from_fn(|_| {
AesEncryptor::new(prg.random_block())
}));
let cuckoo = CuckooHash::new(hashes.clone());
let input: [u32; NUM] = std::array::from_fn(|i| i as u32);

cuckoo.insert(&input).unwrap();
let table = cuckoo.insert(&input).unwrap();

let mut bucket = Bucket::new(&hashes, cuckoo.m);
bucket.insert((2 * NUM) as u32);
let bucket = Bucket::new(hashes, table.len());
let buckets = bucket.insert((2 * NUM) as u32);

assert!(cuckoo
.table
assert!(table
.iter()
.zip(bucket.buckets.iter())
.zip(buckets.iter())
.all(|(value, bin)| match value {
Some(x) => bin.contains(x),
None => true,
}));

let _: Vec<usize> = cuckoo
.table
let _: Vec<usize> = table
.iter()
.zip(bucket.buckets.iter())
.zip(buckets.iter())
.map(|(value, bin)| {
if let Some(x) = value {
find_pos(bin, x).unwrap()
Expand Down
2 changes: 1 addition & 1 deletion ot/mpz-ot-core/src/ferret/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! An implementation of the [`Ferret`](https://eprint.iacr.org/2020/924.pdf) protocol.
pub mod cuckoo;
pub mod mpcot;
pub mod spcot;
pub mod utils;

/// Computational security parameter
pub const CSP: usize = 128;
Expand Down
2 changes: 1 addition & 1 deletion ot/mpz-ot-core/src/ferret/mpcot/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Errors that can occur when using the MPCOT protocol.
use crate::ferret::utils::{BucketError, CuckooHashError};
use crate::ferret::cuckoo::{BucketError, CuckooHashError};
/// Errors that can occur when using the MPCOT sender.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
Expand Down
4 changes: 2 additions & 2 deletions ot/mpz-ot-core/src/ferret/mpcot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod tests {
fn mpcot_general_test() {
let mut prg = Prg::new();
let delta = prg.random_block();
let mut ideal_spcot = IdealSpcot::init_with_delta(delta);
let mut ideal_spcot = IdealSpcot::new_with_delta(delta);

let sender = MpcotSender::new();
let receiver = MpcotReceiver::new();
Expand Down Expand Up @@ -96,7 +96,7 @@ mod tests {
fn mpcot_regular_test() {
let mut prg = Prg::new();
let delta = prg.random_block();
let mut ideal_spcot = IdealSpcot::init_with_delta(delta);
let mut ideal_spcot = IdealSpcot::new_with_delta(delta);

let sender = RegularSender::new();
let receiver = RegularReceiver::new();
Expand Down
21 changes: 11 additions & 10 deletions ot/mpz-ot-core/src/ferret/mpcot/receiver.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! MPCOT receiver for general indices.
use std::sync::Arc;

use crate::ferret::{
cuckoo::{find_pos, hash_to_index, Bucket, CuckooHash, Item},
mpcot::error::ReceiverError,
utils::{find_pos, hash_to_index, Bucket, CuckooHash, Item},
CUCKOO_HASH_NUM,
};
use mpz_core::{aes::AesEncryptor, prg::Prg, Block};
Expand Down Expand Up @@ -38,7 +39,7 @@ impl Receiver {
state: state::Extension {
counter: 0,
m: 0,
hashes,
hashes: Arc::new(hashes),
buckets: Vec::default(),
buckets_length: Vec::default(),
},
Expand Down Expand Up @@ -71,22 +72,22 @@ impl Receiver<state::Extension> {
));
}

let mut cuckoo = CuckooHash::new(&self.state.hashes);
let cuckoo = CuckooHash::new(self.state.hashes.clone());

// Inserts all the alpha's.
cuckoo.insert(alphas)?;
let table = cuckoo.insert(alphas)?;

self.state.m = cuckoo.m;
self.state.m = table.len();

let mut bucket = Bucket::new(&self.state.hashes, self.state.m);
let bucket = Bucket::new(self.state.hashes.clone(), self.state.m);

// Geneates the buckets.
bucket.insert(n);
let buckets = bucket.insert(n);

// Generates queries for SPCOT.
// See Step 4 in Figure 7.
let mut p = vec![];
for (alpha, bin) in cuckoo.table.iter().zip(bucket.buckets.iter()) {
for (alpha, bin) in table.iter().zip(buckets.iter()) {
if let Some(x) = alpha {
let pos = find_pos(bin, x)?;
if let Some(power) = (bin.len() + 1).checked_next_power_of_two() {
Expand All @@ -108,7 +109,7 @@ impl Receiver<state::Extension> {
}

// Stores the buckets.
self.state.buckets = bucket.buckets;
self.state.buckets = buckets;

Ok(p)
}
Expand Down Expand Up @@ -196,7 +197,7 @@ pub mod state {
/// Current length of Cuckoo hash table, will possibly be changed in each extension.
pub(super) m: usize,
/// The hashes to generate Cuckoo hash table.
pub(super) hashes: [AesEncryptor; CUCKOO_HASH_NUM],
pub(super) hashes: Arc<[AesEncryptor; CUCKOO_HASH_NUM]>,
/// The buckets contains all the hash values, will be cleared after each extension.
pub(super) buckets: Vec<Vec<Item>>,
/// The padded buckets length (power of 2).
Expand Down
15 changes: 8 additions & 7 deletions ot/mpz-ot-core/src/ferret/mpcot/sender.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! MPCOT sender for general indices.
use std::sync::Arc;

use crate::ferret::{
cuckoo::{compute_table_length, find_pos, hash_to_index, Bucket, Item},
mpcot::error::SenderError,
utils::{compute_table_length, find_pos, hash_to_index, Bucket, Item},
CUCKOO_HASH_NUM,
};
use mpz_core::{aes::AesEncryptor, prg::Prg, Block};
Expand Down Expand Up @@ -39,7 +40,7 @@ impl Sender {
delta,
counter: 0,
m: 0,
hashes,
hashes: Arc::new(hashes),
buckets: Vec::default(),
buckets_length: Vec::default(),
},
Expand Down Expand Up @@ -67,14 +68,14 @@ impl Sender<state::Extension> {
// Compute m = 1.5 * t.
self.state.m = compute_table_length(t);

let mut bucket = Bucket::new(&self.state.hashes, self.state.m);
let bucket = Bucket::new(self.state.hashes.clone(), self.state.m);

// Geneates the buckets.
bucket.insert(n);
let buckets = bucket.insert(n);

// Computes `log(length + 1)` of each bucket.
let mut bs = vec![];
for bin in bucket.buckets.iter() {
for bin in buckets.iter() {
if let Some(power) = (bin.len() + 1).checked_next_power_of_two() {
bs.push(power.ilog2() as usize);
self.state.buckets_length.push(power);
Expand All @@ -86,7 +87,7 @@ impl Sender<state::Extension> {
}

// Stores the buckets.
self.state.buckets = bucket.buckets;
self.state.buckets = buckets;

Ok(bs)
}
Expand Down Expand Up @@ -180,7 +181,7 @@ pub mod state {
/// Current length of Cuckoo hash table, will possibly be changed in each extension.
pub(super) m: usize,
/// The hashes to generate Cuckoo hash table.
pub(super) hashes: [AesEncryptor; CUCKOO_HASH_NUM],
pub(super) hashes: Arc<[AesEncryptor; CUCKOO_HASH_NUM]>,
/// The buckets contains all the hash values.
pub(super) buckets: Vec<Vec<Item>>,
/// The padded buckets length (power of 2).
Expand Down
4 changes: 2 additions & 2 deletions ot/mpz-ot-core/src/ferret/spcot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ mod tests {

#[test]
fn spcot_test() {
let mut ideal_cot = IdealCOT::init();
let mut ideal_cot = IdealCOT::new();
let sender = SpcotSender::new();
let receiver = SpcotReceiver::new();

let mut prg = Prg::new();
let sender_seed = prg.random_block();
let delta = ideal_cot.delta;
let delta = ideal_cot.delta();

let mut sender = sender.setup(delta, sender_seed);
let mut receiver = receiver.setup();
Expand Down
Loading

0 comments on commit 8125e9e

Please sign in to comment.