#secret-sharing #fss #shared-secret #secret-key #dcf #dpf

fss-rs

Function secret sharing including distributed comparison & point functions

7 releases

0.6.0 Jul 30, 2024
0.5.0 Jun 28, 2024
0.4.4 Jun 19, 2024
0.4.3 Apr 10, 2024
0.4.1 Jan 28, 2024

#256 in Cryptography

25 downloads per month

Apache-2.0

63KB
1K SLoC

fss

Crates.io docs.rs

Function secret sharing including distributed comparison & point functions

Get Started

First add the crate as a dependency:

# Run in your project directory
cargo add fss-rs

By default the PRG implementations and multi-threading are included. You can disable the default feature to select by yourself. If you are on ARM machines, see the Performance section for hardware acceleration.

Then construct a PRG implementing the corresponding Prg trait, and construct an impl DcfImpl or DpfImpl to use the PRG. Check the doc comment for the meanings of the generic parameters. If you want to set the bit length of the domain (input domain, instead of the range that is the output domain), check new_with_filter method.

use rand::prelude::*;

// Matyas-Meyer-Oseas (via AES128) provides 128-bit security and should be enough.
// Hirose (via AES256) still only provides 128-bit security because the output is not chained.
// But Hirose can be helpful is you are forced to choose AES256.
use fss_rs::prg::Aes128MatyasMeyerOseasPrg;
use fss_rs::dcf::{Dcf, DcfImpl};

let keys: [[u8; 16]; 4] = thread_rng().gen();
let prg = Aes128MatyasMeyerOseasPrg::<16, 2, 4>::new(std::array::from_fn(|i| &keys[i]));
// DCF for example
let dcf = DcfImpl::<16, 16, _>::new(prg);

Finally, for key generation, construct the function to be shared together with 2 init keys, and call gen:

use fss_rs::dcf::{BoundState, CmpFn};
use fss_rs::group::byte::ByteGroup;
use fss_rs::group::Group;

let s0s: [[u8; 16]; 2] = thread_rng().gen();
let f = CmpFn {
  alpha: thread_rng().gen(),
  // `ByteGroup` for example
  beta: ByteGroup(thread_rng().gen()),
  bound: BoundState::LtBeta,
};
let keys = dcf.gen(&f, [&s0s[0], &s0s[1]]);

See the doc comment of the returned Share for how to split it into 2 shares. The 2 shares are combined like this because they share many fields.

And for evaluation, construct the evaluated points, reverse the output buffer, and call eval:

let x: [u8; 16] = thread_rng().gen();
let mut y = ByteGroup::zero();
// The 2 parties use `true` / `false` to evaluate independently
dcf.eval(false, &k, &[&x], &mut [&mut y]);

For full domain evaluation, use full_eval instead. While similar to eval, full_eval does not accept a vector of x, and instead expects a vector of y whose length is 2 ** (IN_BLEN * 8) to store all evaluated y.

More examples are available as benchmarks in the benches dir

References

Performance

The hot path of the project is PRG and XOR operations. We use the aes crate of RustCrypto for PRG. For XOR operations, We use Rust std SIMD for nightly Rust, or the wide crate that has 9M all-time downloads for stable Rust.

For PRG, enabling archtecture-specified CPU intrinsics can largely boost the performance. The aes crate by default performs runtime detection of CPU intrinsics and uses them if available.

For x86/x86_64 (i686/x86_64 in targets), AES-NI is used if available, which works out-of-the-box.

For ARMv8 (aarch64 in targets), while ARMv8 Cryptography Extensions is supported, to use it, in addition to the above, you need to set some flags to enable it. See the doc of the aes crate for details. We also quote the section here:

From https://docs.rs/aes/0.8.3/aes/#armv8-intrinsics-rust-161

ARMv8 intrinsics (Rust 1.61+)

On aarch64 targets including aarch64-apple-darwin (Apple M1) and Linux targets such as aarch64-unknown-linux-gnu and aarch64-unknown-linux-musl, support for using AES intrinsics provided by the ARMv8 Cryptography Extensions is available when using Rust 1.61 or above, and can be enabled using the aes_armv8 configuration flag.

On Linux and macOS, when the aes_armv8 flag is enabled support for AES intrinsics is autodetected at runtime. On other platforms the aes target feature must be enabled via RUSTFLAGS.

Benchmark

We use Criterion.rs for benchmarking. Criterion.rs reports criterion.tar.zst are included in releases.

We use a (my) laptop as the benchmarking machine. It is charged with the power plugged in when benchmarking. Its CPU is AMD Ryzen 7 5800H, which is 8C16T. We use rayon as the data-parallelism library, which establishes 16 threads when benchmarking with multithreading. We ensure its memory is enough for benchmarking. Notice that we do not close other programs as many as possible to reduce scheduling, though all GUI applications except VSCode are closed and we do avoid doing any other things at the same time.

Changelog

Correctness fixes:

  • fss_rs::group::int_prime impl is corrected at v0.6.0.
  • fss_rs::dcf::prg::Aes256HirosePrg impl is corrected at v0.4.3 of the crate fss-rs, v0.5.2 and v0.6.3 of the crate dcf, and v0.5.2 of the crate dpf-fss (since it depends on the crate dcf).

See CHANGELOG.md for the full changelog.

License

Copyright (C) 2023 Yulong Ming (myl7)

SPDX-License-Identifier: Apache-2.0

Dependencies

~0.9–1.5MB
~36K SLoC