diff --git a/synedrion/src/cggmp21/aux_gen.rs b/synedrion/src/cggmp21/aux_gen.rs index fe2c5cb..7dee0bb 100644 --- a/synedrion/src/cggmp21/aux_gen.rs +++ b/synedrion/src/cggmp21/aux_gen.rs @@ -466,7 +466,7 @@ impl Round3 { let pi = SchProof::new( &context.tau_y, - context.y.expose_secret(), + &context.y, &context.data_precomp.data.cap_b, &context.data_precomp.data.cap_y, &aux, diff --git a/synedrion/src/cggmp21/interactive_signing.rs b/synedrion/src/cggmp21/interactive_signing.rs index 0f51fb0..f018f19 100644 --- a/synedrion/src/cggmp21/interactive_signing.rs +++ b/synedrion/src/cggmp21/interactive_signing.rs @@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize}; use super::{ entities::{AuxInfo, AuxInfoPrecomputed, KeyShare, PresigningData, PresigningValues}, - params::SchemeParams, + params::{secret_scalar_from_signed, secret_signed_from_scalar, secret_uint_from_scalar, SchemeParams}, sigma::{AffGProof, DecProof, EncProof, LogStarProof, MulProof, MulStarProof}, }; use crate::{ @@ -160,10 +160,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, &P::uint_from_scalar(gamma.expose_secret()), &nu.to_wire()); + let cap_g = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::

(&gamma), &nu.to_wire()); let rho = Randomizer::::random(rng, pk); - let cap_k = Ciphertext::new_with_randomizer(pk, &P::uint_from_scalar(k.expose_secret()), &rho.to_wire()); + let cap_k = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::

(&k), &rho.to_wire()); Ok(BoxedRound::new_dynamic(Round1 { context: Context { @@ -267,7 +267,7 @@ impl Round for Round1 { let aux = (&self.context.ssid_hash, &destination); let psi0 = EncProof::new( rng, - &P::signed_from_scalar(self.context.k.expose_secret()), + &secret_signed_from_scalar::

(&self.context.k), &self.context.rho, self.context.aux_info.secret_aux.paillier_sk.public_key(), &self.cap_k, @@ -446,21 +446,24 @@ impl Round for Round2 { let hat_r = Randomizer::random(rng, pk); let hat_s = Randomizer::random(rng, target_pk); - let cap_f = Ciphertext::new_with_randomizer_signed(pk, beta.expose_secret(), &r.to_wire()); - let cap_d = &self.all_cap_k[destination] * P::signed_from_scalar(self.context.gamma.expose_secret()) - + Ciphertext::new_with_randomizer_signed(target_pk, &-beta.expose_secret(), &s.to_wire()); + let gamma = secret_signed_from_scalar::

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

(&self.context.key_share.secret_share); - let hat_cap_f = Ciphertext::new_with_randomizer_signed(pk, hat_beta.expose_secret(), &hat_r.to_wire()); + 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 hat_cap_f = Ciphertext::new_with_randomizer_signed(pk, &hat_beta, &hat_r.to_wire()); let hat_cap_d = &self.all_cap_k[destination] * P::signed_from_scalar(self.context.key_share.secret_share.expose_secret()) - + Ciphertext::new_with_randomizer_signed(target_pk, &-hat_beta.expose_secret(), &hat_s.to_wire()); + + Ciphertext::new_with_randomizer_signed(target_pk, &-&hat_beta, &hat_s.to_wire()); let public_aux = &self.context.aux_info.public_aux[destination]; let rp = &public_aux.rp_params; let psi = AffGProof::new( rng, - &P::signed_from_scalar(self.context.gamma.expose_secret()), + &gamma, &beta, s.clone(), r.clone(), @@ -476,7 +479,7 @@ impl Round for Round2 { let hat_psi = AffGProof::new( rng, - &P::signed_from_scalar(self.context.key_share.secret_share.expose_secret()), + &x, &hat_beta, hat_s.clone(), hat_r.clone(), @@ -492,7 +495,7 @@ impl Round for Round2 { let hat_psi_prime = LogStarProof::new( rng, - &P::signed_from_scalar(self.context.gamma.expose_secret()), + &gamma, &self.context.nu, pk, &self.all_cap_g[&self.context.my_id], @@ -638,18 +641,18 @@ impl Round for Round2 { let alpha_sum: Signed<_> = payloads.values().map(|p| p.alpha).sum(); let beta_sum: Signed<_> = artifacts.values().map(|p| p.beta.expose_secret()).sum(); - let delta = P::signed_from_scalar(self.context.gamma.expose_secret()) - * P::signed_from_scalar(self.context.k.expose_secret()) - + alpha_sum - + beta_sum; + let delta = secret_signed_from_scalar::

(&self.context.gamma) + * secret_signed_from_scalar::

(&self.context.k) + + &alpha_sum + + &beta_sum; let hat_alpha_sum: Signed<_> = payloads.values().map(|payload| payload.hat_alpha).sum(); let hat_beta_sum: Signed<_> = artifacts .values() .map(|artifact| artifact.hat_beta.expose_secret()) .sum(); - let chi = Secret::init_with(|| P::signed_from_scalar(self.context.key_share.secret_share.expose_secret())) - * Secret::init_with(|| P::signed_from_scalar(self.context.k.expose_secret())) + let chi = secret_signed_from_scalar::

(&self.context.key_share.secret_share) + * secret_signed_from_scalar::

(&self.context.k) + &hat_alpha_sum + &hat_beta_sum; @@ -676,7 +679,7 @@ impl Round for Round2 { #[derive(Debug)] struct Round3 { context: Context, - delta: Signed<::Uint>, + delta: Secret::Uint>>, chi: Secret::Uint>>, cap_delta: Point, cap_gamma: Point, @@ -691,13 +694,13 @@ struct Round3 { #[serde(bound(serialize = "LogStarProof

: Serialize"))] #[serde(bound(deserialize = "LogStarProof

: for<'x> Deserialize<'x>"))] struct Round3Message { - delta: Scalar, + delta: Secret, cap_delta: Point, psi_pprime: LogStarProof

, } struct Round3Payload { - delta: Scalar, + delta: Secret, cap_delta: Point, } @@ -734,7 +737,7 @@ impl Round for Round3 { let psi_pprime = LogStarProof::new( rng, - &P::signed_from_scalar(self.context.k.expose_secret()), + &secret_signed_from_scalar::

(&self.context.k), &self.context.rho, pk, &self.all_cap_k[&self.context.my_id], @@ -747,7 +750,7 @@ impl Round for Round3 { let dm = DirectMessage::new( serializer, Round3Message::

{ - delta: P::scalar_from_signed(&self.delta), + delta: secret_scalar_from_signed::

(&self.delta), cap_delta: self.cap_delta, psi_pprime, }, @@ -806,19 +809,20 @@ impl Round for Round3 { .map(|(id, payload)| ((id.clone(), payload.delta), (id, payload.cap_delta))) .unzip(); - let scalar_delta = P::scalar_from_signed(&self.delta); - let assembled_delta: Scalar = scalar_delta + deltas.values().sum::(); - let assembled_cap_delta: Point = self.cap_delta + cap_deltas.values().sum::(); - - if assembled_delta.mul_by_generator() == assembled_cap_delta { - let inv_delta: Scalar = Option::from(assembled_delta.invert()).ok_or_else(|| { - LocalError::new(concat![ - "The assembled delta is zero. ", - "Either all other nodes are malicious, or it's a freak accident. ", - "Restart the protocol." - ]) - })?; - let nonce = (self.cap_gamma * inv_delta).x_coordinate(); + let scalar_delta = secret_scalar_from_signed::

(&self.delta); + let assembled_delta: Secret = &scalar_delta + deltas.values().sum::>(); + let assembled_cap_delta: Point = self.cap_delta + cap_deltas.values().sum(); + + if assembled_delta.expose_secret().mul_by_generator() == assembled_cap_delta { + let inv_delta = Secret::try_init_with(|| Option::from(assembled_delta.expose_secret().invert()).ok_or(())) + .map_err(|_err| { + LocalError::new(concat![ + "The assembled delta is zero. ", + "Either all other nodes are malicious, or it's a freak accident. ", + "Restart the protocol." + ]) + })?; + let nonce = (self.cap_gamma * inv_delta.expose_secret()).x_coordinate(); let my_id = self.context.my_id.clone(); let values = self @@ -879,7 +883,7 @@ impl Round for Round3 { let p_aff_g = AffGProof::

::new( rng, - &P::signed_from_scalar(self.context.gamma.expose_secret()), + &secret_signed_from_scalar::

(&self.context.gamma), beta, s.to_precomputed(target_pk), r.to_precomputed(pk), @@ -916,7 +920,7 @@ impl Round for Round3 { let p_mul = MulProof::

::new( rng, - &P::signed_from_scalar(self.context.k.expose_secret()), + &secret_signed_from_scalar::

(&self.context.k), &self.context.rho, &rho, pk, @@ -958,14 +962,14 @@ impl Round for Round3 { &self.delta, &rho, pk, - &scalar_delta, + scalar_delta.expose_secret(), &ciphertext, &self.context.aux_info.public_aux[id_j].rp_params, &aux, ); assert!(p_dec.verify( pk, - &scalar_delta, + scalar_delta.expose_secret(), &ciphertext, &self.context.aux_info.public_aux[id_j].rp_params, &aux @@ -1105,7 +1109,7 @@ impl Round for Round4 { let p_aff_g = AffGProof::

::new( rng, - &P::signed_from_scalar(self.context.key_share.secret_share.expose_secret()), + &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), @@ -1150,7 +1154,7 @@ impl Round for Round4 { for id_l in self.context.other_ids.iter() { let p_mul = MulStarProof::

::new( rng, - &P::signed_from_scalar(x.expose_secret()), + &secret_signed_from_scalar::

(x), &rho, pk, &self.presigning.cap_k, @@ -1192,9 +1196,9 @@ impl Round for Round4 { let rho = ciphertext.derive_randomizer(sk); // This is the same as `s_part` but if all the calculations were performed // without reducing modulo curve order. - let s_part_nonreduced = P::signed_from_scalar(self.presigning.ephemeral_scalar_share.expose_secret()) + let s_part_nonreduced = secret_signed_from_scalar::

(&self.presigning.ephemeral_scalar_share) * P::signed_from_scalar(&self.context.message) - + *self.presigning.product_share_nonreduced.expose_secret() * P::signed_from_scalar(&r); + + self.presigning.product_share_nonreduced.clone() * P::signed_from_scalar(&r); let mut dec_proofs = Vec::new(); for id_l in self.context.other_ids.iter() { diff --git a/synedrion/src/cggmp21/key_init.rs b/synedrion/src/cggmp21/key_init.rs index b1506d4..c39775e 100644 --- a/synedrion/src/cggmp21/key_init.rs +++ b/synedrion/src/cggmp21/key_init.rs @@ -401,7 +401,7 @@ impl Round for Round3 { let aux = (&self.context.sid_hash, &self.context.my_id, &self.rid); let psi = SchProof::new( &self.context.tau, - self.context.x.expose_secret(), + &self.context.x, &self.context.public_data.cap_a, &self.context.public_data.cap_x, &aux, diff --git a/synedrion/src/cggmp21/key_refresh.rs b/synedrion/src/cggmp21/key_refresh.rs index b83c436..3692d5f 100644 --- a/synedrion/src/cggmp21/key_refresh.rs +++ b/synedrion/src/cggmp21/key_refresh.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; use super::{ entities::{AuxInfo, KeyShareChange, PublicAuxInfo, SecretAuxInfo}, - params::SchemeParams, + params::{secret_uint_from_scalar, SchemeParams}, sigma::{FacProof, ModProof, PrmProof, SchCommitment, SchProof, SchSecret}, }; use crate::{ @@ -540,7 +540,7 @@ impl Round3 { let pi = SchProof::new( &context.tau_y, - context.y.expose_secret(), + &context.y, &context.data_precomp.data.cap_b, &context.data_precomp.data.cap_y, &aux, @@ -609,11 +609,11 @@ impl Round for Round3 { let x_secret = &self.context.x_to_send[destination]; let x_public = self.context.data_precomp.data.cap_x_to_send[destination_idx]; - let ciphertext = Ciphertext::new(rng, &data.paillier_pk, &P::uint_from_scalar(x_secret.expose_secret())); + let ciphertext = Ciphertext::new(rng, &data.paillier_pk, &secret_uint_from_scalar::

(x_secret)); let psi_sch = SchProof::new( &self.context.tau_x[destination], - x_secret.expose_secret(), + &x_secret, &self.context.data_precomp.data.cap_a_to_send[destination_idx], &x_public, &aux, diff --git a/synedrion/src/cggmp21/params.rs b/synedrion/src/cggmp21/params.rs index d437a0b..44daed7 100644 --- a/synedrion/src/cggmp21/params.rs +++ b/synedrion/src/cggmp21/params.rs @@ -5,12 +5,17 @@ use core::fmt::Debug; // So as long as that is the case, `k256` `Uint` is separate // from the one used throughout the crate. use k256::elliptic_curve::bigint::Uint as K256Uint; +use secrecy::{ExposeSecret, ExposeSecretMut}; use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; use crate::{ curve::{Curve, Scalar, ORDER}, paillier::PaillierParams, - tools::hashing::{Chain, HashableType}, + tools::{ + hashing::{Chain, HashableType}, + Secret, + }, uint::{ subtle::ConditionallySelectable, Bounded, Encoding, NonZero, Signed, U1024Mod, U2048Mod, U4096Mod, U512Mod, Uint, Zero, U1024, U2048, U4096, U512, U8192, @@ -192,6 +197,47 @@ pub trait SchemeParams: Debug + Clone + Send + PartialEq + Eq + Send + Sync + 's } } +pub(crate) fn secret_uint_from_scalar( + value: &Secret, +) -> Secret<::Uint> { + let scalar_bytes = Secret::init_with(|| value.expose_secret().to_bytes()); + let mut repr = Secret::init_with(|| ::Uint::zero().to_be_bytes()); + + let uint_len = repr.expose_secret().as_ref().len(); + let scalar_len = scalar_bytes.expose_secret().len(); + + debug_assert!(uint_len >= scalar_len); + repr.expose_secret_mut().as_mut()[uint_len - scalar_len..].copy_from_slice(scalar_bytes.expose_secret()); + Secret::init_with(|| { + let mut repr = repr.expose_secret().clone(); + let result = ::Uint::from_be_bytes(repr); + repr.zeroize(); + result + }) +} + +pub(crate) fn secret_signed_from_scalar( + value: &Secret, +) -> Secret::Uint>> { + Secret::init_with(|| { + let mut uint = secret_uint_from_scalar::

(value).expose_secret().clone(); + let result = Signed::new_positive(uint, ORDER.bits_vartime() as u32).expect(concat![ + "a curve scalar value is smaller than the curve order, ", + "and the curve order fits in `PaillierParams::Uint`" + ]); + uint.zeroize(); + result + }) +} + +pub(crate) fn secret_scalar_from_signed( + value: &Secret::Uint>>, +) -> Secret { + // TODO: wrap in secrets properly + let abs_value = P::scalar_from_uint(&value.expose_secret().abs()); + Secret::init_with(|| Scalar::conditional_select(&abs_value, &-abs_value, value.expose_secret().is_negative())) +} + impl HashableType for P { fn chain_type(digest: C) -> C { digest.chain_type::() diff --git a/synedrion/src/cggmp21/sigma/aff_g.rs b/synedrion/src/cggmp21/sigma/aff_g.rs index 832452a..4cbd4e5 100644 --- a/synedrion/src/cggmp21/sigma/aff_g.rs +++ b/synedrion/src/cggmp21/sigma/aff_g.rs @@ -4,7 +4,7 @@ use rand_core::CryptoRngCore; use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{params::secret_scalar_from_signed, SchemeParams}; use crate::{ curve::Point, paillier::{ @@ -64,7 +64,7 @@ impl AffGProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, + x: &Secret::Uint>>, y: &Secret::Uint>>, rho: Randomizer, rho_y: Randomizer, @@ -77,7 +77,7 @@ impl AffGProof

{ setup: &RPParams, aux: &impl Hashable, ) -> Self { - x.assert_bound(P::L_BOUND); + x.expose_secret().assert_bound(P::L_BOUND); y.expose_secret().assert_bound(P::LP_BOUND); assert!(cap_c.public_key() == pk0); assert!(cap_d.public_key() == pk0); @@ -85,8 +85,8 @@ impl AffGProof

{ let hat_cap_n = &setup.modulus_bounded(); - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); - let beta = Signed::random_bounded_bits(rng, P::LP_BOUND + P::EPS_BOUND); + 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); @@ -96,8 +96,10 @@ impl AffGProof

{ let delta = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); let mu = 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_b_x = P::scalar_from_signed(&alpha).mul_by_generator(); + let cap_a = (cap_c * &alpha + Ciphertext::new_with_randomizer_signed(pk0, &beta, &r_mod.to_wire())).to_wire(); + let cap_b_x = secret_scalar_from_signed::

(&alpha) + .expose_secret() + .mul_by_generator(); let cap_b_y = Ciphertext::new_with_randomizer_signed(pk1, &beta, &r_y_mod.to_wire()).to_wire(); let cap_e = setup.commit(&alpha, &gamma).to_wire(); let cap_s = setup.commit(x, &m).to_wire(); @@ -106,7 +108,7 @@ impl AffGProof

{ // NOTE: deviation from the paper to support a different $D$ // (see the comment in `AffGProof`) // Original: $s^y$. Modified: $s^{-y}$ - let cap_t = setup.commit(&(-y.expose_secret()), &mu).to_wire(); + let cap_t = setup.commit(&(-y), &mu).to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) // commitments @@ -132,13 +134,13 @@ impl AffGProof

{ let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); let e_wide = e.into_wide(); - let z1 = alpha + e * x; + let z1 = (alpha + x * e).expose_secret().clone(); // NOTE: deviation from the paper to support a different $D$ // (see the comment in `AffGProof`) // Original: $z_2 = \beta + e y$ // Modified: $z_2 = \beta - e y$ - let z2 = beta + e * (-y.expose_secret()); + let z2 = (beta + (-y) * e).expose_secret().clone(); let z3 = gamma + e_wide * m; let z4 = delta + e_wide * mu; @@ -223,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_with_randomizer_signed(pk0, &self.z2, &self.omega) + if cap_c * self.z1 + Ciphertext::new_nonsecret_with_randomizer_signed(pk0, &self.z2, &self.omega) != cap_d * e + self.cap_a.to_precomputed(pk0) { return false; @@ -239,7 +241,7 @@ 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_with_randomizer_signed(pk1, &self.z2, &self.omega_y) + if Ciphertext::new_nonsecret_with_randomizer_signed(pk1, &self.z2, &self.omega_y) != cap_y * (-e) + self.cap_b_y.to_precomputed(pk1) { return false; @@ -248,14 +250,14 @@ impl AffGProof

{ // 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(&self.z1, &self.z3) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z1, &self.z3) != &cap_e_mod * &cap_s_mod.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(&self.z2, &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z2, &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e) { return false; } @@ -270,7 +272,7 @@ mod tests { use super::AffGProof; use crate::{ - cggmp21::{SchemeParams, TestParams}, + cggmp21::{params::secret_scalar_from_signed, SchemeParams, TestParams}, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, tools::Secret, uint::Signed, @@ -291,17 +293,19 @@ mod tests { let aux: &[u8] = b"abcde"; - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + let x = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let y = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::LP_BOUND)); let rho = Randomizer::random(&mut OsRng, pk0); let rho_y = Randomizer::random(&mut OsRng, pk1); - let secret = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + 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.expose_secret(), &rho.to_wire()); - let cap_y = Ciphertext::new_with_randomizer_signed(pk1, y.expose_secret(), &rho_y.to_wire()); - let cap_x = Params::scalar_from_signed(&x).mul_by_generator(); + 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_x = secret_scalar_from_signed::(&x) + .expose_secret() + .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, diff --git a/synedrion/src/cggmp21/sigma/dec.rs b/synedrion/src/cggmp21/sigma/dec.rs index b15b5fb..a14536e 100644 --- a/synedrion/src/cggmp21/sigma/dec.rs +++ b/synedrion/src/cggmp21/sigma/dec.rs @@ -1,9 +1,10 @@ //! Paillier decryption modulo $q$ ($\Pi^{dec}$, Section C.6, Fig. 30) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{params::secret_scalar_from_signed, SchemeParams}; use crate::{ curve::Scalar, paillier::{ @@ -11,6 +12,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -46,7 +48,7 @@ impl DecProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - y: &Signed<::Uint>, + y: &Secret::Uint>>, rho: &Randomizer, pk0: &PublicKeyPaillier, x: &Scalar, @@ -58,7 +60,7 @@ impl DecProof

{ let hat_cap_n = &setup.modulus_bounded(); // $\hat{N}$ - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); let nu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); let r = Randomizer::random(rng, pk0); @@ -66,7 +68,10 @@ 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 gamma = P::scalar_from_signed(&alpha); + + // `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. + let gamma = secret_scalar_from_signed::

(&alpha).expose_secret().clone(); let mut reader = XofHasher::new_with_dst(HASH_TAG) // commitments @@ -88,7 +93,7 @@ impl DecProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha.into_wide() + e.mul_wide(y); + let z1 = (alpha.into_wide() + y.mul_wide(&e)).expose_secret().clone(); let z2 = nu + e.into_wide() * mu; let omega = (r * rho.pow_signed_vartime(&e)).to_wire(); @@ -137,7 +142,7 @@ impl DecProof

{ } // enc(z_1, \omega) == A (+) C (*) e - if Ciphertext::new_with_randomizer_wide(pk0, &self.z1, &self.omega) + if Ciphertext::new_nonsecret_with_randomizer_wide(pk0, &self.z1, &self.omega) != self.cap_a.to_precomputed(pk0) + cap_c * e { return false; @@ -151,7 +156,7 @@ 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_wide(&self.z1, &self.z2) != &cap_t_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret_wide(&self.z1, &self.z2) != &cap_t_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -162,11 +167,13 @@ impl DecProof

{ #[cfg(test)] mod tests { use rand_core::OsRng; + use secrecy::ExposeSecret; use super::DecProof; use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, PaillierParams, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -183,8 +190,8 @@ mod tests { let aux: &[u8] = b"abcde"; // We need something within the range -N/2..N/2 so that it doesn't wrap around. - let y = Signed::random_bounded_bits(&mut OsRng, Paillier::PRIME_BITS * 2 - 2); - let x = Params::scalar_from_signed(&y); + let y = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Paillier::PRIME_BITS * 2 - 2)); + let x = Params::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()); diff --git a/synedrion/src/cggmp21/sigma/enc.rs b/synedrion/src/cggmp21/sigma/enc.rs index 0c89665..5adeb56 100644 --- a/synedrion/src/cggmp21/sigma/enc.rs +++ b/synedrion/src/cggmp21/sigma/enc.rs @@ -1,6 +1,7 @@ //! Paillier encryption in range ($\Pi^{enc}$, Section 6.1, Fig. 14) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::super::SchemeParams; @@ -10,6 +11,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -41,21 +43,21 @@ pub(crate) struct EncProof { impl EncProof

{ pub fn new( rng: &mut impl CryptoRngCore, - k: &Signed<::Uint>, + k: &Secret::Uint>>, rho: &Randomizer, pk0: &PublicKeyPaillier, cap_k: &Ciphertext, setup: &RPParams, aux: &impl Hashable, ) -> Self { - k.assert_bound(P::L_BOUND); + k.expose_secret().assert_bound(P::L_BOUND); assert_eq!(cap_k.public_key(), pk0); let hat_cap_n = &setup.modulus_bounded(); // $\hat{N}$ // TODO (#86): should we instead sample in range $+- 2^{\ell + \eps} - q 2^\ell$? // This will ensure that the range check on the prover side will pass. - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); let r = Randomizer::random(rng, pk0); let gamma = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); @@ -79,7 +81,7 @@ impl EncProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * k; + let z1 = (alpha + k * e).expose_secret().clone(); let z2 = (r * rho.pow_signed_vartime(&e)).to_wire(); let z3 = gamma + mu * e.into_wide(); @@ -128,7 +130,7 @@ impl EncProof

{ } // enc_0(z1, z2) == A (+) K (*) e - let c = Ciphertext::new_with_randomizer_signed(pk0, &self.z1, &self.z2); + let c = Ciphertext::new_nonsecret_with_randomizer_signed(pk0, &self.z1, &self.z2); if c != self.cap_a.to_precomputed(pk0) + cap_k * e { return false; } @@ -136,7 +138,7 @@ impl EncProof

{ // 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(&self.z1, &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z1, &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -152,6 +154,7 @@ mod tests { use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -167,7 +170,7 @@ mod tests { let aux: &[u8] = b"abcde"; - let secret = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + 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()); diff --git a/synedrion/src/cggmp21/sigma/fac.rs b/synedrion/src/cggmp21/sigma/fac.rs index fe407e6..6296239 100644 --- a/synedrion/src/cggmp21/sigma/fac.rs +++ b/synedrion/src/cggmp21/sigma/fac.rs @@ -65,10 +65,12 @@ impl FacProof

{ ) .expect("the value is bounded by `2^PRIME_BITS` by construction"); - let alpha = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, &sqrt_cap_n); - let beta = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, &sqrt_cap_n); + let alpha = + Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, &sqrt_cap_n)); + let beta = + Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, &sqrt_cap_n)); let mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); - let nu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); + let nu = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n)); // N_0 \hat{N} let scale = pk0.modulus_bounded().mul_wide(hat_cap_n); @@ -86,11 +88,11 @@ impl FacProof

{ let p = sk0.p_signed(); let q = sk0.q_signed(); - let cap_p = setup.commit(p.expose_secret(), &mu).to_wire(); - let cap_q = setup.commit(q.expose_secret(), &nu); + let cap_p = setup.commit(&p, &mu).to_wire(); + let cap_q = setup.commit_secret(&q, &nu); let cap_a = setup.commit_wide(&alpha, &x).to_wire(); let cap_b = setup.commit_wide(&beta, &y).to_wire(); - let cap_t = (&cap_q.pow_signed_wide(&alpha) * &setup.commit_base_xwide(&r)).to_wire(); + let cap_t = (&cap_q.pow_secret_signed_wide(&alpha) * &setup.commit_base_xwide(&r)).to_wire(); let cap_q = cap_q.to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) @@ -113,12 +115,12 @@ impl FacProof

{ let p_wide = sk0.p_wide_signed(); - let hat_sigma = sigma - (nu * p_wide.expose_secret()).into_wide(); - let z1 = alpha + (e * p.expose_secret()).into_wide(); - let z2 = beta + (e * q.expose_secret()).into_wide(); + let hat_sigma = sigma - ((p_wide * &nu).into_wide()).expose_secret(); + let z1 = (alpha + (p * e).into_wide()).expose_secret().clone(); + let z2 = (beta + (q * e).into_wide()).expose_secret().clone(); let omega1 = x + e_wide * mu; - let omega2 = y + e_wide * nu; - let v = r + (e_wide.into_wide() * hat_sigma); + let omega2 = (nu * e_wide + &y).expose_secret().clone(); + let v = r + e_wide.into_wide() * &hat_sigma; Self { e, @@ -169,14 +171,14 @@ impl FacProof

{ // 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_wide(&self.z1, &self.omega1) != &cap_a_mod * &cap_p_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret_wide(&self.z1, &self.omega1) != &cap_a_mod * &cap_p_mod.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_wide(&self.z2, &self.omega2) != &cap_b_mod * &cap_q_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret_wide(&self.z2, &self.omega2) != &cap_b_mod * &cap_q_mod.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 7c9b1f6..21c4c81 100644 --- a/synedrion/src/cggmp21/sigma/log_star.rs +++ b/synedrion/src/cggmp21/sigma/log_star.rs @@ -1,9 +1,10 @@ //! Knowledge of Exponent vs Paillier Encryption ($\Pi^{log*}$, Section C.2, Fig. 25) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{params::secret_scalar_from_signed, SchemeParams}; use crate::{ curve::Point, paillier::{ @@ -11,6 +12,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -46,7 +48,7 @@ impl LogStarProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, + x: &Secret::Uint>>, rho: &Randomizer, pk0: &PublicKeyPaillier, cap_c: &Ciphertext, @@ -55,19 +57,19 @@ impl LogStarProof

{ setup: &RPParams, aux: &impl Hashable, ) -> Self { - x.assert_bound(P::L_BOUND); + x.expose_secret().assert_bound(P::L_BOUND); assert_eq!(cap_c.public_key(), pk0); let hat_cap_n = &setup.modulus_bounded(); // $\hat{N}$ - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); let r = Randomizer::random(rng, pk0); let gamma = 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_y = g * &P::scalar_from_signed(&alpha); + let cap_y = g * secret_scalar_from_signed::

(&alpha).expose_secret(); let cap_d = setup.commit(&alpha, &gamma).to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) @@ -88,7 +90,7 @@ impl LogStarProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * x; + let z1 = (alpha + x * e).expose_secret().clone(); let z2 = (r * rho.pow_signed_vartime(&e)).to_wire(); let z3 = gamma + mu * e.into_wide(); @@ -144,7 +146,7 @@ impl LogStarProof

{ } // enc_0(z1, z2) == A (+) C (*) e - let c = Ciphertext::new_with_randomizer_signed(pk0, &self.z1, &self.z2); + let c = Ciphertext::new_nonsecret_with_randomizer_signed(pk0, &self.z1, &self.z2); if c != self.cap_a.to_precomputed(pk0) + cap_c * e { return false; } @@ -157,7 +159,7 @@ 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(&self.z1, &self.z3) != &cap_d_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z1, &self.z3) != &cap_d_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -168,12 +170,14 @@ impl LogStarProof

{ #[cfg(test)] mod tests { use rand_core::OsRng; + use secrecy::ExposeSecret; use super::LogStarProof; use crate::{ - cggmp21::{SchemeParams, TestParams}, + cggmp21::{params::secret_scalar_from_signed, SchemeParams, TestParams}, curve::{Point, Scalar}, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -190,10 +194,10 @@ mod tests { let aux: &[u8] = b"abcde"; let g = Point::GENERATOR * Scalar::random(&mut OsRng); - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + 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_x = g * Params::scalar_from_signed(&x); + let cap_x = g * secret_scalar_from_signed::(&x).expose_secret(); let proof = LogStarProof::::new(&mut OsRng, &x, &rho, pk, &cap_c, &g, &cap_x, &setup, &aux); assert!(proof.verify(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 3d2af27..b1a8596 100644 --- a/synedrion/src/cggmp21/sigma/mul.rs +++ b/synedrion/src/cggmp21/sigma/mul.rs @@ -1,12 +1,16 @@ //! Paillier multiplication ($\Pi^{mul}$, Section C.6, Fig. 29) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::super::SchemeParams; use crate::{ paillier::{Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, Randomizer, RandomizerWire}, - tools::hashing::{Chain, Hashable, XofHasher}, + tools::{ + hashing::{Chain, Hashable, XofHasher}, + Secret, + }, uint::{Bounded, Retrieve, Signed}, }; @@ -42,7 +46,7 @@ impl MulProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, + x: &Secret::Uint>>, rho_x: &Randomizer, rho: &Randomizer, pk: &PublicKeyPaillier, @@ -59,13 +63,15 @@ impl MulProof

{ let r_mod = Randomizer::random(rng, pk); let s_mod = Randomizer::random(rng, pk); - let alpha = Bounded::new(alpha_mod.retrieve(), ::MODULUS_BITS) - .expect("the value is bounded by `MODULUS_BITS` by construction"); + let alpha = Secret::init_with(|| { + Bounded::new(alpha_mod.retrieve(), ::MODULUS_BITS) + .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(pk, alpha.as_ref(), &s).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(); let mut reader = XofHasher::new_with_dst(HASH_TAG) // commitments @@ -82,11 +88,13 @@ impl MulProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z = alpha + let z = (alpha .into_wide() .into_signed() .expect("conversion to `WideUint` provides enough space for a sign bit") - + e.mul_wide(x); + + x.mul_wide(&e)) + .expose_secret() + .clone(); let u = (r_mod * rho.pow_signed_vartime(&e)).to_wire(); let v = (s_mod * rho_x.pow_signed_vartime(&e)).to_wire(); @@ -138,7 +146,9 @@ impl MulProof

{ // enc(z, v) == B * X^e \mod N^2 // (Note: typo in the paper, it uses `c` and not `v` here) - if Ciphertext::new_with_randomizer_wide(pk, &self.z, &self.v) != self.cap_b.to_precomputed(pk) + cap_x * e { + if Ciphertext::new_nonsecret_with_randomizer_wide(pk, &self.z, &self.v) + != self.cap_b.to_precomputed(pk) + cap_x * e + { return false; } @@ -154,6 +164,7 @@ mod tests { use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -167,14 +178,14 @@ mod tests { let aux: &[u8] = b"abcde"; - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); - let y = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + let x = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); + let y = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); 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_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.to_wire()); 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 f48e15c..88383cd 100644 --- a/synedrion/src/cggmp21/sigma/mul_star.rs +++ b/synedrion/src/cggmp21/sigma/mul_star.rs @@ -1,9 +1,10 @@ //! Multiplication Paillier vs Group ($\Pi^{mul}$, Section C.6, Fig. 31) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{params::secret_scalar_from_signed, SchemeParams}; use crate::{ curve::Point, paillier::{ @@ -11,6 +12,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -46,7 +48,7 @@ impl MulStarProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, + x: &Secret::Uint>>, rho: &Randomizer, pk0: &PublicKeyPaillier, cap_c: &Ciphertext, @@ -62,19 +64,21 @@ impl MulStarProof

{ - $\beta$ used to create $A$ is not mentioned anywhere else - a typo, it is effectively == 0 */ - x.assert_bound(P::L_BOUND); + x.expose_secret().assert_bound(P::L_BOUND); assert_eq!(cap_c.public_key(), pk0); assert_eq!(cap_d.public_key(), pk0); let hat_cap_n = &setup.modulus_bounded(); // $\hat{N}$ let r = Randomizer::random(rng, pk0); - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let gamma = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); let m = 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_b_x = P::scalar_from_signed(&alpha).mul_by_generator(); + let cap_a = (cap_c * &alpha).mul_randomizer(&r.to_wire()).to_wire(); + let cap_b_x = secret_scalar_from_signed::

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

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * x; + let z1 = (alpha + x * e).expose_secret().clone(); let z2 = gamma + e.into_wide() * m; let omega = (r * rho.pow_signed(&e)).to_wire(); @@ -166,7 +170,7 @@ 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(&self.z1, &self.z2) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z1, &self.z2) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -177,11 +181,13 @@ impl MulStarProof

{ #[cfg(test)] mod tests { use rand_core::OsRng; + use secrecy::ExposeSecret; use super::MulStarProof; use crate::{ - cggmp21::{SchemeParams, TestParams}, + cggmp21::{params::secret_scalar_from_signed, SchemeParams, TestParams}, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -197,12 +203,14 @@ mod tests { let aux: &[u8] = b"abcde"; - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); - let secret = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + let x = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); + 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_x = Params::scalar_from_signed(&x).mul_by_generator(); + let cap_d = (&cap_c * &x).mul_randomizer(&rho.to_wire()); + let cap_x = secret_scalar_from_signed::(&x) + .expose_secret() + .mul_by_generator(); let proof = MulStarProof::::new(&mut OsRng, &x, &rho, pk, &cap_c, &cap_d, &cap_x, &setup, &aux); assert!(proof.verify(pk, &cap_c, &cap_d, &cap_x, &setup, &aux)); diff --git a/synedrion/src/cggmp21/sigma/sch.rs b/synedrion/src/cggmp21/sigma/sch.rs index ffc2ac9..eb097fd 100644 --- a/synedrion/src/cggmp21/sigma/sch.rs +++ b/synedrion/src/cggmp21/sigma/sch.rs @@ -74,13 +74,13 @@ pub(crate) struct SchProof { impl SchProof { pub fn new( proof_secret: &SchSecret, - x: &Scalar, + x: &Secret, commitment: &SchCommitment, cap_x: &Point, aux: &impl Hashable, ) -> Self { let challenge = SchChallenge::new(cap_x, commitment, aux); - let proof = *proof_secret.0.expose_secret() + challenge.0 * x; + let proof = (&proof_secret.0 + x * challenge.0).expose_secret().clone(); Self { challenge, proof } } @@ -93,14 +93,15 @@ impl SchProof { #[cfg(test)] mod tests { use rand_core::OsRng; + use secrecy::ExposeSecret; use super::{SchCommitment, SchProof, SchSecret}; - use crate::curve::Scalar; + use crate::{curve::Scalar, tools::Secret}; #[test] fn prove_and_verify() { - let secret = Scalar::random(&mut OsRng); - let public = secret.mul_by_generator(); + let secret = Secret::init_with(|| Scalar::random(&mut OsRng)); + let public = secret.expose_secret().mul_by_generator(); let aux: &[u8] = b"abcde"; let proof_secret = SchSecret::random(&mut OsRng); diff --git a/synedrion/src/paillier/encryption.rs b/synedrion/src/paillier/encryption.rs index 44824e4..40051ef 100644 --- a/synedrion/src/paillier/encryption.rs +++ b/synedrion/src/paillier/encryption.rs @@ -13,9 +13,12 @@ use super::{ keys::{PublicKeyPaillier, SecretKeyPaillier}, params::PaillierParams, }; -use crate::uint::{ - subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}, - Bounded, Exponentiable, HasWide, Retrieve, Signed, ToMontgomery, +use crate::{ + tools::Secret, + uint::{ + subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}, + Bounded, Exponentiable, HasWide, Retrieve, Signed, ToMontgomery, + }, }; // A ciphertext randomizer (an invertible element of $\mathbb{Z}_N$). @@ -118,7 +121,7 @@ impl Ciphertext

{ /// Encrypts the plaintext with the provided randomizer. fn new_with_randomizer_inner( pk: &PublicKeyPaillier

, - abs_plaintext: &P::Uint, + abs_plaintext: &Secret, randomizer: &RandomizerWire

, plaintext_is_negative: Choice, ) -> Self { @@ -139,6 +142,10 @@ impl Ciphertext

{ // 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 + + // TODO: wrap all the secrets in this method + let abs_plaintext = abs_plaintext.expose_secret().clone(); + 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); @@ -160,35 +167,81 @@ impl Ciphertext

{ } /// Encrypts the plaintext with the provided randomizer. - pub fn new_with_randomizer(pk: &PublicKeyPaillier

, plaintext: &P::Uint, randomizer: &RandomizerWire

) -> Self { + pub fn new_with_randomizer( + pk: &PublicKeyPaillier

, + plaintext: &Secret, + randomizer: &RandomizerWire

, + ) -> Self { Self::new_with_randomizer_inner(pk, plaintext, randomizer, Choice::from(0)) } - pub fn new_with_randomizer_signed( + pub fn new_with_randomizer_bounded( + pk: &PublicKeyPaillier

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

, + ) -> Self { + Self::new_with_randomizer_inner( + pk, + &Secret::init_with(|| plaintext.expose_secret().as_ref().clone()), + randomizer, + Choice::from(0), + ) + } + + pub fn new_nonsecret_with_randomizer_signed( pk: &PublicKeyPaillier

, plaintext: &Signed, randomizer: &RandomizerWire

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

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

, + ) -> Self { + let plaintext = plaintext.expose_secret().clone(); + Self::new_with_randomizer_inner( + pk, + &Secret::init_with(|| plaintext.abs()), + randomizer, + plaintext.is_negative(), + ) + } + + pub fn new_nonsecret_with_randomizer_wide( pk: &PublicKeyPaillier

, plaintext: &Signed, randomizer: &RandomizerWire

, ) -> Self { - 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_with_randomizer_wide(pk, &Secret::init_with(|| plaintext.clone()), randomizer) + } + + pub fn new_with_randomizer_wide( + pk: &PublicKeyPaillier

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

, + ) -> Self { + let plaintext = plaintext.expose_secret().clone(); + 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()) } /// Encrypts the plaintext with a random randomizer. - pub fn new(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

, plaintext: &P::Uint) -> Self { + pub fn new(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

, plaintext: &Secret) -> Self { Self::new_with_randomizer(pk, plaintext, &RandomizerWire::random(rng, pk)) } #[cfg(test)] - pub fn new_signed(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

, plaintext: &Signed) -> Self { + pub fn new_signed( + rng: &mut impl CryptoRngCore, + pk: &PublicKeyPaillier

, + plaintext: &Secret>, + ) -> Self { Self::new_with_randomizer_signed(pk, plaintext, &RandomizerWire::random(rng, pk)) } @@ -337,7 +390,7 @@ impl Ciphertext

{ } } -impl Add for Ciphertext

{ +impl Add> for Ciphertext

{ type Output = Ciphertext

; fn add(self, other: Ciphertext

) -> Ciphertext

{ self + &other @@ -358,6 +411,13 @@ impl Mul> for Ciphertext

{ } } +impl<'a, P: PaillierParams> Mul<&'a Signed> for Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: &'a Signed) -> Ciphertext

{ + self.homomorphic_mul(other) + } +} + impl Mul> for &Ciphertext

{ type Output = Ciphertext

; fn mul(self, other: Signed) -> Ciphertext

{ @@ -365,6 +425,41 @@ impl Mul> for &Ciphertext

{ } } +impl<'a, P: PaillierParams> Mul<&'a Signed> for &Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: &'a Signed) -> Ciphertext

{ + self.homomorphic_mul_ref(other) + } +} + +impl Mul>> for Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: Secret>) -> Ciphertext

{ + self * other.expose_secret() + } +} + +impl<'a, P: PaillierParams> Mul<&'a Secret>> for Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: &'a Secret>) -> Ciphertext

{ + self * other.expose_secret() + } +} + +impl Mul>> for &Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: Secret>) -> Ciphertext

{ + self * other.expose_secret() + } +} + +impl<'a, P: PaillierParams> Mul<&'a Secret>> for &Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: &'a Secret>) -> Ciphertext

{ + self * other.expose_secret() + } +} + impl Mul> for Ciphertext

{ type Output = Ciphertext

; fn mul(self, other: Bounded) -> Ciphertext

{ @@ -379,18 +474,43 @@ impl Mul> for &Ciphertext

{ } } +impl Mul>> for Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: Secret>) -> Ciphertext

{ + self.homomorphic_mul_unsigned(other.expose_secret()) + } +} + +impl Mul>> for &Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: Secret>) -> Ciphertext

{ + self.homomorphic_mul_unsigned_ref(other.expose_secret()) + } +} + +impl<'a, P: PaillierParams> Mul<&'a Secret>> for &Ciphertext

{ + type Output = Ciphertext

; + fn mul(self, other: &'a Secret>) -> Ciphertext

{ + self.homomorphic_mul_unsigned_ref(other.expose_secret()) + } +} + #[cfg(test)] mod tests { use crypto_bigint::{Encoding, Integer, ShrVartime, WrappingSub}; use rand_core::OsRng; + use secrecy::ExposeSecret; use super::{ super::{params::PaillierTest, PaillierParams, SecretKeyPaillierWire}, Ciphertext, Randomizer, }; - use crate::uint::{ - subtle::{ConditionallyNegatable, ConditionallySelectable}, - HasWide, NonZero, RandomMod, Signed, + use crate::{ + tools::Secret, + uint::{ + subtle::{ConditionallyNegatable, ConditionallySelectable}, + HasWide, NonZero, RandomMod, Signed, + }, }; fn mul_mod(lhs: &T, rhs: &Signed, modulus: &NonZero) -> T @@ -428,10 +548,11 @@ mod tests { fn roundtrip() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext = Ciphertext::::new(&mut OsRng, pk, &plaintext); let plaintext_back = ciphertext.decrypt(&sk); - assert_eq!(plaintext, plaintext_back); + assert_eq!(plaintext.expose_secret(), &plaintext_back); let ciphertext_wire = ciphertext.to_wire(); let ciphertext_back = ciphertext_wire.to_precomputed(pk); @@ -442,10 +563,12 @@ mod tests { fn signed_roundtrip() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2); + let plaintext = Secret::init_with(|| { + Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2) + }); let ciphertext = Ciphertext::new_signed(&mut OsRng, pk, &plaintext); let plaintext_back = ciphertext.decrypt_signed(&sk); - let plaintext_reduced = reduce::(&plaintext, &pk.modulus_nonzero()); + let plaintext_reduced = reduce::(plaintext.expose_secret(), &pk.modulus_nonzero()); assert_eq!(plaintext_reduced, plaintext_back); } @@ -453,7 +576,8 @@ mod tests { fn derive_randomizer() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + 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 randomizer_back = ciphertext.derive_randomizer(&sk); @@ -464,14 +588,18 @@ mod tests { fn homomorphic_mul() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext = Ciphertext::::new(&mut OsRng, pk, &plaintext); let coeff = Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2); let new_ciphertext = ciphertext * coeff; let new_plaintext = new_ciphertext.decrypt(&sk); - assert_eq!(mul_mod(&plaintext, &coeff, &pk.modulus_nonzero()), new_plaintext); + assert_eq!( + mul_mod(plaintext.expose_secret(), &coeff, &pk.modulus_nonzero()), + new_plaintext + ); } #[test] @@ -479,16 +607,23 @@ mod tests { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext1 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext1 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext1 = Ciphertext::::new(&mut OsRng, pk, &plaintext1); - let plaintext2 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext2 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext2 = Ciphertext::::new(&mut OsRng, pk, &plaintext2); let new_ciphertext = ciphertext1 + ciphertext2; let new_plaintext = new_ciphertext.decrypt(&sk); - assert_eq!(plaintext1.add_mod(&plaintext2, pk.modulus()), new_plaintext); + assert_eq!( + plaintext1 + .expose_secret() + .add_mod(plaintext2.expose_secret(), pk.modulus()), + new_plaintext + ); } #[test] @@ -496,9 +631,11 @@ mod tests { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext1 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext1 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let plaintext2 = Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2); - let plaintext3 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext3 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext1 = Ciphertext::::new(&mut OsRng, pk, &plaintext1); let ciphertext3 = Ciphertext::::new(&mut OsRng, pk, &plaintext3); @@ -506,7 +643,8 @@ mod tests { let plaintext_back = result.decrypt(&sk); assert_eq!( - mul_mod(&plaintext1, &plaintext2, &pk.modulus_nonzero()).add_mod(&plaintext3, pk.modulus()), + mul_mod(plaintext1.expose_secret(), &plaintext2, &pk.modulus_nonzero()) + .add_mod(plaintext3.expose_secret(), pk.modulus()), plaintext_back ); } diff --git a/synedrion/src/paillier/params.rs b/synedrion/src/paillier/params.rs index 8e3ff20..6443726 100644 --- a/synedrion/src/paillier/params.rs +++ b/synedrion/src/paillier/params.rs @@ -42,7 +42,7 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn type Uint: Integer + Bounded + ConditionallySelectable - + Encoding + + Encoding + Hashable + HasWide + InvMod @@ -95,7 +95,8 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn + Integer + RandomMod + Serialize - + for<'de> Deserialize<'de>; + + for<'de> Deserialize<'de> + + Zeroize; } /// Paillier parameters for unit tests in this submodule. diff --git a/synedrion/src/paillier/ring_pedersen.rs b/synedrion/src/paillier/ring_pedersen.rs index 01e4169..90550e5 100644 --- a/synedrion/src/paillier/ring_pedersen.rs +++ b/synedrion/src/paillier/ring_pedersen.rs @@ -99,12 +99,40 @@ impl RPParams

{ /// /// Both will be effectively reduced modulo `totient(N)` /// (that is, commitments produced for `x` and `x + totient(N)` are equal). - pub fn commit(&self, secret: &Signed, randomizer: &Signed) -> RPCommitment

{ + pub fn commit(&self, secret: &Secret>, randomizer: &Signed) -> RPCommitment

{ // $t^\rho * s^m mod N$ where $\rho$ is the randomizer and $m$ is the secret. - RPCommitment(self.base.pow_signed_wide(randomizer) * self.power.pow_signed(secret)) + RPCommitment(self.base.pow_signed_wide(randomizer) * self.power.pow_signed(secret.expose_secret())) } - pub fn commit_wide(&self, secret: &Signed, randomizer: &Signed) -> RPCommitment

{ + pub fn commit_secret( + &self, + secret: &Secret>, + randomizer: &Secret>, + ) -> RPCommitment

{ + // $t^\rho * s^m mod N$ where $\rho$ is the randomizer and $m$ is the secret. + RPCommitment( + self.base.pow_signed_wide(randomizer.expose_secret()) * self.power.pow_signed(secret.expose_secret()), + ) + } + + pub fn commit_nonsecret(&self, secret: &Signed, randomizer: &Signed) -> RPCommitment

{ + self.commit(&Secret::init_with(|| secret.clone()), randomizer) + } + + pub fn commit_wide( + &self, + secret: &Secret>, + randomizer: &Signed, + ) -> RPCommitment

{ + // $t^\rho * s^m mod N$ where $\rho$ is the randomizer and $m$ is the secret. + RPCommitment(self.base.pow_signed_wide(randomizer) * self.power.pow_signed_wide(secret.expose_secret())) + } + + pub fn commit_nonsecret_wide( + &self, + secret: &Signed, + randomizer: &Signed, + ) -> RPCommitment

{ // $t^\rho * s^m mod N$ where $\rho$ is the randomizer and $m$ is the secret. RPCommitment(self.base.pow_signed_wide(randomizer) * self.power.pow_signed_wide(secret)) } @@ -170,6 +198,10 @@ impl RPCommitment

{ Self(self.0.pow_signed_vartime(exponent)) } + pub fn pow_secret_signed_wide(&self, exponent: &Secret>) -> Self { + Self(self.0.pow_signed_wide(exponent.expose_secret())) + } + pub fn pow_signed_wide(&self, exponent: &Signed) -> Self { Self(self.0.pow_signed_wide(exponent)) } diff --git a/synedrion/src/tools/hide_debug.rs b/synedrion/src/tools/hide_debug.rs index 3b3a8e5..e546061 100644 --- a/synedrion/src/tools/hide_debug.rs +++ b/synedrion/src/tools/hide_debug.rs @@ -1,12 +1,15 @@ use core::{ fmt::Debug, - ops::{Add, AddAssign, Deref, DerefMut, Mul, MulAssign, Sub, SubAssign}, + ops::{Add, AddAssign, Deref, DerefMut, Mul, MulAssign, Neg, Sub, SubAssign}, }; +use crypto_bigint::{subtle::ConditionallySelectable, Encoding, Integer}; use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use zeroize::Zeroize; +use crate::uint::{Bounded, HasWide, Signed}; + /// A wrapper for `secrecy::SecretBox` with additional convenience methods. pub(crate) struct Secret(SecretBox); @@ -50,6 +53,49 @@ impl From> for Secret { } } +impl Secret> +where + T: Zeroize + Clone + Encoding + Integer + HasWide + ConditionallySelectable + crypto_bigint::Bounded, + T::Wide: ConditionallySelectable + crypto_bigint::Bounded + Zeroize, +{ + pub fn into_wide(&self) -> Secret::Wide>> { + Secret::init_with(|| { + let mut value = self.expose_secret().clone(); + let result = value.into_wide(); + value.zeroize(); + result + }) + } + + pub fn mul_wide(&self, rhs: &Signed) -> 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 + crypto_bigint::Bounded, + T::Wide: crypto_bigint::Bounded + Zeroize, +{ + pub fn into_wide(&self) -> Secret::Wide>> { + Secret::init_with(|| { + let mut value = self.expose_secret().clone(); + let result = value.into_wide(); + value.zeroize(); + result + }) + } + + pub fn into_signed(&self) -> Option>> { + Secret::try_init_with(|| self.expose_secret().clone().into_signed().ok_or(|| ())).ok() + } +} + impl Deref for Secret { type Target = SecretBox; @@ -105,6 +151,42 @@ impl<'a, T: Zeroize + AddAssign<&'a T>> Add<&'a Secret> for Secret { } } +impl AddAssign<&'a T>> Add> for &Secret { + type Output = Secret; + + fn add(self, other: Secret) -> Self::Output { + let mut result = other; + result += self; + result + } +} + +impl<'a, T: Zeroize + Clone + AddAssign<&'a T>> Add<&'a Secret> for &Secret { + type Output = Secret; + + fn add(self, other: &'a Secret) -> Self::Output { + let mut result = self.clone(); + result += other; + result + } +} + +// Negation + +impl> Neg for Secret { + type Output = Secret; + fn neg(self) -> Self::Output { + Secret::init_with(|| self.expose_secret().clone().neg()) + } +} + +impl> Neg for &Secret { + type Output = Secret; + fn neg(self) -> Self::Output { + Secret::init_with(|| self.expose_secret().clone().neg()) + } +} + // Subtraction impl<'a, T: Zeroize + SubAssign<&'a T>> SubAssign<&'a T> for Secret { @@ -169,6 +251,35 @@ impl<'a, T: Zeroize + MulAssign<&'a T>> Mul<&'a T> for Secret { } } +impl<'a, T: Zeroize + Clone + MulAssign<&'a T>> Mul<&'a T> for &Secret { + type Output = Secret; + + fn mul(self, other: &'a T) -> Self::Output { + let mut result: Secret = self.clone(); + result *= other; + result + } +} + +impl MulAssign<&'a T>> Mul for Secret { + type Output = Secret; + + fn mul(mut self, other: T) -> Self::Output { + self *= &other; + self + } +} + +impl MulAssign<&'a T>> Mul for &Secret { + type Output = Secret; + + fn mul(self, other: T) -> Self::Output { + let mut result: Secret = self.clone(); + result *= &other; + result + } +} + impl MulAssign<&'a T>> Mul> for Secret { type Output = Secret; @@ -194,3 +305,9 @@ impl AddAssign<&'a T> + Default> core::iter::Sum fo iter.reduce(Add::add).unwrap_or(Secret::init_with(|| T::default())) } } + +impl<'b, T: Zeroize + Clone + for<'a> AddAssign<&'a T> + Default> core::iter::Sum<&'b Secret> for Secret { + fn sum>>(iter: I) -> Self { + iter.fold(Secret::init_with(|| T::default()), |accum, x| accum + x) + } +} diff --git a/synedrion/src/uint/bounded.rs b/synedrion/src/uint/bounded.rs index e2bec9e..c249db6 100644 --- a/synedrion/src/uint/bounded.rs +++ b/synedrion/src/uint/bounded.rs @@ -119,7 +119,7 @@ impl Bounded where T: HasWide, { - pub fn into_wide(self) -> Bounded { + pub fn into_wide(&self) -> Bounded { Bounded { value: self.value.into_wide(), bound: self.bound, diff --git a/synedrion/src/uint/signed.rs b/synedrion/src/uint/signed.rs index edfd0aa..5d29589 100644 --- a/synedrion/src/uint/signed.rs +++ b/synedrion/src/uint/signed.rs @@ -1,5 +1,5 @@ use alloc::string::String; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use digest::XofReader; use rand_core::CryptoRngCore; @@ -24,7 +24,7 @@ struct PackedSigned { impl From> for PackedSigned where - T: Integer + Encoding + crypto_bigint::Bounded + ConditionallySelectable, + T: ConditionallySelectable + Integer + Encoding + crypto_bigint::Bounded, { fn from(val: Signed) -> Self { Self { @@ -36,7 +36,7 @@ where impl TryFrom for Signed where - T: Integer + Encoding + crypto_bigint::Bounded + ConditionallySelectable, + T: ConditionallySelectable + Integer + Encoding + crypto_bigint::Bounded, { type Error = String; fn try_from(val: PackedSigned) -> Result { @@ -523,6 +523,16 @@ where } } +impl<'a, T> SubAssign<&'a Signed> for Signed +where + T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, +{ + fn sub_assign(&mut self, rhs: &'a Signed) { + // TODO: implement properly + *self = self.checked_sub(rhs).unwrap() + } +} + impl<'a, T> MulAssign<&'a Signed> for Signed where T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, diff --git a/synedrion/src/uint/traits.rs b/synedrion/src/uint/traits.rs index 1369ca4..d42b2a3 100644 --- a/synedrion/src/uint/traits.rs +++ b/synedrion/src/uint/traits.rs @@ -2,7 +2,7 @@ use crypto_bigint::{ modular::MontyForm, nlimbs, subtle::{ConditionallySelectable, CtOption}, - Encoding, Integer, Invert, PowBoundedExp, RandomMod, Square, Zero, U1024, U2048, U4096, U512, U8192, + Encoding, Integer, Invert, PowBoundedExp, RandomMod, Square, Uint, Zero, U1024, U2048, U4096, U512, U8192, }; use crate::uint::{Bounded, Signed}; @@ -134,7 +134,7 @@ pub trait HasWide: Sized + Zero { /// Converts `self` to a new `Wide` uint, setting the higher half to `0`s. /// Consumes `self`. - fn into_wide(self) -> Self::Wide; + fn into_wide(&self) -> Self::Wide; /// Splits a `Wide` in two halves and returns the halves (`Self` sized) in a /// tuple (lower half first). @@ -164,8 +164,8 @@ impl HasWide for U512 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn into_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() @@ -180,8 +180,8 @@ impl HasWide for U1024 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn into_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() @@ -196,8 +196,8 @@ impl HasWide for U2048 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn into_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() @@ -212,8 +212,8 @@ impl HasWide for U4096 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn into_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into()