Skip to content

Commit

Permalink
Merge pull request #32 from nucypher/simple-decryption-precomputed
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Jan 26, 2023
2 parents 50f8269 + bdda3d1 commit cd50056
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 29 deletions.
2 changes: 1 addition & 1 deletion ferveo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ version = "0.10.0"
features = ["alloc"]

[dev-dependencies]
criterion = "0.3"
criterion = "0.3" # supports pprof, # TODO: Figure out if/how we can update to 0.4
pprof = { version = "0.6", features = ["flamegraph", "criterion"] }

[[example]]
Expand Down
4 changes: 2 additions & 2 deletions tpke-wasm/benches/benchmarks.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::redundant_closure)]

use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn bench_encrypt_combine(c: &mut Criterion) {
Expand Down Expand Up @@ -55,14 +57,12 @@ pub fn bench_encrypt_combine(c: &mut Criterion) {
let encrypt_fn = bench_encrypt(*num_shares, *num_shares);
group.measurement_time(core::time::Duration::new(45, 0));
group.bench_function(format!("tpke-wasm::encrypt - num_shares={}, num_entities={}, threshold={}", num_shares, num_shares, num_shares), |b| {
#[allow(clippy::redundant_closure)]
b.iter(|| encrypt_fn())
});

let combine_fn = bench_combine(*num_shares, *num_shares);
group.measurement_time(core::time::Duration::new(45, 0));
group.bench_function(format!("tpke-wasm::combine - num_shares={}, num_entities={}, threshold={}", num_shares, num_shares, num_shares), |b| {
#[allow(clippy::redundant_closure)]
b.iter(|| combine_fn())
});
}
Expand Down
10 changes: 2 additions & 8 deletions tpke/benches/arkworks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,17 +265,11 @@ pub fn bench_random_poly(c: &mut Criterion) {
};
group.bench_function(
BenchmarkId::new("random_polynomial_ark", threshold),
|b| {
#[allow(clippy::redundant_closure)]
b.iter(|| ark())
},
|b| b.iter(|| ark()),
);
group.bench_function(
BenchmarkId::new("random_polynomial_naive", threshold),
|b| {
#[allow(clippy::redundant_closure)]
b.iter(|| naive())
},
|b| b.iter(|| naive()),
);
}
}
Expand Down
57 changes: 51 additions & 6 deletions tpke/benches/tpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use criterion::{
black_box, criterion_group, criterion_main, BenchmarkId, Criterion,
};
use group_threshold_cryptography::*;
use itertools::Itertools;
use rand::prelude::StdRng;
use rand_core::{RngCore, SeedableRng};

Expand Down Expand Up @@ -56,9 +57,6 @@ impl SetupFast {
let prepared_key_shares =
prepare_combine_fast(&pub_contexts, &decryption_shares);

let _shared_secret =
share_combine_fast(&decryption_shares, &prepared_key_shares);

let shared_secret =
share_combine_fast(&decryption_shares, &prepared_key_shares);

Expand Down Expand Up @@ -87,7 +85,7 @@ struct SetupSimple {
contexts: Vec<PrivateDecryptionContextSimple<E>>,
pub_contexts: Vec<PublicDecryptionContextSimple<E>>,
decryption_shares: Vec<DecryptionShareSimple<E>>,
lagrange: Vec<Fr>,
lagrange_coeffs: Vec<Fr>,
}

impl SetupSimple {
Expand Down Expand Up @@ -131,7 +129,7 @@ impl SetupSimple {
contexts,
pub_contexts,
decryption_shares,
lagrange,
lagrange_coeffs: lagrange,
}
}
}
Expand Down Expand Up @@ -173,6 +171,24 @@ pub fn bench_create_decryption_share(c: &mut Criterion) {
})
}
};
let simple_precomputed = {
let setup = SetupSimple::new(shares_num, MSG_SIZE_CASES[0], rng);
move || {
black_box(
setup
.contexts
.iter()
.zip_eq(setup.lagrange_coeffs.iter())
.map(|(context, lagrange_coeff)| {
context.create_share_precomputed(
&setup.shared.ciphertext,
lagrange_coeff,
)
})
.collect::<Vec<_>>(),
);
}
};

group.bench_function(
BenchmarkId::new("share_create_fast", shares_num),
Expand All @@ -182,6 +198,10 @@ pub fn bench_create_decryption_share(c: &mut Criterion) {
BenchmarkId::new("share_create_simple", shares_num),
|b| b.iter(|| simple()),
);
group.bench_function(
BenchmarkId::new("share_create_simple_precomputed", shares_num),
|b| b.iter(|| simple_precomputed()),
);
}
}

Expand Down Expand Up @@ -243,7 +263,28 @@ pub fn bench_share_combine(c: &mut Criterion) {
move || {
black_box(share_combine_simple::<E>(
&setup.decryption_shares,
&setup.lagrange,
&setup.lagrange_coeffs,
));
}
};
let simple_precomputed = {
let setup = SetupSimple::new(shares_num, MSG_SIZE_CASES[0], rng);

let decryption_shares: Vec<_> = setup
.contexts
.iter()
.zip_eq(setup.lagrange_coeffs.iter())
.map(|(context, lagrange_coeff)| {
context.create_share_precomputed(
&setup.shared.ciphertext,
lagrange_coeff,
)
})
.collect();

move || {
black_box(share_combine_simple_precomputed::<E>(
&decryption_shares,
));
}
};
Expand All @@ -256,6 +297,10 @@ pub fn bench_share_combine(c: &mut Criterion) {
BenchmarkId::new("share_combine_simple", shares_num),
|b| b.iter(|| simple()),
);
group.bench_function(
BenchmarkId::new("share_combine_simple_precomputed", shares_num),
|b| b.iter(|| simple_precomputed()),
);
}
}

Expand Down
12 changes: 10 additions & 2 deletions tpke/src/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ pub fn prepare_combine_fast<E: PairingEngine>(
pub fn prepare_combine_simple<E: PairingEngine>(
domain: &[E::Fr],
) -> Vec<E::Fr> {
// See https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm
// In this formula x_i = 0, hence numerator is x_m
// See https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm
lagrange_basis_at::<E>(domain, &E::Fr::zero())
}

/// Calculate lagrange coefficients using optimized formula
/// See https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm
pub fn lagrange_basis_at<E: PairingEngine>(
shares_x: &[E::Fr],
x_i: &E::Fr,
Expand Down Expand Up @@ -95,6 +94,15 @@ pub fn share_combine_simple<E: PairingEngine>(
product_of_shares
}

pub fn share_combine_simple_precomputed<E: PairingEngine>(
shares: &[DecryptionShareSimplePrecomputed<E>],
) -> E::Fqk {
// s = ∏ C_{λ_i}, where λ_i is the Lagrange coefficient for i
shares
.iter()
.fold(E::Fqk::one(), |acc, c_i| acc * c_i.decryption_share)
}

#[cfg(test)]
mod tests {
type Fr = <ark_bls12_381::Bls12_381 as ark_ec::PairingEngine>::Fr;
Expand Down
22 changes: 19 additions & 3 deletions tpke/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ impl<E: PairingEngine> PrivateDecryptionContextFast<E> {
decryption_share,
}
}

pub fn batch_verify_decryption_shares<R: RngCore>(
&self,
ciphertexts: &[Ciphertext<E>],
shares: &[Vec<DecryptionShareFast<E>>],
//ciphertexts_and_shares: &[(Ciphertext<E>, Vec<DecryptionShare<E>>)],
rng: &mut R,
) -> bool {
let num_ciphertexts = ciphertexts.len();
Expand Down Expand Up @@ -131,13 +131,29 @@ impl<E: PairingEngine> PrivateDecryptionContextSimple<E> {
ciphertext: &Ciphertext<E>,
) -> DecryptionShareSimple<E> {
let u = ciphertext.commitment;
let z_i = self.private_key_share.clone();
let z_i = z_i.private_key_share;
let z_i = self.private_key_share.private_key_share;
// C_i = e(U, Z_i)
let c_i = E::pairing(u, z_i);
DecryptionShareSimple {
decrypter_index: self.index,
decryption_share: c_i,
}
}

pub fn create_share_precomputed(
&self,
ciphertext: &Ciphertext<E>,
lagrange_coeff: &E::Fr,
) -> DecryptionShareSimplePrecomputed<E> {
let u = ciphertext.commitment;
// U_{λ_i} = [λ_{i}(0)] U
let u_to_lagrange_coeff = u.mul(lagrange_coeff.into_repr());
let z_i = self.private_key_share.private_key_share;
// C_{λ_i} = e(U_{λ_i}, Z_i)
let c_i = E::pairing(u_to_lagrange_coeff, z_i);
DecryptionShareSimplePrecomputed {
decrypter_index: self.index,
decryption_share: c_i,
}
}
}
6 changes: 6 additions & 0 deletions tpke/src/decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub struct DecryptionShareSimple<E: PairingEngine> {
pub decryption_share: E::Fqk,
}

#[derive(Debug, Clone)]
pub struct DecryptionShareSimplePrecomputed<E: PairingEngine> {
pub decrypter_index: usize,
pub decryption_share: E::Fqk,
}

#[cfg(test)]
mod tests {
use crate::*;
Expand Down
7 changes: 7 additions & 0 deletions tpke/src/key_share.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ pub struct BlindedKeyShare<E: PairingEngine> {
pub blinding_key_prepared: E::G2Prepared,
}

pub fn generate_random<R: RngCore, E: PairingEngine>(
n: usize,
rng: &mut R,
) -> Vec<E::Fr> {
(0..n).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>()
}

impl<E: PairingEngine> BlindedKeyShare<E> {
pub fn verify_blinding<R: RngCore>(
&self,
Expand Down
43 changes: 36 additions & 7 deletions tpke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,19 +264,13 @@ pub fn setup_simple<E: PairingEngine>(
(pubkey.into(), privkey.into(), private_contexts)
}

pub fn generate_random<R: RngCore, E: PairingEngine>(
n: usize,
rng: &mut R,
) -> Vec<E::Fr> {
(0..n).map(|_| E::Fr::rand(rng)).collect::<Vec<_>>()
}

#[cfg(test)]
mod tests {
use crate::*;
use ark_bls12_381::Fr;
use ark_ec::ProjectiveCurve;
use ark_std::test_rng;
use itertools::Itertools;
use rand::prelude::StdRng;

type E = ark_bls12_381::Bls12_381;
Expand Down Expand Up @@ -422,6 +416,7 @@ mod tests {
.iter()
.map(|c| c.create_share(&ciphertext))
.collect();

let domain = contexts[0]
.public_decryption_contexts
.iter()
Expand All @@ -435,6 +430,40 @@ mod tests {
test_ciphertext_validation_fails(msg, aad, &ciphertext, &shared_secret);
}

#[test]
fn simple_threshold_decryption_precomputed() {
let mut rng = &mut test_rng();
let threshold = 16 * 2 / 3;
let shares_num = 16;
let msg: &[u8] = "abc".as_bytes();
let aad: &[u8] = "my-aad".as_bytes();

let (pubkey, _, contexts) =
setup_simple::<E>(threshold, shares_num, &mut rng);

let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng);

let domain = contexts[0]
.public_decryption_contexts
.iter()
.map(|c| c.domain)
.collect::<Vec<_>>();
let lagrange_coeffs = prepare_combine_simple::<E>(&domain);

let decryption_shares: Vec<_> = contexts
.iter()
.zip_eq(lagrange_coeffs.iter())
.map(|(context, lagrange_coeff)| {
context.create_share_precomputed(&ciphertext, lagrange_coeff)
})
.collect();

let shared_secret =
share_combine_simple_precomputed::<E>(&decryption_shares);

test_ciphertext_validation_fails(msg, aad, &ciphertext, &shared_secret);
}

#[test]
/// Ñ parties (where t <= Ñ <= N) jointly execute a "share recovery" algorithm, and the output is 1 new share.
/// The new share is intended to restore a previously existing share, e.g., due to loss or corruption.
Expand Down

0 comments on commit cd50056

Please sign in to comment.