From 07b750d05d13b352c097eb2bc9bb2712684f5bb1 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Tue, 26 Nov 2024 11:55:10 -0800 Subject: [PATCH] Simplify operations with SecretBox --- synedrion/src/cggmp21/aux_gen.rs | 13 +- synedrion/src/cggmp21/entities.rs | 45 +-- synedrion/src/cggmp21/interactive_signing.rs | 192 ++++----- synedrion/src/cggmp21/key_init.rs | 13 +- synedrion/src/cggmp21/key_refresh.rs | 51 +-- synedrion/src/cggmp21/params.rs | 181 ++++++--- synedrion/src/cggmp21/sigma/aff_g.rs | 103 ++--- synedrion/src/cggmp21/sigma/dec.rs | 52 +-- synedrion/src/cggmp21/sigma/enc.rs | 38 +- synedrion/src/cggmp21/sigma/fac.rs | 72 ++-- synedrion/src/cggmp21/sigma/log_star.rs | 51 +-- synedrion/src/cggmp21/sigma/mod_.rs | 1 - synedrion/src/cggmp21/sigma/mul.rs | 63 +-- synedrion/src/cggmp21/sigma/mul_star.rs | 53 +-- synedrion/src/cggmp21/sigma/prm.rs | 7 +- synedrion/src/cggmp21/sigma/sch.rs | 17 +- synedrion/src/curve.rs | 2 +- synedrion/src/curve/arithmetic.rs | 90 +++-- synedrion/src/paillier.rs | 2 +- synedrion/src/paillier/encryption.rs | 376 ++++++++++++------ synedrion/src/paillier/keys.rs | 62 ++- synedrion/src/paillier/params.rs | 25 +- synedrion/src/paillier/ring_pedersen.rs | 130 ++++--- synedrion/src/paillier/rsa.rs | 86 ++--- synedrion/src/tools.rs | 2 - synedrion/src/tools/hide_debug.rs | 35 -- synedrion/src/tools/secret.rs | 385 ++++++++++++++++++- synedrion/src/tools/sss.rs | 63 +-- synedrion/src/uint/bounded.rs | 44 +-- synedrion/src/uint/signed.rs | 182 ++------- synedrion/src/uint/traits.rs | 63 +-- synedrion/src/www02/entities.rs | 40 +- synedrion/src/www02/key_resharing.rs | 21 +- 33 files changed, 1498 insertions(+), 1062 deletions(-) delete mode 100644 synedrion/src/tools/hide_debug.rs diff --git a/synedrion/src/cggmp21/aux_gen.rs b/synedrion/src/cggmp21/aux_gen.rs index 10722d6..cb54a9a 100644 --- a/synedrion/src/cggmp21/aux_gen.rs +++ b/synedrion/src/cggmp21/aux_gen.rs @@ -2,7 +2,6 @@ //! that only generates the auxiliary data. use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, format, string::String, @@ -17,7 +16,6 @@ use manul::protocol::{ ReceiveError, Round, RoundId, Serializer, }; use rand_core::CryptoRngCore; -use secrecy::SecretBox; use serde::{Deserialize, Serialize}; use super::{ @@ -34,7 +32,7 @@ use crate::{ tools::{ bitvec::BitVec, hashing::{Chain, FofHasher, HashOutput}, - DowncastMap, Without, + DowncastMap, Secret, Without, }, }; @@ -138,7 +136,7 @@ impl EntryPoint for AuxGen { let paillier_pk = paillier_sk.public_key(); // El-Gamal key - let y = Scalar::random(rng); + let y = Secret::init_with(|| Scalar::random(rng)); let cap_y = y.mul_by_generator(); // The secret and the commitment for the Schnorr PoK of the El-Gamal key @@ -208,7 +206,7 @@ struct PublicData1Precomp { #[derive(Debug)] struct Context { paillier_sk: SecretKeyPaillier, - y: Scalar, + y: Secret, tau_y: SchSecret, data_precomp: PublicData1Precomp

, my_id: I, @@ -613,7 +611,7 @@ impl Round for Round3 { let secret_aux = SecretAuxInfo { paillier_sk: self.context.paillier_sk.into_wire(), - el_gamal_sk: SecretBox::new(Box::new(self.context.y)), + el_gamal_sk: self.context.y, }; let aux_info = AuxInfo { @@ -635,7 +633,6 @@ mod tests { session::signature::Keypair, }; use rand_core::OsRng; - use secrecy::ExposeSecret; use super::AuxGen; use crate::cggmp21::TestParams; @@ -664,7 +661,7 @@ mod tests { for (id, aux_info) in aux_infos.iter() { for other_aux_info in aux_infos.values() { assert_eq!( - aux_info.secret_aux.el_gamal_sk.expose_secret().mul_by_generator(), + aux_info.secret_aux.el_gamal_sk.mul_by_generator(), other_aux_info.public_aux[id].el_gamal_pk ); } diff --git a/synedrion/src/cggmp21/entities.rs b/synedrion/src/cggmp21/entities.rs index 87aeef5..8034152 100644 --- a/synedrion/src/cggmp21/entities.rs +++ b/synedrion/src/cggmp21/entities.rs @@ -1,5 +1,4 @@ use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, vec::Vec, }; @@ -7,16 +6,16 @@ use core::{fmt::Debug, marker::PhantomData}; use k256::ecdsa::VerifyingKey; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; use serde::{Deserialize, Serialize}; use crate::{ cggmp21::SchemeParams, - curve::{Point, Scalar}, + curve::{secret_split, Point, Scalar}, paillier::{ - Ciphertext, PaillierParams, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, RandomizerWire, + Ciphertext, PaillierParams, PublicKeyPaillier, PublicKeyPaillierWire, RPParams, RPParamsWire, Randomizer, SecretKeyPaillier, SecretKeyPaillierWire, }, + tools::Secret, uint::Signed, }; @@ -25,7 +24,7 @@ use crate::{ pub struct KeyShare { pub(crate) owner: I, /// Secret key share of this node. - pub(crate) secret_share: SecretBox, // `x_i` + pub(crate) secret_share: Secret, // `x_i` pub(crate) public_shares: BTreeMap, // `X_j` // TODO (#27): this won't be needed when Scalar/Point are a part of `P` pub(crate) phantom: PhantomData

, @@ -44,7 +43,7 @@ pub struct AuxInfo { #[serde(bound(deserialize = "SecretKeyPaillierWire: for <'x> Deserialize<'x>"))] pub(crate) struct SecretAuxInfo { pub(crate) paillier_sk: SecretKeyPaillierWire, - pub(crate) el_gamal_sk: SecretBox, // `y_i` + pub(crate) el_gamal_sk: Secret, // `y_i` } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -68,7 +67,7 @@ pub(crate) struct AuxInfoPrecomputed { pub(crate) struct SecretAuxInfoPrecomputed { pub(crate) paillier_sk: SecretKeyPaillier, #[allow(dead_code)] // TODO (#36): this will be needed for the 6-round presigning protocol. - pub(crate) el_gamal_sk: SecretBox, // `y_i` + pub(crate) el_gamal_sk: Secret, // `y_i` } #[derive(Debug, Clone)] @@ -84,7 +83,7 @@ pub(crate) struct PublicAuxInfoPrecomputed { pub struct KeyShareChange { pub(crate) owner: I, /// The value to be added to the secret share. - pub(crate) secret_share_change: SecretBox, // `x_i^* - x_i == \sum_{j} x_j^i` + pub(crate) secret_share_change: Secret, // `x_i^* - x_i == \sum_{j} x_j^i` /// The values to be added to the public shares of remote nodes. pub(crate) public_share_changes: BTreeMap, // `X_k^* - X_k == \sum_j X_j^k`, for all nodes // TODO (#27): this won't be needed when Scalar/Point are a part of `P` @@ -96,13 +95,13 @@ pub struct KeyShareChange { pub(crate) struct PresigningData { pub(crate) nonce: Scalar, // x-coordinate of $R$ /// An additive share of the ephemeral scalar. - pub(crate) ephemeral_scalar_share: SecretBox, // $k_i$ + pub(crate) ephemeral_scalar_share: Secret, // $k_i$ /// An additive share of `k * x` where `x` is the secret key. - pub(crate) product_share: SecretBox, + pub(crate) product_share: Secret, // Values generated during presigning, // kept in case we need to generate a proof of correctness. - pub(crate) product_share_nonreduced: Signed<::Uint>, + pub(crate) product_share_nonreduced: Secret::Uint>>, // $K_i$. pub(crate) cap_k: Ciphertext, @@ -113,9 +112,9 @@ pub(crate) struct PresigningData { #[derive(Debug, Clone)] pub(crate) struct PresigningValues { - pub(crate) hat_beta: SecretBox::Uint>>, - pub(crate) hat_r: RandomizerWire, - pub(crate) hat_s: RandomizerWire, + pub(crate) hat_beta: Secret::Uint>>, + 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, @@ -130,13 +129,11 @@ impl KeyShare { // TODO (#68): check that party_idx is the same for both, and the number of parties is the same assert_eq!(self.owner, change.owner); - let secret_share = SecretBox::new(Box::new( - self.secret_share.expose_secret() + change.secret_share_change.expose_secret(), - )); + let secret_share = self.secret_share + change.secret_share_change; let public_shares = self .public_shares .iter() - .map(|(id, public_share)| (id.clone(), public_share + &change.public_share_changes[id])) + .map(|(id, public_share)| (id.clone(), *public_share + change.public_share_changes[id])) .collect(); Self { @@ -154,12 +151,12 @@ impl KeyShare { ids: &BTreeSet, signing_key: Option<&k256::ecdsa::SigningKey>, ) -> BTreeMap { - let secret = match signing_key { + let secret = Secret::init_with(|| match signing_key { None => Scalar::random(rng), Some(sk) => Scalar::from(sk.as_nonzero_scalar()), - }; + }); - let secret_shares = secret.split(rng, ids.len()); + let secret_shares = secret_split(rng, secret, ids.len()); let public_shares = ids .iter() .zip(secret_shares.iter()) @@ -173,7 +170,7 @@ impl KeyShare { id.clone(), KeyShare { owner: id.clone(), - secret_share: SecretBox::new(Box::new(secret_share)), + secret_share, public_shares: public_shares.clone(), phantom: PhantomData, }, @@ -216,7 +213,7 @@ impl AuxInfo { let secret_aux = (0..ids.len()) .map(|_| SecretAuxInfo { paillier_sk: SecretKeyPaillierWire::::random(rng), - el_gamal_sk: SecretBox::new(Box::new(Scalar::random(rng))), + el_gamal_sk: Secret::init_with(|| Scalar::random(rng)), }) .collect::>(); @@ -228,7 +225,7 @@ impl AuxInfo { id.clone(), PublicAuxInfo { paillier_pk: secret.paillier_sk.public_key(), - el_gamal_pk: secret.el_gamal_sk.expose_secret().mul_by_generator(), + el_gamal_pk: secret.el_gamal_sk.mul_by_generator(), rp_params: RPParams::random(rng).to_wire(), }, ) diff --git a/synedrion/src/cggmp21/interactive_signing.rs b/synedrion/src/cggmp21/interactive_signing.rs index ecffde4..f84fc8f 100644 --- a/synedrion/src/cggmp21/interactive_signing.rs +++ b/synedrion/src/cggmp21/interactive_signing.rs @@ -2,7 +2,6 @@ //! in the paper ECDSA Pre-Signing (Fig. 7) and Signing (Fig. 8). use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, format, string::String, @@ -16,20 +15,22 @@ use manul::protocol::{ ReceiveError, Round, RoundId, Serializer, }; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; use serde::{Deserialize, Serialize}; use super::{ entities::{AuxInfo, AuxInfoPrecomputed, KeyShare, PresigningData, PresigningValues}, - params::SchemeParams, + params::{ + bounded_from_scalar, secret_bounded_from_scalar, secret_scalar_from_signed, secret_signed_from_scalar, + secret_uint_from_scalar, signed_from_scalar, SchemeParams, + }, sigma::{AffGProof, DecProof, EncProof, LogStarProof, MulProof, MulStarProof}, }; use crate::{ curve::{Point, RecoverableSignature, Scalar}, - paillier::{Ciphertext, CiphertextWire, PaillierParams, Randomizer, RandomizerWire}, + paillier::{Ciphertext, CiphertextWire, PaillierParams, Randomizer}, tools::{ hashing::{Chain, FofHasher, HashOutput}, - DowncastMap, Without, + DowncastMap, Secret, Without, }, uint::Signed, }; @@ -154,17 +155,17 @@ impl EntryPoint for InteractiveSigning { // TODO (#68): check that KeyShare is consistent with AuxInfo // The share of an ephemeral scalar - let k = Scalar::random(rng); + let k = Secret::init_with(|| Scalar::random(rng)); // The share of the mask used to generate the inverse of the ephemeral scalar - let gamma = Scalar::random(rng); + let gamma = Secret::init_with(|| Scalar::random(rng)); 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), &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, &P::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 { @@ -193,8 +194,8 @@ struct Context { other_ids: BTreeSet, key_share: KeyShare, aux_info: AuxInfoPrecomputed, - k: Scalar, - gamma: Scalar, + k: Secret, + gamma: Secret, rho: Randomizer, nu: Randomizer, } @@ -268,7 +269,7 @@ impl Round for Round1 { let aux = (&self.context.ssid_hash, &destination); let psi0 = EncProof::new( rng, - &P::signed_from_scalar(&self.context.k), + &secret_signed_from_scalar::

(&self.context.k), &self.context.rho, self.context.aux_info.secret_aux.paillier_sk.public_key(), &self.cap_k, @@ -388,12 +389,12 @@ struct Round2Message { #[derive(Debug, Clone)] struct Round2Artifact { - beta: SecretBox::Uint>>, - hat_beta: SecretBox::Uint>>, - r: RandomizerWire, - s: RandomizerWire, - hat_r: RandomizerWire, - hat_s: RandomizerWire, + beta: Secret::Uint>>, + hat_beta: Secret::Uint>>, + r: Randomizer, + s: Randomizer, + hat_r: Randomizer, + hat_s: Randomizer, cap_d: Ciphertext, cap_f: Ciphertext, hat_cap_d: Ciphertext, @@ -402,8 +403,8 @@ struct Round2Artifact { struct Round2Payload { cap_gamma: Point, - alpha: Signed<::Uint>, - hat_alpha: Signed<::Uint>, + alpha: Secret::Uint>>, + hat_alpha: Secret::Uint>>, cap_d: Ciphertext, hat_cap_d: Ciphertext, } @@ -440,31 +441,34 @@ impl Round for Round2 { let target_pk = &self.context.aux_info.public_aux[destination].paillier_pk; - let beta = SecretBox::new(Box::new(Signed::random_bounded_bits(rng, P::LP_BOUND))); - let hat_beta = SecretBox::new(Box::new(Signed::random_bounded_bits(rng, P::LP_BOUND))); + let beta = Secret::init_with(|| Signed::random_bounded_bits(rng, P::LP_BOUND)); + let hat_beta = Secret::init_with(|| Signed::random_bounded_bits(rng, P::LP_BOUND)); let r = Randomizer::random(rng, pk); let s = Randomizer::random(rng, target_pk); 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) - + 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 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.expose_secret(), &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] - * 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()); + * secret_signed_from_scalar::

(&self.context.key_share.secret_share) + + 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; let psi = AffGProof::new( rng, - &P::signed_from_scalar(&self.context.gamma), + &gamma, &beta, - s.clone(), - r.clone(), + &s, + &r, target_pk, pk, &self.all_cap_k[destination], @@ -477,10 +481,10 @@ 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(), + &hat_s, + &hat_r, target_pk, pk, &self.all_cap_k[destination], @@ -493,7 +497,7 @@ impl Round for Round2 { let hat_psi_prime = LogStarProof::new( rng, - &P::signed_from_scalar(&self.context.gamma), + &gamma, &self.context.nu, pk, &self.all_cap_g[&self.context.my_id], @@ -520,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, @@ -607,12 +611,18 @@ impl Round for Round2 { // `alpha == x * y + z` where `0 <= x, y < q`, and `-2^l' <= z <= 2^l'`, // where `q` is the curve order. // We will need this bound later, so we're asserting it. - let alpha = alpha - .assert_bit_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1) - .ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsAlpha))?; - let hat_alpha = hat_alpha - .assert_bit_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1) - .ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsHatAlpha))?; + let alpha = Secret::try_init_with(|| { + alpha + .expose_secret() + .assert_bit_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1) + .ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsAlpha)) + })?; + let hat_alpha = Secret::try_init_with(|| { + hat_alpha + .expose_secret() + .assert_bit_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1) + .ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsHatAlpha)) + })?; Ok(Payload::new(Round2Payload::

{ cap_gamma: direct_message.cap_gamma, @@ -635,22 +645,21 @@ impl Round for Round2 { let cap_gamma = payloads.values().map(|payload| payload.cap_gamma).sum::() + self.context.gamma.mul_by_generator(); - let cap_delta = cap_gamma * self.context.k; + let cap_delta = cap_gamma * &self.context.k; - 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) * P::signed_from_scalar(&self.context.k) + alpha_sum + beta_sum; + let alpha_sum: Secret> = payloads.values().map(|payload| &payload.alpha).sum(); + let beta_sum: Secret> = artifacts.values().map(|artifact| &artifact.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 = P::signed_from_scalar(self.context.key_share.secret_share.expose_secret()) - * P::signed_from_scalar(&self.context.k) - + hat_alpha_sum - + hat_beta_sum; + let hat_alpha_sum: Secret> = payloads.values().map(|payload| &payload.hat_alpha).sum(); + let hat_beta_sum: Secret> = artifacts.values().map(|artifact| &artifact.hat_beta).sum(); + 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; let (cap_ds, hat_cap_ds) = payloads .into_iter() @@ -675,8 +684,8 @@ impl Round for Round2 { #[derive(Debug)] struct Round3 { context: Context, - delta: Signed<::Uint>, - chi: Signed<::Uint>, + delta: Secret::Uint>>, + chi: Secret::Uint>>, cap_delta: Point, cap_gamma: Point, all_cap_k: BTreeMap>, @@ -690,13 +699,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, } @@ -733,7 +742,7 @@ impl Round for Round3 { let psi_pprime = LogStarProof::new( rng, - &P::signed_from_scalar(&self.context.k), + &secret_signed_from_scalar::

(&self.context.k), &self.context.rho, pk, &self.all_cap_k[&self.context.my_id], @@ -746,7 +755,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, }, @@ -805,12 +814,12 @@ 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::(); + 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.mul_by_generator() == assembled_cap_delta { - let inv_delta: Scalar = Option::from(assembled_delta.invert()).ok_or_else(|| { + let inv_delta = 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. ", @@ -839,8 +848,8 @@ impl Round for Round3 { let presigning_data = PresigningData { nonce, - ephemeral_scalar_share: SecretBox::new(Box::new(self.context.k)), - product_share: SecretBox::new(Box::new(P::scalar_from_signed(&self.chi))), + ephemeral_scalar_share: self.context.k.clone(), + product_share: secret_scalar_from_signed::

(&self.chi), product_share_nonreduced: self.chi, cap_k: self.all_cap_k[&my_id].clone(), values, @@ -878,10 +887,10 @@ impl Round for Round3 { let p_aff_g = AffGProof::

::new( rng, - &P::signed_from_scalar(&self.context.gamma), + &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], @@ -910,12 +919,12 @@ impl Round for Round3 { // Mul proof let rho = Randomizer::random(rng, pk); - let cap_h = (&self.all_cap_g[&self.context.my_id] * P::bounded_from_scalar(&self.context.k)) - .mul_randomizer(&rho.to_wire()); + let cap_h = (&self.all_cap_g[&self.context.my_id] * secret_bounded_from_scalar::

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

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

(&self.context.k), &self.context.rho, &rho, pk, @@ -957,14 +966,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 @@ -991,8 +1000,8 @@ where { fn new(context: Context, presigning: PresigningData) -> Self { let r = presigning.nonce; - let sigma = presigning.ephemeral_scalar_share.expose_secret() * &context.message - + r * presigning.product_share.expose_secret(); + let sigma = + *(&presigning.ephemeral_scalar_share * context.message + &presigning.product_share * r).expose_secret(); Self { context, presigning, @@ -1104,10 +1113,10 @@ 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), + &values.hat_s, + &values.hat_r, target_pk, pk, &values.cap_k, @@ -1139,8 +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 * P::bounded_from_scalar(x.expose_secret())).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); @@ -1149,7 +1157,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, @@ -1185,15 +1193,15 @@ impl Round for Round4 { let r = self.presigning.nonce; - let ciphertext = ciphertext * P::bounded_from_scalar(&r) - + &self.presigning.cap_k * P::bounded_from_scalar(&self.context.message); + let ciphertext = ciphertext * bounded_from_scalar::

(&r) + + &self.presigning.cap_k * bounded_from_scalar::

(&self.context.message); 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()) - * P::signed_from_scalar(&self.context.message) - + self.presigning.product_share_nonreduced * P::signed_from_scalar(&r); + let s_part_nonreduced = secret_signed_from_scalar::

(&self.presigning.ephemeral_scalar_share) + * signed_from_scalar::

(&self.context.message) + + self.presigning.product_share_nonreduced.clone() * 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 fbf8bb8..f862461 100644 --- a/synedrion/src/cggmp21/key_init.rs +++ b/synedrion/src/cggmp21/key_init.rs @@ -3,7 +3,6 @@ //! auxiliary parameters need to be generated as well (during the KeyRefresh protocol). use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, format, string::String, @@ -17,7 +16,6 @@ use manul::protocol::{ ReceiveError, Round, RoundId, Serializer, }; use rand_core::CryptoRngCore; -use secrecy::SecretBox; use serde::{Deserialize, Serialize}; use super::{ @@ -30,7 +28,7 @@ use crate::{ tools::{ bitvec::BitVec, hashing::{Chain, FofHasher, HashOutput}, - DowncastMap, Without, + DowncastMap, Secret, Without, }, }; @@ -143,7 +141,7 @@ impl EntryPoint for KeyInit { .finalize(); // The secret share - let x = Scalar::random(rng); + let x = Secret::init_with(|| Scalar::random(rng)); // The public share let cap_x = x.mul_by_generator(); @@ -177,7 +175,7 @@ impl EntryPoint for KeyInit { struct Context { other_ids: BTreeSet, my_id: I, - x: Scalar, + x: Secret, tau: SchSecret, public_data: PublicData

, sid_hash: HashOutput, @@ -451,7 +449,7 @@ impl Round for Round3 { public_shares.insert(my_id.clone(), self.context.public_data.cap_x); Ok(FinalizeOutcome::Result(KeyShare { owner: my_id, - secret_share: SecretBox::new(Box::new(self.context.x)), + secret_share: self.context.x, public_shares, phantom: PhantomData, })) @@ -467,7 +465,6 @@ mod tests { session::signature::Keypair, }; use rand_core::OsRng; - use secrecy::ExposeSecret; use super::KeyInit; use crate::cggmp21::TestParams; @@ -508,7 +505,7 @@ mod tests { let public_from_secret = shares .into_iter() - .map(|(id, share)| (id, share.secret_share.expose_secret().mul_by_generator())) + .map(|(id, share)| (id, share.secret_share.mul_by_generator())) .collect(); assert!(public_set == &public_from_secret); diff --git a/synedrion/src/cggmp21/key_refresh.rs b/synedrion/src/cggmp21/key_refresh.rs index 9ade4fb..808550a 100644 --- a/synedrion/src/cggmp21/key_refresh.rs +++ b/synedrion/src/cggmp21/key_refresh.rs @@ -3,7 +3,6 @@ //! for ZK proofs (e.g. Paillier keys). use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, format, string::String, @@ -18,24 +17,23 @@ use manul::protocol::{ ReceiveError, Round, RoundId, Serializer, }; use rand_core::CryptoRngCore; -use secrecy::SecretBox; use serde::{Deserialize, Serialize}; use super::{ entities::{AuxInfo, KeyShareChange, PublicAuxInfo, SecretAuxInfo}, - params::SchemeParams, + params::{secret_scalar_from_uint, secret_uint_from_scalar, SchemeParams}, sigma::{FacProof, ModProof, PrmProof, SchCommitment, SchProof, SchSecret}, }; use crate::{ - curve::{Point, Scalar}, + 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, hashing::{Chain, FofHasher, HashOutput}, - DowncastMap, Without, + DowncastMap, Secret, Without, }, }; @@ -71,7 +69,7 @@ enum KeyRefreshErrorEnum { Round3MismatchedSecret { cap_c: CiphertextWire, x: Scalar, - mu: RandomizerWire, + mu: ::Uint, }, } @@ -160,7 +158,7 @@ impl EntryPoint for KeyRefresh { let paillier_pk = paillier_sk.public_key(); // El-Gamal key - let y = Scalar::random(rng); + let y = Secret::init_with(|| Scalar::random(rng)); let cap_y = y.mul_by_generator(); // The secret and the commitment for the Schnorr PoK of the El-Gamal key @@ -172,7 +170,11 @@ impl EntryPoint for KeyRefresh { .all_ids .iter() .cloned() - .zip(Scalar::ZERO.split(rng, self.all_ids.len())) + .zip(secret_split( + rng, + Secret::init_with(|| Scalar::ZERO), + self.all_ids.len(), + )) .collect::>(); // Public counterparts of secret share updates ($X_i^j$ where $i$ is this party's index). @@ -262,8 +264,8 @@ struct PublicData1Precomp { #[derive(Debug)] struct Context { paillier_sk: SecretKeyPaillier, - y: Scalar, - x_to_send: BTreeMap, // $x_i^j$ where $i$ is this party's index + y: Secret, + x_to_send: BTreeMap>, // $x_i^j$ where $i$ is this party's index tau_y: SchSecret, tau_x: BTreeMap, data_precomp: PublicData1Precomp

, @@ -558,7 +560,7 @@ struct Round3Message { } struct Round3Payload { - x: Scalar, // $x_j^i$, a secret share change received from the party $j$ + x: Secret, // $x_j^i$, a secret share change received from the party $j$ } impl Round for Round3 { @@ -601,13 +603,13 @@ impl Round for Round3 { let destination_idx = self.context.ids_ordering[destination]; - let x_secret = self.context.x_to_send[destination]; + 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)); + 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, + x_secret, &self.context.data_precomp.data.cap_a_to_send[destination_idx], &x_public, &aux, @@ -648,7 +650,7 @@ impl Round for Round3 { .paillier_enc_x .to_precomputed(&self.context.data_precomp.paillier_pk); - let x = P::scalar_from_uint(&enc_x.decrypt(&self.context.paillier_sk)); + let x = secret_scalar_from_uint::

(&enc_x.decrypt(&self.context.paillier_sk)); let my_idx = self.context.ids_ordering[&self.context.my_id]; @@ -657,8 +659,8 @@ impl Round for Round3 { return Err(ReceiveError::protocol(KeyRefreshError( KeyRefreshErrorEnum::Round3MismatchedSecret { cap_c: direct_message.data2.paillier_enc_x, - x, - mu: mu.to_wire(), + x: *x.expose_secret(), + mu: mu.expose(), }, ))); } @@ -717,7 +719,7 @@ impl Round for Round3 { .collect::>(); // The combined secret share change - let x_star = others_x.values().sum::() + self.context.x_to_send[&self.context.my_id]; + let x_star = others_x.into_values().sum::>() + &self.context.x_to_send[&self.context.my_id]; let my_id = self.context.my_id.clone(); let mut all_ids = self.context.other_ids; @@ -754,12 +756,12 @@ impl Round for Round3 { let secret_aux = SecretAuxInfo { paillier_sk: self.context.paillier_sk.into_wire(), - el_gamal_sk: SecretBox::new(Box::new(self.context.y)), + el_gamal_sk: self.context.y, }; let key_share_change = KeyShareChange { owner: my_id.clone(), - secret_share_change: SecretBox::new(Box::new(x_star)), + secret_share_change: x_star, public_share_changes: cap_x_star, phantom: PhantomData, }; @@ -784,7 +786,6 @@ mod tests { session::signature::Keypair, }; use rand_core::OsRng; - use secrecy::ExposeSecret; use super::KeyRefresh; use crate::{cggmp21::TestParams, curve::Scalar}; @@ -819,7 +820,7 @@ mod tests { for (id, change) in changes.iter() { for other_change in changes.values() { assert_eq!( - change.secret_share_change.expose_secret().mul_by_generator(), + change.secret_share_change.mul_by_generator(), other_change.public_share_changes[id] ); } @@ -828,7 +829,7 @@ mod tests { for (id, aux_info) in aux_infos.iter() { for other_aux_info in aux_infos.values() { assert_eq!( - aux_info.secret_aux.el_gamal_sk.expose_secret().mul_by_generator(), + aux_info.secret_aux.el_gamal_sk.mul_by_generator(), other_aux_info.public_aux[id].el_gamal_pk ); } diff --git a/synedrion/src/cggmp21/params.rs b/synedrion/src/cggmp21/params.rs index d437a0b..4c8e4d6 100644 --- a/synedrion/src/cggmp21/params.rs +++ b/synedrion/src/cggmp21/params.rs @@ -10,7 +10,10 @@ use serde::{Deserialize, Serialize}; 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, @@ -123,73 +126,143 @@ pub trait SchemeParams: Debug + Clone + Send + PartialEq + Eq + Send + Sync + 's /// plus one bit (so that any curve scalar still represents a positive value /// when treated as a 2-complement signed integer). type Paillier: PaillierParams; +} - /// Converts a curve scalar to the associated integer type. - fn uint_from_scalar(value: &Scalar) -> ::Uint { - let scalar_bytes = value.to_bytes(); - let mut repr = ::Uint::zero().to_be_bytes(); +/// Converts a curve scalar to the associated integer type. +pub(crate) fn uint_from_scalar(value: &Scalar) -> ::Uint { + let scalar_bytes = value.to_bytes(); + let mut repr = ::Uint::zero().to_be_bytes(); - let uint_len = repr.as_ref().len(); - let scalar_len = scalar_bytes.len(); + let uint_len = repr.as_ref().len(); + let scalar_len = scalar_bytes.len(); - debug_assert!(uint_len >= scalar_len); - repr.as_mut()[uint_len - scalar_len..].copy_from_slice(&scalar_bytes); - ::Uint::from_be_bytes(repr) - } + debug_assert!(uint_len >= scalar_len); + repr.as_mut()[uint_len - scalar_len..].copy_from_slice(&scalar_bytes); + ::Uint::from_be_bytes(repr) +} - /// Converts a curve scalar to the associated integer type, wrapped in `Bounded`. - fn bounded_from_scalar(value: &Scalar) -> Bounded<::Uint> { - Bounded::new(Self::uint_from_scalar(value), 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`" - ]) - } +/// Converts a curve scalar to the associated integer type, wrapped in `Bounded`. +pub(crate) fn bounded_from_scalar(value: &Scalar) -> Bounded<::Uint> { + Bounded::new(uint_from_scalar::

(value), 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`" + ]) +} - /// Converts a curve scalar to the associated integer type, wrapped in `Signed`. - fn signed_from_scalar(value: &Scalar) -> Signed<::Uint> { - Self::bounded_from_scalar(value).into_signed().expect(concat![ - "a curve scalar value is smaller than the half of `PaillierParams::Uint` range, ", - "so it is still positive when treated as a 2-complement signed value" - ]) - } +/// Converts a curve scalar to the associated integer type, wrapped in `Signed`. +pub(crate) fn signed_from_scalar(value: &Scalar) -> Signed<::Uint> { + bounded_from_scalar::

(value).into_signed().expect(concat![ + "a curve scalar value is smaller than the half of `PaillierParams::Uint` range, ", + "so it is still positive when treated as a 2-complement signed value" + ]) +} - /// Converts an integer to the associated curve scalar type. - fn scalar_from_uint(value: &::Uint) -> Scalar { - let r = *value % Self::CURVE_ORDER; +/// Converts an integer to the associated curve scalar type. +pub(crate) fn scalar_from_uint(value: &::Uint) -> Scalar { + let r = *value % P::CURVE_ORDER; - let repr = r.to_be_bytes(); - let uint_len = repr.as_ref().len(); - let scalar_len = Scalar::repr_len(); + let repr = r.to_be_bytes(); + let uint_len = repr.as_ref().len(); + let scalar_len = Scalar::repr_len(); - // Can unwrap here since the value is within the Scalar range - Scalar::try_from_bytes(&repr.as_ref()[uint_len - scalar_len..]) - .expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar") - } + // Can unwrap here since the value is within the Scalar range + Scalar::try_from_bytes(&repr.as_ref()[uint_len - scalar_len..]) + .expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar") +} - /// Converts a `Signed`-wrapped integer to the associated curve scalar type. - fn scalar_from_signed(value: &Signed<::Uint>) -> Scalar { - let abs_value = Self::scalar_from_uint(&value.abs()); - Scalar::conditional_select(&abs_value, &-abs_value, value.is_negative()) - } +/// Converts a `Signed`-wrapped integer to the associated curve scalar type. +pub(crate) fn scalar_from_signed(value: &Signed<::Uint>) -> Scalar { + let abs_value = scalar_from_uint::

(&value.abs()); + Scalar::conditional_select(&abs_value, &-abs_value, value.is_negative()) +} + +/// Converts a wide integer to the associated curve scalar type. +pub(crate) fn scalar_from_wide_uint(value: &::WideUint) -> Scalar { + let r = *value % P::CURVE_ORDER_WIDE; + + let repr = r.to_be_bytes(); + let uint_len = repr.as_ref().len(); + let scalar_len = Scalar::repr_len(); + + // Can unwrap here since the value is within the Scalar range + Scalar::try_from_bytes(&repr.as_ref()[uint_len - scalar_len..]) + .expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar") +} + +/// Converts a `Signed`-wrapped wide integer to the associated curve scalar type. +pub(crate) fn scalar_from_wide_signed( + value: &Signed<::WideUint>, +) -> Scalar { + let abs_value = scalar_from_wide_uint::

(&value.abs()); + Scalar::conditional_select(&abs_value, &-abs_value, value.is_negative()) +} - /// Converts a wide integer to the associated curve scalar type. - fn scalar_from_wide_uint(value: &::WideUint) -> Scalar { - let r = *value % Self::CURVE_ORDER_WIDE; +pub(crate) fn secret_scalar_from_uint( + value: &Secret<::Uint>, +) -> Secret { + let r = value % &P::CURVE_ORDER; - let repr = r.to_be_bytes(); - let uint_len = repr.as_ref().len(); - let scalar_len = Scalar::repr_len(); + let repr = Secret::init_with(|| r.expose_secret().to_be_bytes()); + let uint_len = repr.expose_secret().as_ref().len(); + let scalar_len = Scalar::repr_len(); - // Can unwrap here since the value is within the Scalar range - Scalar::try_from_bytes(&repr.as_ref()[uint_len - scalar_len..]) + // Can unwrap here since the value is within the Scalar range + Secret::init_with(|| { + Scalar::try_from_bytes(&repr.expose_secret().as_ref()[uint_len - scalar_len..]) .expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar") - } + }) +} - /// Converts a `Signed`-wrapped wide integer to the associated curve scalar type. - fn scalar_from_wide_signed(value: &Signed<::WideUint>) -> Scalar { - let abs_value = Self::scalar_from_wide_uint(&value.abs()); - Scalar::conditional_select(&abs_value, &-abs_value, value.is_negative()) - } +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(|| ::Uint::from_be_bytes(*repr.expose_secret())) +} + +pub(crate) fn secret_signed_from_scalar( + value: &Secret, +) -> Secret::Uint>> { + Secret::init_with(|| { + Signed::new_positive( + *secret_uint_from_scalar::

(value).expose_secret(), + 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`" + ]) + }) +} + +pub(crate) fn secret_bounded_from_scalar( + value: &Secret, +) -> Secret::Uint>> { + Secret::init_with(|| { + Bounded::new( + *secret_uint_from_scalar::

(value).expose_secret(), + 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`" + ]) + }) +} + +pub(crate) fn secret_scalar_from_signed( + value: &Secret::Uint>>, +) -> Secret { + // TODO: wrap in secrets properly + let abs_value = 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 { diff --git a/synedrion/src/cggmp21/sigma/aff_g.rs b/synedrion/src/cggmp21/sigma/aff_g.rs index 016511d..fc14cd4 100644 --- a/synedrion/src/cggmp21/sigma/aff_g.rs +++ b/synedrion/src/cggmp21/sigma/aff_g.rs @@ -1,17 +1,22 @@ //! Paillier Affine Operation with Group Commitment in Range ($\Pi^{aff-g}$, Section 6.2, Fig. 15) use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, secret_scalar_from_signed}, + SchemeParams, +}; 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}, + Secret, }, - tools::hashing::{Chain, Hashable, XofHasher}, uint::Signed, }; @@ -53,18 +58,18 @@ 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

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, - y: &SecretBox::Uint>>, - rho: Randomizer, - rho_y: Randomizer, + x: &Secret::Uint>>, + y: &Secret::Uint>>, + rho: &Randomizer, + rho_y: &Randomizer, pk0: &PublicKeyPaillier, pk1: &PublicKeyPaillier, cap_c: &Ciphertext, @@ -74,7 +79,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); @@ -82,20 +87,20 @@ 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); + let r = Randomizer::random(rng, pk0); + let r_y = Randomizer::random(rng, pk1); - 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 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 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_b_x = P::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_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).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(); @@ -103,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 @@ -127,25 +132,25 @@ impl AffGProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let e_wide = e.into_wide(); + let e_wide = e.to_wide(); - let z1 = alpha + e * x; + let z1 = *(alpha + x * e).expose_secret(); // 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(); - let z3 = gamma + e_wide * m; - let z4 = delta + e_wide * mu; + let z3 = *(gamma + m * e_wide).expose_secret(); + let z4 = *(delta + mu * e_wide).expose_secret(); - let omega = (r_mod * rho.pow_signed_vartime(&e)).to_wire(); + 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 = (r_y_mod * rho_y.pow_signed_vartime(&-e)).to_wire(); + let omega_y = rho_y.to_masked(&r_y, &-e); Self { e, @@ -220,14 +225,14 @@ 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_public_with_randomizer_signed(pk0, &self.z2, &self.omega) != cap_d * e + self.cap_a.to_precomputed(pk0) { return false; } // g^{z_1} = B_x X^e - if P::scalar_from_signed(&self.z1).mul_by_generator() != self.cap_b_x + cap_x * &P::scalar_from_signed(&e) { + if scalar_from_signed::

(&self.z1).mul_by_generator() != self.cap_b_x + cap_x * &scalar_from_signed::

(&e) { return false; } @@ -236,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_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(&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(&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; } @@ -263,12 +268,12 @@ impl AffGProof

{ #[cfg(test)] mod tests { use rand_core::OsRng; - use secrecy::{ExposeSecret, SecretBox}; 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, }; @@ -287,20 +292,20 @@ mod tests { let aux: &[u8] = b"abcde"; - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); - let y = SecretBox::new(Box::new(Signed::random_bounded_bits(&mut OsRng, Params::LP_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); + 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 b15b5fb..a241010 100644 --- a/synedrion/src/cggmp21/sigma/dec.rs +++ b/synedrion/src/cggmp21/sigma/dec.rs @@ -3,14 +3,18 @@ use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, scalar_from_wide_signed, secret_scalar_from_signed}, + SchemeParams, +}; 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, uint::Signed, }; @@ -39,14 +43,14 @@ pub(crate) struct DecProof { gamma: Scalar, z1: Signed<::WideUint>, z2: Signed<::WideUint>, - omega: RandomizerWire, + omega: MaskedRandomizer, } 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,15 +62,18 @@ 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 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 alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); + let mu = Secret::init_with(|| 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 + P::EPS_BOUND, hat_cap_n)); let r = Randomizer::random(rng, pk0); 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); + 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. + let gamma = *secret_scalar_from_signed::

(&alpha).expose_secret(); let mut reader = XofHasher::new_with_dst(HASH_TAG) // commitments @@ -88,10 +95,10 @@ 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 z2 = nu + e.into_wide() * mu; + let z1 = *(alpha.to_wide() + y.mul_wide(&e)).expose_secret(); + let z2 = *(nu + mu * e.to_wide()).expose_secret(); - let omega = (r * rho.pow_signed_vartime(&e)).to_wire(); + let omega = rho.to_masked(&r, &e); Self { e, @@ -137,21 +144,21 @@ impl DecProof

{ } // enc(z_1, \omega) == A (+) C (*) e - if Ciphertext::new_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; } // z_1 == \gamma + e x \mod q - if P::scalar_from_wide_signed(&self.z1) != self.gamma + P::scalar_from_signed(&e) * *x { + if scalar_from_wide_signed::

(&self.z1) != self.gamma + scalar_from_signed::

(&e) * *x { return false; } // 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) { + 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; } @@ -165,8 +172,9 @@ mod tests { use super::DecProof; use crate::{ - cggmp21::{SchemeParams, TestParams}, + cggmp21::{params::scalar_from_signed, SchemeParams, TestParams}, paillier::{Ciphertext, PaillierParams, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -183,11 +191,11 @@ 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 = 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 0c89665..aab28e3 100644 --- a/synedrion/src/cggmp21/sigma/enc.rs +++ b/synedrion/src/cggmp21/sigma/enc.rs @@ -6,10 +6,11 @@ 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, uint::Signed, }; @@ -34,34 +35,34 @@ pub(crate) struct EncProof { cap_a: CiphertextWire, cap_c: RPCommitmentWire, z1: Signed<::Uint>, - z2: RandomizerWire, + z2: MaskedRandomizer, z3: Signed<::WideUint>, } 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 mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); + let mu = Secret::init_with(|| 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 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) @@ -79,9 +80,9 @@ impl EncProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * k; - let z2 = (r * rho.pow_signed_vartime(&e)).to_wire(); - let z3 = gamma + mu * e.into_wide(); + let z1 = *(alpha + k * e).expose_secret(); + let z2 = rho.to_masked(&r, &e); + let z3 = *(gamma + mu * e.to_wide()).expose_secret(); Self { e, @@ -128,15 +129,15 @@ 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_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(&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; } @@ -152,6 +153,7 @@ mod tests { use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -167,9 +169,9 @@ 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()); + 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 ca88d62..740c992 100644 --- a/synedrion/src/cggmp21/sigma/fac.rs +++ b/synedrion/src/cggmp21/sigma/fac.rs @@ -1,13 +1,15 @@ //! No small factor proof ($\Pi^{fac}$, Section C.5, Fig. 28) use rand_core::CryptoRngCore; -use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::super::SchemeParams; use crate::{ paillier::{PaillierParams, PublicKeyPaillier, RPCommitmentWire, RPParams, SecretKeyPaillier}, - tools::hashing::{Chain, Hashable, XofHasher}, + tools::{ + hashing::{Chain, Hashable, XofHasher}, + Secret, + }, uint::{Bounded, Integer, Signed}, }; @@ -62,32 +64,36 @@ 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 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 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 = Secret::init_with(|| 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); let sigma = Signed::<::Uint>::random_bounded_bits_scaled_wide(rng, P::L_BOUND, &scale); - let r = Signed::<::Uint>::random_bounded_bits_scaled_wide( - rng, - P::L_BOUND + P::EPS_BOUND, - &scale, - ); - let x = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); - let y = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); + let r = Secret::init_with(|| { + Signed::<::Uint>::random_bounded_bits_scaled_wide( + rng, + P::L_BOUND + P::EPS_BOUND, + &scale, + ) + }); + let x = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); + let y = Secret::init_with(|| Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n)); 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(&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_signed_wide(&alpha) * &setup.commit_zero_xwide(&r)).to_wire(); let cap_q = cap_q.to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) @@ -106,16 +112,16 @@ impl FacProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let e_wide = e.into_wide(); + let e_wide = e.to_wide(); 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 omega1 = x + e_wide * mu; - let omega2 = y + e_wide * nu; - let v = r + (e_wide.into_wide() * hat_sigma); + let hat_sigma = sigma - (p_wide * &nu).expose_secret().to_wide(); + let z1 = *(alpha + (p * e).to_wide()).expose_secret(); + let z2 = *(beta + (q * e).to_wide()).expose_secret(); + let omega1 = *(x + mu * e_wide).expose_secret(); + let omega2 = *(nu * e_wide + &y).expose_secret(); + let v = *(r + &(hat_sigma * e_wide.to_wide())).expose_secret(); Self { e, @@ -161,26 +167,26 @@ impl FacProof

{ } // R = s^{N_0} t^\sigma - let cap_r = &setup.commit_xwide(&pk0.modulus_bounded().into(), &self.sigma); + 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_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_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(&self.z1) * &setup.commit_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 7c9b1f6..287fa44 100644 --- a/synedrion/src/cggmp21/sigma/log_star.rs +++ b/synedrion/src/cggmp21/sigma/log_star.rs @@ -3,14 +3,18 @@ use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, secret_scalar_from_signed}, + SchemeParams, +}; 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, uint::Signed, }; @@ -38,7 +42,7 @@ pub(crate) struct LogStarProof { cap_y: Point, cap_d: RPCommitmentWire, z1: Signed<::Uint>, - z2: RandomizerWire, + z2: MaskedRandomizer, z3: Signed<::WideUint>, } @@ -46,7 +50,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 +59,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 mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); + let mu = Secret::init_with(|| 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 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_y = g * &P::scalar_from_signed(&alpha); + 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(); let mut reader = XofHasher::new_with_dst(HASH_TAG) @@ -88,9 +92,9 @@ impl LogStarProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * x; - let z2 = (r * rho.pow_signed_vartime(&e)).to_wire(); - let z3 = gamma + mu * e.into_wide(); + let z1 = *(alpha + x * e).expose_secret(); + let z2 = rho.to_masked(&r, &e); + let z3 = *(gamma + mu * e.to_wide()).expose_secret(); Self { e, @@ -144,20 +148,20 @@ 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_public_with_randomizer_signed(pk0, &self.z1, &self.z2); if c != self.cap_a.to_precomputed(pk0) + cap_c * e { return false; } // g^{z_1} == Y X^e - if g * &P::scalar_from_signed(&self.z1) != self.cap_y + cap_x * &P::scalar_from_signed(&e) { + if g * &scalar_from_signed::

(&self.z1) != self.cap_y + cap_x * &scalar_from_signed::

(&e) { return false; } // 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) { + 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; } @@ -171,9 +175,10 @@ mod tests { 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 +195,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_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); assert!(proof.verify(pk, &cap_c, &g, &cap_x, &setup, &aux)); diff --git a/synedrion/src/cggmp21/sigma/mod_.rs b/synedrion/src/cggmp21/sigma/mod_.rs index d687ca6..051fc55 100644 --- a/synedrion/src/cggmp21/sigma/mod_.rs +++ b/synedrion/src/cggmp21/sigma/mod_.rs @@ -4,7 +4,6 @@ use alloc::vec::Vec; use crypto_bigint::Square; use rand_core::CryptoRngCore; -use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::super::SchemeParams; diff --git a/synedrion/src/cggmp21/sigma/mul.rs b/synedrion/src/cggmp21/sigma/mul.rs index 29414de..1f141a9 100644 --- a/synedrion/src/cggmp21/sigma/mul.rs +++ b/synedrion/src/cggmp21/sigma/mul.rs @@ -5,9 +5,12 @@ use serde::{Deserialize, Serialize}; use super::super::SchemeParams; use crate::{ - paillier::{Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, Randomizer, RandomizerWire}, - tools::hashing::{Chain, Hashable, XofHasher}, - uint::{Bounded, Retrieve, Signed}, + paillier::{Ciphertext, CiphertextWire, MaskedRandomizer, PaillierParams, PublicKeyPaillier, Randomizer}, + tools::{ + hashing::{Chain, Hashable, XofHasher}, + Secret, + }, + uint::{Bounded, Signed}, }; const HASH_TAG: &[u8] = b"P_mul"; @@ -18,8 +21,8 @@ pub(crate) struct MulProof { cap_a: CiphertextWire, cap_b: CiphertextWire, z: Signed<::WideUint>, - u: RandomizerWire, - v: RandomizerWire, + u: MaskedRandomizer, + v: MaskedRandomizer, } /** @@ -42,7 +45,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, @@ -55,17 +58,17 @@ impl MulProof

{ assert_eq!(cap_y.public_key(), pk); assert_eq!(cap_c.public_key(), pk); - let alpha_mod = pk.random_invertible_residue(rng); - let r_mod = Randomizer::random(rng, pk); - let s_mod = Randomizer::random(rng, pk); + let alpha_uint = Secret::init_with(|| pk.random_invertible_residue(rng)); + let alpha = Secret::init_with(|| { + Bounded::new(*alpha_uint.expose_secret(), ::MODULUS_BITS) + .expect("the value is bounded by `MODULUS_BITS` by construction") + }); - let alpha = 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 r = Randomizer::random(rng, pk); + let s = Randomizer::random(rng, pk); - 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,13 +85,14 @@ impl MulProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z = alpha - .into_wide() - .into_signed() + let z = *(alpha + .to_wide() + .to_signed() .expect("conversion to `WideUint` provides enough space for a sign bit") - + e.mul_wide(x); - let u = (r_mod * rho.pow_signed_vartime(&e)).to_wire(); - let v = (s_mod * rho_x.pow_signed_vartime(&e)).to_wire(); + + x.mul_wide(&e)) + .expose_secret(); + let u = rho.to_masked(&r, &e); + let v = rho_x.to_masked(&s, &e); Self { e, @@ -132,13 +136,17 @@ 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_with_randomizer_wide(pk, &self.z, &self.v) != self.cap_b.to_precomputed(pk) + cap_x * e { + if Ciphertext::new_public_with_randomizer_wide(pk, &self.z, &self.v) + != self.cap_b.to_precomputed(pk) + cap_x * e + { return false; } @@ -154,6 +162,7 @@ mod tests { use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -167,14 +176,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_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 f48e15c..f43d120 100644 --- a/synedrion/src/cggmp21/sigma/mul_star.rs +++ b/synedrion/src/cggmp21/sigma/mul_star.rs @@ -3,14 +3,18 @@ use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, secret_scalar_from_signed}, + SchemeParams, +}; 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, uint::Signed, }; @@ -39,14 +43,14 @@ pub(crate) struct MulStarProof { cap_s: RPCommitmentWire, z1: Signed<::Uint>, z2: Signed<::WideUint>, - omega: RandomizerWire, + omega: MaskedRandomizer, } 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 +66,19 @@ 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 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 alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); + 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_b_x = P::scalar_from_signed(&alpha).mul_by_generator(); + 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(); @@ -96,9 +100,9 @@ impl MulStarProof

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * x; - let z2 = gamma + e.into_wide() * m; - let omega = (r * rho.pow_signed(&e)).to_wire(); + let z1 = *(alpha + x * e).expose_secret(); + let z2 = *(gamma + m * e.to_wide()).expose_secret(); + let omega = rho.to_masked(&r, &e); Self { e, @@ -154,19 +158,19 @@ 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; } // g^{z_1} == B_x X^e - if P::scalar_from_signed(&self.z1).mul_by_generator() != self.cap_b_x + cap_x * &P::scalar_from_signed(&e) { + if scalar_from_signed::

(&self.z1).mul_by_generator() != self.cap_b_x + cap_x * &scalar_from_signed::

(&e) { return false; } // 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) { + 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; } @@ -180,8 +184,9 @@ mod tests { 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 +202,12 @@ 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); + 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); assert!(proof.verify(pk, &cap_c, &cap_d, &cap_x, &setup, &aux)); diff --git a/synedrion/src/cggmp21/sigma/prm.rs b/synedrion/src/cggmp21/sigma/prm.rs index bf799de..2687d67 100644 --- a/synedrion/src/cggmp21/sigma/prm.rs +++ b/synedrion/src/cggmp21/sigma/prm.rs @@ -7,7 +7,6 @@ use alloc::{vec, vec::Vec}; use digest::XofReader; use rand_core::CryptoRngCore; -use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::super::SchemeParams; @@ -92,7 +91,7 @@ impl PrmProof

{ ) -> Self { debug_assert!(&secret.modulus() == setup.modulus()); let proof_secret = PrmSecret::

::random(rng, secret); - let commitment = PrmCommitment::new(&proof_secret, setup.base()); + let commitment = PrmCommitment::new(&proof_secret, setup.base_randomizer()); let totient = secret.totient_nonzero(); let challenge = PrmChallenge::new(&commitment, setup, aux); @@ -126,8 +125,8 @@ impl PrmProof

{ let z = self.proof[i]; let e = challenge.0[i]; let a = self.commitment.0[i].to_montgomery(monty_params); - let pwr = setup.base().pow_bounded(&z); - let test = if e { pwr == a * setup.power() } else { pwr == a }; + let pwr = setup.base_randomizer().pow_bounded(&z); + let test = if e { pwr == a * setup.base_value() } else { pwr == a }; if !test { return false; } diff --git a/synedrion/src/cggmp21/sigma/sch.rs b/synedrion/src/cggmp21/sigma/sch.rs index 6e52e46..08e3bfc 100644 --- a/synedrion/src/cggmp21/sigma/sch.rs +++ b/synedrion/src/cggmp21/sigma/sch.rs @@ -5,28 +5,27 @@ use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use zeroize::ZeroizeOnDrop; use crate::{ curve::{Point, Scalar}, tools::{ hashing::{Chain, FofHasher, Hashable}, - HideDebug, + Secret, }, }; const HASH_TAG: &[u8] = b"P_sch"; /// Secret data the proof is based on (~ signing key) -#[derive(Debug, Clone, ZeroizeOnDrop)] +#[derive(Debug, Clone)] pub(crate) struct SchSecret( /// `\alpha` - HideDebug, + Secret, ); impl SchSecret { pub fn random(rng: &mut impl CryptoRngCore) -> Self { - Self(Scalar::random(rng).into()) + Self(Secret::init_with(|| Scalar::random(rng))) } } @@ -73,13 +72,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 + challenge.0 * x; + let proof = *(&proof_secret.0 + x * challenge.0).expose_secret(); Self { challenge, proof } } @@ -94,11 +93,11 @@ mod tests { use rand_core::OsRng; 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 secret = Secret::init_with(|| Scalar::random(&mut OsRng)); let public = secret.mul_by_generator(); let aux: &[u8] = b"abcde"; diff --git a/synedrion/src/curve.rs b/synedrion/src/curve.rs index 787389c..32b0ab2 100644 --- a/synedrion/src/curve.rs +++ b/synedrion/src/curve.rs @@ -6,6 +6,6 @@ mod arithmetic; mod ecdsa; -pub(crate) use arithmetic::{Curve, Point, Scalar, ORDER}; +pub(crate) use arithmetic::{secret_split, Curve, Point, Scalar, ORDER}; pub use self::ecdsa::RecoverableSignature; diff --git a/synedrion/src/curve/arithmetic.rs b/synedrion/src/curve/arithmetic.rs index 5bd84ba..4101706 100644 --- a/synedrion/src/curve/arithmetic.rs +++ b/synedrion/src/curve/arithmetic.rs @@ -1,7 +1,7 @@ use alloc::{format, string::String, vec, vec::Vec}; use core::{ default::Default, - ops::{Add, Mul, Neg, Sub}, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; use digest::Digest; @@ -16,6 +16,7 @@ use k256::elliptic_curve::{ Field, FieldBytesSize, NonZeroScalar, + SecretKey, }; use k256::{ ecdsa::{SigningKey, VerifyingKey}, @@ -23,12 +24,14 @@ use k256::{ Secp256k1, }; use rand_core::CryptoRngCore; -use secrecy::{CloneableSecret, SerializableSecret}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_encoded_bytes::{Hex, SliceLike}; -use zeroize::DefaultIsZeroes; +use zeroize::Zeroize; -use crate::tools::hashing::{Chain, HashableType}; +use crate::tools::{ + hashing::{Chain, HashableType}, + Secret, +}; pub(crate) type Curve = Secp256k1; pub(crate) type BackendScalar = k256::Scalar; @@ -54,8 +57,8 @@ impl HashableType for Curve { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PartialOrd, Ord)] -pub struct Scalar(BackendScalar); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PartialOrd, Ord, Zeroize)] +pub(crate) struct Scalar(BackendScalar); impl Scalar { pub const ZERO: Self = Self(BackendScalar::ZERO); @@ -107,13 +110,8 @@ impl Scalar { self.0 } - pub fn to_signing_key(self) -> Option { - let scalar: Option> = NonZeroScalar::new(self.0).into(); - Some(SigningKey::from(scalar?)) - } - - pub fn from_signing_key(sk: &SigningKey) -> Self { - Self(*sk.as_nonzero_scalar().as_ref()) + pub fn from_signing_key(sk: &SigningKey) -> Secret { + Secret::init_with(|| Self(*sk.as_nonzero_scalar().as_ref())) } pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result { @@ -124,18 +122,30 @@ impl Scalar { .map(Self) .ok_or_else(|| "Invalid curve scalar representation".into()) } +} - pub(crate) fn split(&self, rng: &mut impl CryptoRngCore, num: usize) -> Vec { - // TODO (#5): do all the parts have to be non-zero? - if num == 1 { - return vec![*self]; - } +impl Secret { + pub fn to_signing_key(&self) -> Option { + let nonzero_scalar: Secret> = + Secret::maybe_init_with(|| Option::from(NonZeroScalar::new(self.expose_secret().0)))?; + // SigningKey can be instantiated from NonZeroScalar directly, but that method takes it by value, + // so it is more likely to leave traces of secret data on the stack. `SecretKey::from()` takes a reference. + let secret_key = SecretKey::from(nonzero_scalar.expose_secret()); + Some(SigningKey::from(&secret_key)) + } +} - let mut parts = (0..(num - 1)).map(|_| Scalar::random(rng)).collect::>(); - let partial_sum: Scalar = parts.iter().sum(); - parts.push(self - &partial_sum); - parts +pub(crate) fn secret_split(rng: &mut impl CryptoRngCore, scalar: Secret, num: usize) -> Vec> { + if num == 1 { + return vec![scalar]; } + + let mut parts = (0..(num - 1)) + .map(|_| Secret::init_with(|| Scalar::random_nonzero(rng))) + .collect::>(); + let partial_sum: Secret = parts.iter().cloned().sum(); + parts.push(scalar - partial_sum); + parts } impl<'a> TryFrom<&'a [u8]> for Scalar { @@ -169,14 +179,8 @@ impl<'de> Deserialize<'de> for Scalar { } } -impl DefaultIsZeroes for Scalar {} - -impl CloneableSecret for Scalar {} - -impl SerializableSecret for Scalar {} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Point(BackendPoint); +pub(crate) struct Point(BackendPoint); impl Point { pub const GENERATOR: Self = Self(BackendPoint::GENERATOR); @@ -267,18 +271,16 @@ impl Add<&Scalar> for &Scalar { } } -impl Add for Point { - type Output = Point; - - fn add(self, other: Point) -> Point { - Point(self.0.add(&(other.0))) +impl AddAssign<&Scalar> for Scalar { + fn add_assign(&mut self, other: &Scalar) { + self.0.add_assign(&other.0) } } -impl Add<&Point> for &Point { +impl Add for Point { type Output = Point; - fn add(self, other: &Point) -> Point { + fn add(self, other: Point) -> Point { Point(self.0.add(&(other.0))) } } @@ -291,11 +293,9 @@ impl Sub for Scalar { } } -impl Sub<&Scalar> for &Scalar { - type Output = Scalar; - - fn sub(self, other: &Scalar) -> Scalar { - Scalar(self.0.sub(&(other.0))) +impl SubAssign<&Scalar> for Scalar { + fn sub_assign(&mut self, other: &Scalar) { + self.0.sub_assign(&other.0) } } @@ -339,11 +339,9 @@ impl Mul<&Scalar> for Scalar { } } -impl Mul<&Scalar> for &Scalar { - type Output = Scalar; - - fn mul(self, other: &Scalar) -> Scalar { - Scalar(self.0.mul(&(other.0))) +impl MulAssign<&Scalar> for Scalar { + fn mul_assign(&mut self, other: &Scalar) { + self.0.mul_assign(&other.0) } } diff --git a/synedrion/src/paillier.rs b/synedrion/src/paillier.rs index 6790489..0ac563e 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 d6060e2..ae3a55f 100644 --- a/synedrion/src/paillier/encryption.rs +++ b/synedrion/src/paillier/encryption.rs @@ -3,87 +3,69 @@ use core::{ ops::{Add, Mul}, }; -use crypto_bigint::{Monty, ShrVartime, WrappingSub}; +use crypto_bigint::{subtle::ConstantTimeGreater, Monty, ShrVartime}; use rand_core::CryptoRngCore; -use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use zeroize::ZeroizeOnDrop; 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}, + Bounded, Exponentiable, HasWide, Retrieve, Signed, ToMontgomery, + }, }; -// 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() - } +/// A public randomizer-like quantity used in ZK proofs. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct MaskedRandomizer(P::Uint); - pub fn to_precomputed(&self, pk: &PublicKeyPaillier

) -> Randomizer

{ - Randomizer(self.0.to_montgomery(pk.monty_params_mod_n())) - } +/// A ciphertext randomizer (an invertible element of $\mathbb{Z}_N$). +#[derive(Debug, Clone)] +pub(crate) struct Randomizer { + randomizer: Secret, + randomizer_mod: Secret, } -#[derive(Debug, Clone, PartialEq, Eq, ZeroizeOnDrop)] -pub(crate) struct Randomizer(P::UintMod); - impl Randomizer

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

) -> Self { - Self(pk.random_invertible_residue(rng)) - } - - pub fn to_wire(&self) -> RandomizerWire

{ - RandomizerWire(self.0.retrieve()) - } - - pub fn pow_signed(&self, exponent: &Signed) -> Self { - Self(self.0.pow_signed(exponent)) - } - - pub fn pow_signed_vartime(&self, exponent: &Signed) -> Self { - Self(self.0.pow_signed_vartime(exponent)) - } -} - -impl<'a, P: PaillierParams> Mul<&'a Randomizer

> for &'a Randomizer

{ - type Output = Randomizer

; - fn mul(self, rhs: &Randomizer

) -> Self::Output { - Randomizer(self.0 * rhs.0) + fn new_mod(randomizer_mod: Secret) -> Self { + let randomizer = randomizer_mod.retrieve(); + Self { + randomizer, + randomizer_mod, + } } -} -impl Mul> for &Randomizer

{ - type Output = Randomizer

; - fn mul(self, rhs: Randomizer

) -> Self::Output { - self * &rhs + fn new(pk: &PublicKeyPaillier

, randomizer: Secret) -> Self { + let randomizer_mod = randomizer.to_montgomery(pk.monty_params_mod_n()); + Self { + randomizer, + randomizer_mod, + } } -} -impl Mul<&Randomizer

> for Randomizer

{ - type Output = Randomizer

; - fn mul(self, rhs: &Randomizer

) -> Self::Output { - &self * rhs + pub fn random(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

) -> Self { + let randomizer = Secret::init_with(|| pk.random_invertible_residue(rng)); + Self::new(pk, randomizer) } -} -impl Mul> for Randomizer

{ - type Output = Randomizer

; - fn mul(self, rhs: Self) -> Self::Output { - &self * &rhs + /// 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() } -} -impl AsRef for Randomizer

{ - fn as_ref(&self) -> &P::UintMod { - &self.0 + /// Converts the randomizer to a publishable form by masking it with another randomizer and a public exponent. + pub fn to_masked(&self, coeff: &Self, exponent: &Signed) -> MaskedRandomizer

{ + MaskedRandomizer( + (self.randomizer_mod.pow_signed_vartime(exponent) * &coeff.randomizer_mod) + .expose_secret() + .retrieve(), + ) } } @@ -118,8 +100,8 @@ impl Ciphertext

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

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

, + abs_plaintext: &Secret, + randomizer: &Randomizer

, plaintext_is_negative: Choice, ) -> Self { // Technically if `abs_plaintext` is greater than the modulus of `pk`, @@ -139,14 +121,43 @@ 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 + + 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 { + // Same as `new_with_randomizer_inner`, but works on public data. + 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.0.into_wide(); - let pk_mod_bound = pk.modulus_bounded().into_wide(); + let randomizer = randomizer.0.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); @@ -160,40 +171,75 @@ 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: &Randomizer

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

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

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

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

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

, plaintext: &Signed, - randomizer: &RandomizerWire

, + randomizer: &MaskedRandomizer

, ) -> Self { - Self::new_with_randomizer_inner(pk, &plaintext.abs(), randomizer, plaintext.is_negative()) + 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: &Signed, - randomizer: &RandomizerWire

, + randomizer: &MaskedRandomizer

, ) -> Self { - let plaintext_reduced = P::Uint::try_from_wide(plaintext.abs() % pk.modulus_wide_nonzero()) + 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_inner(pk, &plaintext_reduced, randomizer, plaintext.is_negative()) + 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: &P::Uint) -> Self { - Self::new_with_randomizer(pk, plaintext, &RandomizerWire::random(rng, pk)) + pub fn new(rng: &mut impl CryptoRngCore, pk: &PublicKeyPaillier

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

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

, + plaintext: &Secret>, + ) -> Self { + Self::new_with_randomizer_signed(pk, plaintext, &Randomizer::random(rng, pk)) } /// Decrypts this ciphertext assuming that the plaintext is in range `[0, N)`. - pub fn decrypt(&self, sk: &SecretKeyPaillier

) -> P::Uint { + pub fn decrypt(&self, sk: &SecretKeyPaillier

) -> Secret { assert_eq!(sk.public_key(), &self.pk); let pk = sk.public_key(); @@ -207,33 +253,42 @@ impl Ciphertext

{ // `C^phi mod N^2` may be 0 if `C == N`, which is very unlikely for large `N`. // Note that `C^phi mod N^2 / N < N`, so we can unwrap when converting to `Uint` // (because `N` itself fits into `Uint`). - let x = P::Uint::try_from_wide( - (self.ciphertext.pow_bounded(totient_wide.expose_secret()) - - P::WideUintMod::one(pk.monty_params_mod_n_squared().clone())) - .retrieve() - / pk.modulus_wide_nonzero(), - ) - .expect("the value is within `Uint` limtis by construction"); + + // Calculate `C^phi mod N^2`. The result is already secret. + // The exponent may leave traces on the stack in the `pow` implementation in `crypto-bigint` + // (since it'll probably be decomposed with a small radix), but we can't do much about that. + let t = Secret::init_with(|| self.ciphertext.pow_bounded(totient_wide.expose_secret())); + let one = P::WideUintMod::one(pk.monty_params_mod_n_squared().clone()); + let x = (t - &one).retrieve() / pk.modulus_wide_nonzero(); + let x = Secret::init_with(|| { + P::Uint::try_from_wide(x.expose_secret()).expect("the value is within `Uint` limtis by construction") + }); let x_mod = x.to_montgomery(pk.monty_params_mod_n()); - (x_mod * sk.inv_totient().expose_secret()).retrieve() + (x_mod * sk.inv_totient()).retrieve() } /// Decrypts this ciphertext assuming that the plaintext is in range `[-N/2, N/2)`. - pub fn decrypt_signed(&self, sk: &SecretKeyPaillier

) -> Signed { + pub fn decrypt_signed(&self, sk: &SecretKeyPaillier

) -> Secret> { assert_eq!(sk.public_key(), &self.pk); let pk = sk.public_key(); - let positive_result = self.decrypt(sk); // Note that this is in range `[0, N)` - let negative_result = pk.modulus().wrapping_sub(&positive_result); - let is_negative = Choice::from((positive_result > pk.modulus().wrapping_shr_vartime(1)) as u8); - let mut result = Signed::new_from_unsigned( - P::Uint::conditional_select(&positive_result, &negative_result, is_negative), - P::MODULUS_BITS - 1, - ) - .expect("the value is within `[-2^(MODULUS_BITS-1), 2^(MODULUS_BITS-1)]` by construction"); + // Note that this is in range `[0, N)` + let positive_result = self.decrypt(sk); + + // Can't define a `Sub` for `Uint`, so have to re-wrap manually. + let negative_result = Secret::init_with(|| *pk.modulus() - positive_result.expose_secret()); + let is_negative = positive_result + .expose_secret() + .ct_gt(&pk.modulus().wrapping_shr_vartime(1)); + + let uint_result = Secret::::conditional_select(&positive_result, &negative_result, is_negative); + let mut result = Secret::init_with(|| { + Signed::new_from_unsigned(*uint_result.expose_secret(), P::MODULUS_BITS - 1) + .expect("the value is within `[-2^(MODULUS_BITS-1), 2^(MODULUS_BITS-1)]` by construction") + }); result.conditional_negate(is_negative); result @@ -253,14 +308,16 @@ impl Ciphertext

{ // = rho^N + m * N * rho^N + k * N^2, // where `k` is some integer. // Therefore `C mod N = rho^N mod N`. - let ciphertext_mod_n = P::Uint::try_from_wide(self.ciphertext.retrieve() % pk.modulus_wide_nonzero()) + let ciphertext_mod_n = P::Uint::try_from_wide(&(self.ciphertext.retrieve() % pk.modulus_wide_nonzero())) .expect("a value reduced modulo N fits into `Uint`"); let ciphertext_mod_n = ciphertext_mod_n.to_montgomery(pk.monty_params_mod_n()); // 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(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_mod(randomizer_mod) } // Note: while it is true that `enc(x) (*) rhs == enc((x * rhs) mod N)`, @@ -271,14 +328,14 @@ impl Ciphertext

{ fn homomorphic_mul(self, rhs: &Signed) -> Self { Self { pk: self.pk, - ciphertext: self.ciphertext.pow_signed(&rhs.into_wide()), + ciphertext: self.ciphertext.pow_signed(&rhs.to_wide()), } } fn homomorphic_mul_ref(&self, rhs: &Signed) -> Self { Self { pk: self.pk.clone(), - ciphertext: self.ciphertext.pow_signed(&rhs.into_wide()), + ciphertext: self.ciphertext.pow_signed(&rhs.to_wide()), } } @@ -293,7 +350,7 @@ impl Ciphertext

{ } fn homomorphic_mul_unsigned(self, rhs: &Bounded) -> Self { - let rhs_wide = rhs.into_wide(); + let rhs_wide = rhs.to_wide(); Self { pk: self.pk, ciphertext: self.ciphertext.pow_bounded(&rhs_wide), @@ -301,7 +358,7 @@ impl Ciphertext

{ } fn homomorphic_mul_unsigned_ref(&self, rhs: &Bounded) -> Self { - let rhs_wide = rhs.into_wide(); + let rhs_wide = rhs.to_wide(); Self { pk: self.pk.clone(), ciphertext: self.ciphertext.pow_bounded(&rhs_wide), @@ -316,12 +373,12 @@ impl Ciphertext

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

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

) -> Self { let randomizer_mod = randomizer .0 - .into_wide() + .to_wide() .to_montgomery(self.pk.monty_params_mod_n_squared()); - let pk_modulus_wide = self.pk.modulus_bounded().into_wide(); + let pk_modulus_wide = self.pk.modulus_bounded().to_wide(); let ciphertext = self.ciphertext * randomizer_mod.pow_bounded(&pk_modulus_wide); Self { pk: self.pk, @@ -329,6 +386,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(), @@ -337,7 +407,7 @@ impl Ciphertext

{ } } -impl Add for Ciphertext

{ +impl Add> for Ciphertext

{ type Output = Ciphertext

; fn add(self, other: Ciphertext

) -> Ciphertext

{ self + &other @@ -365,6 +435,27 @@ 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: Bounded) -> Ciphertext

{ @@ -379,6 +470,20 @@ impl Mul> for &Ciphertext

{ } } +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}; @@ -388,9 +493,12 @@ mod tests { 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 @@ -403,8 +511,8 @@ mod tests { // Note that modulus here may be even, so we can't use Montgomery representation let wide_product = lhs.mul_wide(&rhs.abs()); - let wide_modulus = modulus.as_ref().into_wide(); - let result = T::try_from_wide(wide_product % NonZero::new(wide_modulus).unwrap()).unwrap(); + let wide_modulus = modulus.as_ref().to_wide(); + let result = T::try_from_wide(&(wide_product % NonZero::new(wide_modulus).unwrap())).unwrap(); if rhs.is_negative().into() { modulus.as_ref().checked_sub(&result).unwrap() } else { @@ -428,10 +536,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.expose_secret()); let ciphertext_wire = ciphertext.to_wire(); let ciphertext_back = ciphertext_wire.to_precomputed(pk); @@ -442,36 +551,43 @@ 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()); - assert_eq!(plaintext_reduced, plaintext_back); + let plaintext_reduced = reduce::(plaintext.expose_secret(), &pk.modulus_nonzero()); + assert_eq!(&plaintext_reduced, plaintext_back.expose_secret()); } #[test] 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 ciphertext = Ciphertext::::new_with_randomizer(pk, &plaintext, &randomizer); let randomizer_back = ciphertext.derive_randomizer(&sk); - assert_eq!(randomizer, randomizer_back); + assert_eq!(randomizer.expose(), randomizer_back.expose()); } #[test] 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.expose_secret() + ); } #[test] @@ -479,16 +595,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.expose_secret() + ); } #[test] @@ -496,9 +619,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,8 +631,9 @@ mod tests { let plaintext_back = result.decrypt(&sk); assert_eq!( - mul_mod(&plaintext1, &plaintext2, &pk.modulus_nonzero()).add_mod(&plaintext3, pk.modulus()), - plaintext_back + &mul_mod(plaintext1.expose_secret(), &plaintext2, &pk.modulus_nonzero()) + .add_mod(plaintext3.expose_secret(), pk.modulus()), + plaintext_back.expose_secret() ); } } diff --git a/synedrion/src/paillier/keys.rs b/synedrion/src/paillier/keys.rs index 7886acb..59ea0b1 100644 --- a/synedrion/src/paillier/keys.rs +++ b/synedrion/src/paillier/keys.rs @@ -5,7 +5,6 @@ use core::{ use crypto_bigint::{InvMod, Monty, Odd, ShrVartime, Square, WrappingAdd}; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; use serde::{Deserialize, Serialize}; use super::{ @@ -74,10 +73,10 @@ where let primes = secret_key.primes.into_precomputed(); let modulus = primes.modulus_wire().into_precomputed(); - let monty_params_mod_p = P::HalfUintMod::new_params_vartime(*primes.p_half_odd().expose_secret()); - let monty_params_mod_q = P::HalfUintMod::new_params_vartime(*primes.q_half_odd().expose_secret()); + let monty_params_mod_p = P::HalfUintMod::new_params_vartime(primes.p_half_odd().expose_secret().clone()); + let monty_params_mod_q = P::HalfUintMod::new_params_vartime(primes.q_half_odd().expose_secret().clone()); - let inv_totient = SecretBox::init_with(|| { + let inv_totient = Secret::init_with(|| { primes .totient() .expose_secret() @@ -87,10 +86,9 @@ where "The modulus is pq. ϕ(pq) = (p-1)(q-1) is invertible mod pq because ", "neither (p-1) nor (q-1) share factors with pq." ]) - }) - .into(); + }); - let inv_modulus = SecretBox::init_with(|| { + let inv_modulus = Secret::init_with(|| { Bounded::new( (*modulus.modulus()) .inv_mod(primes.totient().expose_secret()) @@ -98,19 +96,19 @@ where P::MODULUS_BITS, ) .expect("We assume `P::MODULUS_BITS` is properly configured") - }) - .into(); + }); - let inv_p_mod_q = Secret::from(SecretBox::init_with(|| { + let inv_p_mod_q = Secret::init_with(|| { primes .p_half() .expose_secret() + .clone() // NOTE: `monty_params_mod_q` is cloned here and can remain on the stack. // See https://github.com/RustCrypto/crypto-bigint/issues/704 .to_montgomery(&monty_params_mod_q) .invert() .expect("All non-zero integers mod a prime have a multiplicative inverse") - })); + }); // Calculate $u$ such that $u = -1 \mod p$ and $u = 1 \mod q$. // Using one step of Garner's algorithm: @@ -118,7 +116,7 @@ where // Calculate $t = 2 p^{-1} - 1 \mod q$ - let one = SecretBox::init_with(|| { + let one = Secret::init_with(|| { // NOTE: `monty_params_mod_q` is cloned here and can remain on the stack. // See https://github.com/RustCrypto/crypto-bigint/issues/704 P::HalfUintMod::one(monty_params_mod_q.clone()) @@ -126,25 +124,25 @@ where let mut t_mod = inv_p_mod_q.clone(); t_mod.expose_secret_mut().add_assign(inv_p_mod_q.expose_secret()); t_mod.expose_secret_mut().sub_assign(one.expose_secret()); - let t = SecretBox::init_with(|| t_mod.expose_secret().retrieve()); + let t = Secret::init_with(|| t_mod.expose_secret().retrieve()); // Calculate $u$ // I am not entirely sure if it can be used to learn something about `p` and `q`, // so just to be on the safe side it lives in the secret key. - let u = SecretBox::init_with(|| t.expose_secret().mul_wide(primes.p_half().expose_secret())); - let u = SecretBox::init_with(|| { + let u = Secret::init_with(|| t.expose_secret().mul_wide(primes.p_half().expose_secret())); + let u = Secret::init_with(|| { u.expose_secret() .checked_add(primes.p().expose_secret()) .expect("does not overflow by construction") }); - let u = SecretBox::init_with(|| { + let u = Secret::init_with(|| { u.expose_secret() .checked_sub(&::one()) .expect("does not overflow by construction") }); let nonsquare_sampling_constant = - SecretBox::init_with(|| P::UintMod::new(*u.expose_secret(), modulus.monty_params_mod_n().clone())).into(); + Secret::init_with(|| P::UintMod::new(*u.expose_secret(), modulus.monty_params_mod_n().clone())); let public_key = PublicKeyPaillier::new(modulus); @@ -166,30 +164,30 @@ where } } - pub fn p_signed(&self) -> SecretBox> { + pub fn p_signed(&self) -> Secret> { self.primes.p_signed() } - pub fn q_signed(&self) -> SecretBox> { + pub fn q_signed(&self) -> Secret> { self.primes.q_signed() } - pub fn p_wide_signed(&self) -> SecretBox> { + pub fn p_wide_signed(&self) -> Secret> { self.primes.p_wide_signed() } - /// Returns Euler's totient function (`φ(n)`) of the modulus, wrapped in a [`SecretBox`]. - pub fn totient_wide_bounded(&self) -> SecretBox> { + /// Returns Euler's totient function (`φ(n)`) of the modulus, wrapped in a [`Secret`]. + pub fn totient_wide_bounded(&self) -> Secret> { self.primes.totient_wide_bounded() } /// Returns $\phi(N)^{-1} \mod N$ - pub fn inv_totient(&self) -> &SecretBox { + pub fn inv_totient(&self) -> &Secret { &self.inv_totient } /// Returns $N^{-1} \mod \phi(N)$ - pub fn inv_modulus(&self) -> &SecretBox> { + pub fn inv_modulus(&self) -> &Secret> { &self.inv_modulus } @@ -202,8 +200,8 @@ where // but it needs to be supported by `crypto-bigint`. let p_rem = *elem % self.primes.p_nonzero().expose_secret(); let q_rem = *elem % self.primes.q_nonzero().expose_secret(); - let p_rem_half = P::HalfUint::try_from_wide(p_rem).expect("`p` fits into `HalfUint`"); - let q_rem_half = P::HalfUint::try_from_wide(q_rem).expect("`q` fits into `HalfUint`"); + let p_rem_half = P::HalfUint::try_from_wide(&p_rem).expect("`p` fits into `HalfUint`"); + let q_rem_half = P::HalfUint::try_from_wide(&q_rem).expect("`q` fits into `HalfUint`"); // NOTE: `monty_params_mod_q` is cloned here and can remain on the stack. // See https://github.com/RustCrypto/crypto-bigint/issues/704 @@ -213,12 +211,12 @@ where (p_rem_mod, q_rem_mod) } - fn sqrt_part(&self, x: &P::HalfUintMod, modulus: &SecretBox) -> Option { + fn sqrt_part(&self, x: &P::HalfUintMod, modulus: &Secret) -> Option { // Both `p` and `q` are safe primes, so they're 3 mod 4. // This means that if square root exists, it must be of the form `+/- x^((modulus+1)/4)`. // Also it means that `(modulus+1)/4 == modulus/4+1` // (this will help avoid a possible overflow). - let power = SecretBox::init_with(|| { + let power = Secret::init_with(|| { modulus .expose_secret() .wrapping_shr_vartime(2) @@ -252,9 +250,9 @@ where let (a_mod_p, b_mod_q) = rns; let a_half = a_mod_p.retrieve(); - let a_mod_q = a_half.to_montgomery(&self.monty_params_mod_q); + let a_mod_q = a_half.clone().to_montgomery(&self.monty_params_mod_q); let x = ((b_mod_q.clone() - a_mod_q) * self.inv_p_mod_q.expose_secret()).retrieve(); - let a = a_half.into_wide(); + let a = a_half.to_wide(); // Will not overflow since 0 <= x < q, and 0 <= a < p. a.checked_add(&self.primes.p_half().expose_secret().mul_wide(&x)) @@ -356,7 +354,7 @@ impl PublicKeyPaillier

{ } pub fn modulus_wide_nonzero(&self) -> NonZero { - NonZero::new(self.modulus.modulus().into_wide()).expect("the modulus is non-zero") + NonZero::new(self.modulus.modulus().to_wide()).expect("the modulus is non-zero") } /// Returns precomputed parameters for integers modulo N @@ -371,7 +369,7 @@ impl PublicKeyPaillier

{ /// Finds an invertible group element via rejection sampling. Returns the /// element in Montgomery form. - pub fn random_invertible_residue(&self, rng: &mut impl CryptoRngCore) -> P::UintMod { + pub fn random_invertible_residue(&self, rng: &mut impl CryptoRngCore) -> P::Uint { self.modulus.random_invertible_residue(rng) } } diff --git a/synedrion/src/paillier/params.rs b/synedrion/src/paillier/params.rs index 7e09035..61d8590 100644 --- a/synedrion/src/paillier/params.rs +++ b/synedrion/src/paillier/params.rs @@ -1,11 +1,13 @@ +use core::ops::RemAssign; + use crypto_bigint::{ modular::Retrieve, - subtle::{ConditionallyNegatable, ConditionallySelectable, CtOption}, - Bounded, Encoding, Gcd, Integer, InvMod, Invert, Monty, RandomMod, + subtle::{ConditionallyNegatable, ConditionallySelectable, ConstantTimeGreater, CtOption}, + Bounded, Encoding, Gcd, Integer, InvMod, Invert, Monty, NonZero, RandomMod, }; use crypto_primes::RandomPrimeWithRng; use serde::{Deserialize, Serialize}; -use zeroize::{DefaultIsZeroes, Zeroize}; +use zeroize::Zeroize; #[cfg(test)] use crate::uint::{U1024Mod, U2048Mod, U512Mod, U1024, U2048, U4096, U512}; @@ -17,8 +19,10 @@ use crate::{ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Sync { /// The size of one of the pair of RSA primes. const PRIME_BITS: u32; + /// The size of the RSA modulus (a product of two primes). const MODULUS_BITS: u32 = Self::PRIME_BITS * 2; + /// An integer that fits a single RSA prime. type HalfUint: Integer + Bounded @@ -28,8 +32,7 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn + for<'de> Deserialize<'de> + HasWide + ToMontgomery - + Zeroize - + DefaultIsZeroes; + + Zeroize; /// A modulo-residue counterpart of `HalfUint`. type HalfUintMod: Monty @@ -42,9 +45,12 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn + Bounded + Gcd + ConditionallySelectable - + Encoding + + ConstantTimeGreater + + Encoding + Hashable + HasWide + // TODO: remove when https://github.com/RustCrypto/crypto-bigint/pull/709 is merged + + for<'a> RemAssign<&'a NonZero> + InvMod + RandomMod + RandomPrimeWithRng @@ -52,6 +58,7 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn + for<'de> Deserialize<'de> + ToMontgomery + Zeroize; + /// A modulo-residue counterpart of `Uint`. type UintMod: ConditionallySelectable + Exponentiable @@ -80,7 +87,8 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn + ConditionallyNegatable + ConditionallySelectable + Invert> - + Retrieve; + + Retrieve + + Zeroize; /// An integer that fits the squared RSA modulus times a small factor. /// Used in some ZK proofs. @@ -94,7 +102,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 8b271e3..0b19560 100644 --- a/synedrion/src/paillier/ring_pedersen.rs +++ b/synedrion/src/paillier/ring_pedersen.rs @@ -3,7 +3,6 @@ use core::ops::Mul; use crypto_bigint::{Monty, NonZero, RandomMod, ShrVartime}; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; use serde::{Deserialize, Serialize}; use super::{ @@ -26,20 +25,19 @@ impl RPSecret

{ pub fn random(rng: &mut impl CryptoRngCore) -> Self { let primes = SecretPrimesWire::

::random_safe(rng).into_precomputed(); - let bound = SecretBox::init_with(|| { + let bound = Secret::init_with(|| { NonZero::new(primes.totient().expose_secret().wrapping_shr_vartime(2)) .expect("totient / 4 is still non-zero because p, q >= 5") }); - let lambda = SecretBox::init_with(|| { + let lambda = Secret::init_with(|| { Bounded::new(P::Uint::random_mod(rng, bound.expose_secret()), P::MODULUS_BITS - 2) .expect("totient < N < 2^MODULUS_BITS, so totient / 4 < 2^(MODULUS_BITS - 2)") - }) - .into(); + }); Self { primes, lambda } } - pub fn lambda(&self) -> &SecretBox> { + pub fn lambda(&self) -> &Secret> { &self.lambda } @@ -47,7 +45,7 @@ impl RPSecret

{ self.primes.random_residue_mod_totient(rng) } - pub fn totient_nonzero(&self) -> SecretBox> { + pub fn totient_nonzero(&self) -> Secret> { self.primes.totient_nonzero() } @@ -63,10 +61,11 @@ impl RPSecret

{ pub(crate) struct RPParams { /// The public modulus $\hat{N}$ modulus: PublicModulus

, - /// The ring-Pedersen base. - base: P::UintMod, // $t$ - /// The ring-Pedersen power (a number belonging to the group produced by the base). - power: P::UintMod, // $s = t^\lambda$, where $\lambda$ is the secret + /// The ring-Pedersen base for randomizer exponentiation. + base_randomizer: P::UintMod, // $t$ + /// The ring-Pedersen base for secret exponentiation + /// (a number belonging to the group produced by the randomizer base). + base_value: P::UintMod, // $s = t^\lambda$, where $\lambda$ is the secret } impl RPParams

{ @@ -78,18 +77,22 @@ impl RPParams

{ pub fn random_with_secret(rng: &mut impl CryptoRngCore, secret: &RPSecret

) -> Self { let modulus = secret.primes.modulus_wire().into_precomputed(); - let base = modulus.random_quadratic_residue(rng); // $t$ - let power = base.pow_bounded(secret.lambda.expose_secret()); // $s$ + let base_randomizer = modulus.random_quadratic_residue(rng); // $t$ + let base_value = base_randomizer.pow_bounded(secret.lambda.expose_secret()); // $s$ - Self { modulus, base, power } + Self { + modulus, + base_randomizer, + base_value, + } } - pub fn base(&self) -> &P::UintMod { - &self.base + pub fn base_randomizer(&self) -> &P::UintMod { + &self.base_randomizer } - pub fn power(&self) -> &P::UintMod { - &self.power + pub fn base_value(&self) -> &P::UintMod { + &self.base_value } pub fn modulus(&self) -> &P::Uint { @@ -104,39 +107,60 @@ impl RPParams

{ self.modulus.monty_params_mod_n() } - /// Creates a commitment for `secret` with the randomizer `randomizer`. - /// - /// 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

{ - // $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)) + /// Creates a commitment for a secret `value` with a secret `randomizer`. + pub fn commit(&self, value: &Secret>, randomizer: &Secret>) -> RPCommitment

{ + RPCommitment( + self.base_value.pow_signed(value.expose_secret()) + * self.base_randomizer.pow_signed_wide(randomizer.expose_secret()), + ) + } + + /// Creates a commitment for a secret `value` with a secret `randomizer`. + pub fn commit_wide( + &self, + value: &Secret>, + randomizer: &Secret>, + ) -> RPCommitment

{ + RPCommitment( + self.base_value.pow_signed_wide(value.expose_secret()) + * self.base_randomizer.pow_signed_wide(randomizer.expose_secret()), + ) + } + + /// Creates a commitment for a secret `randomizer` and the value 0. + pub fn commit_zero_xwide(&self, randomizer: &Secret>) -> RPCommitment

{ + RPCommitment(self.base_randomizer.pow_signed_extra_wide(randomizer.expose_secret())) } - pub fn commit_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)) + /// Creates a commitment for a public `value` with a public `randomizer`. + pub fn commit_public(&self, value: &Signed, randomizer: &Signed) -> RPCommitment

{ + RPCommitment(self.base_value.pow_signed(value) * self.base_randomizer.pow_signed_wide(randomizer)) } - pub fn commit_xwide( + /// Creates a commitment for a public `value` with a public `randomizer`. + pub fn commit_public_wide(&self, value: &Signed, randomizer: &Signed) -> RPCommitment

{ + RPCommitment(self.base_value.pow_signed_wide(value) * self.base_randomizer.pow_signed_wide(randomizer)) + } + + /// Creates a commitment for a public `value` with a public `randomizer`. + pub fn commit_public_xwide( &self, - secret: &SecretBox>, + value: &Bounded, randomizer: &Signed, ) -> RPCommitment

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

{ - // $t^\rho mod N$ where $\rho$ is the randomizer. - RPCommitment(self.base.pow_signed_extra_wide(randomizer)) + /// Creates a commitment for a public `randomizer` and the value 0. + pub fn commit_public_base_xwide(&self, randomizer: &Signed) -> RPCommitment

{ + RPCommitment(self.base_randomizer.pow_signed_extra_wide(randomizer)) } pub fn to_wire(&self) -> RPParamsWire

{ RPParamsWire { modulus: self.modulus.to_wire(), - base: self.base.retrieve(), - power: self.power.retrieve(), + base_randomizer: self.base_randomizer.retrieve(), + base_value: self.base_value.retrieve(), } } } @@ -148,18 +172,23 @@ impl RPParams

{ pub(crate) struct RPParamsWire { /// The public modulus $\hat{N}$ modulus: PublicModulusWire

, - /// The ring-Pedersen base. - base: P::Uint, // $t$ - /// The ring-Pedersen power (a number belonging to the group produced by the base). - power: P::Uint, // $s$ + /// The ring-Pedersen base for randomizer exponentiation. + base_randomizer: P::Uint, // $t$ + /// The ring-Pedersen base for secret exponentiation + /// (a number belonging to the group produced by the randomizer base). + base_value: P::Uint, // $s = t^\lambda$, where $\lambda$ is the secret } impl RPParamsWire

{ pub fn to_precomputed(&self) -> RPParams

{ let modulus = self.modulus.clone().into_precomputed(); - let base = self.base.to_montgomery(modulus.monty_params_mod_n()); - let power = self.power.to_montgomery(modulus.monty_params_mod_n()); - RPParams { modulus, base, power } + let base_randomizer = self.base_randomizer.to_montgomery(modulus.monty_params_mod_n()); + let base_value = self.base_value.to_montgomery(modulus.monty_params_mod_n()); + RPParams { + modulus, + base_randomizer, + base_value, + } } } @@ -174,13 +203,20 @@ impl RPCommitment

{ /// Raise to the power of `exponent`. /// /// Note: this is variable time in `exponent`. - /// `exponent` will be effectively reduced modulo `totient(N)`. pub fn pow_signed_vartime(&self, exponent: &Signed) -> Self { Self(self.0.pow_signed_vartime(exponent)) } - pub fn pow_signed_wide(&self, exponent: &Signed) -> Self { - Self(self.0.pow_signed_wide(exponent)) + /// Raise to the power of `exponent`. + /// + /// Note: this is variable time in `exponent`. + pub fn pow_signed_wide_vartime(&self, exponent: &Signed) -> Self { + Self(self.0.pow_signed_wide_vartime(exponent)) + } + + /// Raise to the power of `exponent`. + pub fn pow_signed_wide(&self, exponent: &Secret>) -> Self { + Self(self.0.pow_signed_wide(exponent.expose_secret())) } } diff --git a/synedrion/src/paillier/rsa.rs b/synedrion/src/paillier/rsa.rs index 16ea727..c6265e0 100644 --- a/synedrion/src/paillier/rsa.rs +++ b/synedrion/src/paillier/rsa.rs @@ -1,14 +1,12 @@ use crypto_bigint::{CheckedSub, Gcd, Integer, Monty, NonZero, Odd, RandomMod, Square}; use crypto_primes::RandomPrimeWithRng; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; use super::params::PaillierParams; use crate::{ tools::Secret, - uint::{Bounded, HasWide, Signed}, + uint::{Bounded, HasWide, Signed, ToMontgomery}, }; fn random_paillier_blum_prime(rng: &mut impl CryptoRngCore) -> P::HalfUint { @@ -43,16 +41,16 @@ impl SecretPrimesWire

{ /// that is `p` and `q` are regular primes with an additional condition `p, q mod 3 = 4`. pub fn random_paillier_blum(rng: &mut impl CryptoRngCore) -> Self { Self::new( - SecretBox::init_with(|| random_paillier_blum_prime::

(rng)).into(), - SecretBox::init_with(|| random_paillier_blum_prime::

(rng)).into(), + Secret::init_with(|| random_paillier_blum_prime::

(rng)), + Secret::init_with(|| random_paillier_blum_prime::

(rng)), ) } /// Creates a pair of safe primes. pub fn random_safe(rng: &mut impl CryptoRngCore) -> Self { Self::new( - SecretBox::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS)).into(), - SecretBox::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS)).into(), + Secret::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS)), + Secret::init_with(|| P::HalfUint::generate_safe_prime_with_rng(rng, P::PRIME_BITS)), ) } @@ -92,7 +90,7 @@ impl SecretPrimes

{ // Euler's totient function of $N = p q$ - the number of positive integers up to $N$ // that are relatively prime to it. // Since $p$ and $q$ are primes, $\phi(N) = (p - 1) (q - 1)$. - let totient = SecretBox::init_with(|| p_minus_one.mul_wide(&q_minus_one)).into(); + let totient = Secret::init_with(|| p_minus_one.mul_wide(&q_minus_one)); Self { primes, totient } } @@ -105,80 +103,70 @@ impl SecretPrimes

{ PublicModulusWire::new(&self.primes) } - pub fn p_half(&self) -> &SecretBox { + pub fn p_half(&self) -> &Secret { &self.primes.p } - pub fn q_half(&self) -> &SecretBox { + pub fn q_half(&self) -> &Secret { &self.primes.q } - pub fn p_half_odd(&self) -> SecretBox> { - SecretBox::init_with(|| Odd::new(*self.primes.p.expose_secret()).expect("`p` is an odd prime")) + pub fn p_half_odd(&self) -> Secret> { + Secret::init_with(|| Odd::new(self.primes.p.expose_secret().clone()).expect("`p` is an odd prime")) } - pub fn q_half_odd(&self) -> SecretBox> { - SecretBox::init_with(|| Odd::new(*self.primes.q.expose_secret()).expect("`q` is an odd prime")) + pub fn q_half_odd(&self) -> Secret> { + Secret::init_with(|| Odd::new(self.primes.q.expose_secret().clone()).expect("`q` is an odd prime")) } - pub fn p(&self) -> SecretBox { - SecretBox::init_with(|| { - let mut p = *self.primes.p.expose_secret(); - let p_wide = p.into_wide(); - p.zeroize(); - p_wide - }) + pub fn p(&self) -> Secret { + Secret::init_with(|| self.primes.p.expose_secret().to_wide()) } - pub fn q(&self) -> SecretBox { - SecretBox::init_with(|| { - let mut q = *self.primes.q.expose_secret(); - let q_wide = q.into_wide(); - q.zeroize(); - q_wide - }) + pub fn q(&self) -> Secret { + Secret::init_with(|| self.primes.q.expose_secret().to_wide()) } - pub fn p_signed(&self) -> SecretBox> { - SecretBox::init_with(|| { + pub fn p_signed(&self) -> Secret> { + Secret::init_with(|| { Signed::new_positive(*self.p().expose_secret(), P::PRIME_BITS).expect("`P::PRIME_BITS` is valid") }) } - pub fn q_signed(&self) -> SecretBox> { - SecretBox::init_with(|| { + pub fn q_signed(&self) -> Secret> { + Secret::init_with(|| { Signed::new_positive(*self.q().expose_secret(), P::PRIME_BITS).expect("`P::PRIME_BITS` is valid") }) } - pub fn p_nonzero(&self) -> SecretBox> { - SecretBox::init_with(|| NonZero::new(*self.p().expose_secret()).expect("`p` is non-zero")) + pub fn p_nonzero(&self) -> Secret> { + Secret::init_with(|| NonZero::new(*self.p().expose_secret()).expect("`p` is non-zero")) } - pub fn q_nonzero(&self) -> SecretBox> { - SecretBox::init_with(|| NonZero::new(*self.q().expose_secret()).expect("`q` is non-zero")) + pub fn q_nonzero(&self) -> Secret> { + Secret::init_with(|| NonZero::new(*self.q().expose_secret()).expect("`q` is non-zero")) } - pub fn p_wide_signed(&self) -> SecretBox> { - SecretBox::init_with(|| self.p_signed().expose_secret().into_wide()) + pub fn p_wide_signed(&self) -> Secret> { + self.p_signed().to_wide() } - pub fn totient(&self) -> &SecretBox { + pub fn totient(&self) -> &Secret { &self.totient } - pub fn totient_bounded(&self) -> SecretBox> { - SecretBox::init_with(|| { + pub fn totient_bounded(&self) -> Secret> { + Secret::init_with(|| { Bounded::new(*self.totient.expose_secret(), P::MODULUS_BITS).expect("`P::MODULUS_BITS` is valid") }) } - pub fn totient_wide_bounded(&self) -> SecretBox> { - SecretBox::init_with(|| self.totient_bounded().expose_secret().into_wide()) + pub fn totient_wide_bounded(&self) -> Secret> { + self.totient_bounded().to_wide() } - pub fn totient_nonzero(&self) -> SecretBox> { - SecretBox::init_with(|| { + pub fn totient_nonzero(&self) -> Secret> { + Secret::init_with(|| { NonZero::new(*self.totient.expose_secret()).expect(concat![ "φ(n) is never zero for n >= 1; n is strictly greater than 1 ", "because it is (p-1)(q-1) and given that both p and q are prime ", @@ -268,18 +256,20 @@ impl PublicModulus

{ } /// Returns a uniformly chosen number in range $[0, N)$ such that it is invertible modulo $N$, in Montgomery form. - pub fn random_invertible_residue(&self, rng: &mut impl CryptoRngCore) -> P::UintMod { + pub fn random_invertible_residue(&self, rng: &mut impl CryptoRngCore) -> P::Uint { let modulus = self.modulus_nonzero(); loop { let r = P::Uint::random_mod(rng, &modulus); if r.gcd(&self.modulus.0) == P::Uint::one() { - return P::UintMod::new(r, self.monty_params_mod_n.clone()); + return r; } } } /// Returns a uniformly chosen quadratic residue modulo $N$, in Montgomery form. pub fn random_quadratic_residue(&self, rng: &mut impl CryptoRngCore) -> P::UintMod { - self.random_invertible_residue(rng).square() + self.random_invertible_residue(rng) + .to_montgomery(&self.monty_params_mod_n) + .square() } } diff --git a/synedrion/src/tools.rs b/synedrion/src/tools.rs index 3241579..aeada74 100644 --- a/synedrion/src/tools.rs +++ b/synedrion/src/tools.rs @@ -2,11 +2,9 @@ use alloc::collections::{BTreeMap, BTreeSet}; pub(crate) mod bitvec; pub(crate) mod hashing; -mod hide_debug; mod secret; pub(crate) mod sss; -pub(crate) use hide_debug::HideDebug; pub(crate) use secret::Secret; use manul::protocol::{Artifact, LocalError, Payload}; diff --git a/synedrion/src/tools/hide_debug.rs b/synedrion/src/tools/hide_debug.rs deleted file mode 100644 index ddda150..0000000 --- a/synedrion/src/tools/hide_debug.rs +++ /dev/null @@ -1,35 +0,0 @@ -use core::{ - fmt::Debug, - ops::{Deref, DerefMut}, -}; - -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Copy, Serialize, Deserialize)] -pub(crate) struct HideDebug(T); - -impl Debug for HideDebug { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("").finish() - } -} - -impl From for HideDebug { - fn from(value: T) -> Self { - Self(value) - } -} - -impl Deref for HideDebug { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for HideDebug { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} diff --git a/synedrion/src/tools/secret.rs b/synedrion/src/tools/secret.rs index 394b5b4..3acec6d 100644 --- a/synedrion/src/tools/secret.rs +++ b/synedrion/src/tools/secret.rs @@ -1,12 +1,22 @@ use core::{ fmt::Debug, - ops::{Deref, DerefMut}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, }; -use secrecy::{ExposeSecret, SecretBox}; +use crypto_bigint::{ + modular::Retrieve, + subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}, + Encoding, Integer, Monty, NonZero, +}; +use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use zeroize::Zeroize; +use crate::{ + curve::{Point, Scalar}, + uint::{Bounded, Exponentiable, HasWide, Signed}, +}; + /// A helper wrapper for managing secret values. /// /// On top of `secrecy::SecretBox` functionality, it provides: @@ -15,6 +25,30 @@ use zeroize::Zeroize; /// - Safe serialization/deserialization (down to `serde` API; what happens there we cannot control) pub(crate) struct Secret(SecretBox); +impl Secret { + pub fn expose_secret(&self) -> &T { + self.0.expose_secret() + } + + pub fn expose_secret_mut(&mut self) -> &mut T { + self.0.expose_secret_mut() + } +} + +impl Secret { + pub fn init_with(ctr: impl FnOnce() -> T) -> Self { + Self(SecretBox::init_with(ctr)) + } + + pub fn try_init_with(ctr: impl FnOnce() -> Result) -> Result { + Ok(Self(SecretBox::try_init_with(ctr)?)) + } + + pub fn maybe_init_with(ctr: impl FnOnce() -> Option) -> Option { + Some(Self(SecretBox::try_init_with(|| ctr().ok_or(())).ok()?)) + } +} + impl Clone for Secret { fn clone(&self) -> Self { Self(SecretBox::init_with(|| self.0.expose_secret().clone())) @@ -39,22 +73,349 @@ impl Debug for Secret { } } -impl From> for Secret { - fn from(value: SecretBox) -> Self { - Self(value) +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, + T::Wide: ConditionallySelectable + crypto_bigint::Bounded + Zeroize, +{ + pub fn to_wide(&self) -> Secret::Wide>> { + Secret::init_with(|| { + let mut value = *self.expose_secret(); + let result = value.to_wide(); + value.zeroize(); + result + }) + } + + pub fn mul_wide(&self, rhs: &Signed) -> Secret> { + Secret::init_with(|| { + let mut value = *self.expose_secret(); + 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 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 to_signed(&self) -> Option>> { + Secret::maybe_init_with(|| self.expose_secret().clone().into_signed()) + } +} + +// Addition + +impl<'a, T: Zeroize + AddAssign<&'a T>> AddAssign<&'a T> for Secret { + fn add_assign(&mut self, other: &'a T) { + self.expose_secret_mut().add_assign(other); + } +} + +impl<'a, T: Zeroize + AddAssign<&'a T>> AddAssign<&'a Secret> for Secret { + fn add_assign(&mut self, other: &'a Secret) { + self.add_assign(other.expose_secret()); + } +} + +impl<'a, T: Zeroize + AddAssign<&'a T>> Add<&'a T> for Secret { + type Output = Secret; + + fn add(mut self, other: &'a T) -> Self::Output { + self += other; + self + } +} + +impl AddAssign<&'a T>> Add> for Secret { + type Output = Secret; + + fn add(mut self, other: Secret) -> Self::Output { + self += &other; + self + } +} + +impl<'a, T: Zeroize + AddAssign<&'a T>> Add<&'a Secret> for Secret { + type Output = Secret; + + fn add(mut self, other: &'a Secret) -> Self::Output { + self += other.expose_secret(); + self + } +} + +impl AddAssign<&'a T>> Add> for &Secret { + type Output = Secret; + + fn add(self, other: Secret) -> Self::Output { + let mut result = other; + result += self; + result + } +} + +// Negation + +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 { + fn sub_assign(&mut self, other: &'a T) { + self.expose_secret_mut().sub_assign(other); + } +} + +impl<'a, T: Zeroize + SubAssign<&'a T>> SubAssign<&'a Secret> for Secret { + fn sub_assign(&mut self, other: &'a Secret) { + self.sub_assign(other.expose_secret()); + } +} + +impl<'a, T: Zeroize + SubAssign<&'a T>> Sub<&'a T> for Secret { + type Output = Secret; + + fn sub(mut self, other: &'a T) -> Self::Output { + self -= other; + self } } -impl Deref for Secret { - type Target = SecretBox; +impl SubAssign<&'a T>> Sub> for Secret { + type Output = Secret; + + fn sub(mut self, other: Secret) -> Self::Output { + self -= &other; + self + } +} + +// Multiplication + +impl<'a, T: Zeroize + MulAssign<&'a T>> MulAssign<&'a T> for Secret { + fn mul_assign(&mut self, other: &'a T) { + self.expose_secret_mut().mul_assign(other) + } +} + +impl<'a, T: Zeroize + MulAssign<&'a T>> MulAssign<&'a Secret> for Secret { + fn mul_assign(&mut self, other: &'a Secret) { + self.mul_assign(other.expose_secret()) + } +} + +impl<'a, T: Zeroize + MulAssign<&'a T>> Mul<&'a T> for Secret { + type Output = Secret; + + fn mul(mut self, other: &'a T) -> Self::Output { + self *= other; + self + } +} + +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; + + fn mul(mut self, other: Secret) -> Self::Output { + self *= &other; + self + } +} + +impl<'a, T: Zeroize + MulAssign<&'a T>> Mul<&'a Secret> for Secret { + type Output = Secret; + + fn mul(mut self, other: &'a Secret) -> Self::Output { + self *= other.expose_secret(); + self + } +} + +// Division + +impl<'a, T: Zeroize + DivAssign<&'a NonZero>> DivAssign<&'a NonZero> for Secret { + fn div_assign(&mut self, other: &'a NonZero) { + self.expose_secret_mut().div_assign(other) + } +} + +impl DivAssign<&'a NonZero>> Div> for Secret { + type Output = Secret; + + fn div(mut self, other: NonZero) -> Self::Output { + self /= &other; + self + } +} + +// Remainder + +impl<'a, T: Zeroize + Clone + RemAssign<&'a NonZero>> RemAssign<&'a NonZero> for Secret { + fn rem_assign(&mut self, other: &'a NonZero) { + self.expose_secret_mut().rem_assign(other) + } +} + +impl<'a, T: Zeroize + Clone + RemAssign<&'a NonZero>> Rem<&'a NonZero> for &Secret { + type Output = Secret; + + fn rem(self, other: &'a NonZero) -> Self::Output { + let mut result = self.clone(); + result %= other; + result + } +} + +// Summation + +impl AddAssign<&'a T> + Default> core::iter::Sum for Secret { + fn sum>(iter: I) -> Self { + 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) + } +} + +impl Secret { + pub fn mul_by_generator(&self) -> Point { + self.expose_secret().mul_by_generator() + } + + pub fn invert(&self) -> Option> { + Secret::maybe_init_with(|| Option::from(self.expose_secret().invert())) + } +} + +impl Mul> for Point { + type Output = Point; + fn mul(self, scalar: Secret) -> Self::Output { + self * scalar.expose_secret() + } +} + +impl Mul<&Secret> for Point { + type Output = Point; + fn mul(self, scalar: &Secret) -> Self::Output { + self * scalar.expose_secret() + } +} + +impl Mul> for &Point { + type Output = Point; + fn mul(self, scalar: Secret) -> Self::Output { + self * scalar.expose_secret() + } +} + +impl> Retrieve for Secret { + type Output = Secret; + fn retrieve(&self) -> Self::Output { + Secret::init_with(|| self.expose_secret().retrieve()) + } +} + +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, + 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_signed_vartime(exponent)) + } +} + +impl> Secret { + pub fn to_montgomery(&self, params: &::Params) -> Secret { + // `self` has to be cloned and passed by value, which means it may be retained on the stack. + // Can't help it with the current `Monty::new()` signature. + Secret::init_with(|| ::new(self.expose_secret().clone(), params.clone())) + } +} - fn deref(&self) -> &Self::Target { - &self.0 +// Can't implement `ConditionallySelectable` itself since it is bounded on `Copy`. +impl Secret { + pub fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Secret::init_with(|| T::conditional_select(a.expose_secret(), b.expose_secret(), choice)) } } -impl DerefMut for Secret { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 +impl ConditionallyNegatable for Secret { + fn conditional_negate(&mut self, choice: Choice) { + self.0.expose_secret_mut().conditional_negate(choice) } } diff --git a/synedrion/src/tools/sss.rs b/synedrion/src/tools/sss.rs index 23277ad..9f2bd61 100644 --- a/synedrion/src/tools/sss.rs +++ b/synedrion/src/tools/sss.rs @@ -6,11 +6,10 @@ use core::ops::{Add, Mul}; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use zeroize::ZeroizeOnDrop; use crate::{ curve::{Point, Scalar}, - tools::HideDebug, + tools::Secret, }; #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] @@ -43,25 +42,40 @@ where res } -#[derive(Debug, ZeroizeOnDrop)] -pub(crate) struct Polynomial(HideDebug>); +fn evaluate_polynomial_secret(coeffs: &[Secret], x: &Scalar) -> Secret { + // Evaluate in reverse to save on multiplications. + // Basically: a0 + a1 x + a2 x^2 + a3 x^3 == (((a3 x) + a2) x + a1) x + a0 + let mut res = coeffs[coeffs.len() - 1].clone(); + for i in (0..(coeffs.len() - 1)).rev() { + res = res * x + coeffs[i].expose_secret(); + } + res +} + +#[derive(Debug)] +pub(crate) struct Polynomial(Vec>); impl Polynomial { - pub fn random(rng: &mut impl CryptoRngCore, coeff0: &Scalar, degree: usize) -> Self { + pub fn random(rng: &mut impl CryptoRngCore, coeff0: Secret, degree: usize) -> Self { let mut coeffs = Vec::with_capacity(degree); - coeffs.push(*coeff0); + coeffs.push(coeff0); for _ in 1..degree { - coeffs.push(Scalar::random_nonzero(rng)); + coeffs.push(Secret::init_with(|| Scalar::random_nonzero(rng))); } - Self(coeffs.into()) + Self(coeffs) } - pub fn evaluate(&self, x: &ShareId) -> Scalar { - evaluate_polynomial(&self.0, &x.0) + pub fn evaluate(&self, x: &ShareId) -> Secret { + evaluate_polynomial_secret(&self.0, &x.0) } pub fn public(&self) -> PublicPolynomial { - PublicPolynomial(self.0.iter().map(|coeff| coeff.mul_by_generator()).collect()) + PublicPolynomial( + self.0 + .iter() + .map(|coeff| coeff.expose_secret().mul_by_generator()) + .collect(), + ) } } @@ -80,10 +94,10 @@ impl PublicPolynomial { pub(crate) fn shamir_split( rng: &mut impl CryptoRngCore, - secret: &Scalar, + secret: Secret, threshold: usize, indices: &[ShareId], -) -> BTreeMap { +) -> BTreeMap> { let polynomial = Polynomial::random(rng, secret, threshold); indices.iter().map(|idx| (*idx, polynomial.evaluate(idx))).collect() } @@ -100,12 +114,15 @@ pub(crate) fn interpolation_coeff(share_ids: &BTreeSet, share_id: &Shar .product() } -pub(crate) fn shamir_join_scalars(pairs: &BTreeMap) -> Scalar { +pub(crate) fn shamir_join_scalars(pairs: BTreeMap>) -> Secret { let share_ids = pairs.keys().cloned().collect::>(); - pairs - .iter() - .map(|(share_id, val)| val * &interpolation_coeff(&share_ids, share_id)) - .sum() + let mut sum = Secret::init_with(|| Scalar::ZERO); + + for (share_id, val) in pairs.into_iter() { + sum += &(val * interpolation_coeff(&share_ids, &share_id)); + } + + sum } pub(crate) fn shamir_join_points(pairs: &BTreeMap) -> Point { @@ -121,7 +138,7 @@ mod tests { use rand_core::OsRng; use super::{evaluate_polynomial, shamir_evaluation_points, shamir_join_scalars, shamir_split}; - use crate::curve::Scalar; + use crate::{curve::Scalar, tools::Secret}; #[test] fn evaluate() { @@ -138,14 +155,14 @@ mod tests { fn split_and_join() { let threshold = 3; let num_shares = 5; - let secret = Scalar::random(&mut OsRng); + let secret = Secret::init_with(|| Scalar::random(&mut OsRng)); let points = shamir_evaluation_points(num_shares); - let mut shares = shamir_split(&mut OsRng, &secret, threshold, &points); + let mut shares = shamir_split(&mut OsRng, secret.clone(), threshold, &points); shares.remove(&points[0]); shares.remove(&points[3]); - let recovered_secret = shamir_join_scalars(&shares); - assert_eq!(recovered_secret, secret); + let recovered_secret = shamir_join_scalars(shares); + assert_eq!(recovered_secret.expose_secret(), secret.expose_secret()); } } diff --git a/synedrion/src/uint/bounded.rs b/synedrion/src/uint/bounded.rs index adeedaa..a31bbfd 100644 --- a/synedrion/src/uint/bounded.rs +++ b/synedrion/src/uint/bounded.rs @@ -1,13 +1,12 @@ use alloc::{boxed::Box, format, string::String}; -use secrecy::{CloneableSecret, SecretBox}; use serde::{Deserialize, Serialize}; use serde_encoded_bytes::{Hex, SliceLike}; -use zeroize::DefaultIsZeroes; +use zeroize::Zeroize; use super::{ subtle::{Choice, ConditionallySelectable, ConstantTimeLess, CtOption}, - CheckedAdd, CheckedMul, Encoding, HasWide, Integer, NonZero, Signed, + CheckedMul, Encoding, HasWide, Integer, NonZero, Signed, }; /// A packed representation for serializing Bounded objects. @@ -59,7 +58,7 @@ where } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Zeroize)] #[serde( try_from = "PackedBounded", into = "PackedBounded", @@ -103,11 +102,6 @@ where pub fn into_signed(self) -> Option> { Signed::new_positive(self.value, self.bound) } - - /// Extracts the inner `T` from the `Bounded`. Consumes `self`. - pub fn into_inner(self) -> T { - self.value - } } impl AsRef for Bounded { @@ -120,9 +114,9 @@ impl Bounded where T: HasWide, { - pub fn into_wide(self) -> Bounded { + pub fn to_wide(&self) -> Bounded { Bounded { - value: self.value.into_wide(), + value: self.value.to_wide(), bound: self.bound, } } @@ -136,22 +130,6 @@ where } } -impl CheckedAdd for Bounded -where - T: Integer + crypto_bigint::Bounded, -{ - fn checked_add(&self, rhs: &Self) -> CtOption { - let bound = core::cmp::max(self.bound, rhs.bound) + 1; - let in_range = bound.ct_lt(&::BITS); - - let result = Self { - bound, - value: self.value.wrapping_add(&rhs.value), - }; - CtOption::new(result, in_range) - } -} - impl CheckedMul for Bounded where T: Integer + crypto_bigint::Bounded, @@ -180,18 +158,6 @@ where } } -impl DefaultIsZeroes for Bounded where T: Integer + Copy {} -impl CloneableSecret for Bounded where T: Integer + Copy {} - -impl From> for SecretBox> -where - T: Integer + Copy, -{ - fn from(value: Bounded) -> Self { - Box::new(value).into() - } -} - #[cfg(test)] mod tests { use crypto_bigint::{CheckedMul, U1024, U128, U2048}; diff --git a/synedrion/src/uint/signed.rs b/synedrion/src/uint/signed.rs index 10e6867..fcd5b26 100644 --- a/synedrion/src/uint/signed.rs +++ b/synedrion/src/uint/signed.rs @@ -1,9 +1,8 @@ -use alloc::{boxed::Box, string::String}; -use core::ops::{Add, Mul, Neg, Sub}; +use alloc::string::String; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; use digest::XofReader; use rand_core::CryptoRngCore; -use secrecy::SecretBox; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; @@ -25,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 { @@ -37,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 { @@ -54,7 +53,7 @@ where /// A wrapper over unsigned integers that treats two's complement numbers as negative. // In principle, Bounded could be separate from Signed, but we only use it internally, // and pretty much every time we need a bounded value, it's also signed. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Zeroize)] #[serde( try_from = "PackedSigned", into = "PackedSigned", @@ -290,35 +289,6 @@ impl Default for Signed { } } -impl Zeroize for Signed -where - T: Integer + Zeroize, -{ - fn zeroize(&mut self) { - self.value.zeroize(); - } -} - -impl secrecy::CloneableSecret for Signed where T: Clone + Integer + Zeroize {} - -impl From> for SecretBox> -where - T: Integer + Zeroize, -{ - fn from(value: Signed) -> Self { - Box::new(value).into() - } -} - -impl From<&Signed> for SecretBox> -where - T: Integer + Zeroize, -{ - fn from(value: &Signed) -> Self { - SecretBox::new(Box::new(value.clone())) - } -} - impl ConditionallySelectable for Signed where T: Integer + ConditionallySelectable, @@ -372,7 +342,7 @@ where ); let scaled_bound: ::Wide = scale .clone() - .into_wide() + .to_wide() .as_ref() .overflowing_shl_vartime(bound_bits) .expect("Just asserted that bound bits is smaller than T's bit precision"); @@ -411,8 +381,8 @@ where { /// Returns a [`Signed`] with the same value, but twice the bit-width. /// Consumes `self`, but under the hood this method clones. - pub fn into_wide(self) -> Signed { - let abs_result = self.abs().into_wide(); + pub fn to_wide(self) -> Signed { + let abs_result = self.abs().to_wide(); Signed::new_from_abs(abs_result, self.bound(), self.is_negative()) .expect("the value fit the bound before, and the bound won't overflow for `WideUint`") } @@ -450,7 +420,7 @@ where ); let scaled_bound = scale .as_ref() - .into_wide() + .to_wide() .overflowing_shl_vartime(bound_bits) .expect("Just asserted that bound_bits is smaller than bit precision of T"); @@ -492,17 +462,6 @@ where } } -impl Add<&Signed> for Signed -where - T: Integer + crypto_bigint::Bounded, -{ - type Output = Self; - fn add(self, rhs: &Self) -> Self::Output { - self.checked_add(rhs) - .expect("does not overflow by the construction of the arguments") - } -} - impl CheckedSub> for Signed where T: crypto_bigint::Bounded + ConditionallySelectable + Integer, @@ -532,82 +491,37 @@ where } } -impl Sub<&Signed> for Signed +impl<'a, T> AddAssign<&'a Signed> for Signed where - T: crypto_bigint::Bounded + ConditionallySelectable + Encoding + Integer, + T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, { - type Output = Self; - fn sub(self, rhs: &Self) -> Self::Output { - self.checked_add(&-rhs) - .expect("does not overflow by the construction of the arguments") + fn add_assign(&mut self, rhs: &'a Signed) { + // TODO: implement properly + *self = self.checked_add(rhs).unwrap() } } -impl Mul> for Signed +impl<'a, T> MulAssign<&'a Signed> for Signed where - T: Integer + Encoding + crypto_bigint::Bounded + ConditionallySelectable, + T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, { - type Output = Self; - fn mul(self, rhs: Self) -> Self::Output { - self.checked_mul(&rhs) - .expect("does not overflow by the construction of the arguments") + fn mul_assign(&mut self, rhs: &'a Signed) { + // TODO: implement properly + *self = self.checked_mul(rhs).unwrap() } } -impl Mul<&Signed> for Signed +impl Mul> for Signed where T: Integer + Encoding + crypto_bigint::Bounded + ConditionallySelectable, { type Output = Self; - fn mul(self, rhs: &Self) -> Self::Output { - self.checked_mul(rhs) + fn mul(self, rhs: Self) -> Self::Output { + self.checked_mul(&rhs) .expect("does not overflow by the construction of the arguments") } } -impl core::iter::Sum for Signed -where - T: Integer + Encoding + crypto_bigint::Bounded, -{ - fn sum>(iter: I) -> Self { - iter.reduce(|x, y| x.checked_add(&y).unwrap()) - .unwrap_or(Self::default()) - } -} - -impl<'a, T> core::iter::Sum<&'a Self> for Signed -where - T: Integer + Encoding + crypto_bigint::Bounded, -{ - fn sum>(iter: I) -> Self { - iter.cloned().sum() - } -} - -impl PartialOrd for Signed -where - T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer + PartialOrd, -{ - fn partial_cmp(&self, other: &Self) -> Option { - // The bounds of the two numbers do not come into play, only the signs and absolute values - if bool::from(self.is_negative()) { - if bool::from(other.is_negative()) { - // both are negative, flip comparison - other.abs().partial_cmp(&self.abs()) - } else { - // self is neg, other is not => other is bigger - Some(core::cmp::Ordering::Less) - } - } else if bool::from(other.is_negative()) { - // self is positive, other is not => self is bigger - Some(core::cmp::Ordering::Greater) - } else { - // both are positive, use abs value - self.abs().partial_cmp(&other.abs()) - } - } -} - #[cfg(test)] mod tests { use std::ops::Neg; @@ -620,58 +534,6 @@ mod tests { use crate::uint::U1024; const SEED: u64 = 123; - #[test] - fn partial_ord_pos_vs_pos() { - let bound = 34; - let p1 = Signed::new_from_unsigned(U128::from_u64(10), bound).unwrap(); - let p2 = Signed::new_from_unsigned(U128::from_u64(12), bound).unwrap(); - - assert!(p1 < p2); - assert_eq!(p1, Signed::new_from_unsigned(U128::from_u64(10), bound).unwrap()); - } - - #[test] - fn partial_ord_neg_vs_neg() { - let bound = 114; - let n1 = Signed::new_from_unsigned(U128::from_u64(10), bound).unwrap().neg(); - let n2 = Signed::new_from_unsigned(U128::from_u64(12), bound).unwrap().neg(); - - assert!(n2 < n1); - assert_eq!( - n1 + Signed::new_from_unsigned(U128::from_u64(10), bound).unwrap(), - Signed::new_from_unsigned(U128::ZERO, bound + 1).unwrap() - ); - } - - #[test] - fn partial_ord_pos_vs_neg() { - let bound = 65; - let p = Signed::new_from_unsigned(U128::from_u64(10), bound).unwrap(); - let n = Signed::new_from_unsigned(U128::from_u64(12), bound).unwrap().neg(); - assert!(n < p); - } - - #[test] - fn partial_ord_neg_vs_pos() { - let bound = 93; - let n = Signed::new_from_unsigned(U128::from_u64(10), bound).unwrap().neg(); - let p = Signed::new_from_unsigned(U128::from_u64(12), bound).unwrap(); - assert!(n < p); - } - - #[test] - fn partial_ord_different_bounds() { - let s1 = Signed::new_from_unsigned(U128::from_u8(5), 10).unwrap(); - let s2 = Signed::new_from_unsigned(U128::from_u8(3), 106).unwrap(); - let s3 = Signed::new_from_unsigned(U128::from_u8(30), 127).unwrap(); - let s4 = Signed::new_from_unsigned(U128::from_u8(30), 47).unwrap(); - - assert!(s2 < s1); - assert!(s2 < s3); - assert_ne!(s3, s4); // different bounds compare differently - assert_eq!(s3.abs(), s4.abs()); - } - #[test] fn adding_signed_numbers_increases_the_bound() { let s1 = Signed::new_from_unsigned(U128::from_u8(5), 13).unwrap(); diff --git a/synedrion/src/uint/traits.rs b/synedrion/src/uint/traits.rs index 1369ca4..f05afca 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}; @@ -62,7 +62,7 @@ where let bits = ::BITS; let bound = bound % (2 * bits + 1); - let (lo, hi) = ::from_wide(exp.clone()); + let (lo, hi) = ::from_wide(exp); let lo_res = self.pow_bounded_exp(&lo, core::cmp::min(bits, bound)); // TODO (#34): this may be faster if we could get access to Uint's pow_bounded_exp() that takes @@ -93,7 +93,7 @@ where let bound = exp.bound(); let abs_exponent = exp.abs(); - let (wlo, whi) = ::Wide::from_wide(abs_exponent); + let (wlo, whi) = ::Wide::from_wide(&abs_exponent); let lo_res = self.pow_wide(&wlo, core::cmp::min(bits, bound)); @@ -125,6 +125,25 @@ where abs_result } } + + /// Variable-time exponentiation of an integer in Montgomery form by a "wide" and signed exponent. + /// + /// #Panics + /// + /// Panics if `self` is not invertible. + fn pow_signed_wide_vartime(&self, exp: &Signed<::Wide>) -> Self + where + T: HasWide, + ::Wide: crypto_bigint::Bounded + ConditionallySelectable, + { + let exp_abs = exp.abs(); + let abs_result = self.pow_wide(&exp_abs, exp.bound()); + if exp.is_negative().into() { + abs_result.invert().expect("`self` is assumed invertible") + } else { + abs_result + } + } } pub trait HasWide: Sized + Zero { @@ -134,7 +153,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 to_wide(&self) -> Self::Wide; /// Splits a `Wide` in two halves and returns the halves (`Self` sized) in a /// tuple (lower half first). @@ -142,12 +161,12 @@ pub trait HasWide: Sized + Zero { /// *Note*: The behaviour of this method has changed in v0.2. Previously, /// the order of the halves was `(hi, lo)` but after v0.2 the order is `(lo, /// hi)`. - fn from_wide(value: Self::Wide) -> (Self, Self); + fn from_wide(value: &Self::Wide) -> (Self, Self); /// Tries to convert a `Wide` into a `Self` sized uint. Splits a `Wide` /// value in two halves and returns the lower half if the high half is zero. /// Otherwise returns `None`. - fn try_from_wide(value: Self::Wide) -> Option { + fn try_from_wide(value: &Self::Wide) -> Option { let (lo, hi) = Self::from_wide(value); if hi.is_zero().into() { return Some(lo); @@ -164,11 +183,11 @@ 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 to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } - fn from_wide(value: Self::Wide) -> (Self, Self) { - value.into() + fn from_wide(value: &Self::Wide) -> (Self, Self) { + value.split_mixed() } } @@ -180,11 +199,11 @@ 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 to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } - fn from_wide(value: Self::Wide) -> (Self, Self) { - value.into() + fn from_wide(value: &Self::Wide) -> (Self, Self) { + value.split_mixed() } } @@ -196,11 +215,11 @@ 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 to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } - fn from_wide(value: Self::Wide) -> (Self, Self) { - value.into() + fn from_wide(value: &Self::Wide) -> (Self, Self) { + value.split_mixed() } } @@ -212,11 +231,11 @@ 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 to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } - fn from_wide(value: Self::Wide) -> (Self, Self) { - value.into() + fn from_wide(value: &Self::Wide) -> (Self, Self) { + value.split_mixed() } } diff --git a/synedrion/src/www02/entities.rs b/synedrion/src/www02/entities.rs index b6997ce..49d3a80 100644 --- a/synedrion/src/www02/entities.rs +++ b/synedrion/src/www02/entities.rs @@ -1,5 +1,4 @@ use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, vec::Vec, }; @@ -8,7 +7,6 @@ use core::{fmt::Debug, marker::PhantomData}; use bip32::{DerivationPath, PrivateKey, PrivateKeyBytes, PublicKey}; use k256::ecdsa::{SigningKey, VerifyingKey}; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; use serde::{Deserialize, Serialize}; use crate::{ @@ -17,6 +15,7 @@ use crate::{ tools::{ hashing::{Chain, FofHasher}, sss::{interpolation_coeff, shamir_evaluation_points, shamir_join_points, shamir_split, ShareId}, + Secret, }, }; @@ -28,7 +27,7 @@ pub struct ThresholdKeyShare { // (mainly, that the verifying key is not an identity) pub(crate) owner: I, pub(crate) threshold: u32, - pub(crate) secret_share: SecretBox, + pub(crate) secret_share: Secret, pub(crate) share_ids: BTreeMap, pub(crate) public_shares: BTreeMap, // TODO (#27): this won't be needed when Scalar/Point are a part of `P` @@ -55,13 +54,13 @@ impl ThresholdKeyShare BTreeMap { debug_assert!(threshold <= ids.len()); // TODO (#68): make the method fallible - let secret = match signing_key { + let secret = Secret::init_with(|| match signing_key { None => Scalar::random(rng), Some(sk) => Scalar::from(sk.as_nonzero_scalar()), - }; + }); let share_ids = shamir_evaluation_points(ids.len()); - let secret_shares = shamir_split(rng, &secret, threshold, &share_ids); + let secret_shares = shamir_split(rng, secret, threshold, &share_ids); let share_ids = ids.iter().cloned().zip(share_ids).collect::>(); let public_shares = share_ids @@ -76,7 +75,7 @@ impl ThresholdKeyShare ThresholdKeyShare>(); let share_ids_set = share_ids.values().cloned().collect(); - let secret_share = SecretBox::new(Box::new( - self.secret_share.expose_secret() * &interpolation_coeff(&share_ids_set, &share_id), - )); + let secret_share = self.secret_share.clone() * interpolation_coeff(&share_ids_set, &share_id); let public_shares = ids .iter() .map(|id| { @@ -150,12 +147,10 @@ impl ThresholdKeyShare>(); let share_ids_set = share_ids.values().cloned().collect(); - let secret_share = SecretBox::new(Box::new( - key_share.secret_share.expose_secret() - * &interpolation_coeff(&share_ids_set, &share_ids[key_share.owner()]) - .invert() - .expect("the interpolation coefficient is a non-zero scalar"), - )); + let secret_share = key_share.secret_share.clone() + * interpolation_coeff(&share_ids_set, &share_ids[key_share.owner()]) + .invert() + .expect("the interpolation coefficient is a non-zero scalar"); let public_shares = ids .iter() .map(|id| { @@ -183,15 +178,9 @@ impl ThresholdKeyShare EntryPoint for KeyResharing { }; let old_holder = self.old_holder.map(|old_holder| { - let polynomial = Polynomial::random( - rng, - old_holder.key_share.secret_share.expose_secret(), - self.new_threshold, - ); + let polynomial = Polynomial::random(rng, old_holder.key_share.secret_share.clone(), self.new_threshold); let public_polynomial = polynomial.public(); OldHolderData { @@ -241,11 +235,11 @@ struct Round1BroadcastMessage { #[derive(Debug, Clone, Serialize, Deserialize)] struct Round1DirectMessage { - subshare: Scalar, + subshare: Secret, } struct Round1Payload { - subshare: Scalar, + subshare: Secret, public_polynomial: PublicPolynomial, old_share_id: ShareId, } @@ -396,9 +390,9 @@ impl Round for Round1 { .inputs .old_holders .iter() - .map(|id| (payloads[id].old_share_id, payloads[id].subshare)) + .map(|id| (payloads[id].old_share_id, payloads[id].subshare.clone())) .collect::>(); - let secret_share = SecretBox::new(Box::new(shamir_join_scalars(&subshares))); + let secret_share = shamir_join_scalars(subshares); // Generate the public shares of all the new holders. let public_shares = self @@ -435,7 +429,6 @@ mod tests { session::signature::Keypair, }; use rand_core::OsRng; - use secrecy::ExposeSecret; use super::{KeyResharing, NewHolder, OldHolder, ThresholdKeyShare}; use crate::TestParams; @@ -528,7 +521,7 @@ mod tests { // Check that the public keys correspond to the secret key shares for share in shares.values() { - let public = share.secret_share.expose_secret().mul_by_generator(); + let public = share.secret_share.mul_by_generator(); assert_eq!(public, share.public_shares[&share.owner]); } }