Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raph@generics #31

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 41 additions & 17 deletions src/centralized_telescope/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@ use super::proof::Proof;
use super::round::Round;
use super::setup::Setup;
use super::types::Hash;
use crate::utils::sample;
use crate::utils::types::Element;
use crate::utils::{sample, types::ToBytes};
use blake2::{Blake2s256, Digest};

/// Alba's proving algorithm, based on a depth-first search algorithm.
/// Calls up to setup.max_retries times the prove_index function and returns an empty
/// proof if no suitable candidate is found.
pub fn prove(setup: &Setup, prover_set: &[Element]) -> Option<Proof> {
pub fn prove<Element>(setup: &Setup, prover_set: &[Element]) -> Option<Proof<Element>>
Copy link
Collaborator

@djetchev djetchev Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrtoledo @tolikzinovyev @curiecrypt : if I think about it, the most general setting occurs when prove() is parameterized by a generic type and this generic type under the constraint that this type implemetns the traits Eq + Hash. Can we do something like this

fn prove<T: Eq + Hash>(...)

and similarly for any other function.

Copy link
Collaborator

@djetchev djetchev Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't want to write <T: Eq + Hash> in every function signature, you can first define

trait HashableEq: Eq + Hash {}

impl<T: Eq + Hash> HashableEq for T {}

This implements HashableEq for all types T that requires implementing both Eq and Hash and the second line ensures that any type that implements Eq and Hash automatically satisfies HashableEq, so you can now write

fn prove<T: HashableEq>(...) {}

Copy link
Collaborator Author

@rrtoledo rrtoledo Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djetchev This looks interesting, we would also need the Copy trait however.

I had a look at the Hash trait in the past, and I wanted to stay clear from it as it does not seem it was meant for cryptographic purposes (but DoS for hashmaps).

If you look at how it works, you need to use a Hasher in conjunction to it. However the default hasher is SipHasher13 which is not cryptographically secure but used only to prevent DOS in hashmaps.

I am afraid that by using this trait, we are encourage the use of this default hasher. Perhaps I am being overlycautious, and we can simply provide a different default hash.

where
Element: Copy + ToBytes,
{
// Run prove_index up to max_retries times
(0..setup.max_retries).find_map(|retry_counter| prove_index(setup, prover_set, retry_counter).1)
}

/// Alba's verification algorithm, returns true if the proof is
/// successfully verified, following the DFS verification, false otherwise.
pub fn verify(setup: &Setup, proof: &Proof) -> bool {
pub fn verify<Element>(setup: &Setup, proof: &Proof<Element>) -> bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem

where
Element: Copy + ToBytes,
{
if proof.search_counter >= setup.search_width
|| proof.retry_counter >= setup.max_retries
|| proof.element_sequence.len() as u64 != setup.proof_size
Expand All @@ -27,21 +32,22 @@ pub fn verify(setup: &Setup, proof: &Proof) -> bool {
}

// Initialise a round with given retry and search counters
let Some(mut round) = Round::new(proof.retry_counter, proof.search_counter, setup.set_size)
let Some(mut round) =
Round::<Element>::new(proof.retry_counter, proof.search_counter, setup.set_size)
else {
return false;
};

// For each element in the proof's sequence
for &element in &proof.element_sequence {
// Retrieve the bin id associated to this new element
let Some(bin_id) = bin_hash(setup, proof.retry_counter, element) else {
let Some(bin_id) = bin_hash(setup, proof.retry_counter, &element) else {
return false;
};
// Check that the new element was chosen correctly
// i.e. that we chose the new element such that its bin id equals the round id
if round.id == bin_id {
match Round::update(&round, element) {
match Round::<Element>::update(&round, element) {
Some(r) => round = r,
None => return false,
}
Expand All @@ -55,7 +61,14 @@ pub fn verify(setup: &Setup, proof: &Proof) -> bool {
/// Indexed proving algorithm, returns the total number of DFS calls done
/// to find a proof and Some(proof) if found within setup.dfs_bound calls of DFS,
/// otherwise None
fn prove_index(setup: &Setup, prover_set: &[Element], retry_counter: u64) -> (u64, Option<Proof>) {
fn prove_index<Element>(
setup: &Setup,
prover_set: &[Element],
retry_counter: u64,
) -> (u64, Option<Proof<Element>>)
where
Element: Copy + ToBytes,
{
// Initialise set_size bins
let mut bins: Vec<Vec<Element>> = Vec::with_capacity(setup.set_size as usize);
for _ in 0..setup.set_size {
Expand All @@ -67,7 +80,7 @@ fn prove_index(setup: &Setup, prover_set: &[Element], retry_counter: u64) -> (u6
.iter()
.take(setup.set_size.saturating_mul(2) as usize)
{
match bin_hash(setup, retry_counter, element) {
match bin_hash(setup, retry_counter, &element) {
Some(bin_index) => {
bins[bin_index as usize].push(element);
}
Expand Down Expand Up @@ -103,7 +116,15 @@ fn prove_index(setup: &Setup, prover_set: &[Element], retry_counter: u64) -> (u6
/// that is the first round candidate Round{retry_counter, search_counter, x_1, ..., x_u)} such that:
/// - ∀i ∈ [0, u-1], bin_hash(x_i+1) ∈ bins[round_hash(...round_hash(round_hash(v, t), x_1), ..., x_i)]
/// - proof_hash(round_hash(... round_hash((round_hash(v, t), x_1), ..., x_u)) = true
fn dfs(setup: &Setup, bins: &[Vec<Element>], round: &Round, mut step: u64) -> (u64, Option<Proof>) {
fn dfs<Element>(
setup: &Setup,
bins: &[Vec<Element>],
round: &Round<Element>,
mut step: u64,
) -> (u64, Option<Proof<Element>>)
where
Element: Copy + ToBytes,
{
// If current round comprises proof_size elements, returns it as Proof
if round.element_sequence.len() as u64 == setup.proof_size {
let proof_opt = if proof_hash(setup, round) {
Expand All @@ -125,7 +146,7 @@ fn dfs(setup: &Setup, bins: &[Vec<Element>], round: &Round, mut step: u64) -> (u
return (step, None);
}
// Update round with such element
if let Some(r) = Round::update(round, element) {
if let Some(r) = Round::<Element>::update(round, element) {
// Run DFS on updated round, incrementing step
let (dfs_calls, proof_opt) = dfs(setup, bins, &r, step.saturating_add(1));
// Returns proof if found
Expand All @@ -141,18 +162,22 @@ fn dfs(setup: &Setup, bins: &[Vec<Element>], round: &Round, mut step: u64) -> (u
}

/// Oracle producing a uniformly random value in [0, set_size[ used for prehashing S_p
fn bin_hash(setup: &Setup, retry_counter: u64, element: Element) -> Option<u64> {
fn bin_hash<Element>(setup: &Setup, retry_counter: u64, element: &Element) -> Option<u64>
where
Element: ToBytes,
{
let retry_bytes: [u8; 8] = retry_counter.to_be_bytes();
let element_bytes = element.to_be_bytes();
let mut hasher = Blake2s256::new();
hasher.update(b"Telescope-bin_hash");
hasher.update(retry_bytes);
hasher.update(element);
hasher.update(element_bytes);
let digest: Hash = hasher.finalize().into();
sample::sample_uniform(&digest, setup.set_size)
}

/// Oracle defined as Bernoulli(q) returning 1 with probability q and 0 otherwise
fn proof_hash(setup: &Setup, r: &Round) -> bool {
fn proof_hash<Element>(setup: &Setup, r: &Round<Element>) -> bool {
let mut hasher = Blake2s256::new();
hasher.update(b"Telescope-proof_hash");
hasher.update(r.hash);
Expand All @@ -165,7 +190,6 @@ mod tests {
use super::*;
use crate::centralized_telescope::{init, params::Params};
use crate::utils::test_utils::gen_items;
use crate::utils::types::DATA_LENGTH;
use rand_chacha::ChaCha20Rng;
use rand_core::{RngCore, SeedableRng};

Expand All @@ -182,7 +206,7 @@ mod tests {
};
for _t in 0..nb_tests {
let seed = rng.next_u32().to_be_bytes().to_vec();
let s_p = gen_items::<DATA_LENGTH>(&seed, set_size);
let s_p = gen_items::<32>(&seed, set_size);
let setup = init::make_setup(&params);
let proof = prove(&setup, &s_p).unwrap();
assert!(verify(&setup, &proof.clone()));
Expand All @@ -201,7 +225,7 @@ mod tests {
};
assert!(!verify(&setup, &proof_v));
// Checking that the proof fails when no elements are included
let proof_item = Proof {
let proof_item: Proof<[u8; 32]> = Proof {
retry_counter: proof.retry_counter,
search_counter: proof.search_counter,
element_sequence: Vec::new(),
Expand Down
4 changes: 1 addition & 3 deletions src/centralized_telescope/proof.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! ALBA's Proof structure

use crate::utils::types::Element;

/// Alba proof
#[derive(Debug, Clone)]
pub struct Proof {
pub struct Proof<Element> {
/// Numbers of retries done to find the proof
pub retry_counter: u64,
/// Index of the searched subtree to find the proof
Expand Down
14 changes: 9 additions & 5 deletions src/centralized_telescope/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

use super::types::Hash;
use crate::utils::sample;
use crate::utils::types::Element;
use crate::utils::types::ToBytes;
use blake2::{Blake2s256, Digest};

/// Round parameters
#[derive(Debug, Clone)]
pub struct Round {
pub struct Round<Element> {
/// Numbers of retries done so far
pub retry_counter: u64,
/// Index of the current subtree being searched
Expand All @@ -22,13 +22,16 @@ pub struct Round {
pub set_size: u64,
}

impl Round {
impl<Element> Round<Element>
where
Element: Copy + ToBytes,
{
/// Output a round from retry and search counters as well as set_size
/// Initilialises the hash with round_hash(retry_counter || search_bytes)
/// and random value as oracle(round_hash(retry_counter || search_bytes), set_size)
pub(super) fn new(retry_counter: u64, search_counter: u64, set_size: u64) -> Option<Self> {
let retry_bytes: [u8; 8] = retry_counter.to_be_bytes();
let search_bytes: [u8; 8] = search_counter.to_be_bytes();
let search_bytes = search_counter.to_be_bytes();
let (hash, id_opt) = Self::round_hash(&retry_bytes, &search_bytes, set_size);
id_opt.map(|id| Self {
retry_counter,
Expand All @@ -45,7 +48,8 @@ impl Round {
pub(super) fn update(r: &Self, element: Element) -> Option<Self> {
let mut element_sequence = r.element_sequence.clone();
element_sequence.push(element);
let (hash, id_opt) = Self::round_hash(&r.hash, &element, r.set_size);
let element_bytes = element.to_be_bytes().as_ref().to_vec();
let (hash, id_opt) = Self::round_hash(&r.hash, &element_bytes, r.set_size);
id_opt.map(|id| Self {
retry_counter: r.retry_counter,
search_counter: r.search_counter,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#![doc = include_str!("../README.md")]

//! An implementation of Approximate Lower Bound Arguments
//! (ALBA, <https://eprint.iacr.org/2023/1655.pdf>).

Check warning on line 5 in src/lib.rs

View workflow job for this annotation

GitHub Actions / build

unresolved link to `!IMPORTANT`

Check warning on line 5 in src/lib.rs

View workflow job for this annotation

GitHub Actions / build

this URL is not a hyperlink
mod utils;
pub mod utils;

pub mod centralized_telescope;
pub mod simple_lottery;
Expand Down
4 changes: 3 additions & 1 deletion src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Shared functions, traits and test helpers

pub(crate) mod sample;

pub(crate) mod types;
pub mod types;

#[cfg(test)]
pub(crate) mod test_utils;
40 changes: 38 additions & 2 deletions src/utils/types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
pub(crate) const DATA_LENGTH: usize = 32;
//! Trait to make ALBA input hashable via its bytes representation

pub(crate) type Element = [u8; DATA_LENGTH];
use std::mem;

/// Trait to represent input as bytes
pub trait ToBytes {
/// Reference on unsized array of u8
type ByteArray: AsRef<[u8]>;
/// Returns the byte representation of the element
fn to_be_bytes(&self) -> Self::ByteArray;
}

macro_rules! impl_ToBytes {
(for $($t:ty),+) => {
$(impl ToBytes for $t {
type ByteArray = [u8; mem::size_of::<Self>()];

fn to_be_bytes(&self) -> Self::ByteArray {
return Self::to_be_bytes(*self);
}
})*
}
}

impl_ToBytes!(for u8, u16, u32, u64, u128);
impl_ToBytes!(for i8, i16, i32, i64, i128);

macro_rules! impl_u8ToBytes {
(for $($t:ty),+) => {
$(impl ToBytes for $t {
type ByteArray = Self;

fn to_be_bytes(&self) -> Self::ByteArray {
*self
}
})*
}
}
impl_u8ToBytes!(for [u8;32], [u8;64], [u8;128]);
Loading