From acabb7761ba04ca2ec670967e9a2e658cf7c2db5 Mon Sep 17 00:00:00 2001 From: "sinu.eth" <65924192+sinui0@users.noreply.github.com> Date: Mon, 3 Mar 2025 11:53:20 -0800 Subject: [PATCH] chore: delete dead code (#705) --- crates/components/key-exchange/src/config.rs | 1 - crates/components/universal-hash/Cargo.toml | 45 -- .../src/ghash/ghash_core/core.rs | 150 ------- .../src/ghash/ghash_core/mod.rs | 395 ------------------ .../src/ghash/ghash_core/state.rs | 52 --- .../src/ghash/ghash_inner/config.rs | 19 - .../src/ghash/ghash_inner/ideal.rs | 183 -------- .../src/ghash/ghash_inner/mock.rs | 35 -- .../src/ghash/ghash_inner/mod.rs | 362 ---------------- .../universal-hash/src/ghash/mod.rs | 6 - crates/components/universal-hash/src/lib.rs | 50 --- 11 files changed, 1298 deletions(-) delete mode 100644 crates/components/key-exchange/src/config.rs delete mode 100644 crates/components/universal-hash/Cargo.toml delete mode 100644 crates/components/universal-hash/src/ghash/ghash_core/core.rs delete mode 100644 crates/components/universal-hash/src/ghash/ghash_core/mod.rs delete mode 100644 crates/components/universal-hash/src/ghash/ghash_core/state.rs delete mode 100644 crates/components/universal-hash/src/ghash/ghash_inner/config.rs delete mode 100644 crates/components/universal-hash/src/ghash/ghash_inner/ideal.rs delete mode 100644 crates/components/universal-hash/src/ghash/ghash_inner/mock.rs delete mode 100644 crates/components/universal-hash/src/ghash/ghash_inner/mod.rs delete mode 100644 crates/components/universal-hash/src/ghash/mod.rs delete mode 100644 crates/components/universal-hash/src/lib.rs diff --git a/crates/components/key-exchange/src/config.rs b/crates/components/key-exchange/src/config.rs deleted file mode 100644 index 8b1378917..000000000 --- a/crates/components/key-exchange/src/config.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/components/universal-hash/Cargo.toml b/crates/components/universal-hash/Cargo.toml deleted file mode 100644 index 722af62e3..000000000 --- a/crates/components/universal-hash/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "tlsn-universal-hash" -authors = ["TLSNotary Team"] -description = "A crate which implements different hash functions for two-party computation" -keywords = ["tls", "mpc", "2pc", "hash"] -categories = ["cryptography"] -license = "MIT OR Apache-2.0" -version = "0.1.0-alpha.8-pre" -edition = "2021" - -[features] -default = ["ghash", "ideal"] -ghash = [] -ideal = ["dep:ghash_rc"] - -[dependencies] -# tlsn -mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "b8ae7ac" } -mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "b8ae7ac", features = [ - "ideal", -] } -mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "b8ae7ac" } -mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "b8ae7ac" } - -ghash_rc = { package = "ghash", version = "0.5", optional = true } - -async-trait = { workspace = true } -thiserror = { workspace = true } -opaque-debug = { workspace = true } -tracing = { workspace = true } -derive_builder = { workspace = true } - -[dev-dependencies] -mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "b8ae7ac", features = [ - "test-utils", -] } -mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "b8ae7ac", features = [ - "ideal", -] } - -ghash_rc = { package = "ghash", version = "0.5" } -tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] } -rand_chacha = { workspace = true } -rand = { workspace = true } -generic-array = { workspace = true } diff --git a/crates/components/universal-hash/src/ghash/ghash_core/core.rs b/crates/components/universal-hash/src/ghash/ghash_core/core.rs deleted file mode 100644 index 29a4ececf..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_core/core.rs +++ /dev/null @@ -1,150 +0,0 @@ -use mpz_core::Block; -use mpz_fields::{gf2_128::Gf2_128, Field}; -use tracing::instrument; - -use super::{ - compute_missing_mul_shares, compute_new_add_shares, - state::{Finalized, Init, Intermediate, State}, - GhashError, -}; - -/// The core logic for the 2PC Ghash implementation. -/// -/// `GhashCore` will do all the necessary computation. -#[derive(Debug)] -pub(crate) struct GhashCore { - /// Inner state. - state: T, - /// Maximum number of message blocks we want to authenticate. - max_block_count: usize, -} - -impl GhashCore { - /// Creates a new `GhashCore`. - /// - /// # Arguments - /// - /// * `max_block_count` - Determines the maximum number of 128-bit message - /// blocks we want to authenticate. Panics if `max_block_count` is 0. - pub(crate) fn new(max_block_count: usize) -> Self { - assert!(max_block_count > 0); - - Self { - state: Init, - max_block_count, - } - } - - /// Transforms `self` into a `GhashCore`, holding - /// multiplicative shares of powers of `H`. - /// - /// Converts `H` into `H`, `H^3`, `H^5`, ... depending on - /// `self.max_block_count`. - #[instrument(level = "trace", skip_all)] - pub(crate) fn compute_odd_mul_powers(self, mul_share: Gf2_128) -> GhashCore { - let mut hashkey_powers = vec![mul_share]; - - compute_missing_mul_shares(&mut hashkey_powers, self.max_block_count); - - GhashCore { - state: Intermediate { - odd_mul_shares: hashkey_powers, - cached_add_shares: vec![], - }, - max_block_count: self.max_block_count, - } - } -} - -impl GhashCore { - /// Returns odd multiplicative shares of the hashkey. - /// - /// Takes into account cached additive shares, so that - /// multiplicative ones for which already an additive one - /// exists, are not returned. - #[instrument(level = "trace", skip_all)] - pub(crate) fn odd_mul_shares(&self) -> Vec { - // If we already have some cached additive sharings, we do not need to compute - // new powers. So we compute an offset to ignore them. We divide by 2 - // because `self.state.cached_add_shares` contain even and odd powers, - // while `self.state.odd_mul_shares` only have odd powers. - let offset = self.state.cached_add_shares.len() / 2; - - self.state.odd_mul_shares[offset..].to_vec() - } - - /// Adds new additive shares of hashkey powers by also computing the even - /// ones and transforms `self` into a `GhashCore`. - #[instrument(level = "trace", skip_all)] - pub(crate) fn add_new_add_shares( - mut self, - new_additive_odd_shares: &[Gf2_128], - ) -> GhashCore { - compute_new_add_shares(new_additive_odd_shares, &mut self.state.cached_add_shares); - - GhashCore { - state: Finalized { - add_shares: self.state.cached_add_shares, - odd_mul_shares: self.state.odd_mul_shares, - }, - max_block_count: self.max_block_count, - } - } -} - -impl GhashCore { - /// Returns the currently configured maximum message length. - pub(crate) fn get_max_blocks(&self) -> usize { - self.max_block_count - } - - /// Generates the GHASH output. - /// - /// Computes the 2PC additive share of the GHASH output. - #[instrument(level = "debug", skip_all, err)] - pub(crate) fn finalize(&self, message: &[Block]) -> Result { - if message.len() > self.max_block_count { - return Err(GhashError::InvalidMessageLength); - } - let offset = self.state.add_shares.len() - message.len(); - - let output: Block = message - .iter() - .zip(self.state.add_shares.iter().rev().skip(offset)) - .fold(Gf2_128::zero(), |acc, (block, share)| { - acc + Gf2_128::from(block.reverse_bits()) * *share - }) - .into(); - - Ok(output.reverse_bits()) - } - - /// Changes the maximum hashkey power. - /// - /// If we want to create a GHASH output for a new message, which is longer - /// than the old one, we need to compute the missing shares of the - /// powers of `H`. - #[instrument(level = "debug", skip(self))] - pub(crate) fn change_max_hashkey( - self, - new_highest_hashkey_power: usize, - ) -> GhashCore { - let mut present_odd_mul_shares = self.state.odd_mul_shares; - compute_missing_mul_shares(&mut present_odd_mul_shares, new_highest_hashkey_power); - - GhashCore { - state: Intermediate { - odd_mul_shares: present_odd_mul_shares, - cached_add_shares: self.state.add_shares, - }, - max_block_count: new_highest_hashkey_power, - } - } -} - -#[cfg(test)] -impl GhashCore { - pub(crate) fn state(&self) -> &T { - &self.state - } -} diff --git a/crates/components/universal-hash/src/ghash/ghash_core/mod.rs b/crates/components/universal-hash/src/ghash/ghash_core/mod.rs deleted file mode 100644 index c4eae87e5..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_core/mod.rs +++ /dev/null @@ -1,395 +0,0 @@ -//! This module implements the AES-GCM's GHASH function in a secure two-party -//! computation (2PC) setting. The parties start with their secret XOR shares of -//! H (the GHASH key) and at the end each gets their XOR share of the GHASH -//! output. The method is described here: . -//! -//! At first we will convert the XOR (additive) share of `H`, into a -//! multiplicative share. This allows us to compute all the necessary powers of -//! `H^n` locally. Note, that it is only required to compute the odd -//! multiplicative powers, because of free squaring. Then each of these -//! multiplicative shares will be converted back into additive shares. The even -//! additive shares can then locally be built by using the odd ones. This way, -//! we can batch nearly all oblivious transfers and reduce the round complexity -//! of the protocol. -//! -//! On the whole, we need a single additive-to-multiplicative (A2M) and `n/2`, -//! where `n` is the number of blocks of message, multiplicative-to-additive -//! (M2A) conversions. Finally, having additive shares of `H^n` for all needed -//! `n`, we can compute an additive share of the GHASH output. - -/// Contains the core logic for ghash. -mod core; - -/// Contains the different states. -pub(crate) mod state; - -pub(crate) use self::core::GhashCore; - -use mpz_fields::{compute_product_repeated, gf2_128::Gf2_128}; -use thiserror::Error; -use tracing::instrument; - -#[derive(Debug, Error)] -pub(crate) enum GhashError { - #[error("Message too long")] - InvalidMessageLength, -} - -/// Computes missing odd multiplicative shares of the hashkey powers. -/// -/// Checks if depending on the number of `needed` shares, we need more odd -/// multiplicative shares and computes them. Notice that we only need odd -/// multiplicative shares for the OT, because we can derive even additive shares -/// from odd additive shares, which we call free squaring. -/// -/// # Arguments -/// -/// * `present_odd_mul_shares` - Multiplicative odd shares already present. -/// * `needed` - How many powers we need including odd and -/// even. -#[instrument(level = "trace", skip(present_odd_mul_shares))] -fn compute_missing_mul_shares(present_odd_mul_shares: &mut Vec, needed: usize) { - // Divide by 2 and round up. - let needed_odd_powers: usize = needed / 2 + (needed & 1); - let present_odd_len = present_odd_mul_shares.len(); - - if needed_odd_powers > present_odd_len { - let h_squared = present_odd_mul_shares[0] * present_odd_mul_shares[0]; - compute_product_repeated( - present_odd_mul_shares, - h_squared, - needed_odd_powers - present_odd_len, - ); - } -} - -/// Computes new even (additive) shares from new odd (additive) shares and saves -/// both the new odd shares and the new even shares. -/// -/// This function implements the derivation of even additive shares from odd -/// additive shares, which we refer to as free squaring. Every additive share of -/// an even power of `H` can be computed without an OT interaction by squaring -/// the corresponding additive share of an odd power of `H`, e.g. if we have a -/// share of H^3, we can derive the share of H^6 by doing (H^3)^2. -/// -/// # Arguments -/// -/// * `new_add_odd_shares` - New odd additive shares we got as a result of doing -/// an OT on odd multiplicative shares. -/// * `add_shares` - All additive shares (even and odd) we already have. -/// This is a mutable reference to cached_add_shares in -/// [crate::ghash::state::Intermediate]. -#[instrument(level = "trace", skip_all)] -fn compute_new_add_shares(new_add_odd_shares: &[Gf2_128], add_shares: &mut Vec) { - for (odd_share, current_odd_power) in new_add_odd_shares - .iter() - .zip((add_shares.len() + 1..).step_by(2)) - { - // `add_shares` always have an even number of shares so we simply add the next - // odd share. - add_shares.push(*odd_share); - - // Now we need to compute the next even share and add it. - // Note that the n-th index corresponds to the (n+1)-th power, e.g. - // add_shares[4] is the share of H^5. - let mut base_share = add_shares[current_odd_power / 2]; - base_share = base_share * base_share; - add_shares.push(base_share); - } -} - -#[cfg(test)] -mod tests { - use generic_array::GenericArray; - use ghash_rc::{ - universal_hash::{KeyInit, UniversalHash}, - GHash, - }; - use mpz_core::Block; - use mpz_fields::{gf2_128::Gf2_128, Field}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha12Rng; - - use super::{ - compute_missing_mul_shares, compute_new_add_shares, compute_product_repeated, - state::{Finalized, Intermediate}, - GhashCore, - }; - - #[test] - fn test_ghash_product_sharing() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // The Ghash key. - let h: Gf2_128 = rng.gen(); - let message = Block::random_vec(&mut rng, 10); - let message_len = message.len(); - let number_of_powers_needed: usize = message_len / 2 + (message_len & 1); - - let (sender, receiver) = setup_ghash_to_intermediate_state(h, message_len); - - let mut powers_h = vec![h]; - compute_product_repeated(&mut powers_h, h * h, number_of_powers_needed); - - // Length check. - assert_eq!(sender.state().odd_mul_shares.len(), number_of_powers_needed); - assert_eq!( - receiver.state().odd_mul_shares.len(), - number_of_powers_needed - ); - - // Product check. - for (k, (sender_share, receiver_share)) in std::iter::zip( - sender.state().odd_mul_shares.iter(), - receiver.state().odd_mul_shares.iter(), - ) - .enumerate() - { - assert_eq!(*sender_share * *receiver_share, powers_h[k]); - } - } - - #[test] - fn test_ghash_sum_sharing() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // The Ghash key. - let h: Gf2_128 = rng.gen(); - let message = Block::random_vec(&mut rng, 10); - let message_len = message.len(); - - let (sender, receiver) = setup_ghash_to_intermediate_state(h, message_len); - let (sender, receiver) = ghash_to_finalized(sender, receiver); - - let mut powers_h = vec![h]; - compute_product_repeated(&mut powers_h, h, message_len); - - // Length check. - assert_eq!( - sender.state().add_shares.len(), - message_len + (message_len & 1) - ); - assert_eq!( - receiver.state().add_shares.len(), - message_len + (message_len & 1) - ); - - // Sum check. - for (k, item) in powers_h.iter().enumerate().take(message_len) { - assert_eq!( - sender.state().add_shares[k] + receiver.state().add_shares[k], - *item - ); - } - } - - #[test] - fn test_ghash_output() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // The Ghash key. - let h: Gf2_128 = rng.gen(); - let message = Block::random_vec(&mut rng, 10); - - let (sender, receiver) = setup_ghash_to_intermediate_state(h, message.len()); - let (sender, receiver) = ghash_to_finalized(sender, receiver); - - let output = sender.finalize(&message).unwrap() ^ receiver.finalize(&message).unwrap(); - - assert_eq!( - output, - ghash_reference_impl(h.to_inner().reverse_bits(), &message) - ); - } - - #[test] - fn test_ghash_change_message_short() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // The Ghash key. - let h: Gf2_128 = rng.gen(); - let message = Block::random_vec(&mut rng, 10); - - let (sender, receiver) = setup_ghash_to_intermediate_state(h, message.len()); - let (sender, receiver) = ghash_to_finalized(sender, receiver); - - let message_short = Block::random_vec(&mut rng, 5); - - let (sender, receiver) = ( - sender.change_max_hashkey(message_short.len()), - receiver.change_max_hashkey(message_short.len()), - ); - - let (sender, receiver) = ghash_to_finalized(sender, receiver); - - let output = - sender.finalize(&message_short).unwrap() ^ receiver.finalize(&message_short).unwrap(); - - assert_eq!( - output, - ghash_reference_impl(h.to_inner().reverse_bits(), &message_short) - ); - } - - #[test] - fn test_ghash_change_message_long() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // The Ghash key. - let h: Gf2_128 = rng.gen(); - let message = Block::random_vec(&mut rng, 10); - - let (sender, receiver) = setup_ghash_to_intermediate_state(h, message.len()); - let (sender, receiver) = ghash_to_finalized(sender, receiver); - - let message_long = Block::random_vec(&mut rng, 20); - - let (sender, receiver) = ( - sender.change_max_hashkey(message_long.len()), - receiver.change_max_hashkey(message_long.len()), - ); - - let (sender, receiver) = ghash_to_finalized(sender, receiver); - let output = - sender.finalize(&message_long).unwrap() ^ receiver.finalize(&message_long).unwrap(); - - assert_eq!( - output, - ghash_reference_impl(h.to_inner().reverse_bits(), &message_long) - ); - } - - #[test] - fn test_compute_missing_mul_shares() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - let h: Gf2_128 = rng.gen(); - let mut powers: Vec = vec![h]; - compute_product_repeated(&mut powers, h * h, rng.gen_range(16..128)); - - let powers_len = powers.len(); - let needed = rng.gen_range(1..256); - - compute_missing_mul_shares(&mut powers, needed); - - // Check length. - if needed / 2 + (needed & 1) <= powers_len { - assert_eq!(powers.len(), powers_len); - } else { - assert_eq!(powers.len(), needed / 2 + (needed & 1)) - } - - // Check shares. - let first = *powers.first().unwrap(); - let factor = first * first; - - let mut expected = first; - for share in powers.iter() { - assert_eq!(*share, expected); - expected = expected * factor; - } - } - - #[test] - fn test_compute_new_add_shares() { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - let new_add_odd_shares: Vec = gen_gf2_128_vec(); - let mut add_shares: Vec = gen_gf2_128_vec(); - - // We have the invariant that len of add_shares is always even. - if add_shares.len() & 1 == 1 { - add_shares.push(rng.gen()); - } - - let original_len = add_shares.len(); - - compute_new_add_shares(&new_add_odd_shares, &mut add_shares); - - // Check new length. - assert_eq!( - add_shares.len(), - original_len + 2 * new_add_odd_shares.len() - ); - - // Check odd shares. - for (k, l) in (original_len..add_shares.len()) - .step_by(2) - .zip(0..original_len) - { - assert_eq!(add_shares[k], new_add_odd_shares[l]); - } - - // Check even shares. - for k in (original_len + 1..add_shares.len()).step_by(2) { - assert_eq!(add_shares[k], add_shares[k / 2] * add_shares[k / 2]); - } - } - - fn gen_gf2_128_vec() -> Vec { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // Sample some message. - let message_len: usize = rng.gen_range(16..128); - let mut message: Vec = vec![Gf2_128::zero(); message_len]; - message.iter_mut().for_each(|x| *x = rng.gen()); - message - } - - fn ghash_reference_impl(h: u128, message: &[Block]) -> Block { - let mut ghash = GHash::new(&h.to_be_bytes().into()); - for el in message { - let block = GenericArray::clone_from_slice(el.to_bytes().as_slice()); - ghash.update(&[block]); - } - let ghash_output = ghash.finalize(); - Block::from(ghash_output) - } - - fn setup_ghash_to_intermediate_state( - hashkey: Gf2_128, - max_hashkey_power: usize, - ) -> (GhashCore, GhashCore) { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - - // Create a multiplicative sharing. - let h1_multiplicative: Gf2_128 = rng.gen(); - let h2_multiplicative: Gf2_128 = hashkey * h1_multiplicative.inverse(); - - let sender = GhashCore::new(max_hashkey_power); - let receiver = GhashCore::new(max_hashkey_power); - - let (sender, receiver) = ( - sender.compute_odd_mul_powers(h1_multiplicative), - receiver.compute_odd_mul_powers(h2_multiplicative), - ); - - (sender, receiver) - } - - fn ghash_to_finalized( - sender: GhashCore, - receiver: GhashCore, - ) -> (GhashCore, GhashCore) { - let (add_shares_sender, add_shares_receiver) = - m2a(&sender.odd_mul_shares(), &receiver.odd_mul_shares()); - let (sender, receiver) = ( - sender.add_new_add_shares(&add_shares_sender), - receiver.add_new_add_shares(&add_shares_receiver), - ); - (sender, receiver) - } - - fn m2a(first: &[Gf2_128], second: &[Gf2_128]) -> (Vec, Vec) { - let mut rng = ChaCha12Rng::from_seed([0; 32]); - let mut first_out = vec![]; - let mut second_out = vec![]; - for (j, k) in first.iter().zip(second.iter()) { - let product = *j * *k; - let first_summand: Gf2_128 = rng.gen(); - let second_summand: Gf2_128 = product + first_summand; - first_out.push(first_summand); - second_out.push(second_summand); - } - (first_out, second_out) - } -} diff --git a/crates/components/universal-hash/src/ghash/ghash_core/state.rs b/crates/components/universal-hash/src/ghash/ghash_core/state.rs deleted file mode 100644 index 3d792b84c..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_core/state.rs +++ /dev/null @@ -1,52 +0,0 @@ -use mpz_fields::gf2_128::Gf2_128; - -mod sealed { - pub(crate) trait Sealed {} - - impl Sealed for super::Init {} - impl Sealed for super::Intermediate {} - impl Sealed for super::Finalized {} -} - -pub(crate) trait State: sealed::Sealed {} - -impl State for Init {} -impl State for Intermediate {} -impl State for Finalized {} - -/// Init state for Ghash protocol. -/// -/// This is before any OT has taken place. -#[derive(Clone)] -pub(crate) struct Init; - -opaque_debug::implement!(Init); - -/// Intermediate state for Ghash protocol. -/// -/// This is when the additive share has been converted into a multiplicative -/// share and all the needed powers have been computed. -#[derive(Clone)] -pub(crate) struct Intermediate { - pub(super) odd_mul_shares: Vec, - // A vec of all additive shares (even and odd) we already have. - // (In order to simplify the code) the n-th index of the vec corresponds to the additive share - // of the (n+1)-th power of H, e.g. the share of H^1 is located at the 0-th index of the vec - // It always contains an even number of consecutive shares starting from the share of H^1 up to - // the share of H^(cached_add_shares.len()). - pub(super) cached_add_shares: Vec, -} - -opaque_debug::implement!(Intermediate); - -/// Final state for Ghash protocol. -/// -/// This is when each party can compute a final share of the ghash output, -/// because both now have additive shares of all the powers of `H`. -#[derive(Clone)] -pub(crate) struct Finalized { - pub(super) odd_mul_shares: Vec, - pub(super) add_shares: Vec, -} - -opaque_debug::implement!(Finalized); diff --git a/crates/components/universal-hash/src/ghash/ghash_inner/config.rs b/crates/components/universal-hash/src/ghash/ghash_inner/config.rs deleted file mode 100644 index 33027550c..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_inner/config.rs +++ /dev/null @@ -1,19 +0,0 @@ -use derive_builder::Builder; - -#[derive(Debug, Clone, Builder)] -/// Configuration struct for [Ghash](crate::ghash::Ghash). -pub struct GhashConfig { - /// Initial number of block shares to provision. - #[builder(default = "1026")] - pub initial_block_count: usize, - /// Maximum number of blocks supported. - #[builder(default = "1026")] - pub max_block_count: usize, -} - -impl GhashConfig { - /// Creates a new builder for the [GhashConfig]. - pub fn builder() -> GhashConfigBuilder { - GhashConfigBuilder::default() - } -} diff --git a/crates/components/universal-hash/src/ghash/ghash_inner/ideal.rs b/crates/components/universal-hash/src/ghash/ghash_inner/ideal.rs deleted file mode 100644 index 900a6d9f0..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_inner/ideal.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! Ideal GHASH functionality. - -use async_trait::async_trait; -use ghash_rc::{ - universal_hash::{KeyInit, UniversalHash as UniversalHashReference}, - GHash, -}; -use mpz_common::{ - ideal::{ideal_f2p, Alice, Bob}, - Context, -}; - -use crate::{UniversalHash, UniversalHashError}; - -/// An ideal GHASH functionality. -#[derive(Debug)] -pub struct IdealGhash { - role: Role, - context: Ctx, -} - -#[derive(Debug)] -enum Role { - Alice(Alice), - Bob(Bob), -} - -#[async_trait] -impl UniversalHash for IdealGhash { - async fn set_key(&mut self, key: Vec) -> Result<(), UniversalHashError> { - match &mut self.role { - Role::Alice(alice) => { - alice - .call( - &mut self.context, - key, - |ghash, alice_key, bob_key: Vec| { - let key = alice_key - .into_iter() - .zip(bob_key) - .map(|(a, b)| a ^ b) - .collect::>(); - *ghash = GHash::new_from_slice(&key).unwrap(); - ((), ()) - }, - ) - .await - } - Role::Bob(bob) => { - bob.call( - &mut self.context, - key, - |ghash, alice_key: Vec, bob_key| { - let key = alice_key - .into_iter() - .zip(bob_key) - .map(|(a, b)| a ^ b) - .collect::>(); - *ghash = GHash::new_from_slice(&key).unwrap(); - ((), ()) - }, - ) - .await - } - } - - Ok(()) - } - - async fn setup(&mut self) -> Result<(), UniversalHashError> { - Ok(()) - } - - async fn preprocess(&mut self) -> Result<(), UniversalHashError> { - Ok(()) - } - - async fn finalize(&mut self, input: Vec) -> Result, UniversalHashError> { - Ok(match &mut self.role { - Role::Alice(alice) => { - alice - .call( - &mut self.context, - input, - |ghash, alice_input, bob_input: Vec| { - assert_eq!(&alice_input, &bob_input); - - let mut ghash = ghash.clone(); - ghash.update_padded(&alice_input); - let output = ghash.finalize().to_vec(); - - let output_bob = vec![0; output.len()]; - let output_alice: Vec = output - .iter() - .zip(output_bob.iter().copied()) - .map(|(o, b)| o ^ b) - .collect(); - (output_alice, output_bob) - }, - ) - .await - } - Role::Bob(bob) => { - bob.call( - &mut self.context, - input, - |ghash, alice_input: Vec, bob_input| { - assert_eq!(&alice_input, &bob_input); - - let mut ghash = ghash.clone(); - ghash.update_padded(&alice_input); - let output = ghash.finalize(); - - let output_bob = vec![0; output.len()]; - let output_alice: Vec = output - .iter() - .zip(output_bob.iter().copied()) - .map(|(o, b)| o ^ b) - .collect(); - (output_alice, output_bob) - }, - ) - .await - } - }) - } -} - -/// Creates an ideal GHASH pair. -pub fn ideal_ghash( - context_alice: Ctx, - context_bob: Ctx, -) -> (IdealGhash, IdealGhash) { - let (alice, bob) = ideal_f2p(GHash::new_from_slice(&[0u8; 16]).unwrap()); - ( - IdealGhash { - role: Role::Alice(alice), - context: context_alice, - }, - IdealGhash { - role: Role::Bob(bob), - context: context_bob, - }, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use mpz_common::executor::test_st_executor; - - #[tokio::test] - async fn test_ideal_ghash() { - let (ctx_a, ctx_b) = test_st_executor(8); - let (mut alice, mut bob) = ideal_ghash(ctx_a, ctx_b); - - let alice_key = vec![42u8; 16]; - let bob_key = vec![69u8; 16]; - let key = alice_key - .iter() - .zip(bob_key.iter()) - .map(|(a, b)| a ^ b) - .collect::>(); - - tokio::try_join!( - alice.set_key(alice_key.clone()), - bob.set_key(bob_key.clone()) - ) - .unwrap(); - - let input = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - let (output_a, output_b) = - tokio::try_join!(alice.finalize(input.clone()), bob.finalize(input.clone())).unwrap(); - - let mut ghash = GHash::new_from_slice(&key).unwrap(); - ghash.update_padded(&input); - let expected_output = ghash.finalize(); - - let output: Vec = output_a.iter().zip(output_b).map(|(a, b)| a ^ b).collect(); - assert_eq!(output, expected_output.to_vec()); - } -} diff --git a/crates/components/universal-hash/src/ghash/ghash_inner/mock.rs b/crates/components/universal-hash/src/ghash/ghash_inner/mock.rs deleted file mode 100644 index a0d5e9d60..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_inner/mock.rs +++ /dev/null @@ -1,35 +0,0 @@ -use mpz_share_conversion::{ - mock::{mock_converter_pair, MockConverterReceiver, MockConverterSender}, - Gf2_128, ReceiverConfig, SenderConfig, -}; - -use super::{Ghash, GhashConfig}; - -/// Create a Ghash sender/receiver pair for testing purpose. -pub fn mock_ghash_pair( - sender_config: GhashConfig, - receiver_config: GhashConfig, -) -> ( - Ghash>, - Ghash>, -) { - let (sender, receiver) = mock_converter_pair::( - SenderConfig::builder() - .id(format!("{}/converter", sender_config.id)) - .record() - .build() - .unwrap(), - ReceiverConfig::builder() - .id(format!("{}/converter", receiver_config.id)) - .record() - .build() - .unwrap(), - ); - - let (sender, receiver) = ( - Ghash::new(sender_config, sender), - Ghash::new(receiver_config, receiver), - ); - - (sender, receiver) -} diff --git a/crates/components/universal-hash/src/ghash/ghash_inner/mod.rs b/crates/components/universal-hash/src/ghash/ghash_inner/mod.rs deleted file mode 100644 index 2b091dd42..000000000 --- a/crates/components/universal-hash/src/ghash/ghash_inner/mod.rs +++ /dev/null @@ -1,362 +0,0 @@ -use crate::{ - ghash::ghash_core::{ - state::{Finalized, Intermediate}, - GhashCore, - }, - UniversalHash, UniversalHashError, -}; -use async_trait::async_trait; -use mpz_common::{Context, Preprocess}; -use mpz_core::Block; -use mpz_fields::gf2_128::Gf2_128; -use mpz_share_conversion::{ShareConversionError, ShareConvert}; -use std::fmt::Debug; -use tracing::instrument; - -mod config; -#[cfg(feature = "ideal")] -pub(crate) mod ideal; - -pub use config::{GhashConfig, GhashConfigBuilder, GhashConfigBuilderError}; - -#[derive(Debug)] -enum State { - Init, - Ready { core: GhashCore }, - Error, -} - -/// This is the common instance used by both sender and receiver. -/// -/// It is an aio wrapper which mostly uses [GhashCore] for computation. -pub struct Ghash { - state: State, - config: GhashConfig, - converter: C, - context: Ctx, -} - -impl Ghash -where - - C: ShareConvert, -{ - /// Creates a new instance. - /// - /// # Arguments - /// - /// * `config` - The configuration for this Ghash instance. - /// * `converter` - An instance which allows to convert multiplicative - /// into additive shares and vice versa. - /// * `context` - The context. - pub fn new(config: GhashConfig, converter: C, context: Ctx) -> Self { - Self { - state: State::Init, - config, - converter, - context, - } - } - - /// Computes all the additive shares of the hashkey powers. - /// - /// We need this when the max block count changes. - #[instrument(level = "debug", skip_all, err)] - async fn compute_add_shares( - &mut self, - core: GhashCore, - ) -> Result, UniversalHashError> { - let odd_mul_shares = core.odd_mul_shares(); - - let add_shares = self - .converter - .to_additive(&mut self.context, odd_mul_shares) - .await?; - let core = core.add_new_add_shares(&add_shares); - - Ok(core) - } -} - -impl Debug for Ghash { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Ghash") - .field("state", &self.state) - .field("config", &self.config) - .field("converter", &"{{ .. }}".to_string()) - .finish() - } -} - -#[async_trait] -impl UniversalHash for Ghash -where - - C: Preprocess + ShareConvert + Send, -{ - #[instrument(level = "info", fields(thread = %self.context.id()), skip_all, err)] - async fn set_key(&mut self, key: Vec) -> Result<(), UniversalHashError> { - if key.len() != 16 { - return Err(UniversalHashError::KeyLengthError(16, key.len())); - } - - if !matches!(&self.state, State::Init) { - return Err(UniversalHashError::InvalidState( - "Key already set".to_string(), - )); - } - - let mut h_additive = [0u8; 16]; - h_additive.copy_from_slice(key.as_slice()); - - // GHASH reflects the bits of the key. - let h_additive = Gf2_128::new(u128::from_be_bytes(h_additive).reverse_bits()); - - let h_multiplicative = self - .converter - .to_multiplicative(&mut self.context, vec![h_additive]) - .await?; - - let core = GhashCore::new(self.config.initial_block_count); - let core = core.compute_odd_mul_powers(h_multiplicative[0]); - let core = self.compute_add_shares(core).await?; - - self.state = State::Ready { core }; - - Ok(()) - } - - #[instrument(level = "debug", fields(thread = %self.context.id()), skip_all, err)] - async fn setup(&mut self) -> Result<(), UniversalHashError> { - // We need only half the number of `max_block_count` M2As because of the free - // squaring trick and we need one extra A2M conversion in the beginning. - // Both M2A and A2M, each require a single OLE. - let ole_count = self.config.max_block_count / 2 + 1; - self.converter.alloc(ole_count); - - Ok(()) - } - - #[instrument(level = "debug", fields(thread = %self.context.id()), skip_all, err)] - async fn preprocess(&mut self) -> Result<(), UniversalHashError> { - self.converter.preprocess(&mut self.context).await?; - - Ok(()) - } - - #[instrument(level = "debug", fields(thread = %self.context.id()), skip_all, err)] - async fn finalize(&mut self, mut input: Vec) -> Result, UniversalHashError> { - // Divide by block length and round up. - let block_count = input.len() / 16 + (input.len() % 16 != 0) as usize; - - if block_count > self.config.max_block_count { - return Err(UniversalHashError::InputLengthError(input.len())); - } - - let state = std::mem::replace(&mut self.state, State::Error); - - // Calling finalize when not setup is a fatal error. - let State::Ready { core } = state else { - return Err(UniversalHashError::InvalidState("Key not set".to_string())); - }; - - // Compute new shares if the block count increased. - let core = if block_count > core.get_max_blocks() { - self.compute_add_shares(core.change_max_hashkey(block_count)) - .await? - } else { - core - }; - - // Pad input to a multiple of 16 bytes. - input.resize(block_count * 16, 0); - - // Convert input to blocks. - let blocks = input - .chunks_exact(16) - .map(|chunk| { - let mut block = [0u8; 16]; - block.copy_from_slice(chunk); - Block::from(block) - }) - .collect::>(); - - let tag = core - .finalize(&blocks) - .expect("Input length should be valid"); - - // Reinsert state. - self.state = State::Ready { core }; - - Ok(tag.to_bytes().to_vec()) - } -} - -#[cfg(test)] -mod tests { - use crate::{ - ghash::{Ghash, GhashConfig}, - UniversalHash, - }; - use ghash_rc::{ - universal_hash::{KeyInit, UniversalHash as UniversalHashReference}, - GHash as GhashReference, - }; - use mpz_common::{executor::test_st_executor, Context}; - use mpz_share_conversion::ideal::{ideal_share_converter, IdealShareConverter}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha12Rng; - - fn create_pair( - block_count: usize, - context_alice: Ctx, - context_bob: Ctx, - ) -> ( - Ghash, - Ghash, - ) { - let (convert_a, convert_b) = ideal_share_converter(); - - let config = GhashConfig::builder() - .initial_block_count(block_count) - .build() - .unwrap(); - - ( - Ghash::new(config.clone(), convert_a, context_alice), - Ghash::new(config, convert_b, context_bob), - ) - } - - #[tokio::test] - async fn test_ghash_output() { - let (ctx_a, ctx_b) = test_st_executor(8); - let mut rng = ChaCha12Rng::from_seed([0; 32]); - let h: u128 = rng.gen(); - let sender_key: u128 = rng.gen(); - let receiver_key: u128 = h ^ sender_key; - let message: Vec = (0..128).map(|_| rng.gen()).collect(); - - let (mut sender, mut receiver) = create_pair(1, ctx_a, ctx_b); - - tokio::try_join!( - sender.set_key(sender_key.to_be_bytes().to_vec()), - receiver.set_key(receiver_key.to_be_bytes().to_vec()) - ) - .unwrap(); - - let (sender_share, receiver_share) = tokio::try_join!( - sender.finalize(message.clone()), - receiver.finalize(message.clone()) - ) - .unwrap(); - - let tag = sender_share - .iter() - .zip(receiver_share.iter()) - .map(|(a, b)| a ^ b) - .collect::>(); - - assert_eq!(tag, ghash_reference_impl(h, &message)); - } - - #[tokio::test] - async fn test_ghash_output_padded() { - let (ctx_a, ctx_b) = test_st_executor(8); - let mut rng = ChaCha12Rng::from_seed([0; 32]); - let h: u128 = rng.gen(); - let sender_key: u128 = rng.gen(); - let receiver_key: u128 = h ^ sender_key; - - // Message length is not a multiple of the block length - let message: Vec = (0..126).map(|_| rng.gen()).collect(); - - let (mut sender, mut receiver) = create_pair(1, ctx_a, ctx_b); - - tokio::try_join!( - sender.set_key(sender_key.to_be_bytes().to_vec()), - receiver.set_key(receiver_key.to_be_bytes().to_vec()) - ) - .unwrap(); - - let (sender_share, receiver_share) = tokio::try_join!( - sender.finalize(message.clone()), - receiver.finalize(message.clone()) - ) - .unwrap(); - - let tag = sender_share - .iter() - .zip(receiver_share.iter()) - .map(|(a, b)| a ^ b) - .collect::>(); - - assert_eq!(tag, ghash_reference_impl(h, &message)); - } - - #[tokio::test] - async fn test_ghash_long_message() { - let (ctx_a, ctx_b) = test_st_executor(8); - let mut rng = ChaCha12Rng::from_seed([0; 32]); - let h: u128 = rng.gen(); - let sender_key: u128 = rng.gen(); - let receiver_key: u128 = h ^ sender_key; - let short_message: Vec = (0..128).map(|_| rng.gen()).collect(); - - // A longer message. - let long_message: Vec = (0..192).map(|_| rng.gen()).collect(); - - // Create and setup sender and receiver for short message length. - let (mut sender, mut receiver) = create_pair(1, ctx_a, ctx_b); - - tokio::try_join!( - sender.set_key(sender_key.to_be_bytes().to_vec()), - receiver.set_key(receiver_key.to_be_bytes().to_vec()) - ) - .unwrap(); - - // Compute the shares for the short message. - tokio::try_join!( - sender.finalize(short_message.clone()), - receiver.finalize(short_message.clone()) - ) - .unwrap(); - - // Now compute the shares for the longer message. - let (sender_share, receiver_share) = tokio::try_join!( - sender.finalize(long_message.clone()), - receiver.finalize(long_message.clone()) - ) - .unwrap(); - - let tag = sender_share - .iter() - .zip(receiver_share.iter()) - .map(|(a, b)| a ^ b) - .collect::>(); - - assert_eq!(tag, ghash_reference_impl(h, &long_message)); - - // We should still be able to generate a Ghash output for the shorter message. - let (sender_share, receiver_share) = tokio::try_join!( - sender.finalize(short_message.clone()), - receiver.finalize(short_message.clone()) - ) - .unwrap(); - - let tag = sender_share - .iter() - .zip(receiver_share.iter()) - .map(|(a, b)| a ^ b) - .collect::>(); - - assert_eq!(tag, ghash_reference_impl(h, &short_message)); - } - - fn ghash_reference_impl(h: u128, message: &[u8]) -> Vec { - let mut ghash = GhashReference::new(&h.to_be_bytes().into()); - ghash.update_padded(message); - let mac = ghash.finalize(); - mac.to_vec() - } -} diff --git a/crates/components/universal-hash/src/ghash/mod.rs b/crates/components/universal-hash/src/ghash/mod.rs deleted file mode 100644 index 0bb71ddf7..000000000 --- a/crates/components/universal-hash/src/ghash/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod ghash_core; -mod ghash_inner; - -#[cfg(feature = "ideal")] -pub use ghash_inner::ideal::{ideal_ghash, IdealGhash}; -pub use ghash_inner::{Ghash, GhashConfig, GhashConfigBuilder, GhashConfigBuilderError}; diff --git a/crates/components/universal-hash/src/lib.rs b/crates/components/universal-hash/src/lib.rs deleted file mode 100644 index 8c364e0a9..000000000 --- a/crates/components/universal-hash/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! A library for computing different kinds of hash functions in a 2PC setting. - -#![deny(missing_docs, unreachable_pub, unused_must_use)] -#![deny(clippy::all)] -#![forbid(unsafe_code)] - -/// This module implements [UniversalHash] for Ghash. -#[cfg(feature = "ghash")] -pub mod ghash; - -use async_trait::async_trait; - -/// Errors for [UniversalHash]. -#[allow(missing_docs)] -#[derive(Debug, thiserror::Error)] -pub enum UniversalHashError { - #[error("Invalid state: {0}")] - InvalidState(String), - #[error("Invalid key length, expected {0}, got {1}")] - KeyLengthError(usize, usize), - #[error("Invalid input length: {0}")] - InputLengthError(usize), - #[error(transparent)] - ShareConversionError(#[from] mpz_share_conversion::ShareConversionError), -} - -#[async_trait] -/// A trait supporting different kinds of hash functions. -pub trait UniversalHash: Send { - /// Sets the key for the hash function - /// - /// # Arguments - /// - /// * `key` - Key to use for the hash function. - async fn set_key(&mut self, key: Vec) -> Result<(), UniversalHashError>; - - /// Performs any necessary one-time setup. - async fn setup(&mut self) -> Result<(), UniversalHashError>; - - /// Preprocesses the hash function. - async fn preprocess(&mut self) -> Result<(), UniversalHashError>; - - /// Computes hash of the input, padding the input to the block size - /// if needed. - /// - /// # Arguments - /// - /// * `input` - Input to hash. - async fn finalize(&mut self, input: Vec) -> Result, UniversalHashError>; -}