This commit is contained in:
themighty1
2022-04-29 06:31:29 +03:00
parent 1af8a949a5
commit 01e30eefe9
7 changed files with 63 additions and 0 deletions

View File

@@ -32,6 +32,16 @@ impl Block {
}
#[inline]
// OT extension Sender must break correlation between his 2 masks before
// using them in 1-out-of-2 Oblivious Transfer. Every pair of masks has
// a constant correlation: their XOR equals a delta (delta is choice bits
// in base OT).
// If masks were used as-is in OT, Receiver could infer bits of delta and break
// the OT security.
// For performance reasons, we don't use a hash but a construction which has
// tweakable correlation robustness (tcr). The GKWY20 paper shows (in
// Section 7.4) how to achieve tcr using a fixed-key cipher C instead of a
// hash, i.e. instead of Hash(x, i) we must do C(C(x) xor i) xor C(x).
pub fn hash_tweak<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,

View File

@@ -1,3 +1,6 @@
//! this crate implements the CO15 Oblivious Transfer protocol from
//! [ref1] https://eprint.iacr.org/2015/267.pdf (see Figure 1)
pub mod errors;
pub mod receiver;
pub mod sender;

View File

@@ -66,6 +66,7 @@ impl<R: Rng + CryptoRng> ReceiveCore for ReceiverCore<R> {
if choice.len() != self.count {
return Err(ReceiverCoreError::InvalidChoiceLength);
}
// point_table is A in [ref1]
let point_table = RistrettoBasepointTable::create(&sender_setup.public_key);
let zero = &Scalar::zero() * &point_table;
let one = &Scalar::one() * &point_table;
@@ -73,10 +74,14 @@ impl<R: Rng + CryptoRng> ReceiveCore for ReceiverCore<R> {
.iter()
.enumerate()
.map(|(i, b)| {
// x is b in [ref1]
let x = Scalar::random(&mut self.rng);
let c = if *b { one } else { zero };
// k is B in [ref1]
let k = c + &x * &RISTRETTO_BASEPOINT_TABLE;
// h is k_r in [ref1] == hash(A^b)
let h = Block::hash_point(&(&x * &point_table), i as u32);
// we send the k values to the Sender and keep the h values
(k, h)
})
.unzip();
@@ -101,7 +106,9 @@ impl<R: Rng + CryptoRng> ReceiveCore for ReceiverCore<R> {
.zip(hashes)
.zip(payload.encrypted_values.iter())
.map(|((c, h), v)| {
// select an encrypted value based on the choice bit
let b = if *c { v[1] } else { v[0] };
// decrypt it with the corresponding key (the key is a hash)
*h ^ b
})
.collect();

View File

@@ -18,7 +18,9 @@ pub enum State {
pub struct SenderCore<R = ChaCha12Rng> {
rng: R,
count: usize,
// private_key is random "a" in [ref1]
private_key: Option<Scalar>,
// public_key is A == g^a in [ref1]
public_key: Option<RistrettoPoint>,
state: State,
}
@@ -84,12 +86,15 @@ impl<R: Rng + CryptoRng> SendCore for SenderCore<R> {
}
let private_key = self.private_key.unwrap();
let ninputs = inputs.len();
// ys is A^a in [ref1]
let ys = private_key * self.public_key.unwrap();
let mut encrypted_values: Vec<[Block; 2]> = Vec::with_capacity(ninputs);
for (i, (input, receiver_key)) in inputs.iter().zip(receiver_setup.keys).enumerate() {
// yr is B^a in [ref1]
let yr = private_key * receiver_key;
let k0 = Block::hash_point(&yr, i as u32);
// yr - ys == (B/A)^a in [ref1]
let k1 = Block::hash_point(&(yr - ys), i as u32);
encrypted_values.push([k0 ^ input[0], k1 ^ input[1]]);
}

View File

@@ -1,3 +1,5 @@
//! This crate implements the KOS15 Oblivious Transfer extension protocol.
pub mod errors;
pub mod receiver;
pub mod sender;
@@ -20,6 +22,7 @@ const K: usize = 40;
pub trait ExtSendCore {
fn state(&self) -> sender::State;
//
fn base_setup(
&mut self,
base_sender_setup: BaseSenderSetup,
@@ -42,6 +45,8 @@ pub trait ExtRandomSendCore: ExtSendCore {
ExtSendCore::state(self)
}
// Sender in OT extension acts as Receiver in base OT and receives a setup
// message from base OT Sender and responds with its own setup message.
fn base_setup(
&mut self,
base_sender_setup: BaseSenderSetup,
@@ -74,6 +79,8 @@ pub trait ExtRandomSendCore: ExtSendCore {
pub trait ExtReceiveCore {
fn state(&self) -> &receiver::State;
// Receiver in OT extension acts as Sender in base OT and sends the first
// base OT setup message.
fn base_setup(&mut self) -> Result<BaseSenderSetup, ExtReceiverCoreError>;
fn base_send(
@@ -96,6 +103,8 @@ pub trait ExtRandomReceiveCore: ExtReceiveCore {
ExtReceiveCore::state(self)
}
// Receiver in OT extension acts as Sender in base OT and sends the first
// base OT setup message.
fn base_setup(&mut self) -> Result<BaseSenderSetup, ExtReceiverCoreError> {
ExtReceiveCore::base_setup(self)
}

View File

@@ -35,6 +35,8 @@ pub struct ExtReceiverCore<R = ChaCha12Rng, C = Aes128, OT = SenderCore<ChaCha12
base: OT,
state: State,
count: usize,
// seeds are the result of running base OT setup. They are used to seed the
// RNGs.
seeds: Option<Vec<[Block; 2]>>,
rngs: Option<Vec<[ChaCha12Rng; 2]>>,
table: Option<Vec<Vec<u8>>>,
@@ -174,12 +176,19 @@ where
.as_mut()
.ok_or(ExtReceiverCoreError::BaseOTNotSetup)?;
// The amount of choice bits must be equal to the amount of OT instances
// needed plus extra 256 instances which will be sacrificed as part of the
// KOS protocol.
let r = utils::boolvec_to_u8vec(choice);
let m = choice.len();
let ncols = if m % 8 != 0 { m + (8 - m % 8) } else { m };
let mut ts: Vec<Vec<u8>> = vec![vec![0u8; ncols / 8]; BASE_COUNT];
let mut gs: Vec<Vec<u8>> = vec![vec![0u8; ncols / 8]; BASE_COUNT];
// Note that for each row j of the matrix gs which will be sent to Sender,
// Sender knows either rng[0] or rng[1] depending on his choice bit during
// base OT. If he knows rng[1] then he will XOR it with gs[j] and get a
// row ( ts[j] ^ r ). But if he knows rng[0] then his row will be ts[j].
for j in 0..BASE_COUNT {
rngs[j][0].fill_bytes(&mut ts[j]);
rngs[j][1].fill_bytes(&mut gs[j]);
@@ -190,6 +199,12 @@ where
.map(|((g, t), r)| *g ^ *t ^ *r)
.collect();
}
// After Sender transposes his matrix, he will have a table S such that
// for each row j:
// self.table[j] = S[j], if our choice bit was 0 or
// self.table[j] = S[j] ^ delta, if our choice bit was 1
// (note that delta is known only to Sender)
self.table = Some(utils::transpose(&ts));
self.state = State::Setup(ChoiceState {
choice: Vec::from(choice),

View File

@@ -30,9 +30,17 @@ pub struct ExtSenderCore<C = Aes128, OT = ReceiverCore<ChaCha12Rng>> {
state: State,
count: usize,
sent: usize,
// choice bits for the base OT protocol
base_choice: Vec<bool>,
// seeds are the result of running base OT setup. They are used to seed the
// RNGs.
seeds: Option<Vec<Block>>,
rngs: Option<Vec<ChaCha12Rng>>,
// table's rows are such that for each row j:
// table[j] = R[j], if Receiver's choice bit was 0 or
// table[j] = R[j] ^ base_choice, if Receiver's choice bit was 1
// (where R is the table which Receiver has. Note that base_choice is known
// only to us).
table: Option<Vec<Vec<u8>>>,
}
@@ -41,6 +49,9 @@ pub struct ExtSenderPayload {
pub encrypted_values: Vec<[Block; 2]>,
}
// Having 2 messages that Receiver chooses from, we encrypt each message with
// a unique mask (i.e. XOR the message them with the mask). Receiver who knows
// only 1 mask will be able to decrypt only 1 message out of 2.
fn encrypt_values<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
cipher: &mut C,
inputs: &[[Block; 2]],
@@ -51,6 +62,9 @@ fn encrypt_values<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
let mut encrypted_values: Vec<[Block; 2]> = Vec::with_capacity(table.len());
let base_choice: [u8; 16] = utils::boolvec_to_u8vec(base_choice).try_into().unwrap();
let delta = Block::from(base_choice);
// If Receiver used *random* choice bits during OT extension setup, he will now
// instruct us to de-randomize, so that the value corresponding to his *actual*
// choice bit would be masked by that mask which Receiver knows.
let flip = flip.unwrap_or(vec![false; inputs.len()]);
for (j, (input, flip)) in inputs.iter().zip(flip).enumerate() {
let q: [u8; 16] = table[j].clone().try_into().unwrap();