Skip to content

Commit

Permalink
Signed improvements (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri authored Dec 19, 2024
2 parents 3a45cdd + 4dd0040 commit a7dc794
Show file tree
Hide file tree
Showing 30 changed files with 1,901 additions and 2,059 deletions.
1 change: 1 addition & 0 deletions synedrion/src/cggmp21.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//! refers to the version of the paper published at <https://eprint.iacr.org/2021/060.pdf>
mod aux_gen;
mod conversion;
mod entities;
mod interactive_signing;
mod key_init;
Expand Down
163 changes: 163 additions & 0 deletions synedrion/src/cggmp21/conversion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use crypto_bigint::{Encoding, Zero};

use super::params::SchemeParams;
use crate::{
curve::{Scalar, ORDER},
paillier::PaillierParams,
tools::Secret,
uint::{PublicSigned, SecretSigned, SecretUnsigned},
};

fn uint_from_scalar<P: SchemeParams>(value: &Scalar) -> <P::Paillier as PaillierParams>::Uint {
let scalar_bytes = value.to_be_bytes();
let mut repr = <P::Paillier as PaillierParams>::Uint::zero().to_be_bytes();

let uint_len = repr.as_ref().len();
let scalar_len = scalar_bytes.len();

repr.as_mut()
.get_mut(uint_len - scalar_len..)
.expect("PaillierParams::Uint is expected to be bigger than a Scalar")
.copy_from_slice(&scalar_bytes);
<P::Paillier as PaillierParams>::Uint::from_be_bytes(repr)
}

/// Converts a [`Scalar`] to a [`PublicSigned`].
///
/// Assumes using a curve whose order is at most the width of `Uint` minus 1 bit.
pub(crate) fn public_signed_from_scalar<P: SchemeParams>(
value: &Scalar,
) -> PublicSigned<<P::Paillier as PaillierParams>::Uint> {
PublicSigned::new_positive(uint_from_scalar::<P>(value), ORDER.bits_vartime() as u32).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.
pub(crate) fn scalar_from_uint<P: SchemeParams>(value: &<P::Paillier as PaillierParams>::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();

// Can unwrap here since the value is within the Scalar range
Scalar::try_from_be_bytes(
repr.as_ref()
.get(uint_len - scalar_len..)
.expect("Uint is assumed to be bigger than Scalar"),
)
.expect("the value was reduced modulo curve order, so it's a valid curve scalar")
}

/// Converts a `PublicSigned`-wrapped integer to the associated curve scalar type.
pub(crate) fn scalar_from_signed<P: SchemeParams>(
value: &PublicSigned<<P::Paillier as PaillierParams>::Uint>,
) -> Scalar {
let abs_value = scalar_from_uint::<P>(&value.abs());
if value.is_negative() {
-abs_value
} else {
abs_value
}
}

/// Converts a wide integer to the associated curve scalar type.
pub(crate) fn scalar_from_wide_uint<P: SchemeParams>(value: &<P::Paillier as PaillierParams>::WideUint) -> Scalar {
let r = *value % P::CURVE_ORDER_WIDE;

let repr = r.to_be_bytes();
let uint_len = repr.as_ref().len();
let scalar_len = Scalar::repr_len();

// Can unwrap here since the value is within the Scalar range
Scalar::try_from_be_bytes(
repr.as_ref()
.get(uint_len - scalar_len..)
.expect("WideUint is assumed to be bigger than Scalar"),
)
.expect("the value was reduced modulo curve order, so it's a valid curve scalar")
}

/// Converts a `PublicSigned`-wrapped wide integer to the associated curve scalar type.
pub(crate) fn scalar_from_wide_signed<P: SchemeParams>(
value: &PublicSigned<<P::Paillier as PaillierParams>::WideUint>,
) -> Scalar {
let abs_value = scalar_from_wide_uint::<P>(&value.abs());
if value.is_negative() {
-abs_value
} else {
abs_value
}
}

/// Converts a secret-wrapped uint to a secret-wrapped [`Scalar`], reducing the value modulo curve order.
pub(crate) fn secret_scalar_from_uint<P: SchemeParams>(
value: &Secret<<P::Paillier as PaillierParams>::Uint>,
) -> Secret<Scalar> {
let r = value % &P::CURVE_ORDER;

let repr = Secret::init_with(|| r.expose_secret().to_be_bytes());
let uint_len = repr.expose_secret().as_ref().len();
let scalar_len = Scalar::repr_len();

// Can unwrap here since the value is within the Scalar range
Secret::init_with(|| {
Scalar::try_from_be_bytes(
repr.expose_secret()
.as_ref()
.get(uint_len - scalar_len..)
.expect("Uint is assumed to be bigger than Scalar"),
)
.expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar")
})
}

fn secret_uint_from_scalar<P: SchemeParams>(value: &Secret<Scalar>) -> Secret<<P::Paillier as PaillierParams>::Uint> {
let scalar_bytes = Secret::init_with(|| value.expose_secret().to_be_bytes());
let mut repr = Secret::init_with(|| <P::Paillier as PaillierParams>::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()
.get_mut(uint_len - scalar_len..)
.expect("<P::Paillier as PaillierParams>::Uint is assumed to be configured to be bigger than Scalar")
.copy_from_slice(scalar_bytes.expose_secret());
Secret::init_with(|| <P::Paillier as PaillierParams>::Uint::from_be_bytes(*repr.expose_secret()))
}

/// Converts a secret-wrapped [`Scalar`] to a [`SecretUnsigned`].
///
/// Assumes using a curve whose order fits in a [`PaillierParams::Uint`].
pub(crate) fn secret_unsigned_from_scalar<P: SchemeParams>(
value: &Secret<Scalar>,
) -> SecretUnsigned<<P::Paillier as PaillierParams>::Uint> {
SecretUnsigned::new(secret_uint_from_scalar::<P>(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 secret-wrapped [`Scalar`] to a [`SecretSigned`].
///
/// Assumes using a curve whose order is at most the width of `Uint` minus 1 bit.
pub(crate) fn secret_signed_from_scalar<P: SchemeParams>(
value: &Secret<Scalar>,
) -> SecretSigned<<P::Paillier as PaillierParams>::Uint> {
SecretSigned::new_positive(secret_uint_from_scalar::<P>(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 [`SecretSigned`] to a secret-wrapped [`Scalar`].
pub(crate) fn secret_scalar_from_signed<P: SchemeParams>(
value: &SecretSigned<<P::Paillier as PaillierParams>::Uint>,
) -> Secret<Scalar> {
let abs_value = secret_scalar_from_uint::<P>(&value.abs_value());
Secret::<Scalar>::conditional_select(&abs_value, &-&abs_value, value.is_negative())
}
6 changes: 3 additions & 3 deletions synedrion/src/cggmp21/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
SecretKeyPaillier, SecretKeyPaillierWire,
},
tools::Secret,
uint::Signed,
uint::SecretSigned,
};

/// The result of the KeyInit protocol.
Expand Down Expand Up @@ -103,7 +103,7 @@ pub(crate) struct PresigningData<P: SchemeParams, I> {

// Values generated during presigning,
// kept in case we need to generate a proof of correctness.
pub(crate) product_share_nonreduced: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
pub(crate) product_share_nonreduced: SecretSigned<<P::Paillier as PaillierParams>::Uint>,

// $K_i$.
pub(crate) cap_k: Ciphertext<P::Paillier>,
Expand All @@ -114,7 +114,7 @@ pub(crate) struct PresigningData<P: SchemeParams, I> {

#[derive(Debug, Clone)]
pub(crate) struct PresigningValues<P: SchemeParams> {
pub(crate) hat_beta: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
pub(crate) hat_beta: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
pub(crate) hat_r: Randomizer<P::Paillier>,
pub(crate) hat_s: Randomizer<P::Paillier>,
pub(crate) cap_k: Ciphertext<P::Paillier>,
Expand Down
79 changes: 36 additions & 43 deletions synedrion/src/cggmp21/interactive_signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};

use super::{
entities::{AuxInfo, AuxInfoPrecomputed, KeyShare, PresigningData, PresigningValues, PublicAuxInfoPrecomputed},
params::{
bounded_from_scalar, secret_bounded_from_scalar, secret_scalar_from_signed, secret_signed_from_scalar,
secret_uint_from_scalar, signed_from_scalar, SchemeParams,
conversion::{
public_signed_from_scalar, secret_scalar_from_signed, secret_signed_from_scalar, secret_unsigned_from_scalar,
},
entities::{AuxInfo, AuxInfoPrecomputed, KeyShare, PresigningData, PresigningValues, PublicAuxInfoPrecomputed},
params::SchemeParams,
sigma::{AffGProof, DecProof, EncProof, LogStarProof, MulProof, MulStarProof},
};
use crate::{
Expand All @@ -32,7 +32,7 @@ use crate::{
hashing::{Chain, FofHasher, HashOutput},
DowncastMap, Secret, Without,
},
uint::Signed,
uint::SecretSigned,
};

/// A protocol for creating all the data necessary for signing
Expand Down Expand Up @@ -162,10 +162,10 @@ impl<P: SchemeParams, I: PartyId> EntryPoint<I> for InteractiveSigning<P, I> {
let pk = aux_info.secret_aux.paillier_sk.public_key();

let nu = Randomizer::<P::Paillier>::random(rng, pk);
let cap_g = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::<P>(&gamma), &nu);
let cap_g = Ciphertext::new_with_randomizer(pk, &secret_unsigned_from_scalar::<P>(&gamma), &nu);

let rho = Randomizer::<P::Paillier>::random(rng, pk);
let cap_k = Ciphertext::new_with_randomizer(pk, &secret_uint_from_scalar::<P>(&k), &rho);
let cap_k = Ciphertext::new_with_randomizer(pk, &secret_unsigned_from_scalar::<P>(&k), &rho);

Ok(BoxedRound::new_dynamic(Round1 {
context: Context {
Expand Down Expand Up @@ -421,8 +421,8 @@ struct Round2Message<P: SchemeParams> {

#[derive(Debug, Clone)]
struct Round2Artifact<P: SchemeParams> {
beta: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
hat_beta: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
beta: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
hat_beta: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
r: Randomizer<P::Paillier>,
s: Randomizer<P::Paillier>,
hat_r: Randomizer<P::Paillier>,
Expand All @@ -435,8 +435,8 @@ struct Round2Artifact<P: SchemeParams> {

struct Round2Payload<P: SchemeParams> {
cap_gamma: Point,
alpha: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
hat_alpha: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
alpha: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
hat_alpha: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
cap_d: Ciphertext<P::Paillier>,
hat_cap_d: Ciphertext<P::Paillier>,
}
Expand Down Expand Up @@ -473,8 +473,8 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round2<P, I> {

let target_pk = &self.context.public_aux(destination)?.paillier_pk;

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 beta = SecretSigned::random_in_exp_range(rng, P::LP_BOUND);
let hat_beta = SecretSigned::random_in_exp_range(rng, P::LP_BOUND);
let r = Randomizer::random(rng, pk);
let s = Randomizer::random(rng, target_pk);
let hat_r = Randomizer::random(rng, pk);
Expand All @@ -496,7 +496,7 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round2<P, I> {
.all_cap_k
.get(destination)
.ok_or(LocalError::new("Missing destination={destination:?} in all_cap_k"))?
* secret_signed_from_scalar::<P>(&self.context.key_share.secret_share)
* &secret_signed_from_scalar::<P>(&self.context.key_share.secret_share)
+ Ciphertext::new_with_randomizer_signed(target_pk, &-&hat_beta, &hat_s);

let rp = &self.context.public_aux(destination)?.rp_params;
Expand Down Expand Up @@ -661,18 +661,10 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round2<P, I> {
// `alpha == x * y + z` where `0 <= x, y < q`, and `-2^l' <= z <= 2^l'`,
// where `q` is the curve order.
// We will need this bound later, so we're asserting it.
let alpha = Secret::try_init_with(|| {
alpha
.expose_secret()
.assert_bit_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1)
.ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsAlpha))
})?;
let hat_alpha = Secret::try_init_with(|| {
hat_alpha
.expose_secret()
.assert_bit_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1)
.ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsHatAlpha))
})?;
let alpha = Option::from(alpha.ensure_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1))
.ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsAlpha))?;
let hat_alpha = Option::from(hat_alpha.ensure_bound(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1))
.ok_or_else(|| ReceiveError::protocol(InteractiveSigningError::OutOfBoundsHatAlpha))?;

Ok(Payload::new(Round2Payload::<P> {
cap_gamma: direct_message.cap_gamma,
Expand All @@ -697,15 +689,15 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round2<P, I> {

let cap_delta = cap_gamma * &self.context.k;

let alpha_sum: Secret<Signed<_>> = payloads.values().map(|payload| &payload.alpha).sum();
let beta_sum: Secret<Signed<_>> = artifacts.values().map(|artifact| &artifact.beta).sum();
let alpha_sum: SecretSigned<_> = payloads.values().map(|payload| &payload.alpha).sum();
let beta_sum: SecretSigned<_> = artifacts.values().map(|artifact| &artifact.beta).sum();
let delta = secret_signed_from_scalar::<P>(&self.context.gamma)
* secret_signed_from_scalar::<P>(&self.context.k)
+ &alpha_sum
+ &beta_sum;

let hat_alpha_sum: Secret<Signed<_>> = payloads.values().map(|payload| &payload.hat_alpha).sum();
let hat_beta_sum: Secret<Signed<_>> = artifacts.values().map(|artifact| &artifact.hat_beta).sum();
let hat_alpha_sum: SecretSigned<_> = payloads.values().map(|payload| &payload.hat_alpha).sum();
let hat_beta_sum: SecretSigned<_> = artifacts.values().map(|artifact| &artifact.hat_beta).sum();
let chi = secret_signed_from_scalar::<P>(&self.context.key_share.secret_share)
* secret_signed_from_scalar::<P>(&self.context.k)
+ &hat_alpha_sum
Expand Down Expand Up @@ -734,8 +726,8 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round2<P, I> {
#[derive(Debug)]
struct Round3<P: SchemeParams, I: Ord> {
context: Context<P, I>,
delta: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
chi: Secret<Signed<<P::Paillier as PaillierParams>::Uint>>,
delta: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
chi: SecretSigned<<P::Paillier as PaillierParams>::Uint>,
cap_delta: Point,
cap_gamma: Point,
all_cap_k: BTreeMap<I, Ciphertext<P::Paillier>>,
Expand Down Expand Up @@ -999,28 +991,28 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round3<P, I> {
// Mul proof
let my_id = &self.context.my_id;
let rho = Randomizer::random(rng, pk);
let cap_x = self
let cap_k = self
.all_cap_k
.get(my_id)
.ok_or_else(|| LocalError::new("my_id={my_id:?} is missing in all_cap_k"))?;
let cap_y = self
let cap_g = self
.all_cap_g
.get(my_id)
.ok_or_else(|| LocalError::new("my_id={my_id:?} is missing in all_cap_g"))?;
let cap_h = (cap_y * secret_bounded_from_scalar::<P>(&self.context.k)).mul_randomizer(&rho);
let cap_h = (cap_g * &secret_unsigned_from_scalar::<P>(&self.context.k)).mul_randomizer(&rho);

let p_mul = MulProof::<P>::new(
rng,
&secret_signed_from_scalar::<P>(&self.context.k),
&self.context.rho,
&rho,
pk,
cap_x,
cap_y,
cap_k,
cap_g,
&cap_h,
&aux,
);
assert!(p_mul.verify(pk, cap_x, cap_y, &cap_h, &aux));
assert!(p_mul.verify(pk, cap_k, cap_g, &cap_h, &aux));

// Dec proof

Expand Down Expand Up @@ -1229,7 +1221,7 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round4<P, I> {
let cap_x = self.context.public_share(&my_id)?;

let rho = Randomizer::random(rng, pk);
let hat_cap_h = (&self.presigning.cap_k * secret_bounded_from_scalar::<P>(x)).mul_randomizer(&rho);
let hat_cap_h = (&self.presigning.cap_k * &secret_unsigned_from_scalar::<P>(x)).mul_randomizer(&rho);

let aux = (&self.context.ssid_hash, &my_id);

Expand Down Expand Up @@ -1267,16 +1259,17 @@ impl<P: SchemeParams, I: PartyId> Round<I> for Round4<P, I> {
}

let r = self.presigning.nonce;
let signed_r = public_signed_from_scalar::<P>(&r);
let signed_message = public_signed_from_scalar::<P>(&self.context.message);

let ciphertext = ciphertext * bounded_from_scalar::<P>(&r)
+ &self.presigning.cap_k * bounded_from_scalar::<P>(&self.context.message);
let ciphertext = ciphertext * &signed_r + &self.presigning.cap_k * &signed_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 = secret_signed_from_scalar::<P>(&self.presigning.ephemeral_scalar_share)
* signed_from_scalar::<P>(&self.context.message)
+ self.presigning.product_share_nonreduced.clone() * signed_from_scalar::<P>(&r);
* signed_message
+ &self.presigning.product_share_nonreduced * signed_r;

let mut dec_proofs = Vec::new();
for id_l in self.context.other_ids.iter() {
Expand Down
Loading

0 comments on commit a7dc794

Please sign in to comment.