Skip to content

Commit

Permalink
Add crate mpz-ole-core (#135)
Browse files Browse the repository at this point in the history
* Add generic support for ideal random OT.

* Add crate `mpz-ole-core`.

* Improved ideal ROT implementation and adapted tests in `mpz-ole-core`.

* Remove unnecessary comment.

* Add feedback.

* Added `Serialize` and `Deserialize` for message types.

* Return `Option` instead of `Result`.

* Remove unused error enum.

* Add transfer id.

* Added using `TransferId` in new batch types.

* Use `split_off` instead of `drain`.

* Return correct number of OLEs from `consume`.

* Use `VecDeque` for cache and please clippy.

* Use `hybrid-array` and get rid of const generics.

* Apply suggestions for improving comments

Co-authored-by: dan <themighty1@users.noreply.github.com>

* Remove `ByteRepr` from `Field` trait.

* Move from `MaskedInput` to `MaskedCorrelation` mental model.

* Simplify array creation in `core/sender.rs`.

* Add more feedback.

- Add default for `BIT_SIZE`
- Rename `BitSizeType` -> `BitSize`.

* Add comment about which function is evaluated.

Co-authored-by: dan <themighty1@users.noreply.github.com>

---------

Co-authored-by: dan <themighty1@users.noreply.github.com>
  • Loading branch information
th4s and themighty1 authored May 29, 2024
1 parent 622c9f7 commit 497a35a
Show file tree
Hide file tree
Showing 16 changed files with 1,027 additions and 35 deletions.
4 changes: 4 additions & 0 deletions crates/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"mpz-share-conversion-core",
"matrix-transpose",
"clmul",
"mpz-ole-core",
]
resolver = "2"

Expand All @@ -38,6 +39,7 @@ mpz-garble = { path = "mpz-garble" }
mpz-garble-core = { path = "mpz-garble-core" }
mpz-share-conversion = { path = "mpz-share-conversion" }
mpz-share-conversion-core = { path = "mpz-share-conversion-core" }
mpz-ole-core = { path = "mpz-ole-core" }
clmul = { path = "clmul" }
matrix-transpose = { path = "matrix-transpose" }

Expand Down Expand Up @@ -110,6 +112,8 @@ hex = "0.4"
lazy_static = "1"
derive_builder = "0.11"
once_cell = "1"
hybrid-array = "0.2.0-rc.8"
typenum = "1"
# DO NOT BUMP, SEE https://github.com/privacy-scaling-explorations/mpz/issues/61
generic-array = "0.14"
itybity = "0.2"
Expand Down
2 changes: 2 additions & 0 deletions crates/mpz-fields/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ num-bigint.workspace = true
opaque-debug.workspace = true
serde.workspace = true
itybity.workspace = true
typenum.workspace = true
hybrid-array.workspace = true

[dev-dependencies]
ghash_rc.workspace = true
Expand Down
5 changes: 3 additions & 2 deletions crates/mpz-fields/src/gf2_128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use rand::{distributions::Standard, prelude::Distribution};
use serde::{Deserialize, Serialize};

use mpz_core::Block;
use typenum::U128;

use super::Field;
use crate::Field;

/// A type for holding field elements of Gf(2^128).
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -98,7 +99,7 @@ impl Neg for Gf2_128 {
}

impl Field for Gf2_128 {
const BIT_SIZE: u32 = 128;
type BitSize = U128;

fn zero() -> Self {
Self::new(0)
Expand Down
16 changes: 11 additions & 5 deletions crates/mpz-fields/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ use std::{
ops::{Add, Mul, Neg},
};

use hybrid_array::ArraySize;
use itybity::{BitLength, FromBitIterator, GetBit, Lsb0, Msb0};
use rand::{distributions::Standard, prelude::Distribution, Rng};
use typenum::Unsigned;

/// A trait for finite fields.
pub trait Field:
Expand All @@ -35,9 +37,13 @@ pub trait Field:
+ GetBit<Lsb0>
+ GetBit<Msb0>
+ BitLength
+ Unpin
{
/// The number of bits of a field element.
const BIT_SIZE: u32;
const BIT_SIZE: usize = <Self::BitSize as Unsigned>::USIZE;

/// The number of bits of a field element as a type number.
type BitSize: ArraySize;

/// Return the additive identity element.
fn zero() -> Self;
Expand Down Expand Up @@ -131,19 +137,19 @@ mod tests {
}

pub(crate) fn test_field_bit_ops<T: Field>() {
let mut a = vec![false; T::BIT_SIZE as usize];
let mut b = vec![false; T::BIT_SIZE as usize];
let mut a = vec![false; T::BIT_SIZE];
let mut b = vec![false; T::BIT_SIZE];

a[0] = true;
b[T::BIT_SIZE as usize - 1] = true;
b[T::BIT_SIZE - 1] = true;

let a = T::from_lsb0_iter(a);
let b = T::from_lsb0_iter(b);

assert_eq!(a, T::one());
assert!(GetBit::<Lsb0>::get_bit(&a, 0));

assert_eq!(b, T::two_pow(T::BIT_SIZE - 1));
assert_eq!(b, T::two_pow(T::BIT_SIZE as u32 - 1));
assert!(GetBit::<Lsb0>::get_bit(&b, (T::BIT_SIZE - 1) as usize));
}
}
5 changes: 3 additions & 2 deletions crates/mpz-fields/src/p256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ use itybity::{BitLength, FromBitIterator, GetBit, Lsb0, Msb0};
use num_bigint::ToBigUint;
use rand::{distributions::Standard, prelude::Distribution};
use serde::{Deserialize, Serialize};
use typenum::U256;

use super::Field;
use crate::Field;

/// A type for holding field elements of P256.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -80,7 +81,7 @@ impl Neg for P256 {
}

impl Field for P256 {
const BIT_SIZE: u32 = 256;
type BitSize = U256;

fn zero() -> Self {
P256(<Fq as Zero>::zero())
Expand Down
18 changes: 18 additions & 0 deletions crates/mpz-ole-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "mpz-ole-core"
version = "0.1.0"
edition = "2021"

[lib]
name = "mpz_ole_core"

[dependencies]
rand.workspace = true
itybity.workspace = true
thiserror.workspace = true
serde = { workspace = true, features = ["derive"] }
hybrid-array.workspace = true

mpz-fields.workspace = true
mpz-core.workspace = true
mpz-ot-core.workspace = true
124 changes: 124 additions & 0 deletions crates/mpz-ole-core/src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//! This implementation is based on the COPEe protocol from <https://eprint.iacr.org/2016/505> page 10
//! with the following modification:
//!
//! - The `Initialize` stage is instantiated using random OT rather than chosen-input OT.
//! - The `Extend` stage can only be called once, since our goal is to implement oblivious linear
//! function evaluation (OLE) rather than vector OLE (VOLE) (which means that we do not use PRGs).
//! - The evaluated function is f(b)=a*b+x rather than f(b)=a*b-x.
//!
//! Note that this is an OLE with errors implementation. A malicious sender is allowed to set its own
//! output and can introduce additive errors into the receiver's output.
mod receiver;
mod sender;

use hybrid_array::Array;
pub use receiver::{ReceiverAdjust, ReceiverShare};
pub use sender::{SenderAdjust, SenderShare};

use mpz_fields::Field;

/// The masked correlation of the sender.
///
/// This is the correlation which is sent to the receiver.
pub struct MaskedCorrelation<F: Field>(pub(crate) Array<F, F::BitSize>);

/// The exchange field element for share adjustment.
///
/// This needs to be sent to each other in order to complete the share adjustment.
#[derive(Debug)]
pub struct ShareAdjust<F>(pub(crate) F);

#[cfg(test)]
mod tests {
use crate::core::{ReceiverShare, SenderShare};
use crate::tests::create_rot;
use mpz_core::{prg::Prg, Block};
use mpz_fields::{p256::P256, UniformRand};
use rand::SeedableRng;

#[test]
fn test_ole_core() {
let mut rng = Prg::from_seed(Block::ZERO);

let sender_input = P256::rand(&mut rng);
let receiver_input = P256::rand(&mut rng);

let (sender_share, receiver_share) = create_ole(sender_input, receiver_input);

let a = sender_input;
let b = receiver_input;
let x = sender_share.inner();
let y = receiver_share.inner();

assert_eq!(y, a * b + x);
}

#[test]
fn test_ole_core_vec() {
let count = 12;
let from_seed = Prg::from_seed(Block::ZERO);
let mut rng = from_seed;

let sender_input: Vec<P256> = (0..count).map(|_| P256::rand(&mut rng)).collect();
let receiver_input: Vec<P256> = (0..count).map(|_| P256::rand(&mut rng)).collect();

let (ot_messages, ot_message_choices) = create_rot(receiver_input.clone());

let (sender_shares, masked) =
SenderShare::new_vec(sender_input.clone(), ot_messages).unwrap();
let receiver_shares =
ReceiverShare::new_vec(receiver_input.clone(), ot_message_choices, masked).unwrap();

sender_input
.iter()
.zip(receiver_input)
.zip(sender_shares)
.zip(receiver_shares)
.for_each(|(((&a, b), x), y)| assert_eq!(y.inner(), a * b + x.inner()));
}

#[test]
fn test_ole_adjust() {
let mut rng = Prg::from_seed(Block::ZERO);

let sender_input = P256::rand(&mut rng);
let receiver_input = P256::rand(&mut rng);

let sender_target = P256::rand(&mut rng);
let receiver_target = P256::rand(&mut rng);

let (sender_share, receiver_share) = create_ole(sender_input, receiver_input);

let (sender_adjust, s_to_r_adjust) = sender_share.adjust(sender_target);
let (receiver_adjust, r_to_s_adjust) = receiver_share.adjust(receiver_target);

let sender_share_adjusted = sender_adjust.finish(r_to_s_adjust);
let receiver_share_adjusted = receiver_adjust.finish(s_to_r_adjust);

let a = sender_target;
let b = receiver_target;
let x = sender_share_adjusted.inner();
let y = receiver_share_adjusted.inner();

assert_eq!(y, a * b + x);
}

fn create_ole(
sender_input: P256,
receiver_input: P256,
) -> (SenderShare<P256>, ReceiverShare<P256>) {
let receiver_input_vec = vec![receiver_input];

let (ot_messages, ot_message_choices) = create_rot(receiver_input_vec);

let ot_messages: [[P256; 2]; 256] = ot_messages.try_into().unwrap();
let ot_message_choices: [P256; 256] = ot_message_choices.try_into().unwrap();
let ot_choice = receiver_input;

let (sender_share, correlation) = SenderShare::new(sender_input, ot_messages);
let receiver_share = ReceiverShare::new(ot_choice, ot_message_choices, correlation);

(sender_share, receiver_share)
}
}
Loading

0 comments on commit 497a35a

Please sign in to comment.