Skip to content

Commit

Permalink
Merge branch 'master' into dp-error-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Oct 18, 2024
2 parents 0b03b43 + 9e24b37 commit da35145
Show file tree
Hide file tree
Showing 26 changed files with 368 additions and 490 deletions.
1 change: 1 addition & 0 deletions synedrion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ crypto-bigint = { version = "0.6.0-rc.2", features = ["serde", "alloc", "rand_co
crypto-primes = "0.6.0-pre.1"

serde = { version = "1", default-features = false, features = ["derive"] }
serde-encoded-bytes = { version = "0.1", default-features = false, features = ["hex", "base64"] }
bincode = { version = "2.0.0-rc.3", default-features = false, features = ["serde", "alloc"] }
displaydoc = { version = "0.2", default-features = false}

Expand Down
168 changes: 130 additions & 38 deletions synedrion/src/cggmp21/protocols/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,68 +315,160 @@ impl<P: SchemeParams, I: Debug + Clone + Ord + Serialize> FinalizableToResult<I>

#[cfg(test)]
mod tests {
use alloc::collections::BTreeSet;
use alloc::collections::{BTreeMap, BTreeSet};

use k256::ecdsa::{signature::hazmat::PrehashVerifier, VerifyingKey};
use rand_core::{OsRng, RngCore};

use super::{Inputs, Round1};
use crate::cggmp21::{AuxInfo, KeyShare, PresigningData, TestParams};
use crate::curve::Scalar;
use crate::rounds::FinalizeError;
use crate::rounds::{
test_utils::{step_result, step_round, Id, Without},
FirstRound,
FinalizableToResult, FirstRound,
};
use crate::{RecoverableSignature, SigningProof};

struct TestContext {
message: Scalar,
randomness: [u8; 32],
presigning_data: BTreeMap<Id, PresigningData<TestParams, Id>>,
key_shares: BTreeMap<Id, KeyShare<TestParams, Id>>,
aux_infos: BTreeMap<Id, AuxInfo<TestParams, Id>>,
}
impl TestContext {
fn new(ids: &BTreeSet<Id>) -> Self {
let mut randomness = [0u8; 32];
OsRng.fill_bytes(&mut randomness);
let key_shares = KeyShare::new_centralized(&mut OsRng, ids, None);
let aux_infos = AuxInfo::new_centralized(&mut OsRng, ids);

let presigning_data =
PresigningData::new_centralized(&mut OsRng, &key_shares, &aux_infos);

Self {
message: Scalar::random(&mut OsRng),
randomness,
presigning_data,
key_shares,
aux_infos,
}
}
}

#[test]
fn execute_signing() {
let mut shared_randomness = [0u8; 32];
OsRng.fill_bytes(&mut shared_randomness);
fn make_test_round1(
shared_randomness: &[u8],
ids: &BTreeSet<Id>,
id: &Id,
presigning_data: &BTreeMap<Id, PresigningData<TestParams, Id>>,
message: Scalar,
key_shares: &BTreeMap<Id, KeyShare<TestParams, Id>>,
aux_infos: &BTreeMap<Id, AuxInfo<TestParams, Id>>,
) -> Round1<TestParams, Id> {
Round1::<TestParams, Id>::new(
&mut OsRng,
shared_randomness,
ids.clone().without(id),
*id,
Inputs {
presigning: presigning_data[id].clone(),
message,
key_share: key_shares[id].clone(),
aux_info: aux_infos[id].clone(),
},
)
.unwrap()
}
fn make_rounds(ctx: &TestContext, ids: &BTreeSet<Id>) -> BTreeMap<Id, Round1<TestParams, Id>> {
ids.iter()
.map(|id| {
let round = make_test_round1(
&ctx.randomness,
ids,
id,
&ctx.presigning_data,
ctx.message,
&ctx.key_shares,
&ctx.aux_infos,
);
(*id, round)
})
.collect()
}

let ids = BTreeSet::from([Id(0), Id(1), Id(2)]);
fn check_sig(
signature: &RecoverableSignature,
key_shares: &BTreeMap<Id, KeyShare<TestParams, Id>>,
message: &Scalar,
) {
let (sig, rec_id) = signature.to_backend();
let vkey = key_shares[&Id(0)].verifying_key().unwrap();

let key_shares = KeyShare::new_centralized(&mut OsRng, &ids, None);
let aux_infos = AuxInfo::new_centralized(&mut OsRng, &ids);
// Check that the signature can be verified
vkey.verify_prehash(&message.to_bytes(), &sig).unwrap();

let presigning_datas = PresigningData::new_centralized(&mut OsRng, &key_shares, &aux_infos);
// Check that the key can be recovered
let recovered_key =
VerifyingKey::recover_from_prehash(&message.to_bytes(), &sig, rec_id).unwrap();

let message = Scalar::random(&mut OsRng);
assert_eq!(recovered_key, vkey);
}

let r1 = ids
.iter()
.map(|id| {
let round = Round1::<TestParams, Id>::new(
&mut OsRng,
&shared_randomness,
ids.clone().without(id),
*id,
Inputs {
presigning: presigning_datas[id].clone(),
message,
key_share: key_shares[id].clone(),
aux_info: aux_infos[id].clone(),
},
)
.unwrap();
(*id, round)
})
.collect();
#[test]
fn execute_signing() {
let ids = BTreeSet::from([Id(0), Id(1), Id(2)]);
let ctx = TestContext::new(&ids);

let r1 = make_rounds(&ctx, &ids);

let r1a = step_round(&mut OsRng, r1).unwrap();

let signatures = step_result(&mut OsRng, r1a).unwrap();

for signature in signatures.values() {
let (sig, rec_id) = signature.to_backend();
check_sig(signature, &ctx.key_shares, &ctx.message);
}
}

let vkey = key_shares[&Id(0)].verifying_key().unwrap();
#[test]
fn cheating_signer() {
let ids = BTreeSet::from([Id(0), Id(1), Id(2)]);
let ctx = TestContext::new(&ids);

let r1 = make_rounds(&ctx, &ids);

let mut r1a = step_round(&mut OsRng, r1).unwrap();

// Check that the signature can be verified
vkey.verify_prehash(&message.to_bytes(), &sig).unwrap();
// Manipulate second party's signature, causing finalize_to_result to fail
let assr = r1a.get_mut(&Id(1)).unwrap();
assr.round.r = Scalar::random_nonzero(&mut OsRng);

// Check that the key can be recovered
let recovered_key =
VerifyingKey::recover_from_prehash(&message.to_bytes(), &sig, rec_id).unwrap();
assert_eq!(recovered_key, vkey);
// First party is fine
match r1a.pop_first() {
Some((id, assr)) => {
assert!(id == Id(0));
let finalized =
assr.round
.finalize_to_result(&mut OsRng, assr.payloads, assr.artifacts);
assert!(finalized.is_ok());
check_sig(&finalized.unwrap(), &ctx.key_shares, &ctx.message);
}
None => unreachable!(),
}
// Second is bad
match r1a.pop_first() {
Some((id, assr)) => {
assert!(id == Id(1));
let finalized =
assr.round
.finalize_to_result(&mut OsRng, assr.payloads, assr.artifacts);
assert!(finalized.is_err());
assert!(
matches!(finalized, Err(err) if matches!(&err, FinalizeError::Proof(SigningProof{..})))
);
}
None => unreachable!(),
}
}
}
14 changes: 6 additions & 8 deletions synedrion/src/cggmp21/sigma/aff_g.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ impl<P: SchemeParams> AffGProof<P> {
let cap_b_x = P::scalar_from_signed(&alpha).mul_by_generator();
let cap_b_y =
CiphertextMod::new_with_randomizer_signed(pk1, &beta, &r_y_mod.retrieve()).retrieve();
let cap_e = setup.commit(&alpha.into(), &gamma).retrieve();
let cap_s = setup.commit(&x.into(), &m).retrieve();
let cap_f = setup.commit(&beta.into(), &delta).retrieve();
let cap_e = setup.commit(&alpha, &gamma).retrieve();
let cap_s = setup.commit(x, &m).retrieve();
let cap_f = setup.commit(&beta, &delta).retrieve();

// 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()).into(), &mu).retrieve();
let cap_t = setup.commit(&(-y.expose_secret()), &mu).retrieve();

let mut reader = XofHasher::new_with_dst(HASH_TAG)
// commitments
Expand Down Expand Up @@ -250,16 +250,14 @@ impl<P: SchemeParams> AffGProof<P> {
// s^{z_1} t^{z_3} = E S^e \mod \hat{N}
let cap_e_mod = self.cap_e.to_mod(aux_pk);
let cap_s_mod = self.cap_s.to_mod(aux_pk);
if setup.commit(&self.z1.into(), &self.z3) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e)
{
if setup.commit(&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_mod(aux_pk);
let cap_t_mod = self.cap_t.to_mod(aux_pk);
if setup.commit(&self.z2.into(), &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e)
{
if setup.commit(&self.z2, &self.z4) != &cap_f_mod * &cap_t_mod.pow_signed_vartime(&e) {
return false;
}

Expand Down
8 changes: 3 additions & 5 deletions synedrion/src/cggmp21/sigma/dec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ impl<P: SchemeParams> DecProof<P> {
let nu = Signed::random_bounded_bits_scaled(rng, P::L_BOUND + P::EPS_BOUND, hat_cap_n);
let r = RandomizerMod::random(rng, pk0);

let cap_s = setup.commit(&y.into(), &mu).retrieve();
let cap_t = setup.commit(&alpha.into(), &nu).retrieve();
let cap_s = setup.commit(y, &mu).retrieve();
let cap_t = setup.commit(&alpha, &nu).retrieve();
let cap_a =
CiphertextMod::new_with_randomizer_signed(pk0, &alpha, &r.retrieve()).retrieve();
let gamma = P::scalar_from_signed(&alpha);
Expand Down Expand Up @@ -150,9 +150,7 @@ impl<P: SchemeParams> DecProof<P> {
// s^{z_1} t^{z_2} == T S^e
let cap_s_mod = self.cap_s.to_mod(setup.public_key());
let cap_t_mod = self.cap_t.to_mod(setup.public_key());
if setup.commit_wide(&self.z1.into(), &self.z2)
!= &cap_t_mod * &cap_s_mod.pow_signed_vartime(&e)
{
if setup.commit_wide(&self.z1, &self.z2) != &cap_t_mod * &cap_s_mod.pow_signed_vartime(&e) {
return false;
}

Expand Down
7 changes: 3 additions & 4 deletions synedrion/src/cggmp21/sigma/enc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ impl<P: SchemeParams> EncProof<P> {
let r = RandomizerMod::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(&k.into(), &mu).retrieve();
let cap_s = setup.commit(k, &mu).retrieve();
let cap_a =
CiphertextMod::new_with_randomizer_signed(pk0, &alpha, &r.retrieve()).retrieve();
let cap_c = setup.commit(&alpha.into(), &gamma).retrieve();
let cap_c = setup.commit(&alpha, &gamma).retrieve();

let mut reader = XofHasher::new_with_dst(HASH_TAG)
// commitments
Expand Down Expand Up @@ -135,8 +135,7 @@ impl<P: SchemeParams> EncProof<P> {
// s^{z_1} t^{z_3} == C S^e \mod \hat{N}
let cap_c_mod = self.cap_c.to_mod(setup.public_key());
let cap_s_mod = self.cap_s.to_mod(setup.public_key());
if setup.commit(&self.z1.into(), &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e)
{
if setup.commit(&self.z1, &self.z3) != &cap_c_mod * &cap_s_mod.pow_signed_vartime(&e) {
return false;
}

Expand Down
12 changes: 6 additions & 6 deletions synedrion/src/cggmp21/sigma/fac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ impl<P: SchemeParams> FacProof<P> {

let (p, q) = sk0.primes();

let cap_p = setup.commit(&p, &mu).retrieve();
let cap_q = setup.commit(&q, &nu);
let cap_a = setup.commit_wide(&alpha.into(), &x).retrieve();
let cap_b = setup.commit_wide(&beta.into(), &y).retrieve();
let cap_p = setup.commit(p.expose_secret(), &mu).retrieve();
let cap_q = setup.commit(q.expose_secret(), &nu);
let cap_a = setup.commit_wide(&alpha, &x).retrieve();
let cap_b = setup.commit_wide(&beta, &y).retrieve();
let cap_t = (&cap_q.pow_signed_wide(&alpha) * &setup.commit_base_xwide(&r)).retrieve();
let cap_q = cap_q.retrieve();

Expand Down Expand Up @@ -171,7 +171,7 @@ impl<P: SchemeParams> FacProof<P> {
// s^{z_1} t^{\omega_1} == A * P^e \mod \hat{N}
let cap_a_mod = self.cap_a.to_mod(aux_pk);
let cap_p_mod = self.cap_p.to_mod(aux_pk);
if setup.commit_wide(&self.z1.into(), &self.omega1)
if setup.commit_wide(&self.z1, &self.omega1)
!= &cap_a_mod * &cap_p_mod.pow_signed_vartime(&e)
{
return false;
Expand All @@ -180,7 +180,7 @@ impl<P: SchemeParams> FacProof<P> {
// s^{z_2} t^{\omega_2} == B * Q^e \mod \hat{N}
let cap_b_mod = self.cap_b.to_mod(aux_pk);
let cap_q_mod = self.cap_q.to_mod(aux_pk);
if setup.commit_wide(&self.z2.into(), &self.omega2)
if setup.commit_wide(&self.z2, &self.omega2)
!= &cap_b_mod * &cap_q_mod.pow_signed_vartime(&e)
{
return false;
Expand Down
7 changes: 3 additions & 4 deletions synedrion/src/cggmp21/sigma/log_star.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ impl<P: SchemeParams> LogStarProof<P> {
let r = RandomizerMod::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.into(), &mu).retrieve();
let cap_s = setup.commit(x, &mu).retrieve();
let cap_a =
CiphertextMod::new_with_randomizer_signed(pk0, &alpha, &r.retrieve()).retrieve();
let cap_y = g * &P::scalar_from_signed(&alpha);
let cap_d = setup.commit(&alpha.into(), &gamma).retrieve();
let cap_d = setup.commit(&alpha, &gamma).retrieve();

let mut reader = XofHasher::new_with_dst(HASH_TAG)
// commitments
Expand Down Expand Up @@ -156,8 +156,7 @@ impl<P: SchemeParams> LogStarProof<P> {
// s^{z_1} t^{z_3} == D S^e \mod \hat{N}
let cap_d_mod = self.cap_d.to_mod(setup.public_key());
let cap_s_mod = self.cap_s.to_mod(setup.public_key());
if setup.commit(&self.z1.into(), &self.z3) != &cap_d_mod * &cap_s_mod.pow_signed_vartime(&e)
{
if setup.commit(&self.z1, &self.z3) != &cap_d_mod * &cap_s_mod.pow_signed_vartime(&e) {
return false;
}

Expand Down
7 changes: 3 additions & 4 deletions synedrion/src/cggmp21/sigma/mul_star.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ impl<P: SchemeParams> MulStarProof<P> {

let cap_a = (cap_c * alpha).mul_randomizer(&r.retrieve()).retrieve();
let cap_b_x = P::scalar_from_signed(&alpha).mul_by_generator();
let cap_e = setup.commit(&alpha.into(), &gamma).retrieve();
let cap_s = setup.commit(&x.into(), &m).retrieve();
let cap_e = setup.commit(&alpha, &gamma).retrieve();
let cap_s = setup.commit(x, &m).retrieve();

let mut reader = XofHasher::new_with_dst(HASH_TAG)
// commitments
Expand Down Expand Up @@ -168,8 +168,7 @@ impl<P: SchemeParams> MulStarProof<P> {
// s^{z_1} t^{z_2} == E S^e
let cap_e_mod = self.cap_e.to_mod(aux_pk);
let cap_s_mod = self.cap_s.to_mod(aux_pk);
if setup.commit(&self.z1.into(), &self.z2) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e)
{
if setup.commit(&self.z1, &self.z2) != &cap_e_mod * &cap_s_mod.pow_signed_vartime(&e) {
return false;
}

Expand Down
3 changes: 2 additions & 1 deletion synedrion/src/cggmp21/sigma/prm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use alloc::{vec, vec::Vec};

use digest::XofReader;
use rand_core::CryptoRngCore;
use secrecy::ExposeSecret;
use serde::{Deserialize, Serialize};

use super::super::SchemeParams;
Expand Down Expand Up @@ -110,7 +111,7 @@ impl<P: SchemeParams> PrmProof<P> {
.iter()
.zip(challenge.0.iter())
.map(|(a, e)| {
let x = a.add_mod(lambda.as_ref(), &totient);
let x = a.add_mod(lambda.as_ref(), totient.expose_secret());
let choice = Choice::from(*e as u8);
Bounded::conditional_select(a, &x, choice)
})
Expand Down
Loading

0 comments on commit da35145

Please sign in to comment.