From 3d0c13b78fa89e3cf221e48c68f9ce7f97dbce17 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Thu, 24 Nov 2022 13:43:51 +0100 Subject: [PATCH 01/14] initial work on simple threshold decryption --- tpke/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index c1f86602..35121c4a 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -291,6 +291,11 @@ pub fn setup_simple( private.public_decryption_contexts = public_contexts.clone(); } + // TODO: Should we also be returning some sort of signed transcript? + // "Post the signed message \(\tau, (F_0, \ldots, F_t), \hat{u}2, (\hat{Y}{i,\omega_j})\) to the blockchain" + // \tau - unique session identifier + // See: https://nikkolasg.github.io/ferveo/pvss.html#dealers-role + (pubkey.into(), privkey.into(), private_contexts) } From 81870afb4381a7acf7fb773c88b4508bd1d507dc Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 23 Dec 2022 13:30:04 +0100 Subject: [PATCH 02/14] wip --- tpke/src/lib.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 3 deletions(-) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index 35121c4a..3caa2b11 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -145,7 +145,7 @@ pub fn setup_fast( // F_0 - The commitment to the constant term, and is the public key output Y from PVDKG // TODO: It seems like the rest of the F_i are not computed? let pubkey = g.mul(x); - let privkey = h.mul(x); + let privkey = h.mul(x); // ek_i in PVSS? let mut private_contexts = vec![]; let mut public_contexts = vec![]; @@ -309,6 +309,7 @@ pub fn generate_random( #[cfg(test)] mod tests { use crate::*; + use ark_bls12_381::Fr; use ark_std::test_rng; type E = ark_bls12_381::Bls12_381; @@ -369,8 +370,7 @@ mod tests { // Source: https://stackoverflow.com/questions/26469715/how-do-i-write-a-rust-unit-test-that-ensures-that-a-panic-has-occurred // TODO: Remove after adding proper error handling to the library - use std::panic; - + use std::{collections::HashMap, panic}; fn catch_unwind_silent R + panic::UnwindSafe, R>( f: F, ) -> std::thread::Result { @@ -520,4 +520,133 @@ mod tests { }); assert!(result.is_err()); } + + fn make_decryption_shares( + private_decryption_contexts: &Vec>, + ciphertext: &Ciphertext, + ) -> Vec> { + private_decryption_contexts + .iter() + .map(|context| { + let u = ciphertext.commitment; + let i = context.index; + let z_i = context.private_key_share.clone(); + // Simplifying to just one key share per node + assert_eq!(z_i.private_key_shares.len(), 1); + let z_i = z_i.private_key_shares[0]; + // Really want to call E::pairing here to avoid heavy computations on client side + // C_i = e(U, Z_i) + let c_i = E::pairing(u, z_i); // TODO: Check whether blinded key share fits here + DecryptionShareSimple { + decrypter_index: i, + decryption_share: c_i, + } + }) + .collect::>() + } + + #[test] + fn simple_threshold_decryption_with_share_recovery() { + 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, _privkey, private_decryption_contexts, dealer_lagrange) = + setup_simple::(threshold, shares_num, &mut rng); + + let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); + + let shares = + make_decryption_shares(&private_decryption_contexts, &ciphertext); + let lagrange = prepare_combine_simple( + &private_decryption_contexts[0].public_decryption_contexts, + ); + let s = share_combine_simple::(&shares, &lagrange); + + let plaintext = + checked_decrypt_with_shared_secret(&ciphertext, aad, &s); + assert_eq!(plaintext, msg); + + // Refresh the shares + + // `private_decryption_contexts` represents set A of participants + // B set of participants contains one participant + // D = A\B + let x_r = Fr::one(); + let original_y_r = &private_decryption_contexts[0].private_key_share; + + let remaining_participants = private_decryption_contexts[1..].to_vec(); + let new_shares = refresh_decryption_shares::( + &remaining_participants, + &x_r, + threshold, + rng, + ); + } + + fn refresh_decryption_shares( + participants: &[PrivateDecryptionContextSimple], + x_r: &E::Fr, + threshold: usize, + rng: &mut impl RngCore, + ) -> Vec> { + let mut deltas: HashMap> = HashMap::new(); + for p1 in participants { + let i = p1.index; + let d_i = make_random_polynomial(threshold, x_r, rng); + for p2 in participants { + let j = p2.index; + let x_j = p2.public_decryption_contexts[j].domain; + if !deltas.contains_key(&i) { + deltas = HashMap::new(); + } + deltas[&i][&j] = evaluate_polynomial(&d_i, x_j); + } + } + + let mut new_shares: Vec> = Vec::new(); + for p1 in participants { + let i = p1.index; + let mut y_prime_i = p1.private_key_share.private_key_shares[0]; + for j in deltas.keys() { + y_prime_i = y_prime_i + deltas[j][&i]; + } + new_shares.push(DecryptionShareSimple { + decrypter_index: i, + decryption_share: y_prime_i, + }); + } + new_shares + } + + fn make_random_polynomial( + threshold: usize, + x_r: E::Fr, + rng: &mut impl RngCore, + ) -> Vec { + let d_i = (0..threshold).map(|_| E::Fr::rand(rng)).collect::>(); + d_i.insert(0, E::Fr::zero()); + + let d_i_at_x_r: E::Fr = evaluate_polynomial::(&d_i, x_r); + assert_eq!(d_i_at_x_r, E::Fr::zero()); + + let d_i_0 = E::Fr::zero() - d_i_at_x_r; + d_i[0] = d_i_0; + d_i + } + + fn evaluate_polynomial( + polynomial: &[E::Fr], + x: E::Fr, + ) -> E::Fr { + let mut result = E::Fr::zero(); + let mut x_power = E::Fr::one(); + for coeff in polynomial { + result += *coeff * x_power; + x_power *= x; + } + result + } } From 2575edd70e5d312e83bbc011c54c666bc7312d42 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 28 Dec 2022 20:19:25 +0100 Subject: [PATCH 03/14] failing to create a proper polynomial for recovery --- tpke/src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index 3caa2b11..6210bf02 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -310,6 +310,7 @@ pub fn generate_random( mod tests { use crate::*; use ark_bls12_381::Fr; + use ark_ec::ProjectiveCurve; use ark_std::test_rng; type E = ark_bls12_381::Bls12_381; @@ -370,7 +371,11 @@ mod tests { // Source: https://stackoverflow.com/questions/26469715/how-do-i-write-a-rust-unit-test-that-ensures-that-a-panic-has-occurred // TODO: Remove after adding proper error handling to the library - use std::{collections::HashMap, panic}; + use std::{ + collections::HashMap, + ops::{Add, AddAssign}, + panic, + }; fn catch_unwind_silent R + panic::UnwindSafe, R>( f: F, ) -> std::thread::Result { @@ -584,6 +589,20 @@ mod tests { threshold, rng, ); + + // TODO: Refresh lagrange coefficeints here? + // let lagrange = prepare_combine_simple( + // &private_decryption_contexts[0].public_decryption_contexts, + // ); + + let s = share_combine_simple::(&new_shares, &lagrange); + + // So far, the ciphertext is valid + let plaintext = + checked_decrypt_with_shared_secret(&ciphertext, aad, &s); + assert_eq!(plaintext, msg); + + } fn refresh_decryption_shares( @@ -595,27 +614,39 @@ mod tests { let mut deltas: HashMap> = HashMap::new(); for p1 in participants { let i = p1.index; - let d_i = make_random_polynomial(threshold, x_r, rng); + let d_i = make_random_polynomial::(threshold, x_r, rng); for p2 in participants { let j = p2.index; let x_j = p2.public_decryption_contexts[j].domain; if !deltas.contains_key(&i) { - deltas = HashMap::new(); + deltas.insert(i, HashMap::new()); } - deltas[&i][&j] = evaluate_polynomial(&d_i, x_j); + let eval = evaluate_polynomial::(&d_i, &x_j); + // TODO: Find a more efficient way keep track of those values + let mut d = deltas.get(&i).unwrap().clone(); + d.insert(j, eval); + deltas.insert(i, d); } } let mut new_shares: Vec> = Vec::new(); - for p1 in participants { - let i = p1.index; - let mut y_prime_i = p1.private_key_share.private_key_shares[0]; + for p in participants { + let h_g2 = E::G2Projective::from(p.h); + + assert_eq!(p.private_key_share.private_key_shares.len(), 1); + let i = p.index; + let mut y_prime_i = E::G2Projective::from( + p.private_key_share.private_key_shares[0], + ); for j in deltas.keys() { - y_prime_i = y_prime_i + deltas[j][&i]; + let delta_g2 = h_g2.mul(deltas[j][&i].into_repr()); + y_prime_i += delta_g2; } + new_shares.push(DecryptionShareSimple { decrypter_index: i, - decryption_share: y_prime_i, + // TODO: Is this a correct method to convert new y_prime_i to E::Fr? + decryption_share: E::pairing(p.g, y_prime_i), }); } new_shares @@ -623,10 +654,10 @@ mod tests { fn make_random_polynomial( threshold: usize, - x_r: E::Fr, + x_r: &E::Fr, rng: &mut impl RngCore, ) -> Vec { - let d_i = (0..threshold).map(|_| E::Fr::rand(rng)).collect::>(); + let mut d_i = (0..threshold).map(|_| E::Fr::rand(rng)).collect::>(); d_i.insert(0, E::Fr::zero()); let d_i_at_x_r: E::Fr = evaluate_polynomial::(&d_i, x_r); @@ -639,7 +670,7 @@ mod tests { fn evaluate_polynomial( polynomial: &[E::Fr], - x: E::Fr, + x: &E::Fr, ) -> E::Fr { let mut result = E::Fr::zero(); let mut x_power = E::Fr::one(); From c0df26e23e31107e24cfcad0319ff38cc17e5d19 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 30 Dec 2022 19:01:57 +0100 Subject: [PATCH 04/14] fix after rebase --- tpke/src/lib.rs | 86 ++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index 6210bf02..1de585f0 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -376,6 +376,7 @@ mod tests { ops::{Add, AddAssign}, panic, }; + fn catch_unwind_silent R + panic::UnwindSafe, R>( f: F, ) -> std::thread::Result { @@ -481,23 +482,10 @@ mod tests { let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); // Creating decryption shares - let decryption_shares = private_decryption_contexts - .iter() - .map(|context| { - let u = ciphertext.commitment; - let z_i = context.private_key_share.clone(); - // Simplifying to just one key share per node - assert_eq!(z_i.private_key_shares.len(), 1); - let z_i = z_i.private_key_shares[0]; - // Really want to call E::pairing here to avoid heavy computations on client side - // C_i = e(U, Z_i) - // TODO: Check whether blinded key share fits here - E::pairing(u, z_i) - }) - .collect::>(); + let decryption_shares = make_decryption_shares(&contexts, &ciphertext); let pub_contexts = - &private_decryption_contexts[0].public_decryption_contexts; + &contexts[0].public_decryption_contexts; let lagrange = prepare_combine_simple::(pub_contexts); let shared_secret = @@ -527,10 +515,10 @@ mod tests { } fn make_decryption_shares( - private_decryption_contexts: &Vec>, + contexts: &Vec>, ciphertext: &Ciphertext, - ) -> Vec> { - private_decryption_contexts + ) -> Vec { + contexts .iter() .map(|context| { let u = ciphertext.commitment; @@ -541,11 +529,7 @@ mod tests { let z_i = z_i.private_key_shares[0]; // Really want to call E::pairing here to avoid heavy computations on client side // C_i = e(U, Z_i) - let c_i = E::pairing(u, z_i); // TODO: Check whether blinded key share fits here - DecryptionShareSimple { - decrypter_index: i, - decryption_share: c_i, - } + E::pairing(u, z_i) }) .collect::>() } @@ -558,31 +542,44 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _privkey, private_decryption_contexts, dealer_lagrange) = + // To be updated + let (pubkey, _privkey, contexts) = setup_simple::(threshold, shares_num, &mut rng); - let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); + // Stays the same + // Ciphertext.commitment is already computed to match U + let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); + + // Creating decryption shares + let decryption_shares = make_decryption_shares(&contexts, &ciphertext); - let shares = - make_decryption_shares(&private_decryption_contexts, &ciphertext); - let lagrange = prepare_combine_simple( - &private_decryption_contexts[0].public_decryption_contexts, - ); - let s = share_combine_simple::(&shares, &lagrange); + let shares_x = &contexts[0] + .public_decryption_contexts + .iter() + .map(|ctxt| ctxt.domain) + .collect::>(); + let lagrange = prepare_combine_simple::(shares_x); - let plaintext = - checked_decrypt_with_shared_secret(&ciphertext, aad, &s); + let shared_secret = + share_combine_simple::(&decryption_shares, &lagrange); + + // So far, the ciphertext is valid + let plaintext = checked_decrypt_with_shared_secret( + &ciphertext, + aad, + &shared_secret, + ); assert_eq!(plaintext, msg); // Refresh the shares - // `private_decryption_contexts` represents set A of participants + // `contexts` represents set A of participants // B set of participants contains one participant // D = A\B let x_r = Fr::one(); - let original_y_r = &private_decryption_contexts[0].private_key_share; + let original_y_r = &contexts[0].private_key_share; - let remaining_participants = private_decryption_contexts[1..].to_vec(); + let remaining_participants = contexts[1..].to_vec(); let new_shares = refresh_decryption_shares::( &remaining_participants, &x_r, @@ -592,7 +589,7 @@ mod tests { // TODO: Refresh lagrange coefficeints here? // let lagrange = prepare_combine_simple( - // &private_decryption_contexts[0].public_decryption_contexts, + // &contexts[0].public_decryption_contexts, // ); let s = share_combine_simple::(&new_shares, &lagrange); @@ -601,8 +598,6 @@ mod tests { let plaintext = checked_decrypt_with_shared_secret(&ciphertext, aad, &s); assert_eq!(plaintext, msg); - - } fn refresh_decryption_shares( @@ -610,7 +605,7 @@ mod tests { x_r: &E::Fr, threshold: usize, rng: &mut impl RngCore, - ) -> Vec> { + ) -> Vec { let mut deltas: HashMap> = HashMap::new(); for p1 in participants { let i = p1.index; @@ -629,7 +624,7 @@ mod tests { } } - let mut new_shares: Vec> = Vec::new(); + let mut new_shares = Vec::new(); for p in participants { let h_g2 = E::G2Projective::from(p.h); @@ -643,11 +638,7 @@ mod tests { y_prime_i += delta_g2; } - new_shares.push(DecryptionShareSimple { - decrypter_index: i, - // TODO: Is this a correct method to convert new y_prime_i to E::Fr? - decryption_share: E::pairing(p.g, y_prime_i), - }); + new_shares.push(E::pairing(p.g, y_prime_i)); } new_shares } @@ -657,7 +648,8 @@ mod tests { x_r: &E::Fr, rng: &mut impl RngCore, ) -> Vec { - let mut d_i = (0..threshold).map(|_| E::Fr::rand(rng)).collect::>(); + let mut d_i = + (0..threshold).map(|_| E::Fr::rand(rng)).collect::>(); d_i.insert(0, E::Fr::zero()); let d_i_at_x_r: E::Fr = evaluate_polynomial::(&d_i, x_r); From 1697924d35d2c0e689ccd20f4f784be2d03c70b6 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 2 Jan 2023 17:54:51 +0100 Subject: [PATCH 05/14] refreshing initial pass --- tpke/src/combine.rs | 35 +++-- tpke/src/lib.rs | 340 +++++++++++++++++++++----------------------- tpke/src/refresh.rs | 125 ++++++++++++++++ 3 files changed, 309 insertions(+), 191 deletions(-) create mode 100644 tpke/src/refresh.rs diff --git a/tpke/src/combine.rs b/tpke/src/combine.rs index 4e1f1ed8..96d0d18f 100644 --- a/tpke/src/combine.rs +++ b/tpke/src/combine.rs @@ -3,7 +3,6 @@ use crate::*; use ark_ec::ProjectiveCurve; -use itertools::zip_eq; pub fn prepare_combine_fast( public_decryption_contexts: &[PublicDecryptionContextFast], @@ -61,17 +60,27 @@ fn lagrange_coeffs_at( x_i: &E::Fr, ) -> Vec { // Calculate lagrange coefficients using optimized formula, see https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm - let mut lagrange_coeffs = vec![]; - for x_j in shares_x { - let mut prod = E::Fr::one(); - for x_m in shares_x { - if x_j != x_m { - prod *= (*x_m - x_i) / (*x_m - *x_j); - } - } - lagrange_coeffs.push(prod); - } - lagrange_coeffs + // In this formula x_i = 0, hence numerator is x_m + lagrange_basis_at::(shares_x, &E::Fr::zero()) +} + +/// Calculates Lagrange coefficients for a given x_i +pub fn lagrange_basis_at( + shares_x: &[E::Fr], + x_i: &E::Fr, +) -> Vec { + shares_x + .iter() + .map(|x_j| { + let mut prod = E::Fr::one(); + shares_x.iter().for_each(|x_m| { + if x_j != x_m { + prod *= (*x_m - x_i) / (*x_m - *x_j); + } + }); + prod + }) + .collect() } pub fn share_combine_fast( @@ -101,7 +110,7 @@ pub fn share_combine_simple( let mut product_of_shares = E::Fqk::one(); // Sum of C_i^{L_i}z - for (c_i, alpha_i) in zip_eq(shares.iter(), lagrange_coeffs.iter()) { + for (c_i, alpha_i) in izip!(shares, lagrange_coeffs) { // Exponentiation by alpha_i let ss = c_i.pow(alpha_i.into_repr()); product_of_shares *= ss; diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index 1de585f0..c939d7ca 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -2,12 +2,16 @@ #![allow(dead_code)] use crate::hash_to_curve::htp_bls12381_g2; -use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine}; +use crate::SetupParams; + +use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, One, PrimeField, ToBytes, UniformRand, Zero}; -use ark_poly::{univariate::DensePolynomial, UVPolynomial}; -use ark_poly::{EvaluationDomain, Polynomial}; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, Polynomial, UVPolynomial, +}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use itertools::izip; + use subproductdomain::SubproductDomain; use rand_core::RngCore; @@ -15,26 +19,19 @@ use std::usize; use thiserror::Error; mod ciphertext; +mod combine; +mod context; +mod decryption; mod hash_to_curve; - -pub use ciphertext::*; - mod key_share; +mod refresh; -pub use key_share::*; - -mod decryption; - -pub use decryption::*; - -mod combine; - +pub use ciphertext::*; pub use combine::*; - -mod context; - -use crate::SetupParams; pub use context::*; +pub use decryption::*; +pub use key_share::*; +pub use refresh::*; // TODO: Turn into a crate features pub mod api; @@ -306,12 +303,23 @@ pub fn generate_random( (0..n).map(|_| E::Fr::rand(rng)).collect::>() } +fn make_decryption_share( + private_share: &PrivateKeyShare, + ciphertext: &Ciphertext, +) -> E::Fqk { + let z_i = private_share; + let u = ciphertext.commitment; + let z_i = z_i.private_key_shares[0]; + E::pairing(u, z_i) +} + #[cfg(test)] mod tests { use crate::*; use ark_bls12_381::Fr; - use ark_ec::ProjectiveCurve; use ark_std::test_rng; + use rand::prelude::StdRng; + use std::panic; type E = ark_bls12_381::Bls12_381; @@ -323,12 +331,10 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "aad".as_bytes(); - let (pubkey, _privkey, _) = + let (pubkey, _, _) = setup_fast::(threshold, shares_num, &mut rng); - let ciphertext = encrypt::( - msg, aad, &pubkey, &mut rng, - ); + let ciphertext = encrypt::(msg, aad, &pubkey, &mut rng); let serialized = ciphertext.to_bytes(); let deserialized: Ciphertext = Ciphertext::from_bytes(&serialized); @@ -361,32 +367,12 @@ mod tests { let (pubkey, privkey, _) = setup_fast::(threshold, shares_num, &mut rng); - let ciphertext = encrypt::( - msg, aad, &pubkey, &mut rng, - ); + let ciphertext = encrypt::(msg, aad, &pubkey, &mut rng); let plaintext = checked_decrypt(&ciphertext, aad, privkey); assert_eq!(msg, plaintext) } - // Source: https://stackoverflow.com/questions/26469715/how-do-i-write-a-rust-unit-test-that-ensures-that-a-panic-has-occurred - // TODO: Remove after adding proper error handling to the library - use std::{ - collections::HashMap, - ops::{Add, AddAssign}, - panic, - }; - - fn catch_unwind_silent R + panic::UnwindSafe, R>( - f: F, - ) -> std::thread::Result { - let prev_hook = panic::take_hook(); - panic::set_hook(Box::new(|_| {})); - let result = panic::catch_unwind(f); - panic::set_hook(prev_hook); - result - } - #[test] fn threshold_encryption() { let mut rng = &mut test_rng(); @@ -395,8 +381,7 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _privkey, contexts) = - setup_fast::(threshold, shares_num, &mut rng); + let (pubkey, _, contexts) = setup_fast::(threshold, shares_num, &mut rng); let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); let mut shares: Vec> = vec![]; @@ -426,14 +411,14 @@ mod tests { // Malformed the ciphertext ciphertext.ciphertext[0] += 1; - let result = std::panic::catch_unwind(|| { + let result = panic::catch_unwind(|| { checked_decrypt_with_shared_secret(&ciphertext, aad, &shared_secret) }); assert!(result.is_err()); // Malformed the AAD let aad = "bad aad".as_bytes(); - let result = std::panic::catch_unwind(|| { + let result = panic::catch_unwind(|| { checked_decrypt_with_shared_secret(&ciphertext, aad, &shared_secret) }); assert!(result.is_err()); @@ -447,11 +432,10 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _privkey, _) = + let (pubkey, _, _) = setup_fast::(threshold, shares_num, &mut rng); - let mut ciphertext = encrypt::( - msg, aad, &pubkey, &mut rng, - ); + let mut ciphertext = + encrypt::(msg, aad, &pubkey, &mut rng); // So far, the ciphertext is valid assert!(check_ciphertext_validity(&ciphertext, aad)); @@ -473,17 +457,19 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - // To be updated - let (pubkey, _privkey, private_decryption_contexts) = + let (pubkey, _, private_decryption_contexts) = setup_simple::(threshold, shares_num, &mut rng); - // Stays the same // Ciphertext.commitment is already computed to match U let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); - // Creating decryption shares - let decryption_shares = make_decryption_shares(&contexts, &ciphertext); - + // Create decryption shares + let decryption_shares: Vec<_> = contexts + .iter() + .map(|ctxt| { + make_decryption_share(&ctxt.private_key_share, &ciphertext) + }) + .collect(); let pub_contexts = &contexts[0].public_decryption_contexts; let lagrange = prepare_combine_simple::(pub_contexts); @@ -501,59 +487,99 @@ mod tests { // Malformed the ciphertext ciphertext.ciphertext[0] += 1; - let result = std::panic::catch_unwind(|| { + let result = panic::catch_unwind(|| { checked_decrypt_with_shared_secret(&ciphertext, aad, &shared_secret) }); assert!(result.is_err()); // Malformed the AAD let aad = "bad aad".as_bytes(); - let result = std::panic::catch_unwind(|| { + let result = panic::catch_unwind(|| { checked_decrypt_with_shared_secret(&ciphertext, aad, &shared_secret) }); assert!(result.is_err()); } - fn make_decryption_shares( - contexts: &Vec>, - ciphertext: &Ciphertext, - ) -> Vec { - contexts - .iter() - .map(|context| { - let u = ciphertext.commitment; - let i = context.index; - let z_i = context.private_key_share.clone(); - // Simplifying to just one key share per node - assert_eq!(z_i.private_key_shares.len(), 1); - let z_i = z_i.private_key_shares[0]; - // Really want to call E::pairing here to avoid heavy computations on client side - // C_i = e(U, Z_i) - E::pairing(u, z_i) - }) - .collect::>() + #[test] + fn simple_threshold_decryption_with_share_refreshing_at_point() { + let mut rng = &mut test_rng(); + let shares_num = 16; + let threshold = shares_num * 2 / 3; + + let (_, _, mut contexts) = + setup_simple::(threshold, shares_num, &mut rng); + + // Prepare participants + + // First, save the soon-to-be-removed participant + let selected_participant = contexts.pop().unwrap(); + let x_r = selected_participant + .public_decryption_contexts + .last() + .unwrap() + .domain; + let original_y_r = + selected_participant.private_key_share.private_key_shares[0]; + + // Now, we have to remove the participant from the contexts and all nested structures + let mut remaining_participants = contexts; + for p in &mut remaining_participants { + p.public_decryption_contexts.pop(); + } + + // Refresh the share + let y_r = recover_share_at_point( + &remaining_participants, + threshold, + &x_r, + rng, + ); + assert_eq!(y_r.into_affine(), original_y_r); } #[test] - fn simple_threshold_decryption_with_share_recovery() { + fn simple_threshold_decryption_with_share_recovery_at_point() { let mut rng = &mut test_rng(); - let threshold = 16 * 2 / 3; let shares_num = 16; + let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - // To be updated - let (pubkey, _privkey, contexts) = + let (pubkey, _, contexts) = setup_simple::(threshold, shares_num, &mut rng); + let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); - // Stays the same - // Ciphertext.commitment is already computed to match U - let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); + // Remove one participant from the contexts and all nested structures + let mut remaining_participants = contexts; + remaining_participants.pop(); + for p in &mut remaining_participants { + p.public_decryption_contexts.pop(); + } + + // Refresh the share + let x_r = Fr::rand(rng); + let y_r = recover_share_at_point( + &remaining_participants, + threshold, + &x_r, + rng, + ); + let recovered_key_share = PrivateKeyShare { + private_key_shares: vec![y_r.into_affine()], + }; // Creating decryption shares - let decryption_shares = make_decryption_shares(&contexts, &ciphertext); + let mut decryption_shares: Vec<_> = remaining_participants + .iter() + .map(|ctxt| { + make_decryption_share(&ctxt.private_key_share, &ciphertext) + }) + .collect(); + decryption_shares + .push(make_decryption_share(&recovered_key_share, &ciphertext)); - let shares_x = &contexts[0] + // Creating a shared secret from remaining shares and the recovered one + let shares_x = &remaining_participants[0] .public_decryption_contexts .iter() .map(|ctxt| ctxt.domain) @@ -563,113 +589,71 @@ mod tests { let shared_secret = share_combine_simple::(&decryption_shares, &lagrange); - // So far, the ciphertext is valid let plaintext = checked_decrypt_with_shared_secret( &ciphertext, aad, &shared_secret, ); assert_eq!(plaintext, msg); + } - // Refresh the shares + #[test] + fn simple_threshold_decryption_with_share_refresh() { + let mut rng = &mut test_rng(); + let shares_num = 16; + let threshold = shares_num * 2 / 3; + let msg: &[u8] = "abc".as_bytes(); + let aad: &[u8] = "my-aad".as_bytes(); - // `contexts` represents set A of participants - // B set of participants contains one participant - // D = A\B - let x_r = Fr::one(); - let original_y_r = &contexts[0].private_key_share; + let (pubkey, _, contexts) = + setup_simple::(threshold, shares_num, &mut rng); + let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); - let remaining_participants = contexts[1..].to_vec(); - let new_shares = refresh_decryption_shares::( + // Remove one participant from the contexts and all nested structures + let mut remaining_participants = contexts; + remaining_participants.pop(); + for p in &mut remaining_participants { + p.public_decryption_contexts.pop(); + } + + // Refresh the share + let x_r = Fr::rand(rng); + let y_r = recover_share_at_point( &remaining_participants, - &x_r, threshold, + &x_r, rng, ); + let recovered_key_share = PrivateKeyShare { + private_key_shares: vec![y_r.into_affine()], + }; - // TODO: Refresh lagrange coefficeints here? - // let lagrange = prepare_combine_simple( - // &contexts[0].public_decryption_contexts, - // ); - - let s = share_combine_simple::(&new_shares, &lagrange); - - // So far, the ciphertext is valid - let plaintext = - checked_decrypt_with_shared_secret(&ciphertext, aad, &s); - assert_eq!(plaintext, msg); - } - - fn refresh_decryption_shares( - participants: &[PrivateDecryptionContextSimple], - x_r: &E::Fr, - threshold: usize, - rng: &mut impl RngCore, - ) -> Vec { - let mut deltas: HashMap> = HashMap::new(); - for p1 in participants { - let i = p1.index; - let d_i = make_random_polynomial::(threshold, x_r, rng); - for p2 in participants { - let j = p2.index; - let x_j = p2.public_decryption_contexts[j].domain; - if !deltas.contains_key(&i) { - deltas.insert(i, HashMap::new()); - } - let eval = evaluate_polynomial::(&d_i, &x_j); - // TODO: Find a more efficient way keep track of those values - let mut d = deltas.get(&i).unwrap().clone(); - d.insert(j, eval); - deltas.insert(i, d); - } - } + // Creating decryption shares + let mut decryption_shares: Vec<_> = remaining_participants + .iter() + .map(|ctxt| { + make_decryption_share(&ctxt.private_key_share, &ciphertext) + }) + .collect(); + decryption_shares + .push(make_decryption_share(&recovered_key_share, &ciphertext)); - let mut new_shares = Vec::new(); - for p in participants { - let h_g2 = E::G2Projective::from(p.h); - - assert_eq!(p.private_key_share.private_key_shares.len(), 1); - let i = p.index; - let mut y_prime_i = E::G2Projective::from( - p.private_key_share.private_key_shares[0], - ); - for j in deltas.keys() { - let delta_g2 = h_g2.mul(deltas[j][&i].into_repr()); - y_prime_i += delta_g2; - } - - new_shares.push(E::pairing(p.g, y_prime_i)); - } - new_shares - } + // Creating a shared secret from remaining shares and the recovered one + let shares_x = &remaining_participants[0] + .public_decryption_contexts + .iter() + .map(|ctxt| ctxt.domain) + .collect::>(); + let lagrange = prepare_combine_simple::(shares_x); - fn make_random_polynomial( - threshold: usize, - x_r: &E::Fr, - rng: &mut impl RngCore, - ) -> Vec { - let mut d_i = - (0..threshold).map(|_| E::Fr::rand(rng)).collect::>(); - d_i.insert(0, E::Fr::zero()); - - let d_i_at_x_r: E::Fr = evaluate_polynomial::(&d_i, x_r); - assert_eq!(d_i_at_x_r, E::Fr::zero()); - - let d_i_0 = E::Fr::zero() - d_i_at_x_r; - d_i[0] = d_i_0; - d_i - } + let shared_secret = + share_combine_simple::(&decryption_shares, &lagrange); - fn evaluate_polynomial( - polynomial: &[E::Fr], - x: &E::Fr, - ) -> E::Fr { - let mut result = E::Fr::zero(); - let mut x_power = E::Fr::one(); - for coeff in polynomial { - result += *coeff * x_power; - x_power *= x; - } - result + let plaintext = checked_decrypt_with_shared_secret( + &ciphertext, + aad, + &shared_secret, + ); + assert_eq!(plaintext, msg); } } diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs new file mode 100644 index 00000000..e79ef8f6 --- /dev/null +++ b/tpke/src/refresh.rs @@ -0,0 +1,125 @@ +use crate::{lagrange_basis_at, PrivateDecryptionContextSimple}; +use ark_ec::{PairingEngine, ProjectiveCurve}; +use ark_ff::{One, PrimeField, Zero}; +use ark_std::UniformRand; +use itertools::zip_eq; +use rand::prelude::StdRng; +use rand_core::RngCore; +use std::collections::HashMap; +use std::usize; + +pub fn recover_share_at_point( + other_participants: &[PrivateDecryptionContextSimple], + threshold: usize, + x_r: &E::Fr, + rng: &mut StdRng, +) -> E::G2Projective { + let share_updates = + prepare_share_updates::(other_participants, x_r, threshold, rng); + + let new_shares_y = + update_decryption_shares::(other_participants, &share_updates); + + // Interpolate new shares to recover y_r + let shares_x = &other_participants[0] + .public_decryption_contexts + .iter() + .map(|ctxt| ctxt.domain) + .collect::>(); + + // Recover y_r + let lagrange = lagrange_basis_at::(shares_x, x_r); + let prods = + zip_eq(new_shares_y, lagrange).map(|(y_j, l)| y_j.mul(l.into_repr())); + prods.fold(E::G2Projective::zero(), |acc, y_j| acc + y_j) +} + +fn prepare_share_updates( + participants: &[PrivateDecryptionContextSimple], + x_r: &E::Fr, + threshold: usize, + rng: &mut impl RngCore, +) -> HashMap> { + // TODO: Refactor this function so that each participant performs it individually + // Each participant prepares an update for each other participant + participants + .iter() + .map(|p1| { + let i = p1.index; + // Generate a new random polynomial with constant term 0 + let d_i = make_random_polynomial::(threshold, x_r, rng); + + // Now, we need to evaluate the polynomial at each of participants' indices + let deltas_i: HashMap<_, _> = participants + .iter() + .map(|p2| { + let j = p2.index; + let x_j = p2.public_decryption_contexts[j].domain; + // Compute the evaluation of the polynomial at the domain element x_j + // d_i(x_j) + let eval = evaluate_polynomial::(&d_i, &x_j); + let h_g2 = E::G2Projective::from(p2.h); + let eval_g2 = h_g2.mul(eval.into_repr()); + (j, eval_g2) + }) + .collect(); + (i, deltas_i) + }) + .collect::>() +} + +fn update_decryption_shares( + participants: &[PrivateDecryptionContextSimple], + deltas: &HashMap>, +) -> Vec { + // TODO: Refactor this function so that each participant performs it individually + participants + .iter() + .map(|p| { + let i = p.index; + let mut new_y = E::G2Projective::from( + p.private_key_share.private_key_shares[0], // y_i + ); + for j in deltas.keys() { + new_y += deltas[j][&i]; + } + new_y + }) + .collect() +} + +fn make_random_polynomial( + threshold: usize, + x_r: &E::Fr, + rng: &mut impl RngCore, +) -> Vec { + // [][threshold-1] + let mut d_i = (0..threshold - 1) + .map(|_| E::Fr::rand(rng)) + .collect::>(); + // [0..][threshold] + d_i.insert(0, E::Fr::zero()); + + // Now, we calculate d_i_0 + // This is the term that will "zero out" the polynomial at x_r, d_i(x_r) = 0 + let d_i_0 = E::Fr::zero() - evaluate_polynomial::(&d_i, x_r); + d_i[0] = d_i_0; + assert_eq!(evaluate_polynomial::(&d_i, x_r), E::Fr::zero()); + + assert_eq!(d_i.len(), threshold); + + d_i +} + +fn evaluate_polynomial( + polynomial: &[E::Fr], + x: &E::Fr, +) -> E::Fr { + let mut result = E::Fr::zero(); + let mut x_power = E::Fr::one(); + for coeff in polynomial { + result += *coeff * x_power; + x_power *= x; + } + result +} From e4e59c8ce60c440c308748097db1423763a358f7 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 3 Jan 2023 09:37:43 +0100 Subject: [PATCH 06/14] share refreshing --- tpke/src/lib.rs | 93 +++++++++++++++++++-------------------------- tpke/src/refresh.rs | 75 +++++++++++++++++++++++++++--------- 2 files changed, 96 insertions(+), 72 deletions(-) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index c939d7ca..f8e10f70 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -4,7 +4,7 @@ use crate::hash_to_curve::htp_bls12381_g2; use crate::SetupParams; -use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine}; use ark_ff::{Field, One, PrimeField, ToBytes, UniformRand, Zero}; use ark_poly::{ univariate::DensePolynomial, EvaluationDomain, Polynomial, UVPolynomial, @@ -317,6 +317,7 @@ fn make_decryption_share( mod tests { use crate::*; use ark_bls12_381::Fr; + use ark_ec::ProjectiveCurve; use ark_std::test_rng; use rand::prelude::StdRng; use std::panic; @@ -325,16 +326,16 @@ mod tests { #[test] fn ciphertext_serialization() { - let mut rng = test_rng(); - let threshold = 3; - let shares_num = 8; + let rng = &mut test_rng(); + let shares_num = 16; + let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); - let aad: &[u8] = "aad".as_bytes(); + let aad: &[u8] = "my-aad".as_bytes(); let (pubkey, _, _) = - setup_fast::(threshold, shares_num, &mut rng); + setup_fast::(threshold, shares_num, rng); - let ciphertext = encrypt::(msg, aad, &pubkey, &mut rng); + let ciphertext = encrypt::(msg, aad, &pubkey, rng); let serialized = ciphertext.to_bytes(); let deserialized: Ciphertext = Ciphertext::from_bytes(&serialized); @@ -358,16 +359,16 @@ mod tests { #[test] fn symmetric_encryption() { - let mut rng = test_rng(); - let threshold = 3; - let shares_num = 8; + let rng = &mut test_rng(); + let shares_num = 16; + let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); let (pubkey, privkey, _) = - setup_fast::(threshold, shares_num, &mut rng); + setup_fast::(threshold, shares_num, rng); - let ciphertext = encrypt::(msg, aad, &pubkey, &mut rng); + let ciphertext = encrypt::(msg, aad, &pubkey, rng); let plaintext = checked_decrypt(&ciphertext, aad, privkey); assert_eq!(msg, plaintext) @@ -375,13 +376,13 @@ mod tests { #[test] fn threshold_encryption() { - let mut rng = &mut test_rng(); - let threshold = 16 * 2 / 3; + let rng = &mut test_rng(); let shares_num = 16; + let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _, contexts) = setup_fast::(threshold, shares_num, &mut rng); + let (pubkey, _, contexts) = setup_fast::(threshold, shares_num, rng); let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); let mut shares: Vec> = vec![]; @@ -426,16 +427,15 @@ mod tests { #[test] fn ciphertext_validity_check() { - let mut rng = test_rng(); - let threshold = 3; - let shares_num = 8; + let rng = &mut test_rng(); + let shares_num = 16; + let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); let (pubkey, _, _) = - setup_fast::(threshold, shares_num, &mut rng); - let mut ciphertext = - encrypt::(msg, aad, &pubkey, &mut rng); + setup_fast::(threshold, shares_num, rng); + let mut ciphertext = encrypt::(msg, aad, &pubkey, rng); // So far, the ciphertext is valid assert!(check_ciphertext_validity(&ciphertext, aad)); @@ -451,14 +451,14 @@ mod tests { #[test] fn simple_threshold_decryption() { - let mut rng = &mut test_rng(); - let threshold = 16 * 2 / 3; + let rng = &mut test_rng(); let shares_num = 16; + let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); let (pubkey, _, private_decryption_contexts) = - setup_simple::(threshold, shares_num, &mut rng); + setup_simple::(threshold, shares_num, rng); // Ciphertext.commitment is already computed to match U let mut ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); @@ -502,12 +502,12 @@ mod tests { #[test] fn simple_threshold_decryption_with_share_refreshing_at_point() { - let mut rng = &mut test_rng(); + let rng = &mut test_rng(); let shares_num = 16; let threshold = shares_num * 2 / 3; let (_, _, mut contexts) = - setup_simple::(threshold, shares_num, &mut rng); + setup_simple::(threshold, shares_num, rng); // Prepare participants @@ -539,14 +539,14 @@ mod tests { #[test] fn simple_threshold_decryption_with_share_recovery_at_point() { - let mut rng = &mut test_rng(); + let rng = &mut test_rng(); let shares_num = 16; let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); let (pubkey, _, contexts) = - setup_simple::(threshold, shares_num, &mut rng); + setup_simple::(threshold, shares_num, rng); let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); // Remove one participant from the contexts and all nested structures @@ -599,47 +599,32 @@ mod tests { #[test] fn simple_threshold_decryption_with_share_refresh() { - let mut rng = &mut test_rng(); + let rng = &mut test_rng(); let shares_num = 16; let threshold = shares_num * 2 / 3; let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); let (pubkey, _, contexts) = - setup_simple::(threshold, shares_num, &mut rng); + setup_simple::(threshold, shares_num, rng); let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); - // Remove one participant from the contexts and all nested structures - let mut remaining_participants = contexts; - remaining_participants.pop(); - for p in &mut remaining_participants { - p.public_decryption_contexts.pop(); - } - - // Refresh the share - let x_r = Fr::rand(rng); - let y_r = recover_share_at_point( - &remaining_participants, - threshold, - &x_r, - rng, - ); - let recovered_key_share = PrivateKeyShare { - private_key_shares: vec![y_r.into_affine()], - }; + // Refresh shares + let fresh_shares = refresh_shares::(&contexts, threshold, rng); // Creating decryption shares - let mut decryption_shares: Vec<_> = remaining_participants + let decryption_shares: Vec<_> = fresh_shares .iter() - .map(|ctxt| { - make_decryption_share(&ctxt.private_key_share, &ciphertext) + .map(|private_share| { + let private_share = PrivateKeyShare { + private_key_shares: vec![private_share.into_affine()], + }; + make_decryption_share(&private_share, &ciphertext) }) .collect(); - decryption_shares - .push(make_decryption_share(&recovered_key_share, &ciphertext)); // Creating a shared secret from remaining shares and the recovered one - let shares_x = &remaining_participants[0] + let shares_x = &contexts[0] .public_decryption_contexts .iter() .map(|ctxt| ctxt.domain) diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index e79ef8f6..4b47e81b 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -14,11 +14,15 @@ pub fn recover_share_at_point( x_r: &E::Fr, rng: &mut StdRng, ) -> E::G2Projective { - let share_updates = - prepare_share_updates::(other_participants, x_r, threshold, rng); + let share_updates = prepare_share_updates_for_recovery::( + other_participants, + x_r, + threshold, + rng, + ); let new_shares_y = - update_decryption_shares::(other_participants, &share_updates); + update_shares_for_recovery::(other_participants, &share_updates); // Interpolate new shares to recover y_r let shares_x = &other_participants[0] @@ -34,7 +38,7 @@ pub fn recover_share_at_point( prods.fold(E::G2Projective::zero(), |acc, y_j| acc + y_j) } -fn prepare_share_updates( +fn prepare_share_updates_for_recovery( participants: &[PrivateDecryptionContextSimple], x_r: &E::Fr, threshold: usize, @@ -50,25 +54,14 @@ fn prepare_share_updates( let d_i = make_random_polynomial::(threshold, x_r, rng); // Now, we need to evaluate the polynomial at each of participants' indices - let deltas_i: HashMap<_, _> = participants - .iter() - .map(|p2| { - let j = p2.index; - let x_j = p2.public_decryption_contexts[j].domain; - // Compute the evaluation of the polynomial at the domain element x_j - // d_i(x_j) - let eval = evaluate_polynomial::(&d_i, &x_j); - let h_g2 = E::G2Projective::from(p2.h); - let eval_g2 = h_g2.mul(eval.into_repr()); - (j, eval_g2) - }) - .collect(); + let deltas_i: HashMap<_, _> = + compute_polynomial_deltas::(participants, &d_i); (i, deltas_i) }) .collect::>() } -fn update_decryption_shares( +fn update_shares_for_recovery( participants: &[PrivateDecryptionContextSimple], deltas: &HashMap>, ) -> Vec { @@ -123,3 +116,49 @@ fn evaluate_polynomial( } result } + +fn prepare_share_updates_for_refreshing( + participants: &[PrivateDecryptionContextSimple], + threshold: usize, + rng: &mut impl RngCore, +) -> HashMap { + let coeffs = make_random_polynomial::(threshold, &E::Fr::zero(), rng); + compute_polynomial_deltas(participants, &coeffs) +} + +fn compute_polynomial_deltas( + participants: &[PrivateDecryptionContextSimple], + coeffs: &Vec, +) -> HashMap { + participants + .iter() + .map(|p| { + let i = p.index; + let x_i = p.public_decryption_contexts[i].domain; + let eval = evaluate_polynomial::(coeffs, &x_i); + let h_g2 = E::G2Projective::from(p.h); + let eval_g2 = h_g2.mul(eval.into_repr()); + (i, eval_g2) + }) + .collect::>() +} + +pub fn refresh_shares( + participants: &[PrivateDecryptionContextSimple], + threshold: usize, + rng: &mut impl RngCore, +) -> Vec { + let share_updates = + prepare_share_updates_for_refreshing::(participants, threshold, rng); + participants + .iter() + .map(|p| { + let i = p.index; + let mut new_y = E::G2Projective::from( + p.private_key_share.private_key_shares[0], // y_i + ); + new_y += share_updates[&i]; + new_y + }) + .collect() +} From 232737832b34658df95a500b61fe856d7bd767f1 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 3 Jan 2023 10:22:56 +0100 Subject: [PATCH 07/14] fix clippy warnings --- tpke/src/refresh.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index 4b47e81b..a4256a69 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -128,7 +128,7 @@ fn prepare_share_updates_for_refreshing( fn compute_polynomial_deltas( participants: &[PrivateDecryptionContextSimple], - coeffs: &Vec, + coeffs: &[E::Fr], ) -> HashMap { participants .iter() From 7d5ecd9a54873719f0a1f1ec42957eb94ff97945 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 4 Jan 2023 11:01:24 +0100 Subject: [PATCH 08/14] fix after rebase --- tpke/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index f8e10f70..cc48e50a 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -457,7 +457,7 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _, private_decryption_contexts) = + let (pubkey, _, contexts) = setup_simple::(threshold, shares_num, rng); // Ciphertext.commitment is already computed to match U From 5456c422a9f9b2a3964c2d3dd8de5700f0dccdd3 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Fri, 6 Jan 2023 16:15:41 +0100 Subject: [PATCH 09/14] add comments after initial review --- tpke/src/refresh.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index a4256a69..6d84bb36 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -24,6 +24,7 @@ pub fn recover_share_at_point( let new_shares_y = update_shares_for_recovery::(other_participants, &share_updates); + // From the PSS paper, section 4.2.4, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) // Interpolate new shares to recover y_r let shares_x = &other_participants[0] .public_decryption_contexts @@ -44,14 +45,16 @@ fn prepare_share_updates_for_recovery( threshold: usize, rng: &mut impl RngCore, ) -> HashMap> { + // From PSS paper, section 4.2.1, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) + // TODO: Refactor this function so that each participant performs it individually // Each participant prepares an update for each other participant participants .iter() .map(|p1| { let i = p1.index; - // Generate a new random polynomial with constant term 0 - let d_i = make_random_polynomial::(threshold, x_r, rng); + // Generate a new random polynomial with constant term x_r + let d_i = make_random_polynomial_at::(threshold, x_r, rng); // Now, we need to evaluate the polynomial at each of participants' indices let deltas_i: HashMap<_, _> = @@ -65,6 +68,7 @@ fn update_shares_for_recovery( participants: &[PrivateDecryptionContextSimple], deltas: &HashMap>, ) -> Vec { + // From PSS paper, section 4.2.3, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) // TODO: Refactor this function so that each participant performs it individually participants .iter() @@ -81,9 +85,9 @@ fn update_shares_for_recovery( .collect() } -fn make_random_polynomial( +fn make_random_polynomial_at( threshold: usize, - x_r: &E::Fr, + root: &E::Fr, rng: &mut impl RngCore, ) -> Vec { // [][threshold-1] @@ -95,9 +99,9 @@ fn make_random_polynomial( // Now, we calculate d_i_0 // This is the term that will "zero out" the polynomial at x_r, d_i(x_r) = 0 - let d_i_0 = E::Fr::zero() - evaluate_polynomial::(&d_i, x_r); + let d_i_0 = E::Fr::zero() - evaluate_polynomial::(&d_i, root); d_i[0] = d_i_0; - assert_eq!(evaluate_polynomial::(&d_i, x_r), E::Fr::zero()); + assert_eq!(evaluate_polynomial::(&d_i, root), E::Fr::zero()); assert_eq!(d_i.len(), threshold); @@ -122,7 +126,7 @@ fn prepare_share_updates_for_refreshing( threshold: usize, rng: &mut impl RngCore, ) -> HashMap { - let coeffs = make_random_polynomial::(threshold, &E::Fr::zero(), rng); + let coeffs = make_random_polynomial_at::(threshold, &E::Fr::zero(), rng); compute_polynomial_deltas(participants, &coeffs) } From 48732e7d6e221ff985bde4fca35a0137f2ce123a Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 11 Jan 2023 14:34:07 +0100 Subject: [PATCH 10/14] apply pr suggestions --- tpke/src/lib.rs | 109 +++++++++++++++++++++++++++----------------- tpke/src/refresh.rs | 42 +++++++---------- 2 files changed, 83 insertions(+), 68 deletions(-) diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index cc48e50a..e822f099 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -142,7 +142,7 @@ pub fn setup_fast( // F_0 - The commitment to the constant term, and is the public key output Y from PVDKG // TODO: It seems like the rest of the F_i are not computed? let pubkey = g.mul(x); - let privkey = h.mul(x); // ek_i in PVSS? + let privkey = h.mul(x); let mut private_contexts = vec![]; let mut public_contexts = vec![]; @@ -501,7 +501,9 @@ mod tests { } #[test] - fn simple_threshold_decryption_with_share_refreshing_at_point() { + /// Ñ 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. + fn simple_threshold_decryption_with_share_recovery_at_selected_point() { let rng = &mut test_rng(); let shares_num = 16; let threshold = shares_num * 2 / 3; @@ -527,7 +529,7 @@ mod tests { p.public_decryption_contexts.pop(); } - // Refresh the share + // Recover the share let y_r = recover_share_at_point( &remaining_participants, threshold, @@ -537,8 +539,38 @@ mod tests { assert_eq!(y_r.into_affine(), original_y_r); } + fn make_shared_secret_from_contexts( + contexts: &[PrivateDecryptionContextSimple], + ciphertext: &Ciphertext, + ) -> E::Fqk { + let decryption_shares: Vec<_> = contexts + .iter() + .map(|ctxt| { + make_decryption_share(&ctxt.private_key_share, ciphertext) + }) + .collect(); + make_shared_secret( + &contexts[0].public_decryption_contexts, + &decryption_shares, + ) + } + + fn make_shared_secret( + pub_contexts: &[PublicDecryptionContextSimple], + decryption_shares: &[E::Fqk], + ) -> E::Fqk { + let shares_x = pub_contexts + .iter() + .map(|context| context.domain) + .collect::>(); + let lagrange = prepare_combine_simple::(&shares_x); + share_combine_simple::(decryption_shares, &lagrange) + } + #[test] - fn simple_threshold_decryption_with_share_recovery_at_point() { + /// Ñ parties (where t <= Ñ <= N) jointly execute a "share recovery" algorithm, and the output is 1 new share. + /// The new share is independent from the previously existing shares. We can use this to on-board a new participant into an existing cohort. + fn simple_threshold_decryption_with_share_recovery_at_random_point() { let rng = &mut test_rng(); let shares_num = 16; let threshold = shares_num * 2 / 3; @@ -549,14 +581,20 @@ mod tests { setup_simple::(threshold, shares_num, rng); let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); + // Create an initial shared secret + let old_shared_secret = + make_shared_secret_from_contexts(&contexts, &ciphertext); + + // Now, we're going to recover a new share at a random point and check that the shared secret is still the same + // Remove one participant from the contexts and all nested structures let mut remaining_participants = contexts; - remaining_participants.pop(); + remaining_participants.pop().unwrap(); for p in &mut remaining_participants { - p.public_decryption_contexts.pop(); + p.public_decryption_contexts.pop().unwrap(); } - // Refresh the share + // Recover the share let x_r = Fr::rand(rng); let y_r = recover_share_at_point( &remaining_participants, @@ -579,26 +617,19 @@ mod tests { .push(make_decryption_share(&recovered_key_share, &ciphertext)); // Creating a shared secret from remaining shares and the recovered one - let shares_x = &remaining_participants[0] - .public_decryption_contexts - .iter() - .map(|ctxt| ctxt.domain) - .collect::>(); - let lagrange = prepare_combine_simple::(shares_x); - - let shared_secret = - share_combine_simple::(&decryption_shares, &lagrange); - - let plaintext = checked_decrypt_with_shared_secret( - &ciphertext, - aad, - &shared_secret, + let new_shared_secret = make_shared_secret( + &remaining_participants[0].public_decryption_contexts, + &decryption_shares, ); - assert_eq!(plaintext, msg); + + assert_eq!(old_shared_secret, new_shared_secret); } + /// Ñ parties (where t <= Ñ <= N) jointly execute a "share refresh" algorithm. + /// The output is M new shares (with M <= Ñ), with each of the M new shares substituting the + /// original share (i.e., the original share is deleted). #[test] - fn simple_threshold_decryption_with_share_refresh() { + fn simple_threshold_decryption_with_share_refreshing() { let rng = &mut test_rng(); let shares_num = 16; let threshold = shares_num * 2 / 3; @@ -607,13 +638,20 @@ mod tests { let (pubkey, _, contexts) = setup_simple::(threshold, shares_num, rng); + let pub_contexts = contexts[0].public_decryption_contexts.clone(); let ciphertext = encrypt::<_, E>(msg, aad, &pubkey, rng); + // Create an initial shared secret + let old_shared_secret = + make_shared_secret_from_contexts(&contexts, &ciphertext); + + // Now, we're going to refresh the shares and check that the shared secret is the same + // Refresh shares - let fresh_shares = refresh_shares::(&contexts, threshold, rng); + let new_shares = refresh_shares::(&contexts, threshold, rng); - // Creating decryption shares - let decryption_shares: Vec<_> = fresh_shares + // Creating new decryption shares + let new_decryption_shares: Vec<_> = new_shares .iter() .map(|private_share| { let private_share = PrivateKeyShare { @@ -623,22 +661,9 @@ mod tests { }) .collect(); - // Creating a shared secret from remaining shares and the recovered one - let shares_x = &contexts[0] - .public_decryption_contexts - .iter() - .map(|ctxt| ctxt.domain) - .collect::>(); - let lagrange = prepare_combine_simple::(shares_x); - - let shared_secret = - share_combine_simple::(&decryption_shares, &lagrange); + let new_shared_secret = + make_shared_secret(&pub_contexts, &new_decryption_shares); - let plaintext = checked_decrypt_with_shared_secret( - &ciphertext, - aad, - &shared_secret, - ); - assert_eq!(plaintext, msg); + assert_eq!(old_shared_secret, new_shared_secret); } } diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index 6d84bb36..a7b16e70 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -1,6 +1,8 @@ use crate::{lagrange_basis_at, PrivateDecryptionContextSimple}; use ark_ec::{PairingEngine, ProjectiveCurve}; -use ark_ff::{One, PrimeField, Zero}; +use ark_ff::{PrimeField, Zero}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::{Polynomial, UVPolynomial}; use ark_std::UniformRand; use itertools::zip_eq; use rand::prelude::StdRng; @@ -39,14 +41,13 @@ pub fn recover_share_at_point( prods.fold(E::G2Projective::zero(), |acc, y_j| acc + y_j) } +/// From PSS paper, section 4.2.1, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) fn prepare_share_updates_for_recovery( participants: &[PrivateDecryptionContextSimple], x_r: &E::Fr, threshold: usize, rng: &mut impl RngCore, ) -> HashMap> { - // From PSS paper, section 4.2.1, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) - // TODO: Refactor this function so that each participant performs it individually // Each participant prepares an update for each other participant participants @@ -64,11 +65,11 @@ fn prepare_share_updates_for_recovery( .collect::>() } +/// From PSS paper, section 4.2.3, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) fn update_shares_for_recovery( participants: &[PrivateDecryptionContextSimple], deltas: &HashMap>, ) -> Vec { - // From PSS paper, section 4.2.3, (https://link.springer.com/content/pdf/10.1007/3-540-44750-4_27.pdf) // TODO: Refactor this function so that each participant performs it individually participants .iter() @@ -89,58 +90,47 @@ fn make_random_polynomial_at( threshold: usize, root: &E::Fr, rng: &mut impl RngCore, -) -> Vec { +) -> DensePolynomial { // [][threshold-1] let mut d_i = (0..threshold - 1) .map(|_| E::Fr::rand(rng)) .collect::>(); // [0..][threshold] d_i.insert(0, E::Fr::zero()); + let mut d_i = DensePolynomial::from_coefficients_vec(d_i); // Now, we calculate d_i_0 // This is the term that will "zero out" the polynomial at x_r, d_i(x_r) = 0 - let d_i_0 = E::Fr::zero() - evaluate_polynomial::(&d_i, root); + let d_i_0 = E::Fr::zero() - d_i.evaluate(root); d_i[0] = d_i_0; - assert_eq!(evaluate_polynomial::(&d_i, root), E::Fr::zero()); - assert_eq!(d_i.len(), threshold); + debug_assert!(d_i.evaluate(root) == E::Fr::zero()); + debug_assert!(d_i.len() == threshold); d_i } -fn evaluate_polynomial( - polynomial: &[E::Fr], - x: &E::Fr, -) -> E::Fr { - let mut result = E::Fr::zero(); - let mut x_power = E::Fr::one(); - for coeff in polynomial { - result += *coeff * x_power; - x_power *= x; - } - result -} - fn prepare_share_updates_for_refreshing( participants: &[PrivateDecryptionContextSimple], threshold: usize, rng: &mut impl RngCore, ) -> HashMap { - let coeffs = make_random_polynomial_at::(threshold, &E::Fr::zero(), rng); - compute_polynomial_deltas(participants, &coeffs) + let polynomial = + make_random_polynomial_at::(threshold, &E::Fr::zero(), rng); + compute_polynomial_deltas(participants, &polynomial) } fn compute_polynomial_deltas( participants: &[PrivateDecryptionContextSimple], - coeffs: &[E::Fr], + polynomial: &DensePolynomial, ) -> HashMap { + let h_g2 = E::G2Projective::from(participants[0].h); participants .iter() .map(|p| { let i = p.index; let x_i = p.public_decryption_contexts[i].domain; - let eval = evaluate_polynomial::(coeffs, &x_i); - let h_g2 = E::G2Projective::from(p.h); + let eval = polynomial.evaluate(&x_i); let eval_g2 = h_g2.mul(eval.into_repr()); (i, eval_g2) }) From 989415a9581063cc1105a7b361f79d780bf55111 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 11 Jan 2023 15:54:36 +0100 Subject: [PATCH 11/14] fix after rebase --- tpke/src/combine.rs | 10 +--------- tpke/src/context.rs | 1 + tpke/src/lib.rs | 20 +++++++------------- tpke/src/refresh.rs | 2 +- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/tpke/src/combine.rs b/tpke/src/combine.rs index 96d0d18f..2f82df90 100644 --- a/tpke/src/combine.rs +++ b/tpke/src/combine.rs @@ -51,17 +51,9 @@ pub fn prepare_combine_simple( .map(|ctxt| ctxt.domain) .collect::>(); - // In this formula x_i = 0, hence numerator is x_m - lagrange_coeffs_at::(&shares_x, &E::Fr::zero()) -} - -fn lagrange_coeffs_at( - shares_x: &Vec, - x_i: &E::Fr, -) -> Vec { // Calculate lagrange coefficients using optimized formula, see https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm // In this formula x_i = 0, hence numerator is x_m - lagrange_basis_at::(shares_x, &E::Fr::zero()) + lagrange_basis_at::(&shares_x, &E::Fr::zero()) } /// Calculates Lagrange coefficients for a given x_i diff --git a/tpke/src/context.rs b/tpke/src/context.rs index 0cbd140d..635f19e1 100644 --- a/tpke/src/context.rs +++ b/tpke/src/context.rs @@ -23,6 +23,7 @@ pub struct SetupParams { pub g: E::G1Affine, pub g_inv: E::G1Prepared, pub h_inv: E::G2Prepared, + pub h: E::G2Affine, } #[derive(Clone, Debug)] diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index e822f099..baa5d3cc 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -177,6 +177,7 @@ pub fn setup_fast( g, g_inv: E::G1Prepared::from(-g), h_inv: E::G2Prepared::from(-h), + h, }, private_key_share, public_decryption_contexts: vec![], @@ -272,6 +273,7 @@ pub fn setup_simple( g, g_inv: E::G1Prepared::from(-g), h_inv: E::G2Prepared::from(-h), + h, }, private_key_share, public_decryption_contexts: vec![], @@ -332,8 +334,7 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _, _) = - setup_fast::(threshold, shares_num, rng); + let (pubkey, _, _) = setup_fast::(threshold, shares_num, rng); let ciphertext = encrypt::(msg, aad, &pubkey, rng); @@ -365,8 +366,7 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, privkey, _) = - setup_fast::(threshold, shares_num, rng); + let (pubkey, privkey, _) = setup_fast::(threshold, shares_num, rng); let ciphertext = encrypt::(msg, aad, &pubkey, rng); let plaintext = checked_decrypt(&ciphertext, aad, privkey); @@ -433,8 +433,7 @@ mod tests { let msg: &[u8] = "abc".as_bytes(); let aad: &[u8] = "my-aad".as_bytes(); - let (pubkey, _, _) = - setup_fast::(threshold, shares_num, rng); + let (pubkey, _, _) = setup_fast::(threshold, shares_num, rng); let mut ciphertext = encrypt::(msg, aad, &pubkey, rng); // So far, the ciphertext is valid @@ -470,8 +469,7 @@ mod tests { make_decryption_share(&ctxt.private_key_share, &ciphertext) }) .collect(); - let pub_contexts = - &contexts[0].public_decryption_contexts; + let pub_contexts = &contexts[0].public_decryption_contexts; let lagrange = prepare_combine_simple::(pub_contexts); let shared_secret = @@ -559,11 +557,7 @@ mod tests { pub_contexts: &[PublicDecryptionContextSimple], decryption_shares: &[E::Fqk], ) -> E::Fqk { - let shares_x = pub_contexts - .iter() - .map(|context| context.domain) - .collect::>(); - let lagrange = prepare_combine_simple::(&shares_x); + let lagrange = prepare_combine_simple::(pub_contexts); share_combine_simple::(decryption_shares, &lagrange) } diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index a7b16e70..f08753da 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -124,7 +124,7 @@ fn compute_polynomial_deltas( participants: &[PrivateDecryptionContextSimple], polynomial: &DensePolynomial, ) -> HashMap { - let h_g2 = E::G2Projective::from(participants[0].h); + let h_g2 = E::G2Projective::from(participants[0].setup_params.h); participants .iter() .map(|p| { From 92f6f551bbbaba5229ae8f3628b8d89147c8fe1a Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 12 Jan 2023 10:44:22 +0100 Subject: [PATCH 12/14] Use arkworks for polynomails and benchmark relevant functions --- tpke/benches/benchmarks.rs | 54 ++++++++++++++++++++++++++++++++++++-- tpke/src/refresh.rs | 19 +++++++++++--- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/tpke/benches/benchmarks.rs b/tpke/benches/benchmarks.rs index 01ac8420..5aa71e32 100644 --- a/tpke/benches/benchmarks.rs +++ b/tpke/benches/benchmarks.rs @@ -1,6 +1,11 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use ark_std::Zero; +use criterion::{ + black_box, criterion_group, criterion_main, BenchmarkId, Criterion, +}; use group_threshold_cryptography::*; +type Fr = ::Fr; + pub fn bench_decryption(c: &mut Criterion) { use rand::SeedableRng; use rand_core::RngCore; @@ -143,5 +148,50 @@ pub fn bench_decryption(c: &mut Criterion) { } } -criterion_group!(benches, bench_decryption); +pub fn bench_random_poly(c: &mut Criterion) { + use rand::SeedableRng; + let mut group = c.benchmark_group("RandomPoly"); + type E = ark_bls12_381::Bls12_381; + group.sample_size(10); + + for threshold in [20, 40, 60, 80, 100] { + let rng = &mut rand::rngs::StdRng::seed_from_u64(0); + let mut ark = { + let mut rng = rng.clone(); + move || { + black_box(make_random_ark_polynomial_at::( + threshold, + &Fr::zero(), + &mut rng, + )) + } + }; + let mut vec = { + let mut rng = rng.clone(); + move || { + black_box(make_random_polynomial_at::( + threshold, + &Fr::zero(), + &mut rng, + )) + } + }; + group.bench_function( + BenchmarkId::new("random_polynomial_ark", threshold), + |b| { + #[allow(clippy::redundant_closure)] + b.iter(|| ark()) + }, + ); + group.bench_function( + BenchmarkId::new("random_polynomial_vec", threshold), + |b| { + #[allow(clippy::redundant_closure)] + b.iter(|| vec()) + }, + ); + } +} + +criterion_group!(benches, bench_decryption, bench_random_poly); criterion_main!(benches); diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index f08753da..bacf5551 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -1,8 +1,7 @@ use crate::{lagrange_basis_at, PrivateDecryptionContextSimple}; use ark_ec::{PairingEngine, ProjectiveCurve}; use ark_ff::{PrimeField, Zero}; -use ark_poly::univariate::DensePolynomial; -use ark_poly::{Polynomial, UVPolynomial}; +use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial}; use ark_std::UniformRand; use itertools::zip_eq; use rand::prelude::StdRng; @@ -86,7 +85,7 @@ fn update_shares_for_recovery( .collect() } -fn make_random_polynomial_at( +pub fn make_random_polynomial_at( threshold: usize, root: &E::Fr, rng: &mut impl RngCore, @@ -110,6 +109,20 @@ fn make_random_polynomial_at( d_i } +pub fn make_random_ark_polynomial_at( + threshold: usize, + root: &E::Fr, + rng: &mut impl RngCore, +) -> Vec { + let mut threshold_poly = DensePolynomial::::rand(threshold - 1, rng); + threshold_poly[0] = E::Fr::zero(); + let d_i_0 = E::Fr::zero() - threshold_poly.evaluate(root); + threshold_poly[0] = d_i_0; + assert_eq!(threshold_poly.evaluate(root), E::Fr::zero()); + assert_eq!(threshold_poly.coeffs.len(), threshold); + threshold_poly.coeffs +} + fn prepare_share_updates_for_refreshing( participants: &[PrivateDecryptionContextSimple], threshold: usize, From 871fdf4b8734fb0d55e9aed095557cbe3e61cedf Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 13 Jan 2023 10:09:22 +0100 Subject: [PATCH 13/14] Use `debug_assert` to make benchmarks more consistent Co-authored-by: piotr-roslaniec <39299780+piotr-roslaniec@users.noreply.github.com> --- tpke/src/refresh.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tpke/src/refresh.rs b/tpke/src/refresh.rs index bacf5551..e330829e 100644 --- a/tpke/src/refresh.rs +++ b/tpke/src/refresh.rs @@ -118,8 +118,8 @@ pub fn make_random_ark_polynomial_at( threshold_poly[0] = E::Fr::zero(); let d_i_0 = E::Fr::zero() - threshold_poly.evaluate(root); threshold_poly[0] = d_i_0; - assert_eq!(threshold_poly.evaluate(root), E::Fr::zero()); - assert_eq!(threshold_poly.coeffs.len(), threshold); + debug_assert!(threshold_poly.evaluate(root) == E::Fr::zero()); + debug_assert!(threshold_poly.coeffs.len() == threshold); threshold_poly.coeffs } From d8d325ce88f957c6c97fe5e4c58943bb6a2fc10f Mon Sep 17 00:00:00 2001 From: James Campbell Date: Fri, 13 Jan 2023 10:33:11 +0100 Subject: [PATCH 14/14] Apply PR suggestions --- tpke/benches/benchmarks.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tpke/benches/benchmarks.rs b/tpke/benches/benchmarks.rs index 5aa71e32..45380d2c 100644 --- a/tpke/benches/benchmarks.rs +++ b/tpke/benches/benchmarks.rs @@ -5,6 +5,7 @@ use criterion::{ use group_threshold_cryptography::*; type Fr = ::Fr; +type E = ark_bls12_381::Bls12_381; pub fn bench_decryption(c: &mut Criterion) { use rand::SeedableRng; @@ -151,10 +152,9 @@ pub fn bench_decryption(c: &mut Criterion) { pub fn bench_random_poly(c: &mut Criterion) { use rand::SeedableRng; let mut group = c.benchmark_group("RandomPoly"); - type E = ark_bls12_381::Bls12_381; group.sample_size(10); - for threshold in [20, 40, 60, 80, 100] { + for threshold in [1, 2, 4, 8, 16, 32, 64] { let rng = &mut rand::rngs::StdRng::seed_from_u64(0); let mut ark = { let mut rng = rng.clone();