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

Add (R)OLE to mpz #103

Closed
wants to merge 51 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
5611c16
Create ole crate
th4s Jan 17, 2024
38b8087
Added drafts for ole traits (OLEe, ROLEe) and implementations
th4s Jan 19, 2024
1e7007e
Reorganize crate structure
th4s Jan 19, 2024
b9a1a99
Improve comments
th4s Jan 19, 2024
5998e98
Reworked trait structure and implemented ROLE provider
th4s Jan 24, 2024
49db0ff
Added role implementation for evaluator
th4s Jan 25, 2024
d6e6862
Bugfix for ROLE evaluator
th4s Jan 25, 2024
0230c4b
Added (non-working) test and various bugfixes
th4s Jan 26, 2024
3ac2cfe
Bugfix, test working for ROLEe
th4s Jan 26, 2024
4e552de
Implement OLEe from ROLEe provider
th4s Jan 29, 2024
e7aae38
Added OLEe provider, evaluator and added test
th4s Jan 29, 2024
abb812f
Added ideal ROLE implementation
th4s Jan 29, 2024
38cc0a5
Added ideal OLE implementation
th4s Jan 29, 2024
0f02425
Bugfix for OLE implementation
th4s Jan 29, 2024
b7780e1
Added crate `mpz-ole-core`
th4s Feb 13, 2024
84589b2
Fix typos
th4s Feb 15, 2024
a74a9b8
Added role provider in ole-core and refactored async wrapper.
th4s Feb 16, 2024
9ee2b14
Improved error handling
th4s Feb 16, 2024
628cd9e
Added mpz-ole-core ROLEeEvaluator
th4s Feb 16, 2024
1ee5622
Add error handling for evaluator in ole-core
th4s Feb 16, 2024
188a152
Extract error check into private function
th4s Feb 16, 2024
c73871d
WIP: Refactoring mpz-ole-core...
th4s Feb 16, 2024
7d26d90
Add documentation for mpz-ole-core ROLEe evaluator.
th4s Feb 16, 2024
0a4362a
WIP: Adding mpz-ole-core ROLE test.
th4s Feb 16, 2024
b31c276
Added test for role in mpz-ole-core.
th4s Feb 19, 2024
54647fd
Added OLE implementation in mpz-ole-core
th4s Feb 19, 2024
721f883
Added mpz-ole-core OLE evaluator
th4s Feb 19, 2024
afda8b4
Added functionality for OLE in mpz-ole-core
th4s Feb 20, 2024
92e26fb
Add ideal runctionality for role in mpz-ole core.
th4s Feb 20, 2024
f2cbefd
Added test for ole based on role in mpz-ole-chore
th4s Feb 20, 2024
8324933
Make clippy happy.
th4s Feb 20, 2024
3263497
Improve code quality for `mpz-ole-core`.
th4s Feb 21, 2024
21f1a36
Improve error handling and trait documentation in `mpz-ole`
th4s Feb 22, 2024
2181e33
Remove chacha dependency in `mpz-ole`
th4s Feb 22, 2024
3d675de
Improve code quality and documentation in `mpz-ole`
th4s Feb 22, 2024
5b2810b
WIP: Adapting `mpz-ole` to new OT traits...
th4s Feb 27, 2024
1a15344
Finish adapting `mpz-ole` to new IO model.
th4s Feb 27, 2024
234caa0
Fix imports of `mpz-fields`
th4s Mar 7, 2024
5cbe034
Adapt OLE trait definitions
th4s Mar 7, 2024
6ba4d97
Adapt OLE crates to new threading
th4s Mar 7, 2024
a7a0aa5
Remove leftover from rebasing
th4s Mar 7, 2024
10449e0
Adapt to new crate structure
th4s Mar 7, 2024
67cf946
Get rid of unneeded feature flag
th4s Mar 7, 2024
4e6d80e
Add support for generic ideal ROT.
th4s May 10, 2024
410b6f8
Rewrite mpz-ole-core to use COPEe.
th4s May 10, 2024
6cd3a07
Rewrote `mpz-ole-core` to use share types.
th4s May 13, 2024
938c45e
Bugfix for OLE.
th4s May 13, 2024
9eb6c4e
Add test for share adjustment.
th4s May 13, 2024
6fc1bbb
Added `sender`/`receiver` modules.
th4s May 13, 2024
442511f
Added `OLESender`.
th4s May 14, 2024
36f42f3
Added `OLEReceiver`.
th4s May 14, 2024
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
4 changes: 4 additions & 0 deletions crates/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ members = [
"mpz-share-conversion-core",
"matrix-transpose",
"clmul",
"mpz-ole",
"mpz-ole-core"
]
resolver = "2"

Expand All @@ -38,6 +40,8 @@ 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 = { path = "mpz-ole" }
mpz-ole-core = { path = "mpz-ole-core" }
clmul = { path = "clmul" }
matrix-transpose = { path = "matrix-transpose" }

Expand Down
1 change: 1 addition & 0 deletions crates/mpz-fields/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub trait Field:
+ GetBit<Lsb0>
+ GetBit<Msb0>
+ BitLength
+ Unpin
{
/// The number of bits of a field element.
const BIT_SIZE: u32;
Expand Down
16 changes: 16 additions & 0 deletions crates/mpz-ole-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[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

mpz-fields.workspace = true
mpz-core.workspace = true
mpz-ot-core.workspace = true
113 changes: 113 additions & 0 deletions crates/mpz-ole-core/src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! This implementation uses the COPEe protocol from <https://eprint.iacr.org/2016/505> page 10.
//!
//! We use this construction to implement oblivious linear function evaluation (OLE) instead of
//! vector OLE (VOLE), which means that we do not use PRGs, i.e. Extend can only be called once.
//!
//! Note that this is an OLE with errors implementation. The sender can introduce additive errors.
//! Input privacy is guaranteed, but output privacy is not, when `0` is chosen as an input value.

mod receiver;
mod sender;

pub(crate) use receiver::{ReceiverAdjust, ReceiverShare};
pub(crate) use sender::{SenderAdjust, SenderShare};

use mpz_fields::Field;

/// Workaround because of feature `generic_const_exprs` not available in stable.
///
/// This is used to check at compile-time that the correct const-generic implementation is used for
/// a specific field.
struct Check<const N: usize, F: Field>(std::marker::PhantomData<F>);

impl<const N: usize, F: Field> Check<N, F> {
const IS_BITSIZE_CORRECT: () = assert!(
N as u32 == F::BIT_SIZE,
"Wrong bit size used for field. You need to use `F::BIT_SIZE` for N."
);
}

/// The masked input of the sender.
///
/// This is the correlation which is sent to the receiver and hides the sender's input.
pub struct MaskedInput<const N: usize, F>([F; N]);

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

#[cfg(test)]
mod tests {
use super::*;
use crate::tests::create_rot;
use mpz_core::{prg::Prg, Block};
use mpz_fields::{p256::P256, Field, 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::<256, 32, P256>(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_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::<256, 32, P256>(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);
}

// Unergonomic API because of lack of proper const generic support
// N should be BIT_SIZE of F
// K should be BYTE_SIZE of F
fn create_ole<const N: usize, const K: usize, F: Field>(
sender_input: F,
receiver_input: F,
) -> (SenderShare<F>, ReceiverShare<F>) {
let receiver_input_vec = vec![receiver_input];

let (ot_messages, ot_message_choices) = create_rot::<K, F>(receiver_input_vec);

let ot_messages: [[F; 2]; N] = ot_messages.try_into().unwrap();
let ot_message_choices: [F; N] = 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)
}
}
92 changes: 92 additions & 0 deletions crates/mpz-ole-core/src/core/receiver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! Receiver shares for Oblivious Linear Function Evaluation (OLE).

use super::{Check, MaskedInput, ShareAdjust};
use itybity::ToBits;
use mpz_fields::Field;

/// Receiver share for OLE.
#[derive(Debug)]
pub struct ReceiverShare<F> {
input: F,
output: F,
}

impl<F: Field> ReceiverShare<F> {
/// Creates a new [`ReceiverShare`].
///
/// # Arguments
///
/// * `input` - The receiver's input share. This is his OT choice bits as a field element.
/// * `ot_message_choices` - The receiver's OT message choices as field elements.
/// * `masked` - The correlation masking the sender's input.
///
/// # Returns
///
/// * The receiver's share.
pub(crate) fn new<const N: usize>(
input: F,
ot_message_choices: [F; N],
masked: MaskedInput<N, F>,
) -> Self {
// Check that the right N is used depending on the needed bit size of the field.
let _: () = Check::<N, F>::IS_BITSIZE_CORRECT;

let delta_i = input.iter_lsb0();
let ui = masked.0.iter();
let t_delta_i = ot_message_choices.iter();

let output = delta_i.zip(ui).zip(t_delta_i).enumerate().fold(
F::zero(),
|acc, (i, ((delta, &u), &t))| {
let delta = if delta { F::one() } else { F::zero() };
acc + F::two_pow(i as u32) * (delta * u + t)
},
);

Self { input, output }
}

/// Returns the receiver's output share.
pub fn inner(self) -> F {
self.output
}

/// Adjust a preprocessed share.
///
/// This is an implementation of <https://crypto.stackexchange.com/questions/100634/converting-a-random-ole-oblivious-linear-function-evaluation-to-an-ole>.
///
/// # Arguments
///
/// * `target` - The new target input of the OLE.
///
/// # Returns
///
/// * The intermediate receiver share, which needs the sender's adjustment.
/// * The receiver adjustment which needs to be sent to the sender.
pub(crate) fn adjust(self, target: F) -> (ReceiverAdjust<F>, ShareAdjust<F>) {
(
ReceiverAdjust {
old_output: self.output,
new_input: target,
},
ShareAdjust(self.input + target),
)
}
}

/// Intermediate type for share adjustment of the receiver.
#[derive(Debug)]
pub struct ReceiverAdjust<F> {
old_output: F,
new_input: F,
}

impl<F: Field> ReceiverAdjust<F> {
/// Finishes the adjustment and returns the adjusted receiver's share.
pub(crate) fn finish(self, adjust: ShareAdjust<F>) -> ReceiverShare<F> {
ReceiverShare {
input: self.new_input,
output: self.old_output + self.new_input * adjust.0,
}
}
}
94 changes: 94 additions & 0 deletions crates/mpz-ole-core/src/core/sender.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! Sender shares for Oblivious Linear Function Evaluation (OLE).

use super::{Check, MaskedInput, ShareAdjust};
use mpz_fields::Field;

/// Sender share for OLE.
#[derive(Debug)]
pub struct SenderShare<F> {
input: F,
output: F,
}

impl<F: Field> SenderShare<F> {
/// Creates a new [`SenderShare`].
///
/// # Arguments
///
/// * `input` - The sender's input share.
/// * `ot_messages` - OT messages needed for the correlation.
///
/// # Returns
///
/// * The sender's share.
/// * The correlation which will be sent to the receiver.
pub(crate) fn new<const N: usize>(
input: F,
ot_messages: [[F; 2]; N],
) -> (Self, MaskedInput<N, F>) {
// Check that the right N is used depending on the needed bit size of the field.
let _: () = Check::<N, F>::IS_BITSIZE_CORRECT;

let output = ot_messages
.iter()
.enumerate()
.fold(F::zero(), |acc, (i, &[zero, _])| {
acc + F::two_pow(i as u32) * zero
});
let share = Self { input, output };

let mut ui = [F::zero(); N];
ui.iter_mut()
.zip(ot_messages)
.for_each(|(u, [zero, one])| *u = zero + -one + input);
let masked = MaskedInput(ui);

(share, masked)
}

/// Returns the sender's output share.
pub fn inner(self) -> F {
self.output
}

/// Adjust a preprocessed share.
///
/// This is an implementation of <https://crypto.stackexchange.com/questions/100634/converting-a-random-ole-oblivious-linear-function-evaluation-to-an-ole>.
///
/// # Arguments
///
/// * `target` - The new target input for the OLE.
///
/// # Returns
///
/// * The intermediate sender share, which needs the receiver's adjustment.
/// * The sender adjustment which needs to be sent to the receiver.
pub(crate) fn adjust(self, target: F) -> (SenderAdjust<F>, ShareAdjust<F>) {
(
SenderAdjust {
old_input: self.input,
old_output: self.output,
new_input: target,
},
ShareAdjust(self.input + target),
)
}
}

/// Intermediate type for share adjustment of the sender.
#[derive(Debug)]
pub struct SenderAdjust<F> {
old_input: F,
old_output: F,
new_input: F,
}

impl<F: Field> SenderAdjust<F> {
/// Finishes the adjustment and returns the adjusted sender's share.
pub(crate) fn finish(self, adjust: ShareAdjust<F>) -> SenderShare<F> {
SenderShare {
input: self.new_input,
output: self.old_output + self.old_input * adjust.0,
}
}
}
Loading