From 5f154ace70ccf086e73856ef36db20cf77989839 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 1 Dec 2024 17:42:55 -0800 Subject: [PATCH] Better handling of Paillier randomizers --- synedrion/src/cggmp21/entities.rs | 6 +- synedrion/src/cggmp21/interactive_signing.rs | 52 +++--- synedrion/src/cggmp21/key_refresh.rs | 8 +- synedrion/src/cggmp21/sigma/aff_g.rs | 46 +++--- synedrion/src/cggmp21/sigma/dec.rs | 20 +-- synedrion/src/cggmp21/sigma/enc.rs | 20 +-- synedrion/src/cggmp21/sigma/fac.rs | 18 +-- synedrion/src/cggmp21/sigma/log_star.rs | 20 +-- synedrion/src/cggmp21/sigma/mul.rs | 26 +-- synedrion/src/cggmp21/sigma/mul_star.rs | 20 +-- synedrion/src/paillier.rs | 2 +- synedrion/src/paillier/encryption.rs | 160 ++++++++++++------- synedrion/src/tools/secret.rs | 33 ++++ 13 files changed, 256 insertions(+), 175 deletions(-) diff --git a/synedrion/src/cggmp21/entities.rs b/synedrion/src/cggmp21/entities.rs index cce9a798..e04b4e0d 100644 --- a/synedrion/src/cggmp21/entities.rs +++ b/synedrion/src/cggmp21/entities.rs @@ -12,7 +12,7 @@ use crate::{ cggmp21::SchemeParams, curve::{secret_split, Point, Scalar}, paillier::{ - Ciphertext, PaillierParams, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, RandomizerWire, + Ciphertext, PaillierParams, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, Randomizer, SecretKeyPaillier, SecretKeyPaillierWire, }, tools::Secret, @@ -113,8 +113,8 @@ pub(crate) struct PresigningData { #[derive(Debug, Clone)] pub(crate) struct PresigningValues { pub(crate) hat_beta: Secret::Uint>>, - pub(crate) hat_r: RandomizerWire, - pub(crate) hat_s: RandomizerWire, + pub(crate) hat_r: Randomizer, + pub(crate) hat_s: Randomizer, pub(crate) cap_k: Ciphertext, /// Received $\hat{D}_{i,j}$. pub(crate) hat_cap_d_received: Ciphertext, diff --git a/synedrion/src/cggmp21/interactive_signing.rs b/synedrion/src/cggmp21/interactive_signing.rs index f9fadff8..f84fc8f4 100644 --- a/synedrion/src/cggmp21/interactive_signing.rs +++ b/synedrion/src/cggmp21/interactive_signing.rs @@ -27,7 +27,7 @@ use super::{ }; use crate::{ curve::{Point, RecoverableSignature, Scalar}, - paillier::{Ciphertext, CiphertextWire, PaillierParams, Randomizer, RandomizerWire}, + paillier::{Ciphertext, CiphertextWire, PaillierParams, Randomizer}, tools::{ hashing::{Chain, FofHasher, HashOutput}, DowncastMap, Secret, Without, @@ -162,10 +162,10 @@ impl EntryPoint for InteractiveSigning { let pk = aux_info.secret_aux.paillier_sk.public_key(); let nu = Randomizer::::random(rng, pk); - let cap_g = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::

(&gamma), &nu.to_wire()); + let cap_g = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::

(&gamma), &nu); let rho = Randomizer::::random(rng, pk); - let cap_k = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::

(&k), &rho.to_wire()); + let cap_k = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::

(&k), &rho); Ok(BoxedRound::new_dynamic(Round1 { context: Context { @@ -391,10 +391,10 @@ struct Round2Message { struct Round2Artifact { beta: Secret::Uint>>, hat_beta: Secret::Uint>>, - r: RandomizerWire, - s: RandomizerWire, - hat_r: RandomizerWire, - hat_s: RandomizerWire, + r: Randomizer, + s: Randomizer, + hat_r: Randomizer, + hat_s: Randomizer, cap_d: Ciphertext, cap_f: Ciphertext, hat_cap_d: Ciphertext, @@ -451,14 +451,14 @@ impl Round for Round2 { let gamma = secret_signed_from_scalar::

(&self.context.gamma); let x = secret_signed_from_scalar::

(&self.context.key_share.secret_share); - let cap_f = Ciphertext::new_with_randomizer_signed(pk, &beta, &r.to_wire()); - let cap_d = &self.all_cap_k[destination] * &gamma - + Ciphertext::new_with_randomizer_signed(target_pk, &-&beta, &s.to_wire()); + let cap_f = Ciphertext::new_with_randomizer_signed(pk, &beta, &r); + let cap_d = + &self.all_cap_k[destination] * &gamma + Ciphertext::new_with_randomizer_signed(target_pk, &-&beta, &s); - let hat_cap_f = Ciphertext::new_with_randomizer_signed(pk, &hat_beta, &hat_r.to_wire()); + let hat_cap_f = Ciphertext::new_with_randomizer_signed(pk, &hat_beta, &hat_r); let hat_cap_d = &self.all_cap_k[destination] * secret_signed_from_scalar::

(&self.context.key_share.secret_share) - + Ciphertext::new_with_randomizer_signed(target_pk, &-&hat_beta, &hat_s.to_wire()); + + Ciphertext::new_with_randomizer_signed(target_pk, &-&hat_beta, &hat_s); let public_aux = &self.context.aux_info.public_aux[destination]; let rp = &public_aux.rp_params; @@ -467,8 +467,8 @@ impl Round for Round2 { rng, &gamma, &beta, - s.clone(), - r.clone(), + &s, + &r, target_pk, pk, &self.all_cap_k[destination], @@ -483,8 +483,8 @@ impl Round for Round2 { rng, &x, &hat_beta, - hat_s.clone(), - hat_r.clone(), + &hat_s, + &hat_r, target_pk, pk, &self.all_cap_k[destination], @@ -524,10 +524,10 @@ impl Round for Round2 { let artifact = Artifact::new(Round2Artifact::

{ beta, hat_beta, - r: r.to_wire(), - s: s.to_wire(), - hat_r: hat_r.to_wire(), - hat_s: hat_s.to_wire(), + r, + s, + hat_r, + hat_s, cap_d, cap_f, hat_cap_d, @@ -889,8 +889,8 @@ impl Round for Round3 { rng, &secret_signed_from_scalar::

(&self.context.gamma), beta, - s.to_precomputed(target_pk), - r.to_precomputed(pk), + s, + r, target_pk, pk, &self.all_cap_k[id_j], @@ -920,7 +920,7 @@ impl Round for Round3 { let rho = Randomizer::random(rng, pk); let cap_h = (&self.all_cap_g[&self.context.my_id] * secret_bounded_from_scalar::

(&self.context.k)) - .mul_randomizer(&rho.to_wire()); + .mul_randomizer(&rho); let p_mul = MulProof::

::new( rng, @@ -1115,8 +1115,8 @@ impl Round for Round4 { rng, &secret_signed_from_scalar::

(&self.context.key_share.secret_share), &values.hat_beta, - values.hat_s.to_precomputed(target_pk), - values.hat_r.to_precomputed(pk), + &values.hat_s, + &values.hat_r, target_pk, pk, &values.cap_k, @@ -1148,7 +1148,7 @@ impl Round for Round4 { let cap_x = self.context.key_share.public_shares[&my_id]; let rho = Randomizer::random(rng, pk); - let hat_cap_h = (&self.presigning.cap_k * secret_bounded_from_scalar::

(x)).mul_randomizer(&rho.to_wire()); + let hat_cap_h = (&self.presigning.cap_k * secret_bounded_from_scalar::

(x)).mul_randomizer(&rho); let aux = (&self.context.ssid_hash, &my_id); diff --git a/synedrion/src/cggmp21/key_refresh.rs b/synedrion/src/cggmp21/key_refresh.rs index a411878f..808550aa 100644 --- a/synedrion/src/cggmp21/key_refresh.rs +++ b/synedrion/src/cggmp21/key_refresh.rs @@ -27,8 +27,8 @@ use super::{ use crate::{ curve::{secret_split, Point, Scalar}, paillier::{ - Ciphertext, CiphertextWire, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, RPSecret, - RandomizerWire, SecretKeyPaillier, SecretKeyPaillierWire, + Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, + RPSecret, SecretKeyPaillier, SecretKeyPaillierWire, }, tools::{ bitvec::BitVec, @@ -69,7 +69,7 @@ enum KeyRefreshErrorEnum { Round3MismatchedSecret { cap_c: CiphertextWire, x: Scalar, - mu: RandomizerWire, + mu: ::Uint, }, } @@ -660,7 +660,7 @@ impl Round for Round3 { KeyRefreshErrorEnum::Round3MismatchedSecret { cap_c: direct_message.data2.paillier_enc_x, x: *x.expose_secret(), - mu: mu.to_wire(), + mu: mu.expose(), }, ))); } diff --git a/synedrion/src/cggmp21/sigma/aff_g.rs b/synedrion/src/cggmp21/sigma/aff_g.rs index 812b3b09..fc14cd43 100644 --- a/synedrion/src/cggmp21/sigma/aff_g.rs +++ b/synedrion/src/cggmp21/sigma/aff_g.rs @@ -10,8 +10,8 @@ use super::super::{ use crate::{ curve::Point, paillier::{ - Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, - RandomizerWire, + Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, + Randomizer, }, tools::{ hashing::{Chain, Hashable, XofHasher}, @@ -58,8 +58,8 @@ pub(crate) struct AffGProof { z2: Signed<::Uint>, z3: Signed<::WideUint>, z4: Signed<::WideUint>, - omega: RandomizerWire, - omega_y: RandomizerWire, + omega: MaskedRandomizer, + omega_y: MaskedRandomizer, } impl AffGProof

{ @@ -68,8 +68,8 @@ impl AffGProof

{ rng: &mut impl CryptoRngCore, x: &Secret::Uint>>, y: &Secret::Uint>>, - rho: Randomizer, - rho_y: Randomizer, + rho: &Randomizer, + rho_y: &Randomizer, pk0: &PublicKeyPaillier, pk1: &PublicKeyPaillier, cap_c: &Ciphertext, @@ -90,17 +90,17 @@ impl AffGProof

{ let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let beta = Secret::init_with(|| Signed::random_bounded_bits(rng, P::LP_BOUND + P::EPS_BOUND)); - let r_mod = Randomizer::random(rng, pk0); - let r_y_mod = Randomizer::random(rng, pk1); + let r = Randomizer::random(rng, pk0); + let r_y = Randomizer::random(rng, pk1); let gamma = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); let m = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n)); let delta = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); let mu = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n)); - let cap_a = (cap_c * &alpha + Ciphertext::new_with_randomizer_signed(pk0, &beta, &r_mod.to_wire())).to_wire(); + let cap_a = (cap_c * &alpha + Ciphertext::new_with_randomizer_signed(pk0, &beta, &r)).to_wire(); let cap_b_x = secret_scalar_from_signed::

(&alpha).mul_by_generator(); - let cap_b_y = Ciphertext::new_with_randomizer_signed(pk1, &beta, &r_y_mod.to_wire()).to_wire(); + let cap_b_y = Ciphertext::new_with_randomizer_signed(pk1, &beta, &r_y).to_wire(); let cap_e = setup.commit(&alpha, &gamma).to_wire(); let cap_s = setup.commit(x, &m).to_wire(); let cap_f = setup.commit(&beta, &delta).to_wire(); @@ -145,12 +145,12 @@ impl AffGProof

{ let z3 = *(gamma + m * e_wide).expose_secret(); let z4 = *(delta + mu * e_wide).expose_secret(); - let omega = rho.to_public(&r_mod, &e); + let omega = rho.to_masked(&r, &e); // NOTE: deviation from the paper to support a different $D$ // (see the comment in `AffGProof`) // Original: $\rho_y^e$. Modified: $\rho_y^{-e}$. - let omega_y = rho_y.to_public(&r_y_mod, &-e); + let omega_y = rho_y.to_masked(&r_y, &-e); Self { e, @@ -225,7 +225,7 @@ impl AffGProof

{ // C^{z_1} (1 + N_0)^{z_2} \omega^{N_0} = A D^e \mod N_0^2 // => C (*) z_1 (+) encrypt_0(z_2, \omega) = A (+) D (*) e - if cap_c * self.z1 + Ciphertext::new_nonsecret_with_randomizer_signed(pk0, &self.z2, &self.omega) + if cap_c * self.z1 + Ciphertext::new_public_with_randomizer_signed(pk0, &self.z2, &self.omega) != cap_d * e + self.cap_a.to_precomputed(pk0) { return false; @@ -241,23 +241,23 @@ impl AffGProof

{ // Original: `Y^e`. Modified `Y^{-e}`. // (1 + N_1)^{z_2} \omega_y^{N_1} = B_y Y^(-e) \mod N_1^2 // => encrypt_1(z_2, \omega_y) = B_y (+) Y (*) (-e) - if Ciphertext::new_nonsecret_with_randomizer_signed(pk1, &self.z2, &self.omega_y) + if Ciphertext::new_public_with_randomizer_signed(pk1, &self.z2, &self.omega_y) != cap_y * (-e) + self.cap_b_y.to_precomputed(pk1) { return false; } // s^{z_1} t^{z_3} = E S^e \mod \hat{N} - let cap_e_mod = self.cap_e.to_precomputed(setup); - let cap_s_mod = self.cap_s.to_precomputed(setup); - if setup.commit_public(&self.z1, &self.z3) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { + let cap_e = self.cap_e.to_precomputed(setup); + let cap_s = self.cap_s.to_precomputed(setup); + if setup.commit_public(&self.z1, &self.z3) != &cap_e * &cap_s.pow_signed_vartime(&e) { return false; } // s^{z_2} t^{z_4} = F T^e \mod \hat{N} - let cap_f_mod = self.cap_f.to_precomputed(setup); - let cap_t_mod = self.cap_t.to_precomputed(setup); - if setup.commit_public(&self.z2, &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e) { + let cap_f = self.cap_f.to_precomputed(setup); + let cap_t = self.cap_t.to_precomputed(setup); + if setup.commit_public(&self.z2, &self.z4) != &cap_f * &cap_t.pow_signed_vartime(&e) { return false; } @@ -300,12 +300,12 @@ mod tests { let secret = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let cap_c = Ciphertext::new_signed(&mut OsRng, pk0, &secret); - let cap_d = &cap_c * &x + Ciphertext::new_with_randomizer_signed(pk0, &-&y, &rho.to_wire()); - let cap_y = Ciphertext::new_with_randomizer_signed(pk1, &y, &rho_y.to_wire()); + let cap_d = &cap_c * &x + Ciphertext::new_with_randomizer_signed(pk0, &-&y, &rho); + let cap_y = Ciphertext::new_with_randomizer_signed(pk1, &y, &rho_y); let cap_x = secret_scalar_from_signed::(&x).mul_by_generator(); let proof = AffGProof::::new( - &mut OsRng, &x, &y, rho, rho_y, pk0, pk1, &cap_c, &cap_d, &cap_y, &cap_x, &rp_params, &aux, + &mut OsRng, &x, &y, &rho, &rho_y, pk0, pk1, &cap_c, &cap_d, &cap_y, &cap_x, &rp_params, &aux, ); assert!(proof.verify(pk0, pk1, &cap_c, &cap_d, &cap_y, &cap_x, &rp_params, &aux)); } diff --git a/synedrion/src/cggmp21/sigma/dec.rs b/synedrion/src/cggmp21/sigma/dec.rs index 59015626..a2410100 100644 --- a/synedrion/src/cggmp21/sigma/dec.rs +++ b/synedrion/src/cggmp21/sigma/dec.rs @@ -10,8 +10,8 @@ use super::super::{ use crate::{ curve::Scalar, paillier::{ - Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, - RandomizerWire, + Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, + Randomizer, }, tools::hashing::{Chain, Hashable, XofHasher}, tools::Secret, @@ -43,7 +43,7 @@ pub(crate) struct DecProof { gamma: Scalar, z1: Signed<::WideUint>, z2: Signed<::WideUint>, - omega: RandomizerWire, + omega: MaskedRandomizer, } impl DecProof

{ @@ -69,7 +69,7 @@ impl DecProof

{ let cap_s = setup.commit(y, &mu).to_wire(); let cap_t = setup.commit(&alpha, &nu).to_wire(); - let cap_a = Ciphertext::new_with_randomizer_signed(pk0, &alpha, &r.to_wire()).to_wire(); + let cap_a = Ciphertext::new_with_randomizer_signed(pk0, &alpha, &r).to_wire(); // `alpha` is secret, but `gamma` only uncovers $\ell$ bits of `alpha`'s full $\ell + \eps$ bits, // and it's transmitted to another node, so it can be considered public. @@ -98,7 +98,7 @@ impl DecProof

{ let z1 = *(alpha.to_wide() + y.mul_wide(&e)).expose_secret(); let z2 = *(nu + mu * e.to_wide()).expose_secret(); - let omega = rho.to_public(&r, &e); + let omega = rho.to_masked(&r, &e); Self { e, @@ -144,7 +144,7 @@ impl DecProof

{ } // enc(z_1, \omega) == A (+) C (*) e - if Ciphertext::new_nonsecret_with_randomizer_wide(pk0, &self.z1, &self.omega) + if Ciphertext::new_public_with_randomizer_wide(pk0, &self.z1, &self.omega) != self.cap_a.to_precomputed(pk0) + cap_c * e { return false; @@ -156,9 +156,9 @@ impl DecProof

{ } // s^{z_1} t^{z_2} == T S^e - let cap_s_mod = self.cap_s.to_precomputed(setup); - let cap_t_mod = self.cap_t.to_precomputed(setup); - if setup.commit_public_wide(&self.z1, &self.z2) != &cap_t_mod * &cap_s_mod.pow_signed_vartime(&e) { + let cap_s = self.cap_s.to_precomputed(setup); + let cap_t = self.cap_t.to_precomputed(setup); + if setup.commit_public_wide(&self.z1, &self.z2) != &cap_t * &cap_s.pow_signed_vartime(&e) { return false; } @@ -195,7 +195,7 @@ mod tests { let x = scalar_from_signed::(y.expose_secret()); let rho = Randomizer::random(&mut OsRng, pk); - let cap_c = Ciphertext::new_with_randomizer_signed(pk, &y, &rho.to_wire()); + let cap_c = Ciphertext::new_with_randomizer_signed(pk, &y, &rho); let proof = DecProof::::new(&mut OsRng, &y, &rho, pk, &x, &cap_c, &setup, &aux); assert!(proof.verify(pk, &x, &cap_c, &setup, &aux)); diff --git a/synedrion/src/cggmp21/sigma/enc.rs b/synedrion/src/cggmp21/sigma/enc.rs index 72edcc83..aab28e3d 100644 --- a/synedrion/src/cggmp21/sigma/enc.rs +++ b/synedrion/src/cggmp21/sigma/enc.rs @@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize}; use super::super::SchemeParams; use crate::{ paillier::{ - Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, - RandomizerWire, + Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, + Randomizer, }, tools::hashing::{Chain, Hashable, XofHasher}, tools::Secret, @@ -35,7 +35,7 @@ pub(crate) struct EncProof { cap_a: CiphertextWire, cap_c: RPCommitmentWire, z1: Signed<::Uint>, - z2: RandomizerWire, + z2: MaskedRandomizer, z3: Signed<::WideUint>, } @@ -62,7 +62,7 @@ impl EncProof

{ let gamma = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); let cap_s = setup.commit(k, &mu).to_wire(); - let cap_a = Ciphertext::new_with_randomizer_signed(pk0, &alpha, &r.to_wire()).to_wire(); + let cap_a = Ciphertext::new_with_randomizer_signed(pk0, &alpha, &r).to_wire(); let cap_c = setup.commit(&alpha, &gamma).to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) @@ -81,7 +81,7 @@ impl EncProof

{ let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); let z1 = *(alpha + k * e).expose_secret(); - let z2 = rho.to_public(&r, &e); + let z2 = rho.to_masked(&r, &e); let z3 = *(gamma + mu * e.to_wide()).expose_secret(); Self { @@ -129,15 +129,15 @@ impl EncProof

{ } // enc_0(z1, z2) == A (+) K (*) e - let c = Ciphertext::new_nonsecret_with_randomizer_signed(pk0, &self.z1, &self.z2); + let c = Ciphertext::new_public_with_randomizer_signed(pk0, &self.z1, &self.z2); if c != self.cap_a.to_precomputed(pk0) + cap_k * e { return false; } // s^{z_1} t^{z_3} == C S^e \mod \hat{N} - let cap_c_mod = self.cap_c.to_precomputed(setup); - let cap_s_mod = self.cap_s.to_precomputed(setup); - if setup.commit_public(&self.z1, &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e) { + let cap_c = self.cap_c.to_precomputed(setup); + let cap_s = self.cap_s.to_precomputed(setup); + if setup.commit_public(&self.z1, &self.z3) != &cap_c * &cap_s.pow_signed_vartime(&e) { return false; } @@ -171,7 +171,7 @@ mod tests { let secret = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let randomizer = Randomizer::random(&mut OsRng, pk); - let ciphertext = Ciphertext::new_with_randomizer_signed(pk, &secret, &randomizer.to_wire()); + let ciphertext = Ciphertext::new_with_randomizer_signed(pk, &secret, &randomizer); let proof = EncProof::::new(&mut OsRng, &secret, &randomizer, pk, &ciphertext, &setup, &aux); assert!(proof.verify(pk, &ciphertext, &setup, &aux)); diff --git a/synedrion/src/cggmp21/sigma/fac.rs b/synedrion/src/cggmp21/sigma/fac.rs index c93595a3..740c9926 100644 --- a/synedrion/src/cggmp21/sigma/fac.rs +++ b/synedrion/src/cggmp21/sigma/fac.rs @@ -170,23 +170,23 @@ impl FacProof

{ let cap_r = &setup.commit_public_xwide(&pk0.modulus_bounded(), &self.sigma); // s^{z_1} t^{\omega_1} == A * P^e \mod \hat{N} - let cap_a_mod = self.cap_a.to_precomputed(setup); - let cap_p_mod = self.cap_p.to_precomputed(setup); - if setup.commit_public_wide(&self.z1, &self.omega1) != &cap_a_mod * &cap_p_mod.pow_signed_vartime(&e) { + let cap_a = self.cap_a.to_precomputed(setup); + let cap_p = self.cap_p.to_precomputed(setup); + if setup.commit_public_wide(&self.z1, &self.omega1) != &cap_a * &cap_p.pow_signed_vartime(&e) { return false; } // s^{z_2} t^{\omega_2} == B * Q^e \mod \hat{N} - let cap_b_mod = self.cap_b.to_precomputed(setup); - let cap_q_mod = self.cap_q.to_precomputed(setup); - if setup.commit_public_wide(&self.z2, &self.omega2) != &cap_b_mod * &cap_q_mod.pow_signed_vartime(&e) { + let cap_b = self.cap_b.to_precomputed(setup); + let cap_q = self.cap_q.to_precomputed(setup); + if setup.commit_public_wide(&self.z2, &self.omega2) != &cap_b * &cap_q.pow_signed_vartime(&e) { return false; } // Q^{z_1} * t^v == T * R^e \mod \hat{N} - let cap_t_mod = self.cap_t.to_precomputed(setup); - if &cap_q_mod.pow_signed_wide_vartime(&self.z1) * &setup.commit_public_base_xwide(&self.v) - != &cap_t_mod * &cap_r.pow_signed_vartime(&e) + let cap_t = self.cap_t.to_precomputed(setup); + if &cap_q.pow_signed_wide_vartime(&self.z1) * &setup.commit_public_base_xwide(&self.v) + != &cap_t * &cap_r.pow_signed_vartime(&e) { return false; } diff --git a/synedrion/src/cggmp21/sigma/log_star.rs b/synedrion/src/cggmp21/sigma/log_star.rs index 9212bf44..287fa44d 100644 --- a/synedrion/src/cggmp21/sigma/log_star.rs +++ b/synedrion/src/cggmp21/sigma/log_star.rs @@ -10,8 +10,8 @@ use super::super::{ use crate::{ curve::Point, paillier::{ - Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, - RandomizerWire, + Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, + Randomizer, }, tools::hashing::{Chain, Hashable, XofHasher}, tools::Secret, @@ -42,7 +42,7 @@ pub(crate) struct LogStarProof { cap_y: Point, cap_d: RPCommitmentWire, z1: Signed<::Uint>, - z2: RandomizerWire, + z2: MaskedRandomizer, z3: Signed<::WideUint>, } @@ -70,7 +70,7 @@ impl LogStarProof

{ let gamma = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); let cap_s = setup.commit(x, &mu).to_wire(); - let cap_a = Ciphertext::new_with_randomizer_signed(pk0, &alpha, &r.to_wire()).to_wire(); + let cap_a = Ciphertext::new_with_randomizer_signed(pk0, &alpha, &r).to_wire(); let cap_y = g * secret_scalar_from_signed::

(&alpha); let cap_d = setup.commit(&alpha, &gamma).to_wire(); @@ -93,7 +93,7 @@ impl LogStarProof

{ let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); let z1 = *(alpha + x * e).expose_secret(); - let z2 = rho.to_public(&r, &e); + let z2 = rho.to_masked(&r, &e); let z3 = *(gamma + mu * e.to_wide()).expose_secret(); Self { @@ -148,7 +148,7 @@ impl LogStarProof

{ } // enc_0(z1, z2) == A (+) C (*) e - let c = Ciphertext::new_nonsecret_with_randomizer_signed(pk0, &self.z1, &self.z2); + let c = Ciphertext::new_public_with_randomizer_signed(pk0, &self.z1, &self.z2); if c != self.cap_a.to_precomputed(pk0) + cap_c * e { return false; } @@ -159,9 +159,9 @@ impl LogStarProof

{ } // s^{z_1} t^{z_3} == D S^e \mod \hat{N} - let cap_d_mod = self.cap_d.to_precomputed(setup); - let cap_s_mod = self.cap_s.to_precomputed(setup); - if setup.commit_public(&self.z1, &self.z3) != &cap_d_mod * &cap_s_mod.pow_signed_vartime(&e) { + let cap_d = self.cap_d.to_precomputed(setup); + let cap_s = self.cap_s.to_precomputed(setup); + if setup.commit_public(&self.z1, &self.z3) != &cap_d * &cap_s.pow_signed_vartime(&e) { return false; } @@ -197,7 +197,7 @@ mod tests { let g = Point::GENERATOR * Scalar::random(&mut OsRng); let x = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let rho = Randomizer::random(&mut OsRng, pk); - let cap_c = Ciphertext::new_with_randomizer_signed(pk, &x, &rho.to_wire()); + let cap_c = Ciphertext::new_with_randomizer_signed(pk, &x, &rho); let cap_x = g * secret_scalar_from_signed::(&x); let proof = LogStarProof::::new(&mut OsRng, &x, &rho, pk, &cap_c, &g, &cap_x, &setup, &aux); diff --git a/synedrion/src/cggmp21/sigma/mul.rs b/synedrion/src/cggmp21/sigma/mul.rs index e3cf485a..d66d1d4d 100644 --- a/synedrion/src/cggmp21/sigma/mul.rs +++ b/synedrion/src/cggmp21/sigma/mul.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use super::super::SchemeParams; use crate::{ - paillier::{Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, Randomizer, RandomizerWire}, + paillier::{Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, Randomizer}, tools::{ hashing::{Chain, Hashable, XofHasher}, Secret, @@ -21,8 +21,8 @@ pub(crate) struct MulProof { cap_a: CiphertextWire, cap_b: CiphertextWire, z: Signed<::WideUint>, - u: RandomizerWire, - v: RandomizerWire, + u: MaskedRandomizer, + v: MaskedRandomizer, } /** @@ -59,8 +59,8 @@ impl MulProof

{ assert_eq!(cap_c.public_key(), pk); let alpha_mod = Secret::init_with(|| pk.random_invertible_residue(rng)); - let r_mod = Randomizer::random(rng, pk); - let s_mod = Randomizer::random(rng, pk); + let r = Randomizer::random(rng, pk); + let s = Randomizer::random(rng, pk); let alpha = Secret::init_with(|| { Bounded::new( @@ -69,8 +69,6 @@ impl MulProof

{ ) .expect("the value is bounded by `MODULUS_BITS` by construction") }); - let r = r_mod.to_wire(); - let s = s_mod.to_wire(); let cap_a = (cap_y * &alpha).mul_randomizer(&r).to_wire(); let cap_b = Ciphertext::new_with_randomizer_bounded(pk, &alpha, &s).to_wire(); @@ -96,8 +94,8 @@ impl MulProof

{ .expect("conversion to `WideUint` provides enough space for a sign bit") + x.mul_wide(&e)) .expose_secret(); - let u = rho.to_public(&r_mod, &e); - let v = rho_x.to_public(&s_mod, &e); + let u = rho.to_masked(&r, &e); + let v = rho_x.to_masked(&s, &e); Self { e, @@ -141,13 +139,15 @@ impl MulProof

{ } // Y^z u^N = A * C^e \mod N^2 - if cap_y.homomorphic_mul_wide(&self.z).mul_randomizer(&self.u) != self.cap_a.to_precomputed(pk) + cap_c * e { + if cap_y.homomorphic_mul_wide(&self.z).mul_masked_randomizer(&self.u) + != self.cap_a.to_precomputed(pk) + cap_c * e + { return false; } // enc(z, v) == B * X^e \mod N^2 // (Note: typo in the paper, it uses `c` and not `v` here) - if Ciphertext::new_nonsecret_with_randomizer_wide(pk, &self.z, &self.v) + if Ciphertext::new_public_with_randomizer_wide(pk, &self.z, &self.v) != self.cap_b.to_precomputed(pk) + cap_x * e { return false; @@ -184,9 +184,9 @@ mod tests { let rho_x = Randomizer::random(&mut OsRng, pk); let rho = Randomizer::random(&mut OsRng, pk); - let cap_x = Ciphertext::new_with_randomizer_signed(pk, &x, &rho_x.to_wire()); + let cap_x = Ciphertext::new_with_randomizer_signed(pk, &x, &rho_x); let cap_y = Ciphertext::new_signed(&mut OsRng, pk, &y); - let cap_c = (&cap_y * &x).mul_randomizer(&rho.to_wire()); + let cap_c = (&cap_y * &x).mul_randomizer(&rho); let proof = MulProof::::new(&mut OsRng, &x, &rho_x, &rho, pk, &cap_x, &cap_y, &cap_c, &aux); assert!(proof.verify(pk, &cap_x, &cap_y, &cap_c, &aux)); diff --git a/synedrion/src/cggmp21/sigma/mul_star.rs b/synedrion/src/cggmp21/sigma/mul_star.rs index b6833c2e..f43d120b 100644 --- a/synedrion/src/cggmp21/sigma/mul_star.rs +++ b/synedrion/src/cggmp21/sigma/mul_star.rs @@ -10,8 +10,8 @@ use super::super::{ use crate::{ curve::Point, paillier::{ - Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, Randomizer, - RandomizerWire, + Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, + Randomizer, }, tools::hashing::{Chain, Hashable, XofHasher}, tools::Secret, @@ -43,7 +43,7 @@ pub(crate) struct MulStarProof { cap_s: RPCommitmentWire, z1: Signed<::Uint>, z2: Signed<::WideUint>, - omega: RandomizerWire, + omega: MaskedRandomizer, } impl MulStarProof

{ @@ -77,7 +77,7 @@ impl MulStarProof

{ let gamma = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); let m = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n)); - let cap_a = (cap_c * &alpha).mul_randomizer(&r.to_wire()).to_wire(); + let cap_a = (cap_c * &alpha).mul_randomizer(&r).to_wire(); let cap_b_x = secret_scalar_from_signed::

(&alpha).mul_by_generator(); let cap_e = setup.commit(&alpha, &gamma).to_wire(); let cap_s = setup.commit(x, &m).to_wire(); @@ -102,7 +102,7 @@ impl MulStarProof

{ let z1 = *(alpha + x * e).expose_secret(); let z2 = *(gamma + m * e.to_wide()).expose_secret(); - let omega = rho.to_public(&r, &e); + let omega = rho.to_masked(&r, &e); Self { e, @@ -158,7 +158,7 @@ impl MulStarProof

{ } // C (*) z_1 * \omega^{N_0} == A (+) D (*) e - if (cap_c * self.z1).mul_randomizer(&self.omega) != self.cap_a.to_precomputed(pk0) + cap_d * e { + if (cap_c * self.z1).mul_masked_randomizer(&self.omega) != self.cap_a.to_precomputed(pk0) + cap_d * e { return false; } @@ -168,9 +168,9 @@ impl MulStarProof

{ } // s^{z_1} t^{z_2} == E S^e - let cap_e_mod = self.cap_e.to_precomputed(setup); - let cap_s_mod = self.cap_s.to_precomputed(setup); - if setup.commit_public(&self.z1, &self.z2) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { + let cap_e = self.cap_e.to_precomputed(setup); + let cap_s = self.cap_s.to_precomputed(setup); + if setup.commit_public(&self.z1, &self.z2) != &cap_e * &cap_s.pow_signed_vartime(&e) { return false; } @@ -206,7 +206,7 @@ mod tests { let secret = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let rho = Randomizer::random(&mut OsRng, pk); let cap_c = Ciphertext::new_signed(&mut OsRng, pk, &secret); - let cap_d = (&cap_c * &x).mul_randomizer(&rho.to_wire()); + let cap_d = (&cap_c * &x).mul_randomizer(&rho); let cap_x = secret_scalar_from_signed::(&x).mul_by_generator(); let proof = MulStarProof::::new(&mut OsRng, &x, &rho, pk, &cap_c, &cap_d, &cap_x, &setup, &aux); diff --git a/synedrion/src/paillier.rs b/synedrion/src/paillier.rs index 67904891..0ac563ee 100644 --- a/synedrion/src/paillier.rs +++ b/synedrion/src/paillier.rs @@ -4,7 +4,7 @@ mod params; mod ring_pedersen; mod rsa; -pub(crate) use encryption::{Ciphertext, CiphertextWire, Randomizer, RandomizerWire}; +pub(crate) use encryption::{Ciphertext, CiphertextWire, MaskedRandomizer, Randomizer}; pub(crate) use keys::{PublicKeyPaillier, PublicKeyPaillierWire, SecretKeyPaillier, SecretKeyPaillierWire}; pub(crate) use params::PaillierParams; pub(crate) use ring_pedersen::{RPCommitmentWire, RPParams, RPParamsWire, RPSecret}; diff --git a/synedrion/src/paillier/encryption.rs b/synedrion/src/paillier/encryption.rs index f13d0bfb..24286ab8 100644 --- a/synedrion/src/paillier/encryption.rs +++ b/synedrion/src/paillier/encryption.rs @@ -6,7 +6,6 @@ use core::{ use crypto_bigint::{subtle::ConstantTimeGreater, Monty, ShrVartime}; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use zeroize::ZeroizeOnDrop; use super::{ keys::{PublicKeyPaillier, SecretKeyPaillier}, @@ -20,36 +19,42 @@ use crate::{ }, }; -// A ciphertext randomizer (an invertible element of $\mathbb{Z}_N$). -#[derive(Debug, Clone, Serialize, Deserialize, ZeroizeOnDrop)] -pub(crate) struct RandomizerWire(P::Uint); - -impl RandomizerWire

{ - pub fn random(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

) -> Self { - Randomizer::random(rng, pk).to_wire() - } - - pub fn to_precomputed(&self, pk: &PublicKeyPaillier

) -> Randomizer

{ - Randomizer(Secret::init_with(|| self.0.to_montgomery(pk.monty_params_mod_n()))) - } -} +/// A public randomizer-like quantity used in ZK proofs. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct MaskedRandomizer(P::Uint); +/// A ciphertext randomizer (an invertible element of $\mathbb{Z}_N$). #[derive(Debug, Clone)] -pub(crate) struct Randomizer(Secret); +pub(crate) struct Randomizer { + randomizer: Secret, + randomizer_mod: Secret, +} impl Randomizer

{ + fn new(randomizer_mod: Secret) -> Self { + let randomizer = Secret::init_with(|| randomizer_mod.expose_secret().retrieve()); + Self { + randomizer, + randomizer_mod, + } + } + pub fn random(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

) -> Self { - Self(Secret::init_with(|| pk.random_invertible_residue(rng))) + let randomizer_mod = Secret::init_with(|| pk.random_invertible_residue(rng)); + Self::new(randomizer_mod) } - pub fn to_wire(&self) -> RandomizerWire

{ - RandomizerWire(self.0.expose_secret().retrieve()) + /// Expose this secret randomizer. + /// + /// Supposed to be used in certain error branches where it is needed to generate a malicious behavior evidence. + pub fn expose(&self) -> P::Uint { + *self.randomizer.expose_secret() } /// Converts the randomizer to a publishable form by masking it with another randomizer and a public exponent. - pub fn to_public(&self, coeff: &Self, exponent: &Signed) -> RandomizerWire

{ - RandomizerWire( - (self.0.pow_signed_vartime(exponent) * &coeff.0) + pub fn to_masked(&self, coeff: &Self, exponent: &Signed) -> MaskedRandomizer

{ + MaskedRandomizer( + (self.randomizer_mod.pow_signed_vartime(exponent) * &coeff.randomizer_mod) .expose_secret() .retrieve(), ) @@ -88,7 +93,7 @@ impl Ciphertext

{ fn new_with_randomizer_inner( pk: &PublicKeyPaillier

, abs_plaintext: &Secret, - randomizer: &RandomizerWire

, + randomizer: &Randomizer

, plaintext_is_negative: Choice, ) -> Self { // Technically if `abs_plaintext` is greater than the modulus of `pk`, @@ -109,8 +114,49 @@ impl Ciphertext

{ // Since `m` can be negative, we calculate `m * N +- 1` (never overflows since `m < N`), // then conditionally negate modulo N^2 - // TODO: wrap all the secrets in this method - let abs_plaintext = *abs_plaintext.expose_secret(); + let prod = abs_plaintext.mul_wide(pk.modulus()); + let mut prod_mod = prod.to_montgomery(pk.monty_params_mod_n_squared()); + prod_mod.conditional_negate(plaintext_is_negative); + + let factor1 = prod_mod + &P::WideUintMod::one(pk.monty_params_mod_n_squared().clone()); + + let randomizer = randomizer.randomizer.to_wide(); + let pk_mod_bound = pk.modulus_bounded().to_wide(); + let factor2 = randomizer + .to_montgomery(pk.monty_params_mod_n_squared()) + .pow_bounded(&pk_mod_bound); + + let ciphertext = *(factor1 * factor2).expose_secret(); + + Self { + pk: pk.clone(), + ciphertext, + } + } + + fn new_public_with_randomizer_inner( + pk: &PublicKeyPaillier

, + abs_plaintext: &P::Uint, + randomizer: &MaskedRandomizer

, + plaintext_is_negative: Choice, + ) -> Self { + // Technically if `abs_plaintext` is greater than the modulus of `pk`, + // it will be effectively reduced modulo `pk`. + // But some ZK proofs with `TestParams` may still supply a value larger than `pk` + // because they are not planning on decrypting the resulting ciphertext; + // they just construct an encryption of the same value in two different ways + // and then compare the results. + // (And the value can be larger than `pk` because of some restrictions on + // `SchemeParameters`/`PaillierParameters` values in tests, which can only + // be overcome by fixing #27 and using a small 32- or 64-bit curve for tests) + + // Calculate the ciphertext `C = (N + 1)^m * rho^N mod N^2` + // where `N` is the Paillier composite modulus, `m` is the plaintext, + // and `rho` is the randomizer. + + // Simplify `(N + 1)^m mod N^2 == 1 + m * N mod N^2`. + // Since `m` can be negative, we calculate `m * N +- 1` (never overflows since `m < N`), + // then conditionally negate modulo N^2 let prod = abs_plaintext.mul_wide(pk.modulus()); let mut prod_mod = prod.to_montgomery(pk.monty_params_mod_n_squared()); @@ -136,7 +182,7 @@ impl Ciphertext

{ pub fn new_with_randomizer( pk: &PublicKeyPaillier

, plaintext: &Secret, - randomizer: &RandomizerWire

, + randomizer: &Randomizer

, ) -> Self { Self::new_with_randomizer_inner(pk, plaintext, randomizer, Choice::from(0)) } @@ -144,7 +190,7 @@ impl Ciphertext

{ pub fn new_with_randomizer_bounded( pk: &PublicKeyPaillier

, plaintext: &Secret>, - randomizer: &RandomizerWire

, + randomizer: &Randomizer

, ) -> Self { Self::new_with_randomizer_inner( pk, @@ -154,18 +200,10 @@ impl Ciphertext

{ ) } - pub fn new_nonsecret_with_randomizer_signed( - pk: &PublicKeyPaillier

, - plaintext: &Signed, - randomizer: &RandomizerWire

, - ) -> Self { - Self::new_with_randomizer_signed(pk, &Secret::init_with(|| *plaintext), randomizer) - } - pub fn new_with_randomizer_signed( pk: &PublicKeyPaillier

, plaintext: &Secret>, - randomizer: &RandomizerWire

, + randomizer: &Randomizer

, ) -> Self { let plaintext = *plaintext.expose_secret(); Self::new_with_randomizer_inner( @@ -176,30 +214,27 @@ impl Ciphertext

{ ) } - pub fn new_nonsecret_with_randomizer_wide( + pub fn new_public_with_randomizer_signed( pk: &PublicKeyPaillier

, - plaintext: &Signed, - randomizer: &RandomizerWire

, + plaintext: &Signed, + randomizer: &MaskedRandomizer

, ) -> Self { - Self::new_with_randomizer_wide(pk, &Secret::init_with(|| *plaintext), randomizer) + Self::new_public_with_randomizer_inner(pk, &plaintext.abs(), randomizer, plaintext.is_negative()) } - pub fn new_with_randomizer_wide( + pub fn new_public_with_randomizer_wide( pk: &PublicKeyPaillier

, - plaintext: &Secret>, - randomizer: &RandomizerWire

, + plaintext: &Signed, + randomizer: &MaskedRandomizer

, ) -> Self { - let plaintext = *plaintext.expose_secret(); - let plaintext_reduced = Secret::init_with(|| { - P::Uint::try_from_wide(&(plaintext.abs() % pk.modulus_wide_nonzero())) - .expect("the number within range after reducing modulo N") - }); - Self::new_with_randomizer_inner(pk, &plaintext_reduced, randomizer, plaintext.is_negative()) + let plaintext_reduced = P::Uint::try_from_wide(&(plaintext.abs() % pk.modulus_wide_nonzero())) + .expect("the number within range after reducing modulo N"); + Self::new_public_with_randomizer_inner(pk, &plaintext_reduced, randomizer, plaintext.is_negative()) } /// Encrypts the plaintext with a random randomizer. pub fn new(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

, plaintext: &Secret) -> Self { - Self::new_with_randomizer(pk, plaintext, &RandomizerWire::random(rng, pk)) + Self::new_with_randomizer(pk, plaintext, &Randomizer::random(rng, pk)) } #[cfg(test)] @@ -208,7 +243,7 @@ impl Ciphertext

{ pk: &PublicKeyPaillier

, plaintext: &Secret>, ) -> Self { - Self::new_with_randomizer_signed(pk, plaintext, &RandomizerWire::random(rng, pk)) + Self::new_with_randomizer_signed(pk, plaintext, &Randomizer::random(rng, pk)) } /// Decrypts this ciphertext assuming that the plaintext is in range `[0, N)`. @@ -285,9 +320,9 @@ impl Ciphertext

{ // To isolate `rho`, calculate `(rho^N)^(N^(-1)) mod N`. // The order of `Z_N` is `phi(N)`, so the inversion in the exponent is modulo `phi(N)`. let sk_inv_modulus = sk.inv_modulus(); - Randomizer(Secret::init_with(|| { - ciphertext_mod_n.pow_bounded(sk_inv_modulus.expose_secret()) - })) + let randomizer_mod = Secret::init_with(|| ciphertext_mod_n.pow_bounded(sk_inv_modulus.expose_secret())); + + Randomizer::new(randomizer_mod) } // Note: while it is true that `enc(x) (*) rhs == enc((x * rhs) mod N)`, @@ -343,7 +378,7 @@ impl Ciphertext

{ } } - pub fn mul_randomizer(self, randomizer: &RandomizerWire

) -> Self { + pub fn mul_masked_randomizer(self, randomizer: &MaskedRandomizer

) -> Self { let randomizer_mod = randomizer .0 .to_wide() @@ -356,6 +391,19 @@ impl Ciphertext

{ } } + pub fn mul_randomizer(self, randomizer: &Randomizer

) -> Self { + let randomizer_mod = randomizer + .randomizer + .to_wide() + .to_montgomery(self.pk.monty_params_mod_n_squared()); + let pk_modulus_wide = self.pk.modulus_bounded().to_wide(); + let ciphertext = self.ciphertext * randomizer_mod.pow_bounded(&pk_modulus_wide).expose_secret(); + Self { + pk: self.pk, + ciphertext, + } + } + pub fn to_wire(&self) -> CiphertextWire

{ CiphertextWire { ciphertext: self.ciphertext.retrieve(), @@ -552,9 +600,9 @@ mod tests { let plaintext = Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let randomizer = Randomizer::random(&mut OsRng, pk); - let ciphertext = Ciphertext::::new_with_randomizer(pk, &plaintext, &randomizer.to_wire()); + let ciphertext = Ciphertext::::new_with_randomizer(pk, &plaintext, &randomizer); let randomizer_back = ciphertext.derive_randomizer(&sk); - assert_eq!(randomizer.0.expose_secret(), randomizer_back.0.expose_secret()); + assert_eq!(randomizer.expose(), randomizer_back.expose()); } #[test] diff --git a/synedrion/src/tools/secret.rs b/synedrion/src/tools/secret.rs index b035ba3a..b9916d6c 100644 --- a/synedrion/src/tools/secret.rs +++ b/synedrion/src/tools/secret.rs @@ -79,6 +79,30 @@ impl From> for Secret { } } +impl Secret +where + T: Zeroize + Clone + HasWide, + T::Wide: Zeroize, +{ + pub fn to_wide(&self) -> Secret<::Wide> { + Secret::init_with(|| { + let mut value = self.expose_secret().clone(); + let result = value.to_wide(); + value.zeroize(); + result + }) + } + + pub fn mul_wide(&self, rhs: &T) -> Secret { + Secret::init_with(|| { + let mut value = self.expose_secret().clone(); + let result = value.mul_wide(rhs); + value.zeroize(); + result + }) + } +} + impl Secret> where T: Zeroize + Clone + Encoding + Integer + HasWide + ConditionallySelectable + crypto_bigint::Bounded, @@ -449,6 +473,15 @@ impl> Retrieve for Secret { } impl Secret { + pub fn pow_bounded(&self, exponent: &Bounded) -> Self + where + T: Exponentiable, + V: Integer + crypto_bigint::Bounded + Encoding + ConditionallySelectable, + { + // TODO: do we need to implement our own windowed exponentiation to hide the secret? + Secret::init_with(|| self.expose_secret().pow_bounded(exponent)) + } + pub fn pow_signed_vartime(&self, exponent: &Signed) -> Self where T: Exponentiable,