cryprot_ot/lib.rs
1#![warn(clippy::unwrap_used)]
2//! CryProt-OT implements several [oblivious transfer](https://en.wikipedia.org/wiki/Oblivious_transfer) protocols.
3//!
4//! - base OT: "Simplest OT" [[CO15](https://eprint.iacr.org/2015/267)]
5//! - semi-honest OT extension: optimized [[IKNP03](https://www.iacr.org/archive/crypto2003/27290145/27290145.pdf)]
6//! protocol
7//! - malicious OT extension: optimized [[KOS15](https://eprint.iacr.org/2015/546.pdf)]
8//! protocol
9//! - silent OT extension: [[BCG+19](https://eprint.iacr.org/2019/1159)] silent OT
10//! using [[RRT23](https://eprint.iacr.org/2023/882)] code (semi-honest and malicious
11//! with [[YWL+20](https://dl.acm.org/doi/pdf/10.1145/3372297.3417276)]
12//! consistency check)
13//!
14//! This library is heavily inspired by and in parts a port of the C++ [libOTe](https://github.com/osu-crypto/libOTe) library.
15//!
16//! ## Benchmarks
17//! We continously run the benchmark suite in CI witht the results publicly
18//! available on [bencher.dev](https://bencher.dev/perf/cryprot/plots). The raw criterion output, including throughput is
19//! available in the logs of the [bench workflow](https://github.com/robinhundt/CryProt/actions/workflows/bench.yml)
20//! (latest run > benchmarks job > Run Benchmarks step).
21//!
22//! ## OT Extension Benchmarks
23//! Following are benchmark numbers for several OT protocols on a 4-core VM
24//! running on an AMD EPYC 9454P. For up to date benchmarks view the links in
25//! the benchmarks section. Each OT sender/receiver uses one worker thread and
26//! number of cores many background threads for communication (which by default
27//! is also encrypted as part of QUIC).
28//!
29//! | Benchmark | Mean Throughput (million OT/s) |
30//! |--------------------------------------------------|--------------------------|
31//! | Semi-honest R-OT ext. (2^24 R-OTs) | 51.539 |
32//! | Malicious R-OT ext. (2^24 R-OTs) | 33.663 |
33//! | Semi-Honest Silent C-OT ext. (2^21 C-OTs) | 4.2306 |
34//! | Semi-Honest Silent R-OT ext. (2^21 R-OTs) | 9.5426 |
35//! | Malicious Silent R-OT ext. (2^21 R-OTs) | 7.4180 |
36//!
37//! Silent OT will perform faster for smaller numbers of OTs at slightly
38//! increased communication.
39//!
40//! Our OT implementations should be on par or faster than those in libOTe. In
41//! the future we want to benchmark libOTe on the same hardware for a fair
42//! comparison.
43//!
44//! **Base OT Benchmark:**
45//!
46//! | Benchmark | Mean Time (ms) |
47//! |---------------|---------------|
48//! | 128 base R-OTs | 28.001 |
49
50use std::{fmt::Debug, future::Future};
51
52use cryprot_core::{Block, buf::Buf};
53use cryprot_net::Connection;
54use rand::{CryptoRng, Rng, SeedableRng, distr, prelude::Distribution, rngs::StdRng};
55use subtle::Choice;
56
57pub mod adapter;
58pub mod base;
59pub mod extension;
60pub mod noisy_vole;
61pub mod phase;
62pub mod silent_ot;
63
64/// Trait for OT receivers/senders which hold a [`Connection`].
65pub trait Connected {
66 fn connection(&mut self) -> &mut Connection;
67}
68
69impl<C: Connected> Connected for &mut C {
70 fn connection(&mut self) -> &mut Connection {
71 (*self).connection()
72 }
73}
74
75/// A random OT sender.
76pub trait RotSender: Connected + Send {
77 /// The error type returned by send operations.
78 type Error;
79
80 /// Send `count` many random OTs.
81 ///
82 /// For better performance, use [RotSender::send_into] with an existing
83 /// [`Buf`].
84 fn send(
85 &mut self,
86 count: usize,
87 ) -> impl Future<Output = Result<Vec<[Block; 2]>, Self::Error>> + Send {
88 async move {
89 let mut ots = Vec::zeroed(count);
90 self.send_into(&mut ots).await?;
91 Ok(ots)
92 }
93 }
94
95 /// Store OTs in the provided [`Buf`]fer.
96 ///
97 /// Note that implementations might temporarily take ownership of the
98 /// [`Buf`] pointed to by `ots`. If the future returned by this method is
99 /// dropped befire completion, `ots` might point at an empty `Buf`.
100 ///
101 /// For large number of OTs, using
102 /// [`HugePageMemory`](`cryprot_core::alloc::HugePageMemory`) can
103 /// significantly improve performance on Linux systems.
104 fn send_into(
105 &mut self,
106 ots: &mut impl Buf<[Block; 2]>,
107 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
108}
109
110/// A random OT receiver.
111pub trait RotReceiver: Connected + Send {
112 /// The error type returned by receive operations.
113 type Error;
114
115 /// Receive `choices.len()` many random OTs.
116 ///
117 /// For better performance, use [RotReceiver::receive_into] with an existing
118 /// [`Buf`].
119 fn receive(
120 &mut self,
121 choices: &[Choice],
122 ) -> impl Future<Output = Result<Vec<Block>, Self::Error>> + Send {
123 async {
124 let mut ots = Vec::zeroed(choices.len());
125 self.receive_into(&mut ots, choices).await?;
126 Ok(ots)
127 }
128 }
129
130 /// Store OTs in the provided [`Buf`]fer.
131 ///
132 /// Note that implementations might temporarily take ownership of the
133 /// [`Buf`] pointed to by `ots`. If the future returned by this method is
134 /// dropped befire completion, `ots` might point at an empty `Buf`.
135 ///
136 /// For large number of OTs, using
137 /// [`HugePageMemory`](`cryprot_core::alloc::HugePageMemory`) can
138 /// significantly improve performance on Linux systems.
139 fn receive_into(
140 &mut self,
141 ots: &mut impl Buf<Block>,
142 choices: &[Choice],
143 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
144}
145
146impl<S: RotSender> RotSender for &mut S {
147 type Error = S::Error;
148
149 fn send_into(
150 &mut self,
151 ots: &mut impl Buf<[Block; 2]>,
152 ) -> impl Future<Output = Result<(), Self::Error>> + Send {
153 (*self).send_into(ots)
154 }
155}
156
157impl<R: RotReceiver> RotReceiver for &mut R {
158 type Error = R::Error;
159
160 fn receive_into(
161 &mut self,
162 ots: &mut impl Buf<Block>,
163 choices: &[Choice],
164 ) -> impl Future<Output = Result<(), Self::Error>> + Send {
165 (*self).receive_into(ots, choices)
166 }
167}
168
169/// Marker trait for R-OT Senders that are paired with a random choice receiver.
170pub trait RandChoiceRotSender {}
171
172/// Returns a random choice vector alongside OTs.
173pub trait RandChoiceRotReceiver: Connected + Send {
174 type Error;
175
176 /// Receive `count` many random OTs alongside their respective choices.
177 fn rand_choice_receive(
178 &mut self,
179 count: usize,
180 ) -> impl Future<Output = Result<(Vec<Block>, Vec<Choice>), Self::Error>> + Send {
181 async move {
182 let mut ots = Vec::zeroed(count);
183 let choices = self.rand_choice_receive_into(&mut ots).await?;
184 Ok((ots, choices))
185 }
186 }
187
188 /// Receive `ots.len()` many random OTs, stored into the buffer pointed to
189 /// by `ots` and returns the corresponding choices.
190 fn rand_choice_receive_into(
191 &mut self,
192 ots: &mut impl Buf<Block>,
193 ) -> impl Future<Output = Result<Vec<Choice>, Self::Error>> + Send;
194}
195
196/// Adapt any [`RotReceiver`] into a [`RandChoiceRotReceiver`] by securely
197/// sampling the random choices using [`random_choices`].
198impl<R: RotReceiver> RandChoiceRotReceiver for R {
199 type Error = R::Error;
200
201 async fn rand_choice_receive_into(
202 &mut self,
203 ots: &mut impl Buf<Block>,
204 ) -> Result<Vec<Choice>, Self::Error> {
205 let choices = random_choices(ots.len(), &mut StdRng::from_os_rng());
206 self.receive_into(ots, &choices).await?;
207 Ok(choices)
208 }
209}
210
211/// Correlated OT sender (C-OT).
212pub trait CotSender: Connected + Send {
213 type Error;
214
215 /// Random OTs correlated by the `correlation`` function..
216 ///
217 /// The correlation function is passed the index of a C-OT and must output
218 /// the correlation for this C-OT.
219 fn correlated_send<F>(
220 &mut self,
221 count: usize,
222 correlation: F,
223 ) -> impl Future<Output = Result<Vec<Block>, Self::Error>> + Send
224 where
225 F: FnMut(usize) -> Block + Send,
226 {
227 async move {
228 let mut ots = Vec::zeroed(count);
229 self.correlated_send_into(&mut ots, correlation).await?;
230 Ok(ots)
231 }
232 }
233
234 fn correlated_send_into<B, F>(
235 &mut self,
236 ots: &mut B,
237 correlation: F,
238 ) -> impl Future<Output = Result<(), Self::Error>> + Send
239 where
240 B: Buf<Block>,
241 F: FnMut(usize) -> Block + Send;
242}
243
244pub trait CotReceiver: Connected + Send {
245 type Error;
246
247 fn correlated_receive(
248 &mut self,
249 choices: &[Choice],
250 ) -> impl Future<Output = Result<Vec<Block>, Self::Error>> + Send {
251 async {
252 let mut ots = Vec::zeroed(choices.len());
253 self.correlated_receive_into(&mut ots, choices).await?;
254 Ok(ots)
255 }
256 }
257
258 fn correlated_receive_into<B>(
259 &mut self,
260 ots: &mut B,
261 choices: &[Choice],
262 ) -> impl Future<Output = Result<(), Self::Error>> + Send
263 where
264 B: Buf<Block>;
265}
266
267/// Marker trait for OT implementations secure against semi-honest adversaries.
268pub trait SemiHonest {}
269
270/// Marker trait for OT implementations secure against malicious adversaries.
271pub trait Malicious: SemiHonest {}
272
273/// Used to abstract over [`SemiHonestMarker`] or [`MaliciousMarker`]
274pub trait Security: Send + Sync + Debug + Copy + Clone + private::Sealed {
275 const MALICIOUS_SECURITY: bool;
276}
277
278/// Used as a marker type for semi-honest security OT implementation.
279#[derive(Copy, Clone, Debug)]
280pub struct SemiHonestMarker;
281
282impl Security for SemiHonestMarker {
283 const MALICIOUS_SECURITY: bool = false;
284}
285
286/// Used as a marker type for malicious security OT implementation.
287#[derive(Copy, Clone, Debug)]
288pub struct MaliciousMarker;
289
290impl Security for MaliciousMarker {
291 const MALICIOUS_SECURITY: bool = true;
292}
293
294mod private {
295 pub trait Sealed {}
296
297 impl Sealed for super::SemiHonestMarker {}
298
299 impl Sealed for super::MaliciousMarker {}
300}
301
302/// Sample `count` many [`Choice`]es using the provided rng.
303pub fn random_choices<RNG: Rng + CryptoRng>(count: usize, rng: &mut RNG) -> Vec<Choice> {
304 let uniform = distr::Uniform::new(0, 2).expect("correct range");
305 uniform
306 .sample_iter(rng)
307 .take(count)
308 .map(Choice::from)
309 .collect()
310}