From 417e478c087794ad8f14a2175a04dedc7eb612e4 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 30 May 2023 15:17:09 +0200 Subject: [PATCH] sketch pessimistic case for precomputed cvariant --- tpke/src/combine.rs | 23 ++++--- tpke/src/decryption.rs | 7 ++ tpke/src/lib.rs | 141 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 162 insertions(+), 9 deletions(-) diff --git a/tpke/src/combine.rs b/tpke/src/combine.rs index 39091e88..a8322a7b 100644 --- a/tpke/src/combine.rs +++ b/tpke/src/combine.rs @@ -1,5 +1,3 @@ -#![allow(non_snake_case)] - use std::ops::Mul; use ark_ec::{pairing::Pairing, CurveGroup}; @@ -11,6 +9,12 @@ use serde_with::serde_as; use subproductdomain::SubproductDomain; use zeroize::{Zeroize, ZeroizeOnDrop}; +use crate::{ + verify_decryption_shares_fast, Ciphertext, DecryptionShareFast, + DecryptionSharePrecomputed, DecryptionShareSimple, Error, + PublicDecryptionContextFast, Result, +}; + #[serde_as] #[derive( Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop, @@ -19,12 +23,6 @@ pub struct SharedSecret( #[serde_as(as = "serialization::SerdeAs")] pub(crate) E::TargetField, ); -use crate::{ - verify_decryption_shares_fast, Ciphertext, DecryptionShareFast, - DecryptionSharePrecomputed, DecryptionShareSimple, Error, - PublicDecryptionContextFast, Result, -}; - pub fn prepare_combine_fast( public_decryption_contexts: &[PublicDecryptionContextFast], shares: &[DecryptionShareFast], @@ -141,6 +139,15 @@ pub fn share_combine_simple( SharedSecret(shared_secret) } +pub fn share_combine_simple_updated( + decryption_shares: &[DecryptionShareSimple], +) -> SharedSecret { + let shared_secret = decryption_shares + .iter() + .fold(E::TargetField::one(), |acc, c_i| acc * c_i.decryption_share); + SharedSecret(shared_secret) +} + pub fn share_combine_precomputed( shares: &[DecryptionSharePrecomputed], ) -> SharedSecret { diff --git a/tpke/src/decryption.rs b/tpke/src/decryption.rs index 9eb62471..e4bafe6f 100644 --- a/tpke/src/decryption.rs +++ b/tpke/src/decryption.rs @@ -176,6 +176,13 @@ impl DecryptionSharePrecomputed { ) } + pub fn to_simple(&self) -> DecryptionShareSimple { + DecryptionShareSimple { + decryption_share: self.decryption_share, + validator_checksum: self.validator_checksum.clone(), + } + } + pub fn create_unchecked( validator_index: usize, validator_decryption_key: &E::ScalarField, diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs index 43ebdaa4..ba54d80d 100644 --- a/tpke/src/lib.rs +++ b/tpke/src/lib.rs @@ -280,9 +280,10 @@ mod tests { use ark_bls12_381::Fr; use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; - use ark_ff::Zero; + use ark_ff::{Field, Zero}; use ark_std::{test_rng, UniformRand}; use ferveo_common::{FromBytes, ToBytes}; + use itertools::zip_eq; use rand_core::RngCore; use crate::{ @@ -854,4 +855,142 @@ mod tests { assert_eq!(old_shared_secret, new_shared_secret); } + + #[test] + fn tdec_precomputed_variant_pessimistic_case() { + let mut rng = &mut test_rng(); + let shares_num = 16; + let msg = "my-msg".as_bytes().to_vec(); + let aad: &[u8] = "my-aad".as_bytes(); + + let (pubkey, _, contexts_precomputed) = + setup_precomputed::(shares_num, &mut rng); + let g_inv = &contexts_precomputed[0].setup_params.g_inv; + let ciphertext = + encrypt::(SecretBox::new(msg.clone()), aad, &pubkey, rng) + .unwrap(); + + // Some nodes didn't respond + let missing_node_n = 2; + + let decryption_shares_precomputed: Vec<_> = contexts_precomputed + .iter() + .map(|context| { + context.create_share_precomputed(&ciphertext, aad).unwrap() + }) + .take(shares_num - missing_node_n) + .collect::>(); + + // We need to run another round of setup recreate missing shares + let (_, _, contexts_simple) = + setup_simple::(missing_node_n, missing_node_n, &mut rng); + + let decryption_shares_simple: Vec<_> = contexts_simple + .iter() + .map(|context| context.create_share(&ciphertext, aad).unwrap()) + .collect::>(); + + // Ok, now we are back to `share_num` number of shares. We can proceed with decryption + + // First, we need to prepare the lagrange coefficients to update the shares + // We need to combine the domain points from both contexts + let precomputed_domain_points = contexts_precomputed[0] + .public_decryption_contexts + .iter() + .map(|c| c.domain) + .take(shares_num - missing_node_n) + .collect::>(); + let simple_domain_points = contexts_simple[0] + .public_decryption_contexts + .iter() + .map(|c| c.domain) + .take(missing_node_n) + .collect::>(); + let all_domain_points = precomputed_domain_points + .iter() + .cloned() + .chain(simple_domain_points.iter().cloned()) + .collect::>(); + + // These are the old L coefficients, they correspond to the precomputed shares + let precomputed_lagrange_coeffs = + prepare_combine_simple::(&precomputed_domain_points); + + // These are the new L' coefficients, they correspond to both the precomputed and the simple shares + let new_lagrange_coeffs = + prepare_combine_simple::(&all_domain_points); + + // Now, we update our shares + // We're going to calculate share updates for both the precomputed and the simple shares + + // Update coefficients for precomputed shares + let updated_lagrange_coeffs_initial = zip_eq( + precomputed_lagrange_coeffs, + new_lagrange_coeffs[0..shares_num - missing_node_n].to_vec(), + ) + // L' / L + .map(|(initial, combined)| combined / initial) + .collect::>(); + + // For the simple shares, we can just use the new coefficients + let simple_share_updates = + new_lagrange_coeffs[shares_num - missing_node_n..].to_vec(); + + // Finally, we can update the shares + + let updated_precomputed_shares = zip_eq( + decryption_shares_precomputed, + updated_lagrange_coeffs_initial, + ) + .map(|(share, updated_lagrange_coeff)| { + let mut updated_share = share; + updated_share.decryption_share = + updated_share.decryption_share.pow(updated_lagrange_coeff.0); + updated_share + }) + .collect::>(); + let updated_simple_shares = + zip_eq(decryption_shares_simple, simple_share_updates) + .map(|(share, updated_coeffs)| { + let mut updated_share = share; + updated_share.decryption_share = + updated_share.decryption_share.pow(updated_coeffs.0); + updated_share + }) + .collect::>(); + + // Now, we can combine the share to get the shared secret + // We're going to cast precomputed shares into simple shares + let combined_shares = updated_precomputed_shares + .iter() + .map(|s| s.to_simple()) + .chain(updated_simple_shares.iter().cloned()) + .collect::>(); + let shared_secret = share_combine_simple_updated::(&combined_shares); + + test_ciphertext_validation_fails( + &msg, + aad, + &ciphertext, + &shared_secret, + g_inv, + ); + + // TODO: Sort out this error case + + // Note that in this variant, if we use less than `share_num` shares, we will get a + // decryption error. + + // let not_enough_shares = + // &decryption_shares_precomputed[0..shares_num - 1]; + // let bad_shared_secret = + // share_combine_precomputed::(not_enough_shares); + // assert!(decrypt_with_shared_secret( + // &ciphertext, + // aad, + // &bad_shared_secret, + // g_inv, + // ) + // .is_err()); + } }