From 28e16bb3b972457f513c51744e822550b99a14f8 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 21 May 2024 16:40:33 +0200 Subject: [PATCH] Half-AND protocol --- atlas-spec/mpc-engine/src/messages.rs | 2 + atlas-spec/mpc-engine/src/party.rs | 125 ++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/atlas-spec/mpc-engine/src/messages.rs b/atlas-spec/mpc-engine/src/messages.rs index 46a7df5..78c9c81 100644 --- a/atlas-spec/mpc-engine/src/messages.rs +++ b/atlas-spec/mpc-engine/src/messages.rs @@ -45,6 +45,8 @@ pub enum MessagePayload { SubChannel(Sender, Receiver), /// A bit mac for validity checking Mac(Mac), + /// Values sent over to other parties in the half-AND protocol + HalfAndHashes(bool, bool), /// A garbled AND gate, to be sent to the evaluator GarbledAnd(Vec, Vec, Vec, Vec), /// A MAC on a wire mask share diff --git a/atlas-spec/mpc-engine/src/party.rs b/atlas-spec/mpc-engine/src/party.rs index 3666581..d4c4fa4 100644 --- a/atlas-spec/mpc-engine/src/party.rs +++ b/atlas-spec/mpc-engine/src/party.rs @@ -2,6 +2,7 @@ //! phases of the protocol. use hacspec_lib::Randomness; +use hmac::hkdf_extract; use crate::{ circuit::Circuit, @@ -367,6 +368,7 @@ impl Party { Ok(authenticated_bits[0..len].to_vec()) } + /// Compute `len` authenticated bit shares. fn random_authenticated_shares(&mut self, len: usize) -> Result, Error> { let len_unchecked = len + SEC_MARGIN_SHARE_AUTH; let authenticated_bits: Vec = self.abit_pool.drain(..len_unchecked).collect(); @@ -518,6 +520,129 @@ impl Party { Ok(authenticated_bits[0..len].to_vec()) } + /// Compute unauthenticated cross terms in an AND triple output share. + fn half_and(&mut self, x: &AuthBit, y: &AuthBit) -> Result { + fn half_and_hash(dst: &[u8], input: &[u8]) -> [u8; 32] { + let mut hash = hkdf_extract(dst, input); + hash.truncate(32); + hash.try_into().unwrap() + } + + /// Obtain the least significant bit of some hash output + fn lsb(input: &[u8]) -> bool { + (input[input.len() - 1] & 1) != 0 + } + + let domain_separator = format!("half-and-hash-{}", self.id); + + let mut t_js = vec![false; self.num_parties]; + let mut s_js = vec![false; self.num_parties]; + + // receive earlier hashes + for _j in 0..self.id { + let hashes_message = self.channels.listen.recv().unwrap(); + if let Message { + from, + to, + payload: MessagePayload::HalfAndHashes(hash_j_0, hash_j_1), + } = hashes_message + { + debug_assert_eq!(to, self.id); + let their_mac = x + .macs + .iter() + .find(|(party, _mac)| *party == from) + .expect("should have MACs from all other parties") + .1; + let hash_lsb = lsb(&half_and_hash(domain_separator.as_bytes(), &their_mac)); + let t_j = if x.bit.value { + hash_j_1 ^ hash_lsb + } else { + hash_j_0 ^ hash_lsb + }; + t_js[from] = t_j; + } else { + return Err(Error::UnexpectedMessage(hashes_message)); + } + } + + for j in 0..self.num_parties { + if j == self.id { + continue; + } + let s_j = self + .entropy + .bit() + .expect("sufficient randomness should have been provided externally"); + s_js[j] = s_j; + + // K_i[x^j] + let input_0 = x + .mac_keys + .iter() + .find(|key| key.bit_holder == j) + .expect("should have keys for all other parties") + .mac_key; + + // K_i[x^j] xor Delta_i + let mut input_1 = [0u8; MAC_LENGTH]; + for byte in 0..MAC_LENGTH { + input_1[byte] = input_0[byte] ^ self.global_mac_key[byte]; + } + + let h_0 = lsb(&half_and_hash(domain_separator.as_bytes(), &input_0)) ^ s_j; + let h_1 = + lsb(&half_and_hash(domain_separator.as_bytes(), &input_1)) ^ s_j ^ y.bit.value; + self.channels.parties[j] + .send(Message { + from: self.id, + to: j, + payload: MessagePayload::HalfAndHashes(h_0, h_1), + }) + .unwrap(); + } + + // receive later hashes + for _j in self.id + 1..self.num_parties { + let hashes_message = self.channels.listen.recv().unwrap(); + if let Message { + from, + to, + payload: MessagePayload::HalfAndHashes(hash_j_0, hash_j_1), + } = hashes_message + { + debug_assert_eq!(to, self.id); + let their_mac = x + .macs + .iter() + .find(|(party, _mac)| *party == from) + .expect("should have MACs from all other parties") + .1; + let hash_lsb = lsb(&half_and_hash(domain_separator.as_bytes(), &their_mac)); + let t_j = if x.bit.value { + hash_j_1 ^ hash_lsb + } else { + hash_j_0 ^ hash_lsb + }; + t_js[from] = t_j; + } else { + return Err(Error::UnexpectedMessage(hashes_message)); + } + } + + self.sync().expect("sync should always succeed"); + + let mut v_i = false; + for j in 0..self.num_parties { + if j == self.id { + continue; + } + v_i ^= t_js[j] ^ s_js[j]; + } + + Ok(v_i) + } + /// Perform the active_security check for bit authentication fn bit_auth_check(&mut self, auth_bits: &[AuthBit]) -> Result<(), Error> { for j in 0..SEC_MARGIN_BIT_AUTH {