From 96afaf307864a2baadd3f27d4e55cd050aca247c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Kr=C3=B3l?= Date: Fri, 24 Jul 2026 09:47:43 +0200 Subject: [PATCH] feat(kos): domain-separate instances with a per-instance salt Add an `instance_id` to `kos::Sender`/`Receiver`, mixed into each base-OT-derived setup PRG seed via a tweakable correlation-robust hash (`FIXED_KEY_AES.tccr(instance_id, seed)`). KOS instances that share one global `delta` and the same base OT then derive independent extension transcripts, so per-instance leakage can no longer be composed to sample or recover `delta`. The mix is non-invertible in `seed`: a linear XOR mix would let a malicious receiver, who controls the base-OT seeds, pre-compensate them to cancel the salt and collapse two instances onto one PRG stream. API-breaking: `Sender::new`/`Receiver::new` gain an `instance_id: Block`. Paired sender and receiver must use the same id; distinct instances reusing one `delta` must use distinct ids. --- crates/ot-core/src/kos.rs | 20 ++++++++++---------- crates/ot-core/src/kos/receiver.rs | 12 +++++++++--- crates/ot-core/src/kos/sender.rs | 13 ++++++++++--- crates/ot/src/kos.rs | 4 ++-- crates/ot/src/kos/receiver.rs | 4 ++-- crates/ot/src/kos/sender.rs | 4 ++-- 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/crates/ot-core/src/kos.rs b/crates/ot-core/src/kos.rs index c6708916d..8521437e8 100644 --- a/crates/ot-core/src/kos.rs +++ b/crates/ot-core/src/kos.rs @@ -173,8 +173,8 @@ mod tests { ) { let count = 128; - let sender = Sender::new(SenderConfig::default(), delta); - let receiver = Receiver::new(ReceiverConfig::default()); + let sender = Sender::new(SenderConfig::default(), delta, Block::ZERO); + let receiver = Receiver::new(ReceiverConfig::default(), Block::ZERO); let mut sender = sender.setup(sender_seeds); let mut receiver = receiver.setup(receiver_seeds); @@ -226,8 +226,8 @@ mod tests { let count = sender_config.batch_size() * 3; - let sender = Sender::new(sender_config, delta); - let receiver = Receiver::new(receiver_config); + let sender = Sender::new(sender_config, delta, Block::ZERO); + let receiver = Receiver::new(receiver_config, Block::ZERO); let mut sender = sender.setup(sender_seeds); let mut receiver = receiver.setup(receiver_seeds); @@ -276,8 +276,8 @@ mod tests { ) { let count = 128; - let sender = Sender::new(SenderConfig::default(), delta); - let receiver = Receiver::new(ReceiverConfig::default()); + let sender = Sender::new(SenderConfig::default(), delta, Block::ZERO); + let receiver = Receiver::new(ReceiverConfig::default(), Block::ZERO); let mut sender = sender.setup(sender_seeds); let mut receiver = receiver.setup(receiver_seeds); @@ -308,8 +308,8 @@ mod tests { ) { let count = 128; - let sender = Sender::new(SenderConfig::default(), delta); - let receiver = Receiver::new(ReceiverConfig::default()); + let sender = Sender::new(SenderConfig::default(), delta, Block::ZERO); + let receiver = Receiver::new(ReceiverConfig::default(), Block::ZERO); let mut sender = sender.setup(sender_seeds); let mut receiver = receiver.setup(receiver_seeds); @@ -340,8 +340,8 @@ mod tests { ) { let count = 128; - let sender = Sender::new(SenderConfig::default(), delta); - let receiver = Receiver::new(ReceiverConfig::default()); + let sender = Sender::new(SenderConfig::default(), delta, Block::ZERO); + let receiver = Receiver::new(ReceiverConfig::default(), Block::ZERO); let mut sender = sender.setup(sender_seeds); let mut receiver = receiver.setup(receiver_seeds); diff --git a/crates/ot-core/src/kos/receiver.rs b/crates/ot-core/src/kos/receiver.rs index 54c305d99..6ae49a778 100644 --- a/crates/ot-core/src/kos/receiver.rs +++ b/crates/ot-core/src/kos/receiver.rs @@ -8,7 +8,7 @@ use crate::{ use itybity::{BitLength, FromBitIterator, IntoBitIterator, IntoBits}; use mpz_common::future::{MaybeDone, Sender, new_output}; -use mpz_core::{Block, prg::Prg}; +use mpz_core::{Block, aes::FIXED_KEY_AES, prg::Prg}; use rand::{Rng as _, RngExt, SeedableRng}; @@ -28,6 +28,7 @@ pub struct Receiver { alloc: usize, transfer_id: TransferId, queue: VecDeque, + instance_id: Block, state: T, } @@ -47,7 +48,7 @@ impl Receiver { /// # Arguments /// /// * `config` - The Receiver's configuration - pub fn new(config: ReceiverConfig) -> Self { + pub fn new(config: ReceiverConfig, instance_id: Block) -> Self { Receiver { config, // We need to extend SSP OTs for the consistency check. @@ -56,6 +57,7 @@ impl Receiver { alloc: SSP, transfer_id: TransferId::default(), queue: VecDeque::default(), + instance_id, state: state::Initialized {}, } } @@ -66,15 +68,19 @@ impl Receiver { /// /// * `seeds` - The receiver's rng seeds pub fn setup(self, seeds: [[Block; 2]; CSP]) -> Receiver { + let instance_id = self.instance_id; Receiver { config: self.config, alloc: self.alloc, transfer_id: self.transfer_id, queue: self.queue, + instance_id, state: state::Extension { rngs: seeds .into_iter() - .map(|seeds| seeds.map(Prg::from_seed)) + .map(|seeds| { + seeds.map(|seed| Prg::from_seed(FIXED_KEY_AES.tccr(instance_id, seed))) + }) .collect(), msgs: Vec::default(), choices: Vec::default(), diff --git a/crates/ot-core/src/kos/sender.rs b/crates/ot-core/src/kos/sender.rs index 00b142079..d5cef4ab2 100644 --- a/crates/ot-core/src/kos/sender.rs +++ b/crates/ot-core/src/kos/sender.rs @@ -8,7 +8,7 @@ use crate::{ use itybity::ToBits; use mpz_common::future::{MaybeDone, Sender as OutputSender, new_output}; -use mpz_core::{Block, prg::Prg}; +use mpz_core::{Block, aes::FIXED_KEY_AES, prg::Prg}; use rand::{Rng as _, RngExt, SeedableRng, rng}; @@ -33,6 +33,7 @@ pub struct Sender { queue: VecDeque, transfer_id: TransferId, delta: Block, + instance_id: Block, state: T, } @@ -53,7 +54,7 @@ impl Sender { /// /// * `config` - Sender's configuration. /// * `delta` - Global COT correlation. - pub fn new(config: SenderConfig, delta: Block) -> Self { + pub fn new(config: SenderConfig, delta: Block, instance_id: Block) -> Self { Sender { config, // We need to extend SSP OTs for the consistency check. @@ -63,6 +64,7 @@ impl Sender { transfer_id: TransferId::default(), queue: VecDeque::default(), delta, + instance_id, state: state::Initialized::default(), } } @@ -73,14 +75,19 @@ impl Sender { /// /// * `seeds` - The rng seeds chosen during base OT pub fn setup(self, seeds: [Block; CSP]) -> Sender { + let instance_id = self.instance_id; Sender { config: self.config, alloc: self.alloc, transfer_id: self.transfer_id, queue: self.queue, delta: self.delta, + instance_id, state: state::Extension { - rngs: seeds.into_iter().map(Prg::from_seed).collect(), + rngs: seeds + .into_iter() + .map(|seed| Prg::from_seed(FIXED_KEY_AES.tccr(instance_id, seed))) + .collect(), keys: Vec::default(), extended: false, unchecked_qs_trans: Vec::default(), diff --git a/crates/ot/src/kos.rs b/crates/ot/src/kos.rs index 1a5acf744..fa7fb0b90 100644 --- a/crates/ot/src/kos.rs +++ b/crates/ot/src/kos.rs @@ -31,8 +31,8 @@ mod tests { let mut rng = StdRng::seed_from_u64(0); let (base_sender, base_receiver) = ideal_ot(); let delta = Block::random(&mut rng); - let sender = Sender::new(SenderConfig::default(), delta, base_receiver); - let receiver = Receiver::new(ReceiverConfig::default(), base_sender); + let sender = Sender::new(SenderConfig::default(), delta, Block::ZERO, base_receiver); + let receiver = Receiver::new(ReceiverConfig::default(), Block::ZERO, base_sender); test_rcot(sender, receiver, 128, 1).await; } diff --git a/crates/ot/src/kos/receiver.rs b/crates/ot/src/kos/receiver.rs index 3c32d2461..65368df22 100644 --- a/crates/ot/src/kos/receiver.rs +++ b/crates/ot/src/kos/receiver.rs @@ -41,11 +41,11 @@ impl Receiver { /// /// * `config` - The Receiver's configuration. /// * `base_ot` - Base OT. - pub fn new(config: ReceiverConfig, base_ot: BaseOT) -> Self { + pub fn new(config: ReceiverConfig, instance_id: Block, base_ot: BaseOT) -> Self { Self { state: State::Initialized { base_ot, - receiver: Core::new(config), + receiver: Core::new(config, instance_id), }, } } diff --git a/crates/ot/src/kos/sender.rs b/crates/ot/src/kos/sender.rs index 01fc9f333..d04bc1877 100644 --- a/crates/ot/src/kos/sender.rs +++ b/crates/ot/src/kos/sender.rs @@ -41,11 +41,11 @@ impl Sender { /// * `config` - The Sender's configuration. /// * `delta` - Global COT correlation. /// * `base_ot` - Base OT. - pub fn new(config: SenderConfig, delta: Block, base_ot: BaseOT) -> Self { + pub fn new(config: SenderConfig, delta: Block, instance_id: Block, base_ot: BaseOT) -> Self { Self { state: State::Initialized { base_ot, - sender: Core::new(config, delta), + sender: Core::new(config, delta, instance_id), }, } }