diff --git a/Cargo.lock b/Cargo.lock index e4009f87..44d8697b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4877,6 +4877,7 @@ dependencies = [ "dalek-ff-group", "digest 0.10.7", "dkg", + "dkg-recovery", "flexible-transcript", "hex", "minimal-ed448", diff --git a/crypto/frost/Cargo.toml b/crypto/frost/Cargo.toml index 0b2171d6..74b3318f 100644 --- a/crypto/frost/Cargo.toml +++ b/crypto/frost/Cargo.toml @@ -40,12 +40,14 @@ multiexp = { path = "../multiexp", version = "0.4", default-features = false, fe schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1", default-features = false, features = ["std"] } dkg = { path = "../dkg", version = "0.6", default-features = false, features = ["std"] } +dkg-recovery = { path = "../dkg/recovery", default-features = false, features = ["std"], optional = true } [dev-dependencies] hex = "0.4" serde_json = { version = "1", default-features = false, features = ["std"] } -dkg = { path = "../dkg" } +dkg = { path = "../dkg", default-features = false, features = ["std"] } +dkg-recovery = { path = "../dkg/recovery", default-features = false, features = ["std"] } [features] ed25519 = ["dalek-ff-group", "ciphersuite/ed25519"] @@ -56,4 +58,4 @@ p256 = ["ciphersuite/p256"] ed448 = ["minimal-ed448", "ciphersuite/ed448"] -tests = ["hex", "rand_core/getrandom"] +tests = ["hex", "rand_core/getrandom", "dkg-recovery"] diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index 47862c61..6baf8872 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; use thiserror::Error; /// Distributed key generation protocol. -pub use dkg::{self, Participant, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView}; +pub use dkg::{self, Participant, ThresholdParams, ThresholdKeys, ThresholdView}; /// Curve trait and provided curves/HRAMs, forming various ciphersuites. pub mod curve; diff --git a/crypto/frost/src/tests/mod.rs b/crypto/frost/src/tests/mod.rs index 2bb9e3ea..1b2afa12 100644 --- a/crypto/frost/src/tests/mod.rs +++ b/crypto/frost/src/tests/mod.rs @@ -1,11 +1,18 @@ +use core::ops::Deref; use std::collections::HashMap; +use zeroize::{Zeroize, Zeroizing}; use rand_core::{RngCore, CryptoRng}; -pub use dkg::tests::{key_gen, musig_key_gen, recover_key}; +use ciphersuite::{ + group::ff::{Field, PrimeField}, + Ciphersuite, +}; +use dkg::Interpolation; +pub use dkg_recovery::recover_key; use crate::{ - Curve, Participant, ThresholdKeys, FrostError, + Curve, Participant, ThresholdParams, ThresholdKeys, FrostError, algorithm::{Algorithm, Hram, IetfSchnorr}, sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine}, }; @@ -26,6 +33,56 @@ pub const PARTICIPANTS: u16 = 5; /// Constant threshold of participants to use when signing. pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1; +/// Create a key, for testing purposes. +pub fn key_gen( + rng: &mut R, +) -> HashMap> { + let coefficients: [_; THRESHOLD as usize] = + core::array::from_fn(|_| Zeroizing::new(C::F::random(&mut *rng))); + + fn polynomial( + coefficients: &[Zeroizing], + l: Participant, + ) -> Zeroizing { + let l = F::from(u64::from(u16::from(l))); + // This should never be reached since Participant is explicitly non-zero + assert!(l != F::ZERO, "zero participant passed to polynomial"); + let mut share = Zeroizing::new(F::ZERO); + for (idx, coefficient) in coefficients.iter().rev().enumerate() { + *share += coefficient.deref(); + if idx != (coefficients.len() - 1) { + *share *= l; + } + } + share + } + + let group_key = C::generator() * *coefficients[0]; + let mut secret_shares = HashMap::with_capacity(PARTICIPANTS as usize); + let mut verification_shares = HashMap::with_capacity(PARTICIPANTS as usize); + for i in 1 ..= PARTICIPANTS { + let i = Participant::new(i).unwrap(); + let secret_share = polynomial(&coefficients, i); + secret_shares.insert(i, secret_share.clone()); + verification_shares.insert(i, C::generator() * *secret_share); + } + + let mut res = HashMap::with_capacity(PARTICIPANTS as usize); + for i in 1 ..= PARTICIPANTS { + let i = Participant::new(i).unwrap(); + let keys = ThresholdKeys::new( + ThresholdParams::new(THRESHOLD, PARTICIPANTS, i).unwrap(), + Interpolation::Lagrange, + secret_shares.remove(&i).unwrap(), + verification_shares.clone(), + ) + .unwrap(); + assert_eq!(keys.group_key(), group_key); + res.insert(i, keys); + } + res +} + /// Clone a map without a specific value. pub fn clone_without( map: &HashMap, @@ -238,12 +295,6 @@ pub fn test_schnorr>(rng: &mut R) { test_schnorr_with_keys::<_, _, H>(&mut *rng, &keys) } -/// Test a basic Schnorr signature, yet with MuSig. -pub fn test_musig_schnorr>(rng: &mut R) { - let keys = musig_key_gen(&mut *rng); - test_schnorr_with_keys::<_, _, H>(&mut *rng, &keys) -} - /// Test an offset Schnorr signature. pub fn test_offset_schnorr>(rng: &mut R) { const MSG: &[u8] = b"Hello, World!"; @@ -290,7 +341,6 @@ pub fn test_schnorr_blame>(rng: &mu /// Run a variety of tests against a ciphersuite. pub fn test_ciphersuite>(rng: &mut R) { test_schnorr::(rng); - test_musig_schnorr::(rng); test_offset_schnorr::(rng); test_schnorr_blame::(rng); diff --git a/crypto/frost/src/tests/nonces.rs b/crypto/frost/src/tests/nonces.rs index 7b1480e9..c37d618f 100644 --- a/crypto/frost/src/tests/nonces.rs +++ b/crypto/frost/src/tests/nonces.rs @@ -9,12 +9,10 @@ use transcript::{Transcript, RecommendedTranscript}; use ciphersuite::group::{ff::Field, Group, GroupEncoding}; -pub use dkg::tests::{key_gen, recover_key}; - use crate::{ Curve, Participant, ThresholdView, ThresholdKeys, FrostError, algorithm::Algorithm, - tests::{algorithm_machines, sign}, + tests::{key_gen, algorithm_machines, sign}, }; #[derive(Clone)] diff --git a/crypto/frost/src/tests/vectors.rs b/crypto/frost/src/tests/vectors.rs index dc0453a1..a5369a02 100644 --- a/crypto/frost/src/tests/vectors.rs +++ b/crypto/frost/src/tests/vectors.rs @@ -13,7 +13,7 @@ use ciphersuite::group::{ff::PrimeField, GroupEncoding}; use crate::{ curve::Curve, - Participant, ThresholdCore, ThresholdKeys, + Participant, ThresholdKeys, algorithm::{Hram, IetfSchnorr}, sign::{ Writable, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Preprocess, @@ -115,7 +115,7 @@ fn vectors_to_multisig_keys(vectors: &Vectors) -> HashMap(vectors: &Vectors) -> HashMap::read::<&[u8]>(&mut serialized.as_ref()).unwrap(); + let these_keys = ThresholdKeys::::read::<&[u8]>(&mut serialized.as_ref()).unwrap(); assert_eq!(these_keys.params().t(), vectors.threshold); assert_eq!(usize::from(these_keys.params().n()), shares.len()); let participant = Participant::new(i).unwrap(); assert_eq!(these_keys.params().i(), participant); assert_eq!(these_keys.secret_share().deref(), &shares[usize::from(i - 1)]); assert_eq!(hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key); - keys.insert(participant, ThresholdKeys::new(these_keys)); + keys.insert(participant, these_keys); } keys @@ -157,7 +157,7 @@ pub fn test_with_vectors>( let secret = C::read_F::<&[u8]>(&mut hex::decode(&vectors.group_secret).unwrap().as_ref()).unwrap(); assert_eq!(C::generator() * secret, group_key); - assert_eq!(recover_key(&keys), secret); + assert_eq!(*recover_key(&keys.values().cloned().collect::>()).unwrap(), secret); let mut machines = vec![]; for i in &vectors.included {