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..cce9a79 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, 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,7 +112,7 @@ pub(crate) struct PresigningData { #[derive(Debug, Clone)] pub(crate) struct PresigningValues { - pub(crate) hat_beta: SecretBox::Uint>>, + pub(crate) hat_beta: Secret::Uint>>, pub(crate) hat_r: RandomizerWire, pub(crate) hat_s: RandomizerWire, pub(crate) cap_k: Ciphertext, @@ -130,9 +129,7 @@ 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() @@ -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..6f39058 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,12 +15,15 @@ use manul::protocol::{ ReceiveError, Round, RoundId, Serializer, }; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; +use secrecy::ExposeSecret; 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::{ @@ -29,7 +31,7 @@ use crate::{ paillier::{Ciphertext, CiphertextWire, PaillierParams, Randomizer, RandomizerWire}, tools::{ hashing::{Chain, FofHasher, HashOutput}, - DowncastMap, Without, + DowncastMap, Secret, Without, }, uint::Signed, }; @@ -154,17 +156,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.to_wire()); 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.to_wire()); Ok(BoxedRound::new_dynamic(Round1 { context: Context { @@ -193,8 +195,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 +270,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,8 +390,8 @@ struct Round2Message { #[derive(Debug, Clone)] struct Round2Artifact { - beta: SecretBox::Uint>>, - hat_beta: SecretBox::Uint>>, + beta: Secret::Uint>>, + hat_beta: Secret::Uint>>, r: RandomizerWire, s: RandomizerWire, hat_r: RandomizerWire, @@ -440,28 +442,31 @@ 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.to_wire()); + let cap_d = &self.all_cap_k[destination] * &gamma + + Ciphertext::new_with_randomizer_signed(target_pk, &-&beta, &s.to_wire()); - let hat_cap_f = Ciphertext::new_with_randomizer_signed(pk, hat_beta.expose_secret(), &hat_r.to_wire()); + let hat_cap_f = Ciphertext::new_with_randomizer_signed(pk, &hat_beta, &hat_r.to_wire()); let hat_cap_d = &self.all_cap_k[destination] - * P::signed_from_scalar(self.context.key_share.secret_share.expose_secret()) - + Ciphertext::new_with_randomizer_signed(target_pk, &-hat_beta.expose_secret(), &hat_s.to_wire()); + * secret_signed_from_scalar::

(&self.context.key_share.secret_share) + + Ciphertext::new_with_randomizer_signed(target_pk, &-&hat_beta, &hat_s.to_wire()); let public_aux = &self.context.aux_info.public_aux[destination]; let rp = &public_aux.rp_params; let psi = AffGProof::new( rng, - &P::signed_from_scalar(&self.context.gamma), + &gamma, &beta, s.clone(), r.clone(), @@ -477,7 +482,7 @@ impl Round for Round2 { let hat_psi = AffGProof::new( rng, - &P::signed_from_scalar(self.context.key_share.secret_share.expose_secret()), + &x, &hat_beta, hat_s.clone(), hat_r.clone(), @@ -493,7 +498,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], @@ -635,22 +640,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 beta_sum: Secret> = artifacts.values().map(|p| &p.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_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 +679,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 +694,13 @@ struct Round3 { #[serde(bound(serialize = "LogStarProof

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

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

, } struct Round3Payload { - delta: Scalar, + delta: Secret, cap_delta: Point, } @@ -733,7 +737,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 +750,7 @@ impl Round for Round3 { let dm = DirectMessage::new( serializer, Round3Message::

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

(&self.delta), cap_delta: self.cap_delta, psi_pprime, }, @@ -805,12 +809,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 +843,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,7 +882,7 @@ 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), @@ -910,12 +914,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)) + let cap_h = (&self.all_cap_g[&self.context.my_id] * secret_bounded_from_scalar::

(&self.context.k)) .mul_randomizer(&rho.to_wire()); 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 +961,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 +995,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,7 +1108,7 @@ impl Round for Round4 { let p_aff_g = AffGProof::

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

(&self.context.key_share.secret_share), &values.hat_beta, values.hat_s.to_precomputed(target_pk), values.hat_r.to_precomputed(pk), @@ -1139,8 +1143,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.to_wire()); let aux = (&self.context.ssid_hash, &my_id); @@ -1149,7 +1152,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 +1188,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..1bf97b2 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,16 +17,16 @@ use manul::protocol::{ ReceiveError, Round, RoundId, Serializer, }; use rand_core::CryptoRngCore; -use secrecy::SecretBox; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::{ entities::{AuxInfo, KeyShareChange, PublicAuxInfo, SecretAuxInfo}, - params::SchemeParams, + params::{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, @@ -35,7 +34,7 @@ use crate::{ tools::{ bitvec::BitVec, hashing::{Chain, FofHasher, HashOutput}, - DowncastMap, Without, + DowncastMap, Secret, Without, }, }; @@ -160,7 +159,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 +171,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 +265,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 +561,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 +604,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 +651,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::init_with(|| scalar_from_uint::

(&enc_x.decrypt(&self.context.paillier_sk))); let my_idx = self.context.ids_ordering[&self.context.my_id]; @@ -657,7 +660,7 @@ impl Round for Round3 { return Err(ReceiveError::protocol(KeyRefreshError( KeyRefreshErrorEnum::Round3MismatchedSecret { cap_c: direct_message.data2.paillier_enc_x, - x, + x: *x.expose_secret(), mu: mu.to_wire(), }, ))); @@ -717,7 +720,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 +757,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, }; @@ -819,7 +822,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 +831,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..d8d2013 100644 --- a/synedrion/src/cggmp21/params.rs +++ b/synedrion/src/cggmp21/params.rs @@ -5,12 +5,17 @@ use core::fmt::Debug; // So as long as that is the case, `k256` `Uint` is separate // from the one used throughout the crate. use k256::elliptic_curve::bigint::Uint as K256Uint; +use secrecy::{ExposeSecret, ExposeSecretMut}; use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; use crate::{ curve::{Curve, Scalar, ORDER}, paillier::PaillierParams, - tools::hashing::{Chain, HashableType}, + tools::{ + hashing::{Chain, HashableType}, + Secret, + }, uint::{ subtle::ConditionallySelectable, Bounded, Encoding, NonZero, Signed, U1024Mod, U2048Mod, U4096Mod, U512Mod, Uint, Zero, U1024, U2048, U4096, U512, U8192, @@ -123,73 +128,130 @@ 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. - fn scalar_from_wide_uint(value: &::WideUint) -> Scalar { - let r = *value % Self::CURVE_ORDER_WIDE; +/// 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(); + 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 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()) - } +/// 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()) +} + +pub(crate) fn secret_uint_from_scalar( + value: &Secret, +) -> Secret<::Uint> { + let scalar_bytes = Secret::init_with(|| value.expose_secret().to_bytes()); + let mut repr = Secret::init_with(|| ::Uint::zero().to_be_bytes()); + + let uint_len = repr.expose_secret().as_ref().len(); + let scalar_len = scalar_bytes.expose_secret().len(); + + debug_assert!(uint_len >= scalar_len); + repr.expose_secret_mut().as_mut()[uint_len - scalar_len..].copy_from_slice(scalar_bytes.expose_secret()); + Secret::init_with(|| { + let mut repr = *repr.expose_secret(); + let result = ::Uint::from_be_bytes(repr); + repr.zeroize(); + result + }) +} + +pub(crate) fn secret_signed_from_scalar( + value: &Secret, +) -> Secret::Uint>> { + Secret::init_with(|| { + let mut uint = *secret_uint_from_scalar::

(value).expose_secret(); + let result = Signed::new_positive(uint, ORDER.bits_vartime() as u32).expect(concat![ + "a curve scalar value is smaller than the curve order, ", + "and the curve order fits in `PaillierParams::Uint`" + ]); + uint.zeroize(); + result + }) +} + +pub(crate) fn secret_bounded_from_scalar( + value: &Secret, +) -> Secret::Uint>> { + Secret::init_with(|| { + let mut uint = *secret_uint_from_scalar::

(value).expose_secret(); + let result = Bounded::new(uint, ORDER.bits_vartime() as u32).expect(concat![ + "a curve scalar value is smaller than the curve order, ", + "and the curve order fits in `PaillierParams::Uint`" + ]); + uint.zeroize(); + result + }) +} + +pub(crate) fn secret_scalar_from_signed( + value: &Secret::Uint>>, +) -> Secret { + // TODO: wrap in secrets properly + let abs_value = 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..e86aee8 100644 --- a/synedrion/src/cggmp21/sigma/aff_g.rs +++ b/synedrion/src/cggmp21/sigma/aff_g.rs @@ -1,17 +1,23 @@ //! 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 secrecy::ExposeSecret; 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, }, - tools::hashing::{Chain, Hashable, XofHasher}, + tools::{ + hashing::{Chain, Hashable, XofHasher}, + Secret, + }, uint::Signed, }; @@ -61,8 +67,8 @@ impl AffGProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, - y: &SecretBox::Uint>>, + x: &Secret::Uint>>, + y: &Secret::Uint>>, rho: Randomizer, rho_y: Randomizer, pk0: &PublicKeyPaillier, @@ -74,7 +80,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,8 +88,8 @@ impl AffGProof

{ let hat_cap_n = &setup.modulus_bounded(); - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); - let beta = Signed::random_bounded_bits(rng, P::LP_BOUND + P::EPS_BOUND); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); + let beta = Secret::init_with(|| Signed::random_bounded_bits(rng, P::LP_BOUND + P::EPS_BOUND)); let r_mod = Randomizer::random(rng, pk0); let r_y_mod = Randomizer::random(rng, pk1); @@ -93,8 +99,8 @@ impl AffGProof

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

(&alpha).mul_by_generator(); let cap_b_y = Ciphertext::new_with_randomizer_signed(pk1, &beta, &r_y_mod.to_wire()).to_wire(); let cap_e = setup.commit(&alpha, &gamma).to_wire(); let cap_s = setup.commit(x, &m).to_wire(); @@ -103,7 +109,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,15 +133,15 @@ 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; @@ -220,14 +226,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_nonsecret_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,7 +242,7 @@ impl AffGProof

{ // Original: `Y^e`. Modified `Y^{-e}`. // (1 + N_1)^{z_2} \omega_y^{N_1} = B_y Y^(-e) \mod N_1^2 // => encrypt_1(z_2, \omega_y) = B_y (+) Y (*) (-e) - if Ciphertext::new_with_randomizer_signed(pk1, &self.z2, &self.omega_y) + if Ciphertext::new_nonsecret_with_randomizer_signed(pk1, &self.z2, &self.omega_y) != cap_y * (-e) + self.cap_b_y.to_precomputed(pk1) { return false; @@ -245,14 +251,14 @@ impl AffGProof

{ // s^{z_1} t^{z_3} = E S^e \mod \hat{N} let cap_e_mod = self.cap_e.to_precomputed(setup); let cap_s_mod = self.cap_s.to_precomputed(setup); - if setup.commit(&self.z1, &self.z3) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z1, &self.z3) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } // s^{z_2} t^{z_4} = F T^e \mod \hat{N} let cap_f_mod = self.cap_f.to_precomputed(setup); let cap_t_mod = self.cap_t.to_precomputed(setup); - if setup.commit(&self.z2, &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z2, &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e) { return false; } @@ -263,12 +269,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,17 +293,17 @@ 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.to_wire()); + let cap_y = Ciphertext::new_with_randomizer_signed(pk1, &y, &rho_y.to_wire()); + 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, diff --git a/synedrion/src/cggmp21/sigma/dec.rs b/synedrion/src/cggmp21/sigma/dec.rs index b15b5fb..75b603f 100644 --- a/synedrion/src/cggmp21/sigma/dec.rs +++ b/synedrion/src/cggmp21/sigma/dec.rs @@ -1,9 +1,13 @@ //! Paillier decryption modulo $q$ ($\Pi^{dec}$, Section C.6, Fig. 30) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, scalar_from_wide_signed, secret_scalar_from_signed}, + SchemeParams, +}; use crate::{ curve::Scalar, paillier::{ @@ -11,6 +15,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -46,7 +51,7 @@ impl DecProof

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

{ let hat_cap_n = &setup.modulus_bounded(); // $\hat{N}$ - let alpha = Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND); + let alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let mu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); let nu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); let r = Randomizer::random(rng, pk0); @@ -66,7 +71,10 @@ impl DecProof

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

(&alpha).expose_secret(); let mut reader = XofHasher::new_with_dst(HASH_TAG) // commitments @@ -88,8 +96,8 @@ 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 + e.to_wide() * mu; let omega = (r * rho.pow_signed_vartime(&e)).to_wire(); @@ -137,21 +145,21 @@ impl DecProof

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

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

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

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

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

{ // s^{z_1} t^{z_3} == C S^e \mod \hat{N} let cap_c_mod = self.cap_c.to_precomputed(setup); let cap_s_mod = self.cap_s.to_precomputed(setup); - if setup.commit(&self.z1, &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret(&self.z1, &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -152,6 +154,7 @@ mod tests { use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, RPParams, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -167,7 +170,7 @@ mod tests { let aux: &[u8] = b"abcde"; - let secret = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + let secret = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let randomizer = Randomizer::random(&mut OsRng, pk); let ciphertext = Ciphertext::new_with_randomizer_signed(pk, &secret, &randomizer.to_wire()); diff --git a/synedrion/src/cggmp21/sigma/fac.rs b/synedrion/src/cggmp21/sigma/fac.rs index ca88d62..41f7a9f 100644 --- a/synedrion/src/cggmp21/sigma/fac.rs +++ b/synedrion/src/cggmp21/sigma/fac.rs @@ -7,7 +7,10 @@ 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,10 +65,12 @@ impl FacProof

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

{ let p = sk0.p_signed(); let q = sk0.q_signed(); - let cap_p = setup.commit(p.expose_secret(), &mu).to_wire(); - let cap_q = setup.commit(q.expose_secret(), &nu); + let cap_p = setup.commit(&p, &mu).to_wire(); + let cap_q = setup.commit_secret(&q, &nu); let cap_a = setup.commit_wide(&alpha, &x).to_wire(); let cap_b = setup.commit_wide(&beta, &y).to_wire(); - let cap_t = (&cap_q.pow_signed_wide(&alpha) * &setup.commit_base_xwide(&r)).to_wire(); + let cap_t = (&cap_q.pow_secret_signed_wide(&alpha) * &setup.commit_base_xwide(&r)).to_wire(); let cap_q = cap_q.to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) @@ -106,16 +111,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 hat_sigma = sigma - ((p_wide * &nu).to_wide()).expose_secret(); + let z1 = *(alpha + (p * e).to_wide()).expose_secret(); + let z2 = *(beta + (q * e).to_wide()).expose_secret(); let omega1 = x + e_wide * mu; - let omega2 = y + e_wide * nu; - let v = r + (e_wide.into_wide() * hat_sigma); + let omega2 = *(nu * e_wide + &y).expose_secret(); + let v = r + e_wide.to_wide() * hat_sigma; Self { e, @@ -161,19 +166,19 @@ 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_xwide(&Secret::init_with(|| 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) { + if setup.commit_nonsecret_wide(&self.z1, &self.omega1) != &cap_a_mod * &cap_p_mod.pow_signed_vartime(&e) { return false; } // s^{z_2} t^{\omega_2} == B * Q^e \mod \hat{N} let cap_b_mod = self.cap_b.to_precomputed(setup); let cap_q_mod = self.cap_q.to_precomputed(setup); - if setup.commit_wide(&self.z2, &self.omega2) != &cap_b_mod * &cap_q_mod.pow_signed_vartime(&e) { + if setup.commit_nonsecret_wide(&self.z2, &self.omega2) != &cap_b_mod * &cap_q_mod.pow_signed_vartime(&e) { return false; } diff --git a/synedrion/src/cggmp21/sigma/log_star.rs b/synedrion/src/cggmp21/sigma/log_star.rs index 7c9b1f6..edfb075 100644 --- a/synedrion/src/cggmp21/sigma/log_star.rs +++ b/synedrion/src/cggmp21/sigma/log_star.rs @@ -1,9 +1,13 @@ //! Knowledge of Exponent vs Paillier Encryption ($\Pi^{log*}$, Section C.2, Fig. 25) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, secret_scalar_from_signed}, + SchemeParams, +}; use crate::{ curve::Point, paillier::{ @@ -11,6 +15,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -46,7 +51,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 +60,19 @@ impl LogStarProof

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

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

{ // Non-interactive challenge let e = Signed::from_xof_reader_bounded(&mut reader, &P::CURVE_ORDER); - let z1 = alpha + e * x; + let z1 = *(alpha + x * e).expose_secret(); let z2 = (r * rho.pow_signed_vartime(&e)).to_wire(); - let z3 = gamma + mu * e.into_wide(); + let z3 = gamma + mu * e.to_wide(); Self { e, @@ -144,20 +149,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_nonsecret_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) { + if setup.commit_nonsecret(&self.z1, &self.z3) != &cap_d_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -171,9 +176,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 +196,10 @@ mod tests { let aux: &[u8] = b"abcde"; let g = Point::GENERATOR * Scalar::random(&mut OsRng); - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + let x = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let rho = Randomizer::random(&mut OsRng, pk); let cap_c = Ciphertext::new_with_randomizer_signed(pk, &x, &rho.to_wire()); - let cap_x = g * Params::scalar_from_signed(&x); + let cap_x = g * secret_scalar_from_signed::(&x); let proof = LogStarProof::::new(&mut OsRng, &x, &rho, pk, &cap_c, &g, &cap_x, &setup, &aux); assert!(proof.verify(pk, &cap_c, &g, &cap_x, &setup, &aux)); diff --git a/synedrion/src/cggmp21/sigma/mul.rs b/synedrion/src/cggmp21/sigma/mul.rs index 29414de..15852e4 100644 --- a/synedrion/src/cggmp21/sigma/mul.rs +++ b/synedrion/src/cggmp21/sigma/mul.rs @@ -1,12 +1,16 @@ //! Paillier multiplication ($\Pi^{mul}$, Section C.6, Fig. 29) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::super::SchemeParams; use crate::{ paillier::{Ciphertext, CiphertextWire, PaillierParams, PublicKeyPaillier, Randomizer, RandomizerWire}, - tools::hashing::{Chain, Hashable, XofHasher}, + tools::{ + hashing::{Chain, Hashable, XofHasher}, + Secret, + }, uint::{Bounded, Retrieve, Signed}, }; @@ -42,7 +46,7 @@ impl MulProof

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

{ let r_mod = Randomizer::random(rng, pk); let s_mod = Randomizer::random(rng, pk); - let alpha = Bounded::new(alpha_mod.retrieve(), ::MODULUS_BITS) - .expect("the value is bounded by `MODULUS_BITS` by construction"); + let alpha = Secret::init_with(|| { + Bounded::new(alpha_mod.retrieve(), ::MODULUS_BITS) + .expect("the value is bounded by `MODULUS_BITS` by construction") + }); let r = r_mod.to_wire(); let s = s_mod.to_wire(); - let cap_a = (cap_y * alpha).mul_randomizer(&r).to_wire(); - let cap_b = Ciphertext::new_with_randomizer(pk, alpha.as_ref(), &s).to_wire(); + let cap_a = (cap_y * &alpha).mul_randomizer(&r).to_wire(); + let cap_b = Ciphertext::new_with_randomizer_bounded(pk, &alpha, &s).to_wire(); let mut reader = XofHasher::new_with_dst(HASH_TAG) // commitments @@ -82,11 +88,12 @@ 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); + + x.mul_wide(&e)) + .expose_secret(); let u = (r_mod * rho.pow_signed_vartime(&e)).to_wire(); let v = (s_mod * rho_x.pow_signed_vartime(&e)).to_wire(); @@ -138,7 +145,9 @@ impl MulProof

{ // enc(z, v) == B * X^e \mod N^2 // (Note: typo in the paper, it uses `c` and not `v` here) - if Ciphertext::new_with_randomizer_wide(pk, &self.z, &self.v) != self.cap_b.to_precomputed(pk) + cap_x * e { + if Ciphertext::new_nonsecret_with_randomizer_wide(pk, &self.z, &self.v) + != self.cap_b.to_precomputed(pk) + cap_x * e + { return false; } @@ -154,6 +163,7 @@ mod tests { use crate::{ cggmp21::{SchemeParams, TestParams}, paillier::{Ciphertext, Randomizer, SecretKeyPaillierWire}, + tools::Secret, uint::Signed, }; @@ -167,14 +177,14 @@ mod tests { let aux: &[u8] = b"abcde"; - let x = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); - let y = Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND); + let x = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); + let y = Secret::init_with(|| Signed::random_bounded_bits(&mut OsRng, Params::L_BOUND)); let rho_x = Randomizer::random(&mut OsRng, pk); let rho = Randomizer::random(&mut OsRng, pk); let cap_x = Ciphertext::new_with_randomizer_signed(pk, &x, &rho_x.to_wire()); let cap_y = Ciphertext::new_signed(&mut OsRng, pk, &y); - let cap_c = (&cap_y * x).mul_randomizer(&rho.to_wire()); + let cap_c = (&cap_y * &x).mul_randomizer(&rho.to_wire()); let proof = MulProof::::new(&mut OsRng, &x, &rho_x, &rho, pk, &cap_x, &cap_y, &cap_c, &aux); assert!(proof.verify(pk, &cap_x, &cap_y, &cap_c, &aux)); diff --git a/synedrion/src/cggmp21/sigma/mul_star.rs b/synedrion/src/cggmp21/sigma/mul_star.rs index f48e15c..507d435 100644 --- a/synedrion/src/cggmp21/sigma/mul_star.rs +++ b/synedrion/src/cggmp21/sigma/mul_star.rs @@ -1,9 +1,13 @@ //! Multiplication Paillier vs Group ($\Pi^{mul}$, Section C.6, Fig. 31) use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; -use super::super::SchemeParams; +use super::super::{ + params::{scalar_from_signed, secret_scalar_from_signed}, + SchemeParams, +}; use crate::{ curve::Point, paillier::{ @@ -11,6 +15,7 @@ use crate::{ RandomizerWire, }, tools::hashing::{Chain, Hashable, XofHasher}, + tools::Secret, uint::Signed, }; @@ -46,7 +51,7 @@ impl MulStarProof

{ #[allow(clippy::too_many_arguments)] pub fn new( rng: &mut impl CryptoRngCore, - x: &Signed<::Uint>, + x: &Secret::Uint>>, rho: &Randomizer, pk0: &PublicKeyPaillier, cap_c: &Ciphertext, @@ -62,19 +67,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 alpha = Secret::init_with(|| Signed::random_bounded_bits(rng, P::L_BOUND + P::EPS_BOUND)); let gamma = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n); let m = Signed::random_bounded_bits_scaled(rng, P::L_BOUND, hat_cap_n); - let cap_a = (cap_c * alpha).mul_randomizer(&r.to_wire()).to_wire(); - let cap_b_x = P::scalar_from_signed(&alpha).mul_by_generator(); + let cap_a = (cap_c * &alpha).mul_randomizer(&r.to_wire()).to_wire(); + let cap_b_x = secret_scalar_from_signed::

(&alpha).mul_by_generator(); let cap_e = setup.commit(&alpha, &gamma).to_wire(); let cap_s = setup.commit(x, &m).to_wire(); @@ -96,8 +101,8 @@ 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 z1 = *(alpha + x * e).expose_secret(); + let z2 = gamma + e.to_wide() * m; let omega = (r * rho.pow_signed(&e)).to_wire(); Self { @@ -159,14 +164,14 @@ impl MulStarProof

{ } // 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) { + if setup.commit_nonsecret(&self.z1, &self.z2) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) { return false; } @@ -180,8 +185,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 +203,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.to_wire()); + 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/sch.rs b/synedrion/src/cggmp21/sigma/sch.rs index 6e52e46..3b8d6e9 100644 --- a/synedrion/src/cggmp21/sigma/sch.rs +++ b/synedrion/src/cggmp21/sigma/sch.rs @@ -4,6 +4,7 @@ //! where $g$ is a EC generator. use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use zeroize::ZeroizeOnDrop; @@ -11,7 +12,7 @@ use crate::{ curve::{Point, Scalar}, tools::{ hashing::{Chain, FofHasher, Hashable}, - HideDebug, + Secret, }, }; @@ -21,12 +22,12 @@ const HASH_TAG: &[u8] = b"P_sch"; #[derive(Debug, Clone, ZeroizeOnDrop)] 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 +74,13 @@ pub(crate) struct SchProof { impl SchProof { pub fn new( proof_secret: &SchSecret, - x: &Scalar, + x: &Secret, commitment: &SchCommitment, cap_x: &Point, aux: &impl Hashable, ) -> Self { let challenge = SchChallenge::new(cap_x, commitment, aux); - let proof = *proof_secret.0 + challenge.0 * x; + let proof = *(&proof_secret.0 + x * challenge.0).expose_secret(); Self { challenge, proof } } @@ -94,11 +95,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..347f922 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,15 @@ use k256::{ Secp256k1, }; use rand_core::CryptoRngCore; -use secrecy::{CloneableSecret, SerializableSecret}; +use secrecy::ExposeSecret; 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 +58,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 +111,13 @@ 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(|| { + let mut scalar = *sk.as_nonzero_scalar().as_ref(); + let result = Self(scalar); + scalar.zeroize(); + result + }) } pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result { @@ -124,18 +128,35 @@ 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::try_init_with(|| { + let mut scalar = self.expose_secret().0; + let nz_scalar = Option::from(NonZeroScalar::new(scalar)).ok_or(()); + scalar.zeroize(); + nz_scalar + }) + .ok()?; + // 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 +190,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,6 +282,12 @@ impl Add<&Scalar> for &Scalar { } } +impl AddAssign<&Scalar> for Scalar { + fn add_assign(&mut self, other: &Scalar) { + self.0.add_assign(&other.0) + } +} + impl Add for Point { type Output = Point; @@ -299,6 +320,12 @@ impl Sub<&Scalar> for &Scalar { } } +impl SubAssign<&Scalar> for Scalar { + fn sub_assign(&mut self, other: &Scalar) { + self.0.sub_assign(&other.0) + } +} + impl Mul for Point { type Output = Point; @@ -347,6 +374,12 @@ impl Mul<&Scalar> for &Scalar { } } +impl MulAssign<&Scalar> for Scalar { + fn mul_assign(&mut self, other: &Scalar) { + self.0.mul_assign(&other.0) + } +} + impl core::iter::Sum for Scalar { fn sum>(iter: I) -> Self { iter.reduce(Add::add).unwrap_or(Self::ZERO) diff --git a/synedrion/src/paillier/encryption.rs b/synedrion/src/paillier/encryption.rs index d6060e2..a9650a8 100644 --- a/synedrion/src/paillier/encryption.rs +++ b/synedrion/src/paillier/encryption.rs @@ -13,9 +13,12 @@ use super::{ keys::{PublicKeyPaillier, SecretKeyPaillier}, params::PaillierParams, }; -use crate::uint::{ - subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}, - Bounded, Exponentiable, HasWide, Retrieve, Signed, ToMontgomery, +use crate::{ + tools::Secret, + uint::{ + subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}, + Bounded, Exponentiable, HasWide, Retrieve, Signed, ToMontgomery, + }, }; // A ciphertext randomizer (an invertible element of $\mathbb{Z}_N$). @@ -118,7 +121,7 @@ impl Ciphertext

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

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

, plaintext_is_negative: Choice, ) -> Self { @@ -139,14 +142,18 @@ impl Ciphertext

{ // Simplify `(N + 1)^m mod N^2 == 1 + m * N mod N^2`. // Since `m` can be negative, we calculate `m * N +- 1` (never overflows since `m < N`), // then conditionally negate modulo N^2 + + // TODO: wrap all the secrets in this method + let abs_plaintext = *abs_plaintext.expose_secret(); + 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,35 +167,81 @@ impl Ciphertext

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

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

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

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

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

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

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

, plaintext: &Signed, randomizer: &RandomizerWire

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

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

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

, plaintext: &Signed, randomizer: &RandomizerWire

, ) -> Self { - let plaintext_reduced = P::Uint::try_from_wide(plaintext.abs() % pk.modulus_wide_nonzero()) - .expect("the number within range after reducing modulo N"); + Self::new_with_randomizer_wide(pk, &Secret::init_with(|| *plaintext), randomizer) + } + + pub fn new_with_randomizer_wide( + pk: &PublicKeyPaillier

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

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

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

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

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

, + plaintext: &Secret>, + ) -> Self { Self::new_with_randomizer_signed(pk, plaintext, &RandomizerWire::random(rng, pk)) } @@ -271,14 +324,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 +346,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 +354,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), @@ -319,9 +372,9 @@ impl Ciphertext

{ pub fn mul_randomizer(self, randomizer: &RandomizerWire

) -> 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, @@ -337,7 +390,7 @@ impl Ciphertext

{ } } -impl Add for Ciphertext

{ +impl Add> for Ciphertext

{ type Output = Ciphertext

; fn add(self, other: Ciphertext

) -> Ciphertext

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

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

{ + type Output = Ciphertext

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

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

{ type Output = Ciphertext

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

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

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

{ + type Output = Ciphertext

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

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

{ + type Output = Ciphertext

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

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

{ + type Output = Ciphertext

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

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

{ + type Output = Ciphertext

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

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

{ + type Output = Ciphertext

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

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

{ type Output = Ciphertext

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

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

{ } } +impl Mul>> for Ciphertext

{ + type Output = Ciphertext

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

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

{ + type Output = Ciphertext

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

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

{ + type Output = Ciphertext

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

{ + self.homomorphic_mul_unsigned_ref(other.expose_secret()) + } +} + #[cfg(test)] mod tests { use crypto_bigint::{Encoding, Integer, ShrVartime, WrappingSub}; use rand_core::OsRng; + use secrecy::ExposeSecret; use super::{ super::{params::PaillierTest, PaillierParams, SecretKeyPaillierWire}, Ciphertext, Randomizer, }; - use crate::uint::{ - subtle::{ConditionallyNegatable, ConditionallySelectable}, - HasWide, NonZero, RandomMod, Signed, + use crate::{ + tools::Secret, + uint::{ + subtle::{ConditionallyNegatable, ConditionallySelectable}, + HasWide, NonZero, RandomMod, Signed, + }, }; fn mul_mod(lhs: &T, rhs: &Signed, modulus: &NonZero) -> T @@ -403,7 +523,7 @@ 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 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() @@ -428,10 +548,11 @@ mod tests { fn roundtrip() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext = Ciphertext::::new(&mut OsRng, pk, &plaintext); let plaintext_back = ciphertext.decrypt(&sk); - assert_eq!(plaintext, plaintext_back); + assert_eq!(plaintext.expose_secret(), &plaintext_back); let ciphertext_wire = ciphertext.to_wire(); let ciphertext_back = ciphertext_wire.to_precomputed(pk); @@ -442,10 +563,12 @@ mod tests { fn signed_roundtrip() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2); + let plaintext = Secret::init_with(|| { + Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2) + }); let ciphertext = Ciphertext::new_signed(&mut OsRng, pk, &plaintext); let plaintext_back = ciphertext.decrypt_signed(&sk); - let plaintext_reduced = reduce::(&plaintext, &pk.modulus_nonzero()); + let plaintext_reduced = reduce::(plaintext.expose_secret(), &pk.modulus_nonzero()); assert_eq!(plaintext_reduced, plaintext_back); } @@ -453,7 +576,8 @@ mod tests { fn derive_randomizer() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let randomizer = Randomizer::random(&mut OsRng, pk); let ciphertext = Ciphertext::::new_with_randomizer(pk, &plaintext, &randomizer.to_wire()); let randomizer_back = ciphertext.derive_randomizer(&sk); @@ -464,14 +588,18 @@ mod tests { fn homomorphic_mul() { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext = Ciphertext::::new(&mut OsRng, pk, &plaintext); let coeff = Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2); let new_ciphertext = ciphertext * coeff; let new_plaintext = new_ciphertext.decrypt(&sk); - assert_eq!(mul_mod(&plaintext, &coeff, &pk.modulus_nonzero()), new_plaintext); + assert_eq!( + mul_mod(plaintext.expose_secret(), &coeff, &pk.modulus_nonzero()), + new_plaintext + ); } #[test] @@ -479,16 +607,23 @@ mod tests { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext1 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext1 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext1 = Ciphertext::::new(&mut OsRng, pk, &plaintext1); - let plaintext2 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext2 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext2 = Ciphertext::::new(&mut OsRng, pk, &plaintext2); let new_ciphertext = ciphertext1 + ciphertext2; let new_plaintext = new_ciphertext.decrypt(&sk); - assert_eq!(plaintext1.add_mod(&plaintext2, pk.modulus()), new_plaintext); + assert_eq!( + plaintext1 + .expose_secret() + .add_mod(plaintext2.expose_secret(), pk.modulus()), + new_plaintext + ); } #[test] @@ -496,9 +631,11 @@ mod tests { let sk = SecretKeyPaillierWire::::random(&mut OsRng).into_precomputed(); let pk = sk.public_key(); - let plaintext1 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext1 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let plaintext2 = Signed::random_bounded_bits(&mut OsRng, ::Uint::BITS - 2); - let plaintext3 = ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero()); + let plaintext3 = + Secret::init_with(|| ::Uint::random_mod(&mut OsRng, &pk.modulus_nonzero())); let ciphertext1 = Ciphertext::::new(&mut OsRng, pk, &plaintext1); let ciphertext3 = Ciphertext::::new(&mut OsRng, pk, &plaintext3); @@ -506,7 +643,8 @@ mod tests { let plaintext_back = result.decrypt(&sk); assert_eq!( - mul_mod(&plaintext1, &plaintext2, &pk.modulus_nonzero()).add_mod(&plaintext3, pk.modulus()), + mul_mod(plaintext1.expose_secret(), &plaintext2, &pk.modulus_nonzero()) + .add_mod(plaintext3.expose_secret(), pk.modulus()), plaintext_back ); } diff --git a/synedrion/src/paillier/keys.rs b/synedrion/src/paillier/keys.rs index 7886acb..51366d7 100644 --- a/synedrion/src/paillier/keys.rs +++ b/synedrion/src/paillier/keys.rs @@ -5,7 +5,7 @@ use core::{ use crypto_bigint::{InvMod, Monty, Odd, ShrVartime, Square, WrappingAdd}; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; +use secrecy::{ExposeSecret, ExposeSecretMut}; use serde::{Deserialize, Serialize}; use super::{ @@ -74,10 +74,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 +87,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 +97,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 +117,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 +125,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 +165,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 } @@ -213,12 +212,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 +251,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 +355,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 diff --git a/synedrion/src/paillier/params.rs b/synedrion/src/paillier/params.rs index 7e09035..d53ee1f 100644 --- a/synedrion/src/paillier/params.rs +++ b/synedrion/src/paillier/params.rs @@ -5,7 +5,7 @@ use crypto_bigint::{ }; 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 +17,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 +30,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,7 +43,7 @@ pub trait PaillierParams: core::fmt::Debug + PartialEq + Eq + Clone + Send + Syn + Bounded + Gcd + ConditionallySelectable - + Encoding + + Encoding + Hashable + HasWide + InvMod @@ -52,6 +53,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 @@ -94,7 +96,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..8770b84 100644 --- a/synedrion/src/paillier/ring_pedersen.rs +++ b/synedrion/src/paillier/ring_pedersen.rs @@ -3,7 +3,7 @@ use core::ops::Mul; use crypto_bigint::{Monty, NonZero, RandomMod, ShrVartime}; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use super::{ @@ -26,20 +26,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 +46,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() } @@ -108,19 +107,47 @@ impl RPParams

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

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

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

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

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

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

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

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

{ // $t^\rho * s^m mod N$ where $\rho$ is the randomizer and $m$ is the secret. @@ -179,6 +206,10 @@ impl RPCommitment

{ Self(self.0.pow_signed_vartime(exponent)) } + pub fn pow_secret_signed_wide(&self, exponent: &Secret>) -> Self { + Self(self.0.pow_signed_wide(exponent.expose_secret())) + } + pub fn pow_signed_wide(&self, exponent: &Signed) -> Self { Self(self.0.pow_signed_wide(exponent)) } diff --git a/synedrion/src/paillier/rsa.rs b/synedrion/src/paillier/rsa.rs index 16ea727..200db5f 100644 --- a/synedrion/src/paillier/rsa.rs +++ b/synedrion/src/paillier/rsa.rs @@ -1,7 +1,7 @@ use crypto_bigint::{CheckedSub, Gcd, Integer, Monty, NonZero, Odd, RandomMod, Square}; use crypto_primes::RandomPrimeWithRng; use rand_core::CryptoRngCore; -use secrecy::{ExposeSecret, SecretBox}; +use secrecy::ExposeSecret; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; @@ -43,16 +43,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 +92,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 +105,80 @@ 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(); + pub fn p(&self) -> Secret { + Secret::init_with(|| { + let mut p = self.primes.p.expose_secret().clone(); + let p_wide = p.clone().to_wide(); p.zeroize(); p_wide }) } - pub fn q(&self) -> SecretBox { - SecretBox::init_with(|| { - let mut q = *self.primes.q.expose_secret(); - let q_wide = q.into_wide(); + pub fn q(&self) -> Secret { + Secret::init_with(|| { + let mut q = self.primes.q.expose_secret().clone(); + let q_wide = q.clone().to_wide(); q.zeroize(); q_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 ", 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..bb9785c 100644 --- a/synedrion/src/tools/secret.rs +++ b/synedrion/src/tools/secret.rs @@ -1,12 +1,18 @@ use core::{ fmt::Debug, - ops::{Deref, DerefMut}, + ops::{Add, AddAssign, Deref, DerefMut, Mul, MulAssign, Neg, Sub, SubAssign}, }; -use secrecy::{ExposeSecret, SecretBox}; +use crypto_bigint::{subtle::ConditionallySelectable, Encoding, Integer}; +use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use zeroize::Zeroize; +use crate::{ + curve::{Point, Scalar}, + uint::{Bounded, HasWide, Signed}, +}; + /// A helper wrapper for managing secret values. /// /// On top of `secrecy::SecretBox` functionality, it provides: @@ -15,6 +21,16 @@ 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 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)?)) + } +} + impl Clone for Secret { fn clone(&self) -> Self { Self(SecretBox::init_with(|| self.0.expose_secret().clone())) @@ -45,6 +61,49 @@ impl From> for Secret { } } +impl Secret> +where + T: Zeroize + Clone + Encoding + Integer + HasWide + ConditionallySelectable + crypto_bigint::Bounded, + T::Wide: ConditionallySelectable + crypto_bigint::Bounded + Zeroize, +{ + pub fn 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::try_init_with(|| self.expose_secret().clone().into_signed().ok_or(|| ())).ok() + } +} + impl Deref for Secret { type Target = SecretBox; @@ -58,3 +117,257 @@ impl DerefMut for Secret { &mut self.0 } } + +// 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 + } +} + +impl<'a, T: Zeroize + Clone + AddAssign<&'a T>> Add<&'a Secret> for &Secret { + type Output = Secret; + + fn add(self, other: &'a Secret) -> Self::Output { + let mut result = self.clone(); + result += other; + result + } +} + +// Negation + +impl> Neg for Secret { + type Output = Secret; + fn neg(self) -> Self::Output { + Secret::init_with(|| self.expose_secret().clone().neg()) + } +} + +impl> Neg for &Secret { + type Output = Secret; + fn neg(self) -> Self::Output { + Secret::init_with(|| self.expose_secret().clone().neg()) + } +} + +// Subtraction + +impl<'a, T: Zeroize + SubAssign<&'a T>> SubAssign<&'a T> for Secret { + 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 SubAssign<&'a T>> Sub> for Secret { + type Output = Secret; + + fn sub(mut self, other: Secret) -> Self::Output { + self -= &other; + self + } +} + +impl<'a, T: Zeroize + SubAssign<&'a T>> Sub<&'a Secret> for Secret { + type Output = Secret; + + fn sub(mut self, other: &'a Secret) -> Self::Output { + self -= other.expose_secret(); + 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<'a, T: Zeroize + Clone + MulAssign<&'a T>> Mul<&'a T> for &Secret { + type Output = Secret; + + fn mul(self, other: &'a T) -> Self::Output { + let mut result: Secret = self.clone(); + result *= other; + result + } +} + +impl MulAssign<&'a T>> Mul for Secret { + type Output = Secret; + + fn mul(mut self, other: T) -> Self::Output { + self *= &other; + self + } +} + +impl MulAssign<&'a T>> Mul for &Secret { + type Output = Secret; + + fn mul(self, other: T) -> Self::Output { + let mut result: Secret = self.clone(); + result *= &other; + result + } +} + +impl MulAssign<&'a T>> Mul> for Secret { + type Output = Secret; + + 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 + } +} + +// 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::try_init_with(|| Option::from(self.expose_secret().invert()).ok_or(())).ok() + } +} + +impl Mul for Secret { + type Output = Point; + fn mul(self, point: Point) -> Self::Output { + point * self.expose_secret() + } +} + +impl Mul for &Secret { + type Output = Point; + fn mul(self, point: Point) -> Self::Output { + point * self.expose_secret() + } +} + +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 Mul<&Secret> for &Point { + type Output = Point; + fn mul(self, scalar: &Secret) -> Self::Output { + self * scalar.expose_secret() + } +} diff --git a/synedrion/src/tools/sss.rs b/synedrion/src/tools/sss.rs index 23277ad..286ea45 100644 --- a/synedrion/src/tools/sss.rs +++ b/synedrion/src/tools/sss.rs @@ -5,12 +5,12 @@ use alloc::{ use core::ops::{Add, Mul}; use rand_core::CryptoRngCore; +use secrecy::ExposeSecret; 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 +43,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 +95,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 +115,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 { @@ -119,9 +137,10 @@ pub(crate) fn shamir_join_points(pairs: &BTreeMap) -> Point { #[cfg(test)] mod tests { use rand_core::OsRng; + use secrecy::ExposeSecret; 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 +157,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..7152d79 100644 --- a/synedrion/src/uint/bounded.rs +++ b/synedrion/src/uint/bounded.rs @@ -1,9 +1,8 @@ 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}, @@ -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", @@ -120,9 +119,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, } } @@ -180,18 +179,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..ff108c2 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, SubAssign}; 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"); @@ -543,6 +513,36 @@ where } } +impl<'a, T> AddAssign<&'a Signed> for Signed +where + T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, +{ + fn add_assign(&mut self, rhs: &'a Signed) { + // TODO: implement properly + *self = self.checked_add(rhs).unwrap() + } +} + +impl<'a, T> SubAssign<&'a Signed> for Signed +where + T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, +{ + fn sub_assign(&mut self, rhs: &'a Signed) { + // TODO: implement properly + *self = self.checked_sub(rhs).unwrap() + } +} + +impl<'a, T> MulAssign<&'a Signed> for Signed +where + T: ConditionallySelectable + crypto_bigint::Bounded + Encoding + Integer, +{ + fn mul_assign(&mut self, rhs: &'a Signed) { + // TODO: implement properly + *self = self.checked_mul(rhs).unwrap() + } +} + impl Mul> for Signed where T: Integer + Encoding + crypto_bigint::Bounded + ConditionallySelectable, diff --git a/synedrion/src/uint/traits.rs b/synedrion/src/uint/traits.rs index 1369ca4..ba69182 100644 --- a/synedrion/src/uint/traits.rs +++ b/synedrion/src/uint/traits.rs @@ -2,7 +2,7 @@ use crypto_bigint::{ modular::MontyForm, nlimbs, subtle::{ConditionallySelectable, CtOption}, - Encoding, Integer, Invert, PowBoundedExp, RandomMod, Square, Zero, U1024, U2048, U4096, U512, U8192, + Encoding, Integer, Invert, PowBoundedExp, RandomMod, Square, Uint, Zero, U1024, U2048, U4096, U512, U8192, }; use crate::uint::{Bounded, Signed}; @@ -134,7 +134,7 @@ pub trait HasWide: Sized + Zero { /// Converts `self` to a new `Wide` uint, setting the higher half to `0`s. /// Consumes `self`. - fn into_wide(self) -> Self::Wide; + fn to_wide(&self) -> Self::Wide; /// Splits a `Wide` in two halves and returns the halves (`Self` sized) in a /// tuple (lower half first). @@ -164,8 +164,8 @@ impl HasWide for U512 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() @@ -180,8 +180,8 @@ impl HasWide for U1024 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() @@ -196,8 +196,8 @@ impl HasWide for U2048 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() @@ -212,8 +212,8 @@ impl HasWide for U4096 { fn square_wide(&self) -> Self::Wide { self.square_wide().into() } - fn into_wide(self) -> Self::Wide { - (self, Self::ZERO).into() + fn to_wide(&self) -> Self::Wide { + Uint::concat_mixed(self, &Self::ZERO) } fn from_wide(value: Self::Wide) -> (Self, Self) { value.into() diff --git a/synedrion/src/www02/entities.rs b/synedrion/src/www02/entities.rs index b6997ce..4594ed7 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]); } }