Skip to content

Commit

Permalink
Bundle public keys with Paillier ciphertexts (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri authored Jan 21, 2024
2 parents afe35e9 + 827a3c9 commit de56f6f
Show file tree
Hide file tree
Showing 18 changed files with 400 additions and 314 deletions.
5 changes: 3 additions & 2 deletions synedrion/benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rand_core::OsRng;

use synedrion::{cggmp21::benches, KeyShare, TestParams};
use synedrion::{cggmp21::benches, KeyShare, PresigningData, TestParams};

fn bench_happy_paths(c: &mut Criterion) {
let mut group = c.benchmark_group("happy path");
Expand All @@ -13,8 +13,9 @@ fn bench_happy_paths(c: &mut Criterion) {
b.iter(|| benches::key_init::<Params>(&mut OsRng, 2))
});

let presigning_datas = PresigningData::new_centralized(&mut OsRng, &key_shares);
group.bench_function("Signing, 2 parties", |b| {
b.iter(|| benches::signing::<Params>(&mut OsRng, &key_shares))
b.iter(|| benches::signing::<Params>(&mut OsRng, &key_shares, &presigning_datas))
});

group.sample_size(10);
Expand Down
8 changes: 5 additions & 3 deletions synedrion/src/cggmp21/benches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,14 @@ pub fn presigning<P: SchemeParams>(rng: &mut impl CryptoRngCore, key_shares: &[K
}

/// A sequential execution of the Presigning protocol for all parties.
pub fn signing<P: SchemeParams>(rng: &mut impl CryptoRngCore, key_shares: &[KeyShare<P>]) {
pub fn signing<P: SchemeParams>(
rng: &mut impl CryptoRngCore,
key_shares: &[KeyShare<P>],
presigning_datas: &[PresigningData<P>],
) {
let mut shared_randomness = [0u8; 32];
rng.fill_bytes(&mut shared_randomness);

let presigning_datas = PresigningData::new_centralized(rng, key_shares);

let message = Scalar::random(rng);

let num_parties = presigning_datas.len();
Expand Down
22 changes: 12 additions & 10 deletions synedrion/src/cggmp21/protocols/key_refresh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use crate::cggmp21::{
use crate::common::{KeyShareChange, PublicAuxInfo, SecretAuxInfo};
use crate::curve::{Point, Scalar};
use crate::paillier::{
Ciphertext, PublicKeyPaillier, PublicKeyPaillierPrecomputed, RPParams, RPParamsMod, RPSecret,
Randomizer, SecretKeyPaillier, SecretKeyPaillierPrecomputed,
Ciphertext, CiphertextMod, PublicKeyPaillier, PublicKeyPaillierPrecomputed, RPParams,
RPParamsMod, RPSecret, Randomizer, SecretKeyPaillier, SecretKeyPaillierPrecomputed,
};
use crate::rounds::{
all_parties_except, try_to_holevec, BaseRound, BroadcastRound, DirectRound, Finalizable,
Expand Down Expand Up @@ -522,7 +522,8 @@ impl<P: SchemeParams> DirectRound for Round3<P> {

let x_secret = self.context.xs_secret[idx];
let x_public = self.context.data_precomp.data.xs_public[idx];
let ciphertext = Ciphertext::new(rng, &data.paillier_pk, &P::uint_from_scalar(&x_secret));
let ciphertext =
CiphertextMod::new(rng, &data.paillier_pk, &P::uint_from_scalar(&x_secret));

let sch_proof_x = SchProof::new(
&self.context.sch_secrets_x[idx],
Expand All @@ -536,7 +537,7 @@ impl<P: SchemeParams> DirectRound for Round3<P> {
mod_proof: self.mod_proof.clone(),
fac_proof,
sch_proof_y: self.sch_proof_y.clone(),
paillier_enc_x: ciphertext,
paillier_enc_x: ciphertext.retrieve(),
sch_proof_x,
};

Expand All @@ -550,16 +551,17 @@ impl<P: SchemeParams> DirectRound for Round3<P> {
) -> Result<Self::Payload, ReceiveError<Self::Result>> {
let sender_data = &self.datas.get(from.as_usize()).unwrap();

let x_secret =
P::scalar_from_uint(&msg.data2.paillier_enc_x.decrypt(&self.context.paillier_sk));
let enc_x = msg
.data2
.paillier_enc_x
.to_mod(self.context.paillier_sk.public_key());

let x_secret = P::scalar_from_uint(&enc_x.decrypt(&self.context.paillier_sk));

if x_secret.mul_by_generator()
!= sender_data.data.xs_public[self.context.party_idx.as_usize()]
{
let mu = msg
.data2
.paillier_enc_x
.derive_randomizer(&self.context.paillier_sk);
let mu = enc_x.derive_randomizer(&self.context.paillier_sk);
return Err(ReceiveError::Provable(KeyRefreshError(
KeyRefreshErrorEnum::Round3MismatchedSecret {
cap_c: msg.data2.paillier_enc_x,
Expand Down
126 changes: 59 additions & 67 deletions synedrion/src/cggmp21/protocols/presigning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::cggmp21::{
};
use crate::common::{KeyShare, KeySharePrecomputed, PresigningData};
use crate::curve::{Point, Scalar};
use crate::paillier::{Ciphertext, PaillierParams, Randomizer, RandomizerMod};
use crate::paillier::{Ciphertext, CiphertextMod, PaillierParams, Randomizer, RandomizerMod};
use crate::rounds::{
all_parties_except, try_to_holevec, BaseRound, BroadcastRound, DirectRound, Finalizable,
FinalizableToNextRound, FinalizableToResult, FinalizationRequirement, FinalizeError,
Expand Down Expand Up @@ -57,8 +57,8 @@ pub struct Context<P: SchemeParams> {

pub struct Round1<P: SchemeParams> {
context: Context<P>,
k_ciphertext: Ciphertext<P::Paillier>,
g_ciphertext: Ciphertext<P::Paillier>,
k_ciphertext: CiphertextMod<P::Paillier>,
g_ciphertext: CiphertextMod<P::Paillier>,
}

impl<P: SchemeParams> FirstRound for Round1<P> {
Expand All @@ -83,8 +83,8 @@ impl<P: SchemeParams> FirstRound for Round1<P> {
let nu = RandomizerMod::<P::Paillier>::random(rng, pk);

let g_ciphertext =
Ciphertext::new_with_randomizer(pk, &P::uint_from_scalar(&gamma), &nu.retrieve());
let k_ciphertext = Ciphertext::new_with_randomizer(
CiphertextMod::new_with_randomizer(pk, &P::uint_from_scalar(&gamma), &nu.retrieve());
let k_ciphertext = CiphertextMod::new_with_randomizer(
pk,
&P::uint_from_scalar(&ephemeral_scalar_share),
&rho.retrieve(),
Expand Down Expand Up @@ -153,8 +153,8 @@ impl<P: SchemeParams> BroadcastRound for Round1<P> {

fn make_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result<Self::Message, String> {
Ok(Round1Bcast {
k_ciphertext: self.k_ciphertext.clone(),
g_ciphertext: self.g_ciphertext.clone(),
k_ciphertext: self.k_ciphertext.retrieve(),
g_ciphertext: self.g_ciphertext.retrieve(),
})
}

Expand Down Expand Up @@ -239,13 +239,22 @@ impl<P: SchemeParams> FinalizableToNextRound for Round1<P> {
&self.context.key_share.party_index(),
);

let (k_ciphertexts, g_ciphertexts) = ciphertexts
.map(|data| (data.k_ciphertext, data.g_ciphertext))
.unzip();

let k_ciphertexts = k_ciphertexts
.map_enumerate(|(i, c)| c.to_mod(&self.context.key_share.public_aux[i].paillier_pk));
let g_ciphertexts = g_ciphertexts
.map_enumerate(|(i, c)| c.to_mod(&self.context.key_share.public_aux[i].paillier_pk));

let public_aux =
&self.context.key_share.public_aux[self.context.key_share.party_index().as_usize()];

for ((from, ciphertexts), proof) in ciphertexts.enumerate().zip(proofs.iter()) {
for ((from, k_ciphertext), proof) in k_ciphertexts.enumerate().zip(proofs.iter()) {
if !proof.0.verify(
&self.context.key_share.public_aux[from].paillier_pk,
&ciphertexts.k_ciphertext,
k_ciphertext,
&public_aux.rp_params,
&aux,
) {
Expand All @@ -256,9 +265,6 @@ impl<P: SchemeParams> FinalizableToNextRound for Round1<P> {
}
}

let (k_ciphertexts, g_ciphertexts) = ciphertexts
.map(|data| (data.k_ciphertext, data.g_ciphertext))
.unzip();
let k_ciphertexts = k_ciphertexts.into_vec(self.k_ciphertext);
let g_ciphertexts = g_ciphertexts.into_vec(self.g_ciphertext);
Ok(Round2 {
Expand Down Expand Up @@ -289,8 +295,8 @@ pub struct Round2Direct<P: SchemeParams> {

pub struct Round2<P: SchemeParams> {
context: Context<P>,
k_ciphertexts: Vec<Ciphertext<P::Paillier>>,
g_ciphertexts: Vec<Ciphertext<P::Paillier>>,
k_ciphertexts: Vec<CiphertextMod<P::Paillier>>,
g_ciphertexts: Vec<CiphertextMod<P::Paillier>>,
}

#[derive(Debug, Clone)]
Expand All @@ -301,18 +307,18 @@ pub struct Round2Artifact<P: SchemeParams> {
s: Randomizer<P::Paillier>, // TODO (#77): secret
hat_r: Randomizer<P::Paillier>, // TODO (#77): secret
hat_s: Randomizer<P::Paillier>, // TODO (#77): secret
cap_d: Ciphertext<P::Paillier>,
cap_f: Ciphertext<P::Paillier>,
hat_cap_d: Ciphertext<P::Paillier>,
hat_cap_f: Ciphertext<P::Paillier>,
cap_d: CiphertextMod<P::Paillier>,
cap_f: CiphertextMod<P::Paillier>,
hat_cap_d: CiphertextMod<P::Paillier>,
hat_cap_f: CiphertextMod<P::Paillier>,
}

pub struct Round2Payload<P: SchemeParams> {
gamma: Point,
alpha: Signed<<P::Paillier as PaillierParams>::Uint>,
alpha_hat: Signed<<P::Paillier as PaillierParams>::Uint>,
cap_d: Ciphertext<P::Paillier>,
hat_cap_d: Ciphertext<P::Paillier>,
cap_d: CiphertextMod<P::Paillier>,
hat_cap_d: CiphertextMod<P::Paillier>,
}

impl<P: SchemeParams> BaseRound for Round2<P> {
Expand Down Expand Up @@ -370,25 +376,15 @@ impl<P: SchemeParams> DirectRound for Round2<P> {
let r_hat = RandomizerMod::random(rng, pk);
let s_hat = RandomizerMod::random(rng, target_pk);

let cap_f = Ciphertext::new_with_randomizer_signed(pk, &beta, &r.retrieve());
let cap_f = CiphertextMod::new_with_randomizer_signed(pk, &beta, &r.retrieve());

let d = self.k_ciphertexts[idx]
.homomorphic_mul(target_pk, &P::signed_from_scalar(&self.context.gamma))
.homomorphic_add(
target_pk,
&Ciphertext::new_with_randomizer_signed(target_pk, &-beta, &s.retrieve()),
);
let d = &self.k_ciphertexts[idx] * P::signed_from_scalar(&self.context.gamma)
+ CiphertextMod::new_with_randomizer_signed(target_pk, &-beta, &s.retrieve());

let d_hat = self.k_ciphertexts[idx]
.homomorphic_mul(
target_pk,
&P::signed_from_scalar(&self.context.key_share.secret_share),
)
.homomorphic_add(
target_pk,
&Ciphertext::new_with_randomizer_signed(target_pk, &-beta_hat, &s_hat.retrieve()),
);
let f_hat = Ciphertext::new_with_randomizer_signed(pk, &beta_hat, &r_hat.retrieve());
let d_hat = &self.k_ciphertexts[idx]
* P::signed_from_scalar(&self.context.key_share.secret_share)
+ CiphertextMod::new_with_randomizer_signed(target_pk, &-beta_hat, &s_hat.retrieve());
let f_hat = CiphertextMod::new_with_randomizer_signed(pk, &beta_hat, &r_hat.retrieve());

let public_aux = &self.context.key_share.public_aux[idx];
let rp = &public_aux.rp_params;
Expand Down Expand Up @@ -439,10 +435,10 @@ impl<P: SchemeParams> DirectRound for Round2<P> {

let msg = Round2Direct {
gamma,
d: d.clone(),
f: cap_f.clone(),
d_hat: d_hat.clone(),
f_hat: f_hat.clone(),
d: d.retrieve(),
f: cap_f.retrieve(),
d_hat: d_hat.retrieve(),
f_hat: f_hat.retrieve(),
psi,
psi_hat,
psi_hat_prime,
Expand Down Expand Up @@ -479,12 +475,15 @@ impl<P: SchemeParams> DirectRound for Round2<P> {
&self.context.key_share.public_aux[self.context.key_share.party_index().as_usize()];
let rp = &public_aux.rp_params;

let d = msg.d.to_mod(pk);
let d_hat = msg.d_hat.to_mod(pk);

if !msg.psi.verify(
pk,
from_pk,
&self.k_ciphertexts[self.context.key_share.party_index().as_usize()],
&msg.d,
&msg.f,
&d,
&msg.f.to_mod(from_pk),
&msg.gamma,
rp,
&aux,
Expand All @@ -498,8 +497,8 @@ impl<P: SchemeParams> DirectRound for Round2<P> {
pk,
from_pk,
&self.k_ciphertexts[self.context.key_share.party_index().as_usize()],
&msg.d_hat,
&msg.f_hat,
&d_hat,
&msg.f_hat.to_mod(from_pk),
&big_x,
rp,
&aux,
Expand All @@ -522,29 +521,25 @@ impl<P: SchemeParams> DirectRound for Round2<P> {
)));
}

let alpha = msg
.d
.decrypt_signed(&self.context.key_share.secret_aux.paillier_sk);
let alpha_hat = msg
.d_hat
.decrypt_signed(&self.context.key_share.secret_aux.paillier_sk);
let alpha = d.decrypt_signed(&self.context.key_share.secret_aux.paillier_sk);
let alpha_hat = d_hat.decrypt_signed(&self.context.key_share.secret_aux.paillier_sk);

// `alpha == x * y + z` where `0 <= x, y < q`, and `-2^l' <= z <= 2^l'`,
// where `q` is the curve order.
// We will need this bound later, so we're asserting it.
let alpha = alpha
.assert_bound_usize(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1)
.assert_bit_bound_usize(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1)
.unwrap();
let alpha_hat = alpha_hat
.assert_bound_usize(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1)
.assert_bit_bound_usize(core::cmp::max(2 * P::L_BOUND, P::LP_BOUND) + 1)
.unwrap();

Ok(Round2Payload {
gamma: msg.gamma,
alpha,
alpha_hat,
cap_d: msg.d,
hat_cap_d: msg.d_hat,
cap_d: d,
hat_cap_d: d_hat,
})
}
}
Expand Down Expand Up @@ -629,10 +624,10 @@ pub struct Round3<P: SchemeParams> {
product_share: Signed<<P::Paillier as PaillierParams>::Uint>,
big_delta: Point,
big_gamma: Point,
k_ciphertexts: Vec<Ciphertext<P::Paillier>>,
g_ciphertexts: Vec<Ciphertext<P::Paillier>>,
cap_ds: HoleVec<Ciphertext<P::Paillier>>,
hat_cap_d: HoleVec<Ciphertext<P::Paillier>>,
k_ciphertexts: Vec<CiphertextMod<P::Paillier>>,
g_ciphertexts: Vec<CiphertextMod<P::Paillier>>,
cap_ds: HoleVec<CiphertextMod<P::Paillier>>,
hat_cap_d: HoleVec<CiphertextMod<P::Paillier>>,
round2_artifacts: HoleVec<Round2Artifact<P>>,
}

Expand Down Expand Up @@ -880,12 +875,9 @@ impl<P: SchemeParams> FinalizableToResult for Round3<P> {
// Mul proof

let rho = RandomizerMod::random(rng, pk);
let cap_h = self.g_ciphertexts[my_idx]
.homomorphic_mul_unsigned(
pk,
&P::bounded_from_scalar(&self.context.ephemeral_scalar_share),
)
.mul_randomizer(pk, &rho.retrieve());
let cap_h = (&self.g_ciphertexts[my_idx]
* P::bounded_from_scalar(&self.context.ephemeral_scalar_share))
.mul_randomizer(&rho.retrieve());

let p_mul = MulProof::<P>::new(
rng,
Expand Down Expand Up @@ -914,8 +906,8 @@ impl<P: SchemeParams> FinalizableToResult for Round3<P> {

for j in range {
ciphertext = ciphertext
.homomorphic_add(pk, self.cap_ds.get(j).unwrap())
.homomorphic_add(pk, &self.round2_artifacts.get(j).unwrap().cap_f);
+ self.cap_ds.get(j).unwrap()
+ &self.round2_artifacts.get(j).unwrap().cap_f;
}

let rho = ciphertext.derive_randomizer(sk);
Expand Down
22 changes: 7 additions & 15 deletions synedrion/src/cggmp21/protocols/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,8 @@ impl<P: SchemeParams> FinalizableToResult for Round1<P> {
let cap_x = self.context.key_share.public_shares[self.party_idx().as_usize()];

let rho = RandomizerMod::random(rng, pk);
let hat_cap_h = self.context.presigning.cap_k[my_idx]
.homomorphic_mul_unsigned(pk, &P::bounded_from_scalar(&x))
.mul_randomizer(pk, &rho.retrieve());
let hat_cap_h = (&self.context.presigning.cap_k[my_idx] * P::bounded_from_scalar(&x))
.mul_randomizer(&rho.retrieve());

let aux = (
&self.shared_randomness,
Expand Down Expand Up @@ -262,22 +261,15 @@ impl<P: SchemeParams> FinalizableToResult for Round1<P> {
let mut ciphertext = hat_cap_h.clone();
for j in HoleRange::new(num_parties, my_idx) {
ciphertext = ciphertext
.homomorphic_add(
pk,
self.context.presigning.hat_cap_d_received.get(j).unwrap(),
)
.homomorphic_add(pk, self.context.presigning.hat_cap_f.get(j).unwrap());
+ self.context.presigning.hat_cap_d_received.get(j).unwrap()
+ self.context.presigning.hat_cap_f.get(j).unwrap();
}

let r = self.context.presigning.nonce.x_coordinate();

let ciphertext = ciphertext
.homomorphic_mul_unsigned(pk, &P::bounded_from_scalar(&r))
.homomorphic_add(
pk,
&self.context.presigning.cap_k[my_idx]
.homomorphic_mul_unsigned(pk, &P::bounded_from_scalar(&self.context.message)),
);
let ciphertext = ciphertext * P::bounded_from_scalar(&r)
+ &self.context.presigning.cap_k[my_idx]
* P::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
Expand Down
Loading

0 comments on commit de56f6f

Please sign in to comment.