GaloisInc / swanky

A suite of rust libraries for secure multi-party computation
MIT License
264 stars 55 forks source link

Implement Ferret (correlated OT extension) and improve the AbstractChannel trait. #11

Closed rot256 closed 3 months ago

rot256 commented 3 years ago

This pull request achieves two things:

Improvements to the AbstractChannel trait

Previously the AbstractChannel (scuttlebutt/src/channel.rs) trait looked something like this:

/// A trait for managing I/O. `AbstractChannel`s are clonable, and provide basic
/// read/write capabilities for both common and scuttlebutt-specific types.
pub trait AbstractChannel {
    /// Read a slice of `u8`s from the channel.
    fn read_bytes(&mut self, bytes: &mut [u8]) -> Result<()>;
    /// Write a slice of `u8`s to the channel.
    fn write_bytes(&mut self, bytes: &[u8]) -> Result<()>;
    /// Flush the channel.
    fn flush(&mut self) -> Result<()>;
    /// Clone the channel.
    fn clone(&self) -> Self
    where
        Self: Sized;
    /// Read `nbytes` from the channel, and return it as a `Vec`.
    fn read_vec(&mut self, nbytes: usize) -> Result<Vec<u8>> {...}

    /// Write a `bool` to the channel.
    #[inline(always)]
    fn write_bool(&mut self, b: bool) -> Result<()> {...}

    /// Read a `bool` from the channel.
    #[inline(always)]
    fn read_bool(&mut self) -> Result<bool> {...}

    fn write_u8(&mut self, s: u8) -> Result<()> {...}

    /// Read a `u8` from the channel.
    #[inline(always)]
    fn read_u8(&mut self) -> Result<u8> {

    /// Write a `u16` to the channel.
    #[inline(always)]
    fn write_u16(&mut self, s: u16) -> Result<()> {...}

    /// Read a `u16` from the channel.
    #[inline(always)]
    fn read_u16(&mut self) -> Result<u16> {...}

    /// Write a `u32` to the channel.
    #[inline(always)]
    fn write_u32(&mut self, s: u32) -> Result<()> {...}

    /// Read a `u32` from the channel.
    #[inline(always)]
    fn read_u32(&mut self) -> Result<u32> {... }

    /// Write a `u64` to the channel.
    #[inline(always)]
    fn write_u64(&mut self, s: u64) -> Result<()> { ... }

    /// Read a `u64` from the channel.
    #[inline(always)]
    fn read_u64(&mut self) -> Result<u64> {...}

    /// Write a `usize` to the channel.
    #[inline(always)]
    fn write_usize(&mut self, s: usize) -> Result<()> {...}

    /// Read a `usize` from the channel.
    #[inline(always)]
    fn read_usize(&mut self) -> Result<usize> {...}

    /// Write a `Block` to the channel.
    #[inline(always)]
    fn write_block(&mut self, b: &Block) -> Result<()> {...}

    /// Read a `Block` from the channel.
    #[inline(always)]
    fn read_block(&mut self) -> Result<Block> {...}

    /// Read `n` `Block`s from the channel.
    #[inline(always)]
    fn read_blocks(&mut self, n: usize) -> Result<Vec<Block>> {...}

    /// Write a `Block512` to the channel.
    #[inline(always)]
    fn write_block512(&mut self, b: &Block512) -> Result<()> {...}

    /// Read a `Block512` from the channel.
    #[inline(always)]
    fn read_block512(&mut self) -> Result<Block512> {...}

    /// Write a `RistrettoPoint` to the channel.
    #[cfg(feature = "curve25519-dalek")]
    #[inline(always)]
    fn write_pt(&mut self, pt: &RistrettoPoint) -> Result<()> {...}

    /// Read a `RistrettoPoint` from the channel.
    #[cfg(feature = "curve25519-dalek")]
    #[inline(always)]
    fn read_pt(&mut self) -> Result<RistrettoPoint> {...}
}

The new trait looks like this:

/// A trait for managing I/O. `AbstractChannel`s are clonable, and provide basic
/// read/write capabilities for both common and scuttlebutt-specific types.
pub trait AbstractChannel: Clone {
    /// Read a slice of `u8`s from the channel.
    fn read_bytes(&mut self, bytes: &mut [u8]) -> Result<()>;

    /// Write a slice of `u8`s to the channel.
    fn write_bytes(&mut self, bytes: &[u8]) -> Result<()>;

    /// Flush the channel.
    fn flush(&mut self) -> Result<()>;

    /// Receive a value from the channel
    fn receive<R: Receivable>(&mut self) -> Result<R> { ... }

    /// Receive n instances of a type from the channel
    fn receive_n<R: Receivable>(&mut self, n: usize) -> Result<Vec<R>> { ... }

    /// Send a value to the channel (by reference or by value)
    fn send<S: Sendable>(&mut self, value: S) -> Result<()> { ... }

    /// Read `nbytes` from the channel, and return it as a `Vec`.
    fn read_vec(&mut self, nbytes: usize) -> Result<Vec<u8>> { ... }
}

And adds two new traits Sendable and Receivable. I believe this is an improvement for the following reasons:

  1. It enables deriving implementations for e.g. [T; n] where T: Receivable to receive an array of any size of any Receivable type. Because of this e.g. channel.send(&vs[..]) now works for any vector vs of a Sendable type.
  2. It enables crates outside of swanky to implement Receivable / Sendable for custom types and use these with the AbstractChannel trait from swanky.
  3. The notation is arguably nicer: e.g. let x: usize = channel.receive()?; rather than let x = channel.receive_usize()?;.
  4. Avoids modifying the traits when we wish to send/receive new types in the future.
  5. If we desire we can implement a #[derive(Sendable)] / #[derive(Receivable)] for structs.

I have migrated to the new interface throughout Swanky.

Implementation of Ferret

I have implemented the Ferret maliciously secure correlated OT extension protocol. The implementation includes both both uniform and regular error. The LPN parameters used for either are those in the paper. The base COT is KOS15.

Below an illustration of the interface (over a unix socket):

use std::thread::spawn;

use rand::{rngs::OsRng, Rng};

use scuttlebutt::{channel::unix_channel_pair, Block};
use ocelot::ot::ferret::{FerretReceiver, FerretSender};

const GEN_COTS: usize = 500_000_000;

fn main() {
    let (mut c1, mut c2) = unix_channel_pair();

    let handle = spawn(move || {
        let delta: Block = OsRng.gen();
        let mut sender = FerretSender::init(delta, &mut c1, &mut OsRng).unwrap();
        for _ in 0..GEN_COTS {
            let _output: Block = sender.cot(&mut c1, &mut OsRng).unwrap();
        }
    });

    let mut receiver = FerretReceiver::init(&mut c2, &mut OsRng).unwrap();
    for n in 0..GEN_COTS {
        let _cot: (bool, Block) = receiver.cot(&mut c2, &mut OsRng).unwrap();
    }
    handle.join().unwrap();
}
amaloz commented 2 years ago

Thanks! We're doing some re-org of things like the AbstractChannel trait internally, and when we open source that we can merge this in.

TomMD commented 1 year ago

Ping. Half a year has passed on the PR.