diff --git a/.gitignore b/.gitignore index 5d3d773b..d10b8258 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ Cargo.lock *~ **/.DS_Store .vscode/* +*.swp diff --git a/Cargo.toml b/Cargo.toml index cbf054ff..69771765 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "frost-p256", "frost-ristretto255", "frost-secp256k1", + "frost-secp256k1-tr", "frost-rerandomized", "gencode" ] diff --git a/frost-core/src/batch.rs b/frost-core/src/batch.rs index 37d25bdf..9d4a4197 100644 --- a/frost-core/src/batch.rs +++ b/frost-core/src/batch.rs @@ -32,7 +32,7 @@ where { fn from((vk, sig, msg): (VerifyingKey, Signature, &'msg M)) -> Self { // Compute c now to avoid dependency on the msg lifetime. - let c = crate::challenge(&sig.R, &vk, msg.as_ref()); + let c = ::challenge(&sig.R, &vk, msg.as_ref()); Self { vk, sig, c } } @@ -118,7 +118,12 @@ where for item in self.signatures.iter() { let z = item.sig.z; - let R = item.sig.R; + let mut R = item.sig.R; + let mut vk = item.vk.element; + if ::is_need_tweaking() { + R = ::tweaked_R(&item.sig.R); + vk = ::tweaked_public_key(&item.vk.element); + } let blind = <::Field>::random(&mut rng); @@ -129,7 +134,7 @@ where Rs.push(R); VK_coeffs.push(<::Field>::zero() + (blind * item.c.0)); - VKs.push(item.vk.element); + VKs.push(vk); } let scalars = once(&P_coeff_acc) diff --git a/frost-core/src/keys.rs b/frost-core/src/keys.rs index ffcbc116..b10ed209 100644 --- a/frost-core/src/keys.rs +++ b/frost-core/src/keys.rs @@ -120,6 +120,11 @@ where pub(crate) fn from_coefficients(coefficients: &[Scalar], peer: Identifier) -> Self { Self(evaluate_polynomial(peer, coefficients)) } + + /// Returns negated SigningShare + pub fn negate(&mut self) { + self.0 = <::Field>::negate(&self.0); + } } impl Debug for SigningShare @@ -674,6 +679,11 @@ where min_signers, } } + + /// Negate `SigningShare`. + pub fn negate_signing_share(&mut self) { + self.signing_share.negate(); + } } #[cfg(feature = "serialization")] diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index ba0ed59e..0c47a11d 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -69,7 +69,6 @@ where C: Ciphersuite, { /// Creates a challenge from a scalar. - #[cfg(feature = "internals")] pub fn from_scalar( scalar: <<::Group as Group>::Field as Field>::Scalar, ) -> Self { @@ -77,7 +76,6 @@ where } /// Return the underlying scalar. - #[cfg(feature = "internals")] pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { self.0 } @@ -465,6 +463,11 @@ where pub fn to_element(self) -> ::Element { self.0 } + + /// Check if group commitment is odd + pub fn y_is_odd(&self) -> bool { + ::y_is_odd(&self.0) + } } /// Generates the group commitment which is published as part of the joint @@ -585,6 +588,15 @@ where z = z + signature_share.share; } + if ::is_need_tweaking() { + let challenge = ::challenge( + &group_commitment.0, + &pubkeys.verifying_key, + signing_package.message().as_slice(), + ); + z = ::aggregate_tweak_z(z, &challenge, &pubkeys.verifying_key.element); + } + let signature = Signature { R: group_commitment.0, z, @@ -601,7 +613,7 @@ where #[cfg(feature = "cheater-detection")] if let Err(err) = verification_result { // Compute the per-message challenge. - let challenge = crate::challenge::( + let challenge = ::challenge( &group_commitment.0, &pubkeys.verifying_key, signing_package.message().as_slice(), @@ -636,6 +648,8 @@ where signer_pubkey, lambda_i, &challenge, + &group_commitment, + &pubkeys.verifying_key, )?; } diff --git a/frost-core/src/round1.rs b/frost-core/src/round1.rs index eb4abb3a..df4a3f76 100644 --- a/frost-core/src/round1.rs +++ b/frost-core/src/round1.rs @@ -50,6 +50,11 @@ where Self::nonce_generate_from_random_bytes(secret, random_bytes) } + /// Negate `Nonce`. + pub fn negate(&mut self) { + self.0 = <::Field>::negate(&self.0); + } + /// Generates a nonce from the given random bytes. /// This function allows testing and MUST NOT be made public. pub(crate) fn nonce_generate_from_random_bytes( @@ -263,6 +268,12 @@ where pub fn binding(&self) -> &Nonce { &self.binding } + + /// Negate `SigningShare`. + pub fn negate_nonces(&mut self) { + self.binding.negate(); + self.hiding.negate(); + } } /// Published by each participant in the first round of the signing protocol. diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs index 509a6e02..28382789 100644 --- a/frost-core/src/round2.rs +++ b/frost-core/src/round2.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug}; use crate as frost; use crate::{ - challenge, Challenge, Ciphersuite, Error, Field, Group, {round1, *}, + Challenge, Ciphersuite, Error, Field, Group, {round1, *}, }; #[cfg(feature = "serde")] @@ -90,9 +90,23 @@ where verifying_share: &frost::keys::VerifyingShare, lambda_i: Scalar, challenge: &Challenge, + group_commitment: &frost::GroupCommitment, + verifying_key: &frost::VerifyingKey, ) -> Result<(), Error> { + let mut commitment_share = group_commitment_share.0; + let mut vsh = verifying_share.0; + if ::is_need_tweaking() { + commitment_share = ::tweaked_group_commitment_share( + &group_commitment_share.0, + &group_commitment.0 + ); + vsh = ::tweaked_verifying_share( + &verifying_share.0, + &verifying_key.element + ); + } if (::generator() * self.share) - != (group_commitment_share.0 + (verifying_share.0 * challenge.0 * lambda_i)) + != (commitment_share + (vsh * challenge.0 * lambda_i)) { return Err(Error::InvalidSignatureShare { culprit: identifier, @@ -150,9 +164,7 @@ where } /// Compute the signature share for a signing operation. -#[cfg_attr(feature = "internals", visibility::make(pub))] -#[cfg_attr(docsrs, doc(cfg(feature = "internals")))] -fn compute_signature_share( +pub fn compute_signature_share( signer_nonces: &round1::SigningNonces, binding_factor: BindingFactor, lambda_i: <<::Group as Group>::Field as Field>::Scalar, @@ -214,20 +226,33 @@ pub fn sign( let lambda_i = frost::derive_interpolating_value(key_package.identifier(), signing_package)?; // Compute the per-message challenge. - let challenge = challenge::( + let challenge = ::challenge( &group_commitment.0, &key_package.verifying_key, signing_package.message.as_slice(), ); // Compute the Schnorr signature share. - let signature_share = compute_signature_share( - signer_nonces, - binding_factor, - lambda_i, - key_package, - challenge, - ); + if ::is_need_tweaking() { + let signature_share = ::compute_tweaked_signature_share( + signer_nonces, + binding_factor, + group_commitment, + lambda_i, + key_package, + challenge, + ); - Ok(signature_share) + Ok(signature_share) + } else { + let signature_share = compute_signature_share( + signer_nonces, + binding_factor, + lambda_i, + key_package, + challenge, + ); + + Ok(signature_share) + } } diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs index e913c7de..2daa0905 100644 --- a/frost-core/src/signature.rs +++ b/frost-core/src/signature.rs @@ -1,11 +1,12 @@ //! Schnorr signatures over prime order groups (or subgroups) use debugless_unwrap::DebuglessUnwrap; +use derive_getters::Getters; use crate::{Ciphersuite, Element, Error, Field, Group, Scalar}; /// A Schnorr signature over some prime order group (or subgroup). -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq, Getters)] pub struct Signature { /// The commitment `R` to the signature nonce. pub(crate) R: Element, diff --git a/frost-core/src/signing_key.rs b/frost-core/src/signing_key.rs index 6d165835..bc67c6db 100644 --- a/frost-core/src/signing_key.rs +++ b/frost-core/src/signing_key.rs @@ -2,7 +2,7 @@ use rand_core::{CryptoRng, RngCore}; -use crate::{random_nonzero, Ciphersuite, Error, Field, Group, Scalar, Signature, VerifyingKey}; +use crate::{random_nonzero, Ciphersuite, Error, Field, Group, Scalar, Signature, VerifyingKey, Challenge}; /// A signing key for a Schnorr signature on a FROST [`Ciphersuite::Group`]. #[derive(Copy, Clone, PartialEq, Eq)] @@ -45,14 +45,21 @@ where /// Create a signature `msg` using this `SigningKey`. pub fn sign(&self, mut rng: R, msg: &[u8]) -> Signature { - let k = random_nonzero::(&mut rng); - + let public = VerifyingKey::::from(*self); + let mut secret = self.scalar; + if ::is_need_tweaking() { + secret = ::tweaked_secret_key(secret, &public.element); + } + let mut k = random_nonzero::(&mut rng); let R = ::generator() * k; + if ::is_need_tweaking() { + k = ::tweaked_nonce(k, &R); + } // Generate Schnorr challenge - let c = crate::challenge::(&R, &VerifyingKey::::from(*self), msg); + let c: Challenge = ::challenge(&R, &public, msg); - let z = k + (c.0 * self.scalar); + let z = k + (c.0 * secret); Signature { R, z } } diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 817caf60..258691ea 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -7,7 +7,8 @@ use std::{ use rand_core::{CryptoRng, RngCore}; -use crate::{Error, FieldError, GroupError, Signature, VerifyingKey}; +use crate::{Error, FieldError, GroupError, Signature, VerifyingKey, Challenge, + challenge}; /// A prime order finite field GF(q) over which all scalar values for our prime order group can be /// multiplied are defined. @@ -40,6 +41,12 @@ pub trait Field: Copy + Clone { /// element is zero. fn invert(scalar: &Self::Scalar) -> Result; + /// Computes the negation of the element of the scalar field + #[allow(unused)] + fn negate(scalar: &Self::Scalar) -> Self::Scalar { + panic!("Not implemented"); + } + /// Generate a random scalar from the entire space [0, l-1] /// /// @@ -113,6 +120,12 @@ pub trait Group: Copy + Clone + PartialEq { /// [`ScalarBaseMult()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.5 fn generator() -> Self::Element; + /// Check if element is odd + #[allow(unused)] + fn y_is_odd(element: &Self::Element) -> bool { + panic!("Not implemented"); + } + /// A member function of a group _G_ that maps an [`Element`] to a unique byte array buf of /// fixed length Ne. /// @@ -224,8 +237,108 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug { signature: &Signature, public_key: &VerifyingKey, ) -> Result<(), Error> { - let c = crate::challenge::(&signature.R, public_key, msg); + let c = ::challenge(&signature.R, public_key, msg); public_key.verify_prehashed(c, signature) } + + /// Generates the challenge as is required for Schnorr signatures. + /// + /// Deals in bytes, so that [FROST] and singleton signing and verification can use it with different + /// types. + /// + /// This is the only invocation of the H2 hash function from the [RFC]. + /// + /// [FROST]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-challenge-computa + /// [RFC]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-3.2 + fn challenge(R: &Element, verifying_key: &VerifyingKey, msg: &[u8]) -> Challenge + { + challenge(R, verifying_key, msg) + } + + /// determine tweak is need + fn is_need_tweaking() -> bool { + false + } + + /// aggregate tweak z + #[allow(unused)] + fn aggregate_tweak_z( + z: <::Field as Field>::Scalar, + challenge: &Challenge, + verifying_key: &Element, + ) -> <::Field as Field>::Scalar + { + panic!("Not implemented"); + } + + /// signature_share tweak + #[allow(unused)] + fn compute_tweaked_signature_share( + signer_nonces: &crate::round1::SigningNonces, + binding_factor: crate::BindingFactor, + group_commitment: crate::GroupCommitment, + lambda_i: <::Field as Field>::Scalar, + key_package: &crate::keys::KeyPackage, + challenge: Challenge, + ) -> crate::round2::SignatureShare + { + panic!("Not implemented"); + } + + /// calculate tweaked public key + #[allow(unused)] + fn tweaked_public_key( + public_key: &::Element, + ) -> ::Element { + panic!("Not implemented"); + } + + /// calculate tweaked R + #[allow(unused)] + fn tweaked_R( + public_key: &::Element, + ) -> ::Element { + panic!("Not implemented"); + } + + /// tweaked secret + #[allow(unused)] + fn tweaked_secret_key( + secret: <::Field as Field>::Scalar, + public: &Element, + ) -> <::Field as Field>::Scalar + { + panic!("Not implemented"); + } + + /// tweaked nonce + #[allow(unused)] + fn tweaked_nonce( + nonce: <::Field as Field>::Scalar, + R: &Element, + ) -> <::Field as Field>::Scalar + { + panic!("Not implemented"); + } + + /// tweaked group commitment + #[allow(unused)] + fn tweaked_group_commitment_share( + group_commitment_share: &::Element, + group_commitment: &::Element, + ) -> ::Element + { + panic!("Not implemented"); + } + + /// tweaked verifying share + #[allow(unused)] + fn tweaked_verifying_share( + verifying_share: &::Element, + verifying_key: &::Element, + ) -> ::Element + { + panic!("Not implemented"); + } } diff --git a/frost-core/src/verifying_key.rs b/frost-core/src/verifying_key.rs index 519020b5..7377f971 100644 --- a/frost-core/src/verifying_key.rs +++ b/frost-core/src/verifying_key.rs @@ -1,4 +1,5 @@ use std::fmt::{self, Debug}; +use derive_getters::Getters; #[cfg(any(test, feature = "test-impl"))] use hex::FromHex; @@ -9,7 +10,7 @@ use crate::{Challenge, Ciphersuite, Element, Error, Group, Signature}; use crate::serialization::ElementSerialization; /// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`]. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Getters)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))] #[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization"))] @@ -33,11 +34,15 @@ where } /// Return the underlying element. - #[cfg(feature = "internals")] pub fn to_element(self) -> ::Element { self.element } + /// Check if VerifyingKey is odd + pub fn y_is_odd(&self) -> bool { + ::y_is_odd(&self.element) + } + /// Deserialize from bytes pub fn deserialize( bytes: ::Serialization, @@ -63,9 +68,15 @@ where // h * ( z * B - c * A - R) == 0 // // where h is the cofactor + let mut R = signature.R; + let mut vk = self.element; + if ::is_need_tweaking() { + R = ::tweaked_R(&signature.R); + vk = ::tweaked_public_key(&self.element); + } let zB = C::Group::generator() * signature.z; - let cA = self.element * challenge.0; - let check = (zB - cA - signature.R) * C::Group::cofactor(); + let cA = vk * challenge.0; + let check = (zB - cA - R) * C::Group::cofactor(); if check == C::Group::identity() { Ok(()) diff --git a/frost-secp256k1-tr/Cargo.toml b/frost-secp256k1-tr/Cargo.toml new file mode 100644 index 00000000..a5b16171 --- /dev/null +++ b/frost-secp256k1-tr/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "frost-secp256k1-tr" +edition = "2021" +# When releasing to crates.io: +# - Update CHANGELOG.md +# - Create git tag. +version = "1.0.0-rc.0" +authors = [ + "Deirdre Connolly ", + "Chelsea Komlo ", + "Conrado Gouvea " +] +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/ZcashFoundation/frost" +categories = ["cryptography"] +keywords = ["cryptography", "crypto", "threshold", "signature"] +description = "A Schnorr signature scheme over the secp256k1 curve that supports FROST and Taproot." + +[package.metadata.docs.rs] +features = ["serde"] +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +document-features = "0.2.7" +frost-core = { path = "../frost-core", version = "1.0.0-rc.0" } +frost-rerandomized = { path = "../frost-rerandomized", version = "1.0.0-rc.0" } +k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"] } +rand_core = "0.6" +sha2 = "0.10.2" + +[dev-dependencies] +criterion = "0.5" +frost-core = { path = "../frost-core", version = "1.0.0-rc.0", features = ["test-impl"] } +frost-rerandomized = { path = "../frost-rerandomized", version = "1.0.0-rc.0", features = ["test-impl"] } +insta = { version = "1.31.0", features = ["yaml"] } +hex = "0.4.3" +lazy_static = "1.4" +proptest = "1.0" +rand = "0.8" +rand_chacha = "0.3" +serde_json = "1.0" + +[features] +nightly = [] +default = ["serialization", "cheater-detection"] +serialization = ["serde", "frost-core/serialization"] +#! ## Features +## Enable `serde` support for types that need to be communicated. You +## can use `serde` to serialize structs with any encoder that supports +## `serde` (e.g. JSON with `serde_json`). +serde = ["frost-core/serde"] +## Enable cheater detection +cheater-detection = ["frost-core/cheater-detection"] + +[lib] +# Disables non-criterion benchmark which is not used; prevents errors +# when using criterion-specific flags +bench = false + +[[bench]] +name = "bench" +harness = false diff --git a/frost-secp256k1-tr/README.md b/frost-secp256k1-tr/README.md new file mode 100644 index 00000000..ff60cebc --- /dev/null +++ b/frost-secp256k1-tr/README.md @@ -0,0 +1,121 @@ +An implementation of Schnorr signatures on the secp256k1 curve for both single and threshold numbers +of signers (FROST) with support of Taproot (BIP340/BIP341). + +## Example: key generation with trusted dealer and FROST signing + +Creating a key with a trusted dealer and splitting into shares; then signing a message +and aggregating the signature. Note that the example just simulates a distributed +scenario in a single thread and it abstracts away any communication between peers. + + +```rust +# // ANCHOR: tkg_gen +use frost_secp256k1_tr as frost; +use rand::thread_rng; +use std::collections::BTreeMap; + +let mut rng = thread_rng(); +let max_signers = 5; +let min_signers = 3; +let (shares, pubkey_package) = frost::keys::generate_with_dealer( + max_signers, + min_signers, + frost::keys::IdentifierList::Default, + &mut rng, +)?; +# // ANCHOR_END: tkg_gen + +// Verifies the secret shares from the dealer and store them in a BTreeMap. +// In practice, the KeyPackages must be sent to its respective participants +// through a confidential and authenticated channel. +let mut key_packages: BTreeMap<_, _> = BTreeMap::new(); + +for (identifier, secret_share) in shares { + # // ANCHOR: tkg_verify + let key_package = frost::keys::KeyPackage::try_from(secret_share)?; + # // ANCHOR_END: tkg_verify + key_packages.insert(identifier, key_package); +} + +let mut nonces_map = BTreeMap::new(); +let mut commitments_map = BTreeMap::new(); + +//////////////////////////////////////////////////////////////////////////// +// Round 1: generating nonces and signing commitments for each participant +//////////////////////////////////////////////////////////////////////////// + +// In practice, each iteration of this loop will be executed by its respective participant. +for participant_index in 1..(min_signers as u16 + 1) { + let participant_identifier = participant_index.try_into().expect("should be nonzero"); + let key_package = &key_packages[&participant_identifier]; + // Generate one (1) nonce and one SigningCommitments instance for each + // participant, up to _threshold_. + # // ANCHOR: round1_commit + let (nonces, commitments) = frost::round1::commit( + key_packages[&participant_identifier].signing_share(), + &mut rng, + ); + # // ANCHOR_END: round1_commit + // In practice, the nonces must be kept by the participant to use in the + // next round, while the commitment must be sent to the coordinator + // (or to every other participant if there is no coordinator) using + // an authenticated channel. + nonces_map.insert(participant_identifier, nonces); + commitments_map.insert(participant_identifier, commitments); +} + +// This is what the signature aggregator / coordinator needs to do: +// - decide what message to sign +// - take one (unused) commitment per signing participant +let mut signature_shares = BTreeMap::new(); +# // ANCHOR: round2_package +let message = "message to sign".as_bytes(); +# // In practice, the SigningPackage must be sent to all participants +# // involved in the current signing (at least min_signers participants), +# // using an authenticate channel (and confidential if the message is secret). +let signing_package = frost::SigningPackage::new(commitments_map, message); +# // ANCHOR_END: round2_package + +//////////////////////////////////////////////////////////////////////////// +// Round 2: each participant generates their signature share +//////////////////////////////////////////////////////////////////////////// + +// In practice, each iteration of this loop will be executed by its respective participant. +for participant_identifier in nonces_map.keys() { + let key_package = &key_packages[participant_identifier]; + + let nonces = &nonces_map[participant_identifier]; + + // Each participant generates their signature share. + # // ANCHOR: round2_sign + let signature_share = frost::round2::sign(&signing_package, nonces, key_package)?; + # // ANCHOR_END: round2_sign + + // In practice, the signature share must be sent to the Coordinator + // using an authenticated channel. + signature_shares.insert(*participant_identifier, signature_share); +} + +//////////////////////////////////////////////////////////////////////////// +// Aggregation: collects the signing shares from all participants, +// generates the final signature. +//////////////////////////////////////////////////////////////////////////// + +// Aggregate (also verifies the signature shares) +# // ANCHOR: aggregate +let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?; +# // ANCHOR_END: aggregate + + +// Check that the threshold signature can be verified by the group public +// key (the verification key). +# // ANCHOR: verify +let is_signature_valid = pubkey_package + .verifying_key() + .verify(message, &group_signature) + .is_ok(); +# // ANCHOR_END: verify +assert!(is_signature_valid); + +# Ok::<(), frost::Error>(()) +``` diff --git a/frost-secp256k1-tr/benches/bench.rs b/frost-secp256k1-tr/benches/bench.rs new file mode 100644 index 00000000..c5773633 --- /dev/null +++ b/frost-secp256k1-tr/benches/bench.rs @@ -0,0 +1,19 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::thread_rng; + +use frost_secp256k1::*; + +fn bench_secp256k1_batch_verify(c: &mut Criterion) { + let mut rng = thread_rng(); + + frost_core::benches::bench_batch_verify::(c, "secp256k1", &mut rng); +} + +fn bench_secp256k1_sign(c: &mut Criterion) { + let mut rng = thread_rng(); + + frost_core::benches::bench_sign::(c, "secp256k1", &mut rng); +} + +criterion_group!(benches, bench_secp256k1_batch_verify, bench_secp256k1_sign); +criterion_main!(benches); diff --git a/frost-secp256k1-tr/dkg.md b/frost-secp256k1-tr/dkg.md new file mode 100644 index 00000000..217cbc99 --- /dev/null +++ b/frost-secp256k1-tr/dkg.md @@ -0,0 +1,168 @@ +# Distributed Key Generation (DKG) + +The DKG module supports generating FROST key shares in a distributed manner, +without a trusted dealer. + +Before starting, each participant needs an unique identifier, which can be built from +a `u16`. The process in which these identifiers are allocated is up to the application. + +The distributed key generation process has 3 parts, with 2 communication rounds +between them, in which each participant needs to send a "package" to every other +participant. In the first round, each participant sends the same package +(a [`round1::Package`]) to every other. In the second round, each receiver gets +their own package (a [`round2::Package`]). + +Between part 1 and 2, each participant needs to hold onto a [`round1::SecretPackage`] +that MUST be kept secret. Between part 2 and 3, each participant needs to hold +onto a [`round2::SecretPackage`]. + +After the third part, each participant will get a [`KeyPackage`] with their +long-term secret share that must be kept secret, and a [`PublicKeyPackage`] +that is public (and will be the same between all participants). With those +they can proceed to sign messages with FROST. + + +## Example + +```rust +# // ANCHOR: dkg_import +use rand::thread_rng; +use std::collections::BTreeMap; + +use frost_secp256k1_tr as frost; + +let mut rng = thread_rng(); + +let max_signers = 5; +let min_signers = 3; +# // ANCHOR_END: dkg_import + +//////////////////////////////////////////////////////////////////////////// +// Key generation, Round 1 +//////////////////////////////////////////////////////////////////////////// + +// Keep track of each participant's round 1 secret package. +// In practice each participant will keep its copy; no one +// will have all the participant's packages. +let mut round1_secret_packages = BTreeMap::new(); + +// Keep track of all round 1 packages sent to the given participant. +// This is used to simulate the broadcast; in practice the packages +// will be sent through some communication channel. +let mut received_round1_packages = BTreeMap::new(); + +// For each participant, perform the first part of the DKG protocol. +// In practice, each participant will perform this on their own environments. +for participant_index in 1..=max_signers { + let participant_identifier = participant_index.try_into().expect("should be nonzero"); + # // ANCHOR: dkg_part1 + let (round1_secret_package, round1_package) = frost::keys::dkg::part1( + participant_identifier, + max_signers, + min_signers, + &mut rng, + )?; + # // ANCHOR_END: dkg_part1 + + // Store the participant's secret package for later use. + // In practice each participant will store it in their own environment. + round1_secret_packages.insert(participant_identifier, round1_secret_package); + + // "Send" the round 1 package to all other participants. In this + // test this is simulated using a BTreeMap; in practice this will be + // sent through some communication channel. + for receiver_participant_index in 1..=max_signers { + if receiver_participant_index == participant_index { + continue; + } + let receiver_participant_identifier: frost::Identifier = receiver_participant_index + .try_into() + .expect("should be nonzero"); + received_round1_packages + .entry(receiver_participant_identifier) + .or_insert_with(BTreeMap::new) + .insert(participant_identifier, round1_package.clone()); + } +} + +//////////////////////////////////////////////////////////////////////////// +// Key generation, Round 2 +//////////////////////////////////////////////////////////////////////////// + +// Keep track of each participant's round 2 secret package. +// In practice each participant will keep its copy; no one +// will have all the participant's packages. +let mut round2_secret_packages = BTreeMap::new(); + +// Keep track of all round 2 packages sent to the given participant. +// This is used to simulate the broadcast; in practice the packages +// will be sent through some communication channel. +let mut received_round2_packages = BTreeMap::new(); + +// For each participant, perform the second part of the DKG protocol. +// In practice, each participant will perform this on their own environments. +for participant_index in 1..=max_signers { + let participant_identifier = participant_index.try_into().expect("should be nonzero"); + let round1_secret_package = round1_secret_packages + .remove(&participant_identifier) + .unwrap(); + let round1_packages = &received_round1_packages[&participant_identifier]; + # // ANCHOR: dkg_part2 + let (round2_secret_package, round2_packages) = + frost::keys::dkg::part2(round1_secret_package, round1_packages)?; + # // ANCHOR_END: dkg_part2 + + // Store the participant's secret package for later use. + // In practice each participant will store it in their own environment. + round2_secret_packages.insert(participant_identifier, round2_secret_package); + + // "Send" the round 2 package to all other participants. In this + // test this is simulated using a BTreeMap; in practice this will be + // sent through some communication channel. + // Note that, in contrast to the previous part, here each other participant + // gets its own specific package. + for (receiver_identifier, round2_package) in round2_packages { + received_round2_packages + .entry(receiver_identifier) + .or_insert_with(BTreeMap::new) + .insert(participant_identifier, round2_package); + } +} + +//////////////////////////////////////////////////////////////////////////// +// Key generation, final computation +//////////////////////////////////////////////////////////////////////////// + +// Keep track of each participant's long-lived key package. +// In practice each participant will keep its copy; no one +// will have all the participant's packages. +let mut key_packages = BTreeMap::new(); + +// Keep track of each participant's public key package. +// In practice, if there is a Coordinator, only they need to store the set. +// If there is not, then all candidates must store their own sets. +// All participants will have the same exact public key package. +let mut pubkey_packages = BTreeMap::new(); + +// For each participant, perform the third part of the DKG protocol. +// In practice, each participant will perform this on their own environments. +for participant_index in 1..=max_signers { + let participant_identifier = participant_index.try_into().expect("should be nonzero"); + let round2_secret_package = &round2_secret_packages[&participant_identifier]; + let round1_packages = &received_round1_packages[&participant_identifier]; + let round2_packages = &received_round2_packages[&participant_identifier]; + # // ANCHOR: dkg_part3 + let (key_package, pubkey_package) = frost::keys::dkg::part3( + round2_secret_package, + round1_packages, + round2_packages, + )?; + # // ANCHOR_END: dkg_part3 + key_packages.insert(participant_identifier, key_package); + pubkey_packages.insert(participant_identifier, pubkey_package); +} + +// With its own key package and the pubkey package, each participant can now proceed +// to sign with FROST. +# Ok::<(), frost::Error>(()) +``` diff --git a/frost-secp256k1-tr/src/keys/dkg.rs b/frost-secp256k1-tr/src/keys/dkg.rs new file mode 100644 index 00000000..91c6286c --- /dev/null +++ b/frost-secp256k1-tr/src/keys/dkg.rs @@ -0,0 +1,87 @@ +#![doc = include_str!("../../dkg.md")] +use super::*; + +/// DKG Round 1 structures. +pub mod round1 { + use super::*; + + /// The secret package that must be kept in memory by the participant + /// between the first and second parts of the DKG protocol (round 1). + /// + /// # Security + /// + /// This package MUST NOT be sent to other participants! + pub type SecretPackage = frost::keys::dkg::round1::SecretPackage; + + /// The package that must be broadcast by each participant to all other participants + /// between the first and second parts of the DKG protocol (round 1). + pub type Package = frost::keys::dkg::round1::Package; +} + +/// DKG Round 2 structures. +pub mod round2 { + use super::*; + + /// The secret package that must be kept in memory by the participant + /// between the second and third parts of the DKG protocol (round 2). + /// + /// # Security + /// + /// This package MUST NOT be sent to other participants! + pub type SecretPackage = frost::keys::dkg::round2::SecretPackage; + + /// A package that must be sent by each participant to some other participants + /// in Round 2 of the DKG protocol. Note that there is one specific package + /// for each specific recipient, in contrast to Round 1. + /// + /// # Security + /// + /// The package must be sent on an *confidential* and *authenticated* channel. + pub type Package = frost::keys::dkg::round2::Package; +} + +/// Performs the first part of the distributed key generation protocol +/// for the given participant. +/// +/// It returns the [`round1::SecretPackage`] that must be kept in memory +/// by the participant for the other steps, and the [`round1::Package`] that +/// must be sent to other participants. +pub fn part1( + identifier: Identifier, + max_signers: u16, + min_signers: u16, + mut rng: R, +) -> Result<(round1::SecretPackage, round1::Package), Error> { + frost::keys::dkg::part1(identifier, max_signers, min_signers, &mut rng) +} + +/// Performs the second part of the distributed key generation protocol +/// for the participant holding the given [`round1::SecretPackage`], +/// given the received [`round1::Package`]s received from the other participants. +/// +/// It returns the [`round2::SecretPackage`] that must be kept in memory +/// by the participant for the final step, and the [`round2::Package`]s that +/// must be sent to other participants. +pub fn part2( + secret_package: round1::SecretPackage, + round1_packages: &BTreeMap, +) -> Result<(round2::SecretPackage, BTreeMap), Error> { + frost::keys::dkg::part2(secret_package, round1_packages) +} + +/// Performs the third and final part of the distributed key generation protocol +/// for the participant holding the given [`round2::SecretPackage`], +/// given the received [`round1::Package`]s and [`round2::Package`]s received from +/// the other participants. +/// +/// It returns the [`KeyPackage`] that has the long-lived key share for the +/// participant, and the [`PublicKeyPackage`]s that has public information +/// about all participants; both of which are required to compute FROST +/// signatures. +pub fn part3( + round2_secret_package: &round2::SecretPackage, + round1_packages: &BTreeMap, + round2_packages: &BTreeMap, +) -> Result<(KeyPackage, PublicKeyPackage), Error> { + frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages) +} diff --git a/frost-secp256k1-tr/src/keys/repairable.rs b/frost-secp256k1-tr/src/keys/repairable.rs new file mode 100644 index 00000000..01bb964d --- /dev/null +++ b/frost-secp256k1-tr/src/keys/repairable.rs @@ -0,0 +1,101 @@ +//! Repairable Threshold Scheme +//! +//! Implements the Repairable Threshold Scheme (RTS) from . +//! The RTS is used to help a signer (participant) repair their lost share. This is achieved +//! using a subset of the other signers know here as `helpers`. + +use std::collections::BTreeMap; + +// This is imported separately to make `gencode` work. +// (if it were below, the position of the import would vary between ciphersuites +// after `cargo fmt`) +use crate::{frost, Ciphersuite, CryptoRng, Identifier, RngCore, Scalar}; +use crate::{Error, Secp256K1Sha256}; + +use super::{SecretShare, VerifiableSecretSharingCommitment}; + +/// Step 1 of RTS. +/// +/// Generates the "delta" values from `helper_i` to help `participant` recover their share +/// where `helpers` contains the identifiers of all the helpers (including `helper_i`), and `share_i` +/// is the share of `helper_i`. +/// +/// Returns a BTreeMap mapping which value should be sent to which participant. +pub fn repair_share_step_1( + helpers: &[Identifier], + share_i: &SecretShare, + rng: &mut R, + participant: Identifier, +) -> Result, Error> { + frost::keys::repairable::repair_share_step_1(helpers, share_i, rng, participant) +} + +/// Step 2 of RTS. +/// +/// Generates the `sigma` values from all `deltas` received from `helpers` +/// to help `participant` recover their share. +/// `sigma` is the sum of all received `delta` and the `delta_i` generated for `helper_i`. +/// +/// Returns a scalar +pub fn repair_share_step_2(deltas_j: &[Scalar]) -> Scalar { + frost::keys::repairable::repair_share_step_2::(deltas_j) +} + +/// Step 3 of RTS +/// +/// The `participant` sums all `sigma_j` received to compute the `share`. The `SecretShare` +/// is made up of the `identifier`and `commitment` of the `participant` as well as the +/// `value` which is the `SigningShare`. +pub fn repair_share_step_3( + sigmas: &[Scalar], + identifier: Identifier, + commitment: &VerifiableSecretSharingCommitment, +) -> SecretShare { + frost::keys::repairable::repair_share_step_3(sigmas, identifier, commitment) +} + +#[cfg(test)] +mod tests { + + use lazy_static::lazy_static; + use rand::thread_rng; + use serde_json::Value; + + use crate::Secp256K1Sha256; + + lazy_static! { + pub static ref REPAIR_SHARE: Value = + serde_json::from_str(include_str!("../../tests/helpers/repair-share.json").trim()) + .unwrap(); + } + + #[test] + fn check_repair_share_step_1() { + let rng = thread_rng(); + + frost_core::tests::repairable::check_repair_share_step_1::(rng); + } + + #[test] + fn check_repair_share_step_2() { + frost_core::tests::repairable::check_repair_share_step_2::(&REPAIR_SHARE); + } + + #[test] + fn check_repair_share_step_3() { + let rng = thread_rng(); + frost_core::tests::repairable::check_repair_share_step_3::( + rng, + &REPAIR_SHARE, + ); + } + + #[test] + fn check_repair_share_step_1_fails_with_invalid_min_signers() { + let rng = thread_rng(); + frost_core::tests::repairable::check_repair_share_step_1_fails_with_invalid_min_signers::< + Secp256K1Sha256, + _, + >(rng); + } +} diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs new file mode 100644 index 00000000..1d7a39ec --- /dev/null +++ b/frost-secp256k1-tr/src/lib.rs @@ -0,0 +1,628 @@ +#![allow(non_snake_case)] +#![deny(missing_docs)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc = include_str!("../README.md")] +#![doc = document_features::document_features!()] + +use std::collections::BTreeMap; + +use frost_rerandomized::RandomizedCiphersuite; +use k256::{ + elliptic_curve::{ + bigint::{U256}, + group::prime::PrimeCurveAffine, + hash2curve::{hash_to_field, ExpandMsgXmd}, + sec1::{FromEncodedPoint, ToEncodedPoint}, + Field as FFField, PrimeField, + ScalarPrimitive, + point::{AffineCoordinates, DecompactPoint}, + }, + AffinePoint, ProjectivePoint, Scalar, +}; +use rand_core::{CryptoRng, RngCore}; +use sha2::{Digest, Sha256}; + +use frost_core as frost; + +#[cfg(test)] +mod tests; + +// Re-exports in our public API +pub use frost_core::{serde, Ciphersuite, Field, FieldError, Group, GroupError, + Element, Challenge}; + +pub use rand_core; + +/// An error. +pub type Error = frost_core::Error; + +/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite scalar field. +#[derive(Clone, Copy)] +pub struct Secp256K1ScalarField; + +impl Field for Secp256K1ScalarField { + type Scalar = Scalar; + + type Serialization = [u8; 32]; + + fn zero() -> Self::Scalar { + Scalar::ZERO + } + + fn one() -> Self::Scalar { + Scalar::ONE + } + + fn invert(scalar: &Self::Scalar) -> Result { + // [`Scalar`]'s Eq/PartialEq does a constant-time comparison + if *scalar == ::zero() { + Err(FieldError::InvalidZeroScalar) + } else { + Ok(scalar.invert().unwrap()) + } + } + + fn negate(scalar: &Self::Scalar) -> Self::Scalar { + -scalar + } + + fn random(rng: &mut R) -> Self::Scalar { + Scalar::random(rng) + } + + fn serialize(scalar: &Self::Scalar) -> Self::Serialization { + scalar.to_bytes().into() + } + + fn deserialize(buf: &Self::Serialization) -> Result { + let field_bytes: &k256::FieldBytes = buf.into(); + match Scalar::from_repr(*field_bytes).into() { + Some(s) => Ok(s), + None => Err(FieldError::MalformedScalar), + } + } + + fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { + let mut array = Self::serialize(scalar); + array.reverse(); + array + } +} + +/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite group. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Secp256K1Group; + +impl Group for Secp256K1Group { + type Field = Secp256K1ScalarField; + + type Element = ProjectivePoint; + + /// [SEC 1][1] serialization of a compressed point in secp256k1 takes 33 bytes + /// (1-byte prefix and 32 bytes for the coordinate). + /// + /// Note that, in the SEC 1 spec, the identity is encoded as a single null byte; + /// but here we pad with zeroes. This is acceptable as the identity _should_ never + /// be serialized in FROST, else we error. + /// + /// [1]: https://secg.org/sec1-v2.pdf + type Serialization = [u8; 33]; + + fn cofactor() -> ::Scalar { + Scalar::ONE + } + + fn identity() -> Self::Element { + ProjectivePoint::IDENTITY + } + + fn generator() -> Self::Element { + ProjectivePoint::GENERATOR + } + + fn y_is_odd(element: &Self::Element) -> bool { + element.to_affine().y_is_odd().into() + } + + fn serialize(element: &Self::Element) -> Self::Serialization { + let mut fixed_serialized = [0; 33]; + let serialized_point = element.to_affine().to_encoded_point(true); + let serialized = serialized_point.as_bytes(); + // Sanity check; either it takes all bytes or a single byte (identity). + assert!(serialized.len() == fixed_serialized.len() || serialized.len() == 1); + // Copy to the left of the buffer (i.e. pad the identity with zeroes). + // Note that identity elements shouldn't be serialized in FROST, but we + // do this padding so that this function doesn't have to return an error. + // If this encodes the identity, it will fail when deserializing. + { + let (left, _right) = fixed_serialized.split_at_mut(serialized.len()); + left.copy_from_slice(serialized); + } + fixed_serialized + } + + fn deserialize(buf: &Self::Serialization) -> Result { + let encoded_point = + k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?; + + match Option::::from(AffinePoint::from_encoded_point(&encoded_point)) { + Some(point) => { + if point.is_identity().into() { + // This is actually impossible since the identity is encoded a a single byte + // which will never happen since we receive a 33-byte buffer. + // We leave the check for consistency. + Err(GroupError::InvalidIdentityElement) + } else { + Ok(ProjectivePoint::from(point)) + } + } + None => Err(GroupError::MalformedElement), + } + } +} + +fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { + let mut h = Sha256::new(); + for i in inputs { + h.update(i); + } + let mut output = [0u8; 32]; + output.copy_from_slice(h.finalize().as_slice()); + output +} + +fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar { + let mut u = [Secp256K1ScalarField::zero()]; + hash_to_field::, Scalar>(&[msg], &[domain], &mut u) + .expect("should never return error according to error cases described in ExpandMsgXmd"); + u[0] +} + +/// Context string from the ciphersuite in the [spec]. +/// +/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-1 +const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-TR-v1"; + +/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Secp256K1Sha256; + +/// Digest the hasher to a Scalar +pub fn hasher_to_scalar(hasher: Sha256) -> Scalar { + let sp = ScalarPrimitive::new(U256::from_be_slice(&hasher.finalize())) + .unwrap(); + Scalar::from(&sp) +} + +/// Create a BIP340 compliant tagged hash +pub fn tagged_hash(tag: &str) -> Sha256 { + let mut hasher = Sha256::new(); + let mut tag_hasher = Sha256::new(); + tag_hasher.update(tag.as_bytes()); + let tag_hash = tag_hasher.finalize(); + hasher.update(tag_hash); + hasher.update(tag_hash); + hasher +} + +/// Create a BIP341 compliant taproot tweak +pub fn tweak( + public_key: &<::Group as Group>::Element, + merkle_root: &[u8] +) -> Scalar { + let mut hasher = tagged_hash("TapTweak"); + hasher.update(public_key.to_affine().x()); + hasher.update(merkle_root); + hasher_to_scalar(hasher) +} + +/// Create a BIP341 compliant tweaked public key +pub fn tweaked_public_key( + public_key: &<::Group as Group>::Element, + merkle_root: &[u8], +) -> <::Group as Group>::Element { + let mut pk = public_key.clone(); + if public_key.to_affine().y_is_odd().into() { + pk = -pk; + } + ProjectivePoint::GENERATOR * tweak(&pk, merkle_root) + pk +} + +/// Create a BIP341 compliant tweaked secret key +pub fn tweaked_secret_key( + secret: <<::Group as Group>::Field as Field>::Scalar, + public_key: &<::Group as Group>::Element, + merkle_root: &[u8], +) -> <<::Group as Group>::Field as Field>::Scalar { + let mut secret = secret.clone(); + if public_key.to_affine().y_is_odd().into() { + secret = -secret + } + secret + tweak(&public_key, merkle_root) +} + +impl Ciphersuite for Secp256K1Sha256 { + const ID: &'static str = CONTEXT_STRING; + + type Group = Secp256K1Group; + + type HashOutput = [u8; 32]; + + type SignatureSerialization = [u8; 65]; + + /// H1 for FROST(secp256k1, SHA-256) + /// + /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1 + fn H1(m: &[u8]) -> <::Field as Field>::Scalar { + hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m) + } + + /// H2 for FROST(secp256k1, SHA-256) + /// + /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.2 + fn H2(m: &[u8]) -> <::Field as Field>::Scalar { + let mut hasher = tagged_hash("BIP0340/challenge"); + hasher.update(m); + hasher_to_scalar(hasher) + } + + /// H3 for FROST(secp256k1, SHA-256) + /// + /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.3 + fn H3(m: &[u8]) -> <::Field as Field>::Scalar { + hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m) + } + + /// H4 for FROST(secp256k1, SHA-256) + /// + /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.4 + fn H4(m: &[u8]) -> Self::HashOutput { + hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) + } + + /// H5 for FROST(secp256k1, SHA-256) + /// + /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.5 + fn H5(m: &[u8]) -> Self::HashOutput { + hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) + } + + /// HDKG for FROST(secp256k1, SHA-256) + fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { + Some(hash_to_scalar( + (CONTEXT_STRING.to_owned() + "dkg").as_bytes(), + m, + )) + } + + /// HID for FROST(secp256k1, SHA-256) + fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { + Some(hash_to_scalar( + (CONTEXT_STRING.to_owned() + "id").as_bytes(), + m, + )) + } + + /// Generates the challenge as is required for Schnorr signatures. + fn challenge(R: &Element, verifying_key: &VerifyingKey, msg: &[u8]) -> Challenge + { + let mut preimage = vec![]; + let tweaked_public_key = tweaked_public_key(&verifying_key.to_element(), &[]); + preimage.extend_from_slice(&R.to_affine().x()); + preimage.extend_from_slice(&tweaked_public_key.to_affine().x()); + preimage.extend_from_slice(msg); + Challenge::from_scalar(S::H2(&preimage[..])) + } + + /// determine tweak is need + fn is_need_tweaking() -> bool { + true + } + + /// aggregate tweak z + fn aggregate_tweak_z( + z: <::Field as Field>::Scalar, + challenge: &Challenge, + verifying_key: &Element, + ) -> <::Field as Field>::Scalar + { + let t = tweak(&verifying_key, &[]); + z + t * challenge.clone().to_scalar() + } + + /// compute tweaked signature_share + fn compute_tweaked_signature_share( + signer_nonces: &round1::SigningNonces, + binding_factor: frost::BindingFactor, + group_commitment: frost_core::GroupCommitment, + lambda_i: <::Field as Field>::Scalar, + key_package: &frost::keys::KeyPackage, + challenge: Challenge, + ) -> round2::SignatureShare + { + let mut sn = signer_nonces.clone(); + if group_commitment.y_is_odd() { + sn.negate_nonces(); + } + + let mut kp = key_package.clone(); + if key_package.verifying_key().y_is_odd() { + kp.negate_signing_share(); + } + + frost::round2::compute_signature_share( + &sn, + binding_factor, + lambda_i, + &kp, + challenge, + ) + } + + /// calculate tweaked public key + fn tweaked_public_key( + public_key: &::Element, + ) -> ::Element { + tweaked_public_key(public_key, &[]) + } + + /// calculate tweaked R + fn tweaked_R( + R: &::Element, + ) -> ::Element { + AffinePoint::decompact(&R.to_affine().x()).unwrap().into() + } + + /// tweaked secret + fn tweaked_secret_key( + secret: <::Field as Field>::Scalar, + public: &Element, + ) -> <::Field as Field>::Scalar + { + tweaked_secret_key(secret, &public, &[]) + } + + /// tweaked nonce + fn tweaked_nonce( + nonce: <::Field as Field>::Scalar, + R: &Element, + ) -> <::Field as Field>::Scalar + { + if R.to_affine().y_is_odd().into() { + -nonce + } else { + nonce + } + } + + fn tweaked_group_commitment_share( + group_commitment_share: &Element, + group_commitment: &Element, + ) -> Element + { + if group_commitment.to_affine().y_is_odd().into() { + -group_commitment_share + } else { + *group_commitment_share + } + } + + fn tweaked_verifying_share( + verifying_share: &::Element, + verifying_key: &::Element, + ) -> ::Element + { + let mut vs = verifying_share.clone(); + if verifying_key.to_affine().y_is_odd().into() { + vs = -vs; + } + vs + } +} + +impl RandomizedCiphersuite for Secp256K1Sha256 { + fn hash_randomizer(m: &[u8]) -> Option<<::Field as Field>::Scalar> { + Some(hash_to_scalar( + (CONTEXT_STRING.to_owned() + "randomizer").as_bytes(), + m, + )) + } +} + +type S = Secp256K1Sha256; + +/// A FROST(secp256k1, SHA-256) participant identifier. +pub type Identifier = frost::Identifier; + +/// FROST(secp256k1, SHA-256) keys, key generation, key shares. +pub mod keys { + use super::*; + use std::collections::BTreeMap; + + /// The identifier list to use when generating key shares. + pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, S>; + + /// Allows all participants' keys to be generated using a central, trusted + /// dealer. + pub fn generate_with_dealer( + max_signers: u16, + min_signers: u16, + identifiers: IdentifierList, + mut rng: RNG, + ) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng) + } + + /// Splits an existing key into FROST shares. + /// + /// This is identical to [`generate_with_dealer`] but receives an existing key + /// instead of generating a fresh one. This is useful in scenarios where + /// the key needs to be generated externally or must be derived from e.g. a + /// seed phrase. + pub fn split( + secret: &SigningKey, + max_signers: u16, + min_signers: u16, + identifiers: IdentifierList, + rng: &mut R, + ) -> Result<(BTreeMap, PublicKeyPackage), Error> { + frost::keys::split(secret, max_signers, min_signers, identifiers, rng) + } + + /// Recompute the secret from t-of-n secret shares using Lagrange interpolation. + /// + /// This can be used if for some reason the original key must be restored; e.g. + /// if threshold signing is not required anymore. + /// + /// This is NOT required to sign with FROST; the whole point of FROST is being + /// able to generate signatures only using the shares, without having to + /// reconstruct the original key. + /// + /// The caller is responsible for providing at least `min_signers` shares; + /// if less than that is provided, a different key will be returned. + pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result { + frost::keys::reconstruct(secret_shares) + } + + /// Secret and public key material generated by a dealer performing + /// [`generate_with_dealer`]. + /// + /// # Security + /// + /// To derive a FROST(secp256k1, SHA-256) keypair, the receiver of the [`SecretShare`] *must* call + /// .into(), which under the hood also performs validation. + pub type SecretShare = frost::keys::SecretShare; + + /// A secret scalar value representing a signer's share of the group secret. + pub type SigningShare = frost::keys::SigningShare; + + /// A public group element that represents a single signer's public verification share. + pub type VerifyingShare = frost::keys::VerifyingShare; + + /// A FROST(secp256k1, SHA-256) keypair, which can be generated either by a trusted dealer or using + /// a DKG. + /// + /// When using a central dealer, [`SecretShare`]s are distributed to + /// participants, who then perform verification, before deriving + /// [`KeyPackage`]s, which they store to later use during signing. + pub type KeyPackage = frost::keys::KeyPackage; + + /// Public data that contains all the signers' public keys as well as the + /// group public key. + /// + /// Used for verification purposes before publishing a signature. + pub type PublicKeyPackage = frost::keys::PublicKeyPackage; + + /// Contains the commitments to the coefficients for our secret polynomial _f_, + /// used to generate participants' key shares. + /// + /// [`VerifiableSecretSharingCommitment`] contains a set of commitments to the coefficients (which + /// themselves are scalars) for a secret polynomial f, where f is used to + /// generate each ith participant's key share f(i). Participants use this set of + /// commitments to perform verifiable secret sharing. + /// + /// Note that participants MUST be assured that they have the *same* + /// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using + /// some agreed-upon public location for publication, where each participant can + /// ensure that they received the correct (and same) value. + pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment; + + pub mod dkg; + pub mod repairable; +} + +/// FROST(secp256k1, SHA-256) Round 1 functionality and types. +pub mod round1 { + use crate::keys::SigningShare; + + use super::*; + + /// Comprised of FROST(secp256k1, SHA-256) hiding and binding nonces. + /// + /// Note that [`SigningNonces`] must be used *only once* for a signing + /// operation; re-using nonces will result in leakage of a signer's long-lived + /// signing key. + pub type SigningNonces = frost::round1::SigningNonces; + + /// Published by each participant in the first round of the signing protocol. + /// + /// This step can be batched if desired by the implementation. Each + /// SigningCommitment can be used for exactly *one* signature. + pub type SigningCommitments = frost::round1::SigningCommitments; + + /// A commitment to a signing nonce share. + pub type NonceCommitment = frost::round1::NonceCommitment; + + /// Performed once by each participant selected for the signing operation. + /// + /// Generates the signing nonces and commitments to be used in the signing + /// operation. + pub fn commit(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments) + where + RNG: CryptoRng + RngCore, + { + frost::round1::commit::(secret, rng) + } +} + +/// Generated by the coordinator of the signing operation and distributed to +/// each signing party. +pub type SigningPackage = frost::SigningPackage; + +/// FROST(secp256k1, SHA-256) Round 2 functionality and types, for signature share generation. +pub mod round2 { + use super::*; + + /// A FROST(secp256k1, SHA-256) participant's signature share, which the Coordinator will aggregate with all other signer's + /// shares into the joint signature. + pub type SignatureShare = frost::round2::SignatureShare; + + /// Performed once by each participant selected for the signing operation. + /// + /// Receives the message to be signed and a set of signing commitments and a set + /// of randomizing commitments to be used in that signing operation, including + /// that for this participant. + /// + /// Assumes the participant has already determined which nonce corresponds with + /// the commitment that was assigned by the coordinator in the SigningPackage. + pub fn sign( + signing_package: &SigningPackage, + signer_nonces: &round1::SigningNonces, + key_package: &keys::KeyPackage, + ) -> Result { + frost::round2::sign(signing_package, signer_nonces, key_package) + } +} + +/// A Schnorr signature on FROST(secp256k1, SHA-256). +pub type Signature = frost_core::Signature; + +/// Verifies each FROST(secp256k1, SHA-256) participant's signature share, and if all are valid, +/// aggregates the shares into a signature to publish. +/// +/// Resulting signature is compatible with verification of a plain Schnorr +/// signature. +/// +/// This operation is performed by a coordinator that can communicate with all +/// the signing participants before publishing the final signature. The +/// coordinator can be one of the participants or a semi-trusted third party +/// (who is trusted to not perform denial of service attacks, but does not learn +/// any secret information). Note that because the coordinator is trusted to +/// report misbehaving parties in order to avoid publishing an invalid +/// signature, if the coordinator themselves is a signer and misbehaves, they +/// can avoid that step. However, at worst, this results in a denial of +/// service attack due to publishing an invalid signature. +pub fn aggregate( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + pubkeys: &keys::PublicKeyPackage, +) -> Result { + frost::aggregate(signing_package, signature_shares, pubkeys) +} + +/// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256). +pub type SigningKey = frost_core::SigningKey; + +/// A valid verifying key for Schnorr signatures on FROST(secp256k1, SHA-256). +pub type VerifyingKey = frost_core::VerifyingKey; diff --git a/frost-secp256k1-tr/src/tests.rs b/frost-secp256k1-tr/src/tests.rs new file mode 100644 index 00000000..15a3e184 --- /dev/null +++ b/frost-secp256k1-tr/src/tests.rs @@ -0,0 +1,5 @@ +mod batch; +mod coefficient_commitment; +mod deserialize; +mod proptests; +mod vss_commitment; diff --git a/frost-secp256k1-tr/src/tests/batch.rs b/frost-secp256k1-tr/src/tests/batch.rs new file mode 100644 index 00000000..b87d22a9 --- /dev/null +++ b/frost-secp256k1-tr/src/tests/batch.rs @@ -0,0 +1,24 @@ +use rand::thread_rng; + +use crate::*; + +#[test] +fn check_batch_verify() { + let rng = thread_rng(); + + frost_core::tests::batch::batch_verify::(rng); +} + +#[test] +fn check_bad_batch_verify() { + let rng = thread_rng(); + + frost_core::tests::batch::bad_batch_verify::(rng); +} + +#[test] +fn empty_batch_verify() { + let rng = thread_rng(); + + frost_core::tests::batch::empty_batch_verify::(rng); +} diff --git a/frost-secp256k1-tr/src/tests/coefficient_commitment.rs b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs new file mode 100644 index 00000000..d1b6c22c --- /dev/null +++ b/frost-secp256k1-tr/src/tests/coefficient_commitment.rs @@ -0,0 +1,46 @@ +use lazy_static::lazy_static; +use rand::thread_rng; +use serde_json::Value; + +use crate::*; + +// Tests for serialization and deserialization of CoefficientCommitment + +lazy_static! { + pub static ref ELEMENTS: Value = + serde_json::from_str(include_str!("../../tests/helpers/elements.json").trim()).unwrap(); +} + +#[test] +fn check_serialization_of_coefficient_commitment() { + let rng = thread_rng(); + frost_core::tests::coefficient_commitment::check_serialization_of_coefficient_commitment::< + Secp256K1Sha256, + _, + >(rng); +} + +#[test] +fn check_create_coefficient_commitment() { + let rng = thread_rng(); + frost_core::tests::coefficient_commitment::check_create_coefficient_commitment::< + Secp256K1Sha256, + _, + >(rng); +} +#[test] +fn check_create_coefficient_commitment_error() { + frost_core::tests::coefficient_commitment::check_create_coefficient_commitment_error::< + Secp256K1Sha256, + >(&ELEMENTS); +} + +#[test] +fn check_get_value_of_coefficient_commitment() { + let rng = thread_rng(); + + frost_core::tests::coefficient_commitment::check_get_value_of_coefficient_commitment::< + Secp256K1Sha256, + _, + >(rng); +} diff --git a/frost-secp256k1-tr/src/tests/deserialize.rs b/frost-secp256k1-tr/src/tests/deserialize.rs new file mode 100644 index 00000000..bce82001 --- /dev/null +++ b/frost-secp256k1-tr/src/tests/deserialize.rs @@ -0,0 +1,37 @@ +use crate::*; + +#[test] +fn check_deserialize_non_canonical() { + let mut encoded_generator = ::Group::serialize( + &::Group::generator(), + ); + + let r = ::Group::deserialize(&encoded_generator); + assert!(r.is_ok()); + + // The first byte should be 0x02 or 0x03. Set other value to + // create a non-canonical encoding. + encoded_generator[0] = 0xFF; + let r = ::Group::deserialize(&encoded_generator); + assert_eq!(r, Err(GroupError::MalformedElement)); + + // Besides the first byte, it is still possible to get non-canonical encodings. + // This is x = p + 2 which is non-canonical and maps to a valid prime-order point. + let encoded_point = + hex::decode("02fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc31") + .unwrap() + .try_into() + .unwrap(); + let r = ::Group::deserialize(&encoded_point); + assert_eq!(r, Err(GroupError::MalformedElement)); +} + +#[test] +fn check_deserialize_identity() { + // The identity is actually encoded as a single byte; but the API does not + // allow us to change that. Try to send something similar. + let encoded_identity = [0u8; 33]; + + let r = ::Group::deserialize(&encoded_identity); + assert_eq!(r, Err(GroupError::MalformedElement)); +} diff --git a/frost-secp256k1-tr/src/tests/proptests.rs b/frost-secp256k1-tr/src/tests/proptests.rs new file mode 100644 index 00000000..dd598569 --- /dev/null +++ b/frost-secp256k1-tr/src/tests/proptests.rs @@ -0,0 +1,33 @@ +use crate::*; +use frost_core::tests::proptests::{tweak_strategy, SignatureCase}; +use proptest::prelude::*; + +use rand_chacha::ChaChaRng; +use rand_core::SeedableRng; + +proptest! { + + #[test] + fn tweak_signature( + tweaks in prop::collection::vec(tweak_strategy(), (0,5)), + rng_seed in prop::array::uniform32(any::()), + ) { + // Use a deterministic RNG so that test failures can be reproduced. + // Seeding with 64 bits of entropy is INSECURE and this code should + // not be copied outside of this test! + let rng = ChaChaRng::from_seed(rng_seed); + + // Create a test case for each signature type. + let msg = b"test message for proptests"; + let mut sig = SignatureCase::::new(rng, msg.to_vec()); + + // Apply tweaks to each case. + for t in &tweaks { + sig.apply_tweak(t); + } + + assert!(sig.check()); + } + + +} diff --git a/frost-secp256k1-tr/src/tests/vss_commitment.rs b/frost-secp256k1-tr/src/tests/vss_commitment.rs new file mode 100644 index 00000000..1a09195a --- /dev/null +++ b/frost-secp256k1-tr/src/tests/vss_commitment.rs @@ -0,0 +1,38 @@ +use lazy_static::lazy_static; +use rand::thread_rng; +use serde_json::Value; + +use crate::*; + +// Tests for serialization and deserialization VerifiableSecretSharingCommitment + +lazy_static! { + pub static ref ELEMENTS: Value = + serde_json::from_str(include_str!("../../tests/helpers/elements.json").trim()).unwrap(); +} + +#[test] +fn check_serialize_vss_commitment() { + let rng = thread_rng(); + frost_core::tests::vss_commitment::check_serialize_vss_commitment::(rng); +} + +#[test] +fn check_deserialize_vss_commitment() { + let rng = thread_rng(); + frost_core::tests::vss_commitment::check_deserialize_vss_commitment::(rng); +} + +#[test] +fn check_deserialize_vss_commitment_error() { + let rng = thread_rng(); + frost_core::tests::vss_commitment::check_deserialize_vss_commitment_error::( + rng, &ELEMENTS, + ); +} + +#[test] +fn check_compute_public_key_package() { + let rng = thread_rng(); + frost_core::tests::vss_commitment::check_compute_public_key_package::(rng); +} diff --git a/frost-secp256k1-tr/tests/common_traits_tests.rs b/frost-secp256k1-tr/tests/common_traits_tests.rs new file mode 100644 index 00000000..81b97a95 --- /dev/null +++ b/frost-secp256k1-tr/tests/common_traits_tests.rs @@ -0,0 +1,74 @@ +#![cfg(feature = "serde")] + +mod helpers; + +use frost_secp256k1_tr::SigningKey; +use helpers::samples; +use rand::thread_rng; + +#[allow(clippy::unnecessary_literal_unwrap)] +fn check_common_traits_for_type(v: T) { + // Make sure can be debug-printed. This also catches if the Debug does not + // have an endless recursion (a popular mistake). + println!("{:?}", v); + // Test Clone and Eq + assert_eq!(v, v.clone()); + // Make sure it can be unwrapped in a Result (which requires Debug). + let e: Result = Ok(v.clone()); + assert_eq!(v, e.unwrap()); +} + +#[test] +fn check_signing_key_common_traits() { + let mut rng = thread_rng(); + let signing_key = SigningKey::new(&mut rng); + check_common_traits_for_type(signing_key); +} + +#[test] +fn check_signing_commitments_common_traits() { + let commitments = samples::signing_commitments(); + check_common_traits_for_type(commitments); +} + +#[test] +fn check_signing_package_common_traits() { + let signing_package = samples::signing_package(); + check_common_traits_for_type(signing_package); +} + +#[test] +fn check_signature_share_common_traits() { + let signature_share = samples::signature_share(); + check_common_traits_for_type(signature_share); +} + +#[test] +fn check_secret_share_common_traits() { + let secret_share = samples::secret_share(); + check_common_traits_for_type(secret_share); +} + +#[test] +fn check_key_package_common_traits() { + let key_package = samples::key_package(); + check_common_traits_for_type(key_package); +} + +#[test] +fn check_public_key_package_common_traits() { + let public_key_package = samples::public_key_package(); + check_common_traits_for_type(public_key_package); +} + +#[test] +fn check_round1_package_common_traits() { + let round1_package = samples::round1_package(); + check_common_traits_for_type(round1_package); +} + +#[test] +fn check_round2_package_common_traits() { + let round2_package = samples::round2_package(); + check_common_traits_for_type(round2_package); +} diff --git a/frost-secp256k1-tr/tests/helpers/elements.json b/frost-secp256k1-tr/tests/helpers/elements.json new file mode 100644 index 00000000..e8cd4082 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/elements.json @@ -0,0 +1,5 @@ +{ + "elements": { + "invalid_element": "123456afdf4a7f88885ab26b20d18edb7d4d9589812a6cf1a5a1a09d3808dae5d8" + } +} diff --git a/frost-secp256k1-tr/tests/helpers/mod.rs b/frost-secp256k1-tr/tests/helpers/mod.rs new file mode 100644 index 00000000..87635756 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/mod.rs @@ -0,0 +1 @@ +pub mod samples; diff --git a/frost-secp256k1-tr/tests/helpers/repair-share.json b/frost-secp256k1-tr/tests/helpers/repair-share.json new file mode 100644 index 00000000..f4db9bc8 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/repair-share.json @@ -0,0 +1,15 @@ +{ + "scalar_generation": { + "random_scalar_1": "1847f6c4a85096e5dbc9e200c9691c5164f8e276d32d4a54ebaf4275474a1403", + "random_scalar_2": "eac5595269d108812eaa865bf62c703a2c128a61fa3bd4dc837b9314bc515204", + "random_scalar_3": "5b3b6084e41c273a39a8d9bbbd87fbcd626c07030142bf78c6c91247bf175700", + "random_scalar_sum": "5e48b09bf63dc6a1441d42187d1d885a38c896f51f633e6e76218944f27c7bc6" + }, + "sigma_generation": { + "sigma_1": "ec3aa83140065181d75b746bfd6bbbbaf212bdfbb3a91670f924d1ca899cbc0c", + "sigma_2": "5dd288d659e0a2dd3ef7523a9cc4f80f4a7f919e9980005c7fbec0961d3fb500", + "sigma_3": "3e62e7461db9ca1ed2f1549a8114bbc87fa9242ce0012ed3f9ac9dcf23f4c30a", + "sigma_4": "684c44e7aba416a1982a8db8ec2a3095f5cc6a3f958a4716b69ae76524dd7200", + "sigma_sum": "f0bc5d356344d51f816ea8fa076fa029f7590120136bec7c6958b9081f7864d5" + } +} diff --git a/frost-secp256k1-tr/tests/helpers/samples.json b/frost-secp256k1-tr/tests/helpers/samples.json new file mode 100644 index 00000000..210c6f27 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/samples.json @@ -0,0 +1,6 @@ +{ + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "element1": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "element2": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + "scalar1": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" +} \ No newline at end of file diff --git a/frost-secp256k1-tr/tests/helpers/samples.rs b/frost-secp256k1-tr/tests/helpers/samples.rs new file mode 100644 index 00000000..9ddcc5e8 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/samples.rs @@ -0,0 +1,123 @@ +//! Generate sample, fixed instances of structs for testing. + +use std::collections::BTreeMap; + +use frost_core::{Ciphersuite, Element, Group, Scalar}; +use frost_secp256k1_tr::{ + keys::{ + dkg::{round1, round2}, + KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment, + VerifyingShare, + }, + round1::{NonceCommitment, SigningCommitments}, + round2::SignatureShare, + Field, Signature, SigningPackage, VerifyingKey, +}; + +type C = frost_secp256k1_tr::Secp256K1Sha256; + +fn element1() -> Element { + ::Group::generator() +} + +fn element2() -> Element { + element1() + element1() +} + +fn scalar1() -> Scalar { + let one = <::Group as Group>::Field::one(); + let three = one + one + one; + // To return a fixed non-small number, get the inverse of 3 + <::Group as Group>::Field::invert(&three) + .expect("nonzero elements have inverses") +} + +/// Generate a sample SigningCommitments. +pub fn signing_commitments() -> SigningCommitments { + let serialized_element1 = ::Group::serialize(&element1()); + let serialized_element2 = ::Group::serialize(&element2()); + let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap(); + let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap(); + + SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment) +} + +/// Generate a sample SigningPackage. +pub fn signing_package() -> SigningPackage { + let identifier = 42u16.try_into().unwrap(); + let commitments = BTreeMap::from([(identifier, signing_commitments())]); + let message = "hello world".as_bytes(); + + SigningPackage::new(commitments, message) +} + +/// Generate a sample SignatureShare. +pub fn signature_share() -> SignatureShare { + let serialized_scalar = <::Group as Group>::Field::serialize(&scalar1()); + + SignatureShare::deserialize(serialized_scalar).unwrap() +} + +/// Generate a sample SecretShare. +pub fn secret_share() -> SecretShare { + let identifier = 42u16.try_into().unwrap(); + let serialized_scalar = <::Group as Group>::Field::serialize(&scalar1()); + let serialized_element = ::Group::serialize(&element1()); + let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); + let vss_commitment = + VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); + + SecretShare::new(identifier, signing_share, vss_commitment) +} + +/// Generate a sample KeyPackage. +pub fn key_package() -> KeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_scalar = <::Group as Group>::Field::serialize(&scalar1()); + let serialized_element = ::Group::serialize(&element1()); + let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); + let verifying_share = VerifyingShare::deserialize(serialized_element).unwrap(); + let serialized_element = ::Group::serialize(&element1()); + let verifying_key = VerifyingKey::deserialize(serialized_element).unwrap(); + + KeyPackage::new(identifier, signing_share, verifying_share, verifying_key, 2) +} + +/// Generate a sample PublicKeyPackage. +pub fn public_key_package() -> PublicKeyPackage { + let identifier = 42u16.try_into().unwrap(); + let serialized_element = ::Group::serialize(&element1()); + let verifying_share = VerifyingShare::deserialize(serialized_element).unwrap(); + let serialized_element = ::Group::serialize(&element1()); + let verifying_key = VerifyingKey::deserialize(serialized_element).unwrap(); + let verifying_shares = BTreeMap::from([(identifier, verifying_share)]); + + PublicKeyPackage::new(verifying_shares, verifying_key) +} + +/// Generate a sample round1::Package. +pub fn round1_package() -> round1::Package { + let serialized_scalar = <::Group as Group>::Field::serialize(&scalar1()); + let serialized_element = ::Group::serialize(&element1()); + let serialized_signature = serialized_element + .as_ref() + .iter() + .chain(serialized_scalar.as_ref().iter()) + .cloned() + .collect::>() + .try_into() + .unwrap(); + let vss_commitment = + VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); + let signature = Signature::deserialize(serialized_signature).unwrap(); + + round1::Package::new(vss_commitment, signature) +} + +/// Generate a sample round2::Package. +pub fn round2_package() -> round2::Package { + let serialized_scalar = <::Group as Group>::Field::serialize(&scalar1()); + let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); + + round2::Package::new(signing_share) +} diff --git a/frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json b/frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json new file mode 100644 index 00000000..77e04405 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/vectors-big-identifier.json @@ -0,0 +1,1109 @@ +{ + "config": { + "MAX_PARTICIPANTS": "257", + "NUM_PARTICIPANTS": "3", + "MIN_PARTICIPANTS": "2", + "name": "FROST(secp256k1, SHA-256)", + "group": "secp256k1", + "hash": "SHA-256" + }, + "inputs": { + "participant_list": [ + 129, + 256, + 257 + ], + "group_secret_key": "0d004150d27c3bf2a42f312683d35fac7394b1e9e318249c1bfe7f0795a83114", + "verifying_key_key": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4f", + "message": "74657374", + "share_polynomial_coefficients": [ + "fbf85eadae3058ea14f19148bb72b45e4399c0b16028acaf0395c9b03c823579" + ], + "participant_shares": [ + { + "identifier": 1, + "participant_share": "08f89ffe80ac94dcb920c26f3f46140bfc7f95b493f8310f5fc1ea2b01f4254c" + }, + { + "identifier": 2, + "participant_share": "04f0feac2edcedc6ce1253b7fab8c86b856a797f44d83d82a385554e6e401984" + }, + { + "identifier": 3, + "participant_share": "00e95d59dd0d46b0e303e500b62b7ccb0e555d49f5b849f5e748c071da8c0dbc" + }, + { + "identifier": 4, + "participant_share": "fce1bc078b3d9f9af7f57649719e312951ef1dfb55e0f6a4eade8a22170e4335" + }, + { + "identifier": 5, + "participant_share": "f8da1ab5396df8850ce707922d10e588dada01c606c103182ea1f545835a376d" + }, + { + "identifier": 6, + "participant_share": "f4d27962e79e516f21d898dae88399e863c4e590b7a10f8b72656068efa62ba5" + }, + { + "identifier": 7, + "participant_share": "f0cad81095ceaa5936ca2a23a3f64e47ecafc95b68811bfeb628cb8c5bf21fdd" + }, + { + "identifier": 8, + "participant_share": "ecc336be43ff03434bbbbb6c5f6902a7759aad2619612871f9ec36afc83e1415" + }, + { + "identifier": 9, + "participant_share": "e8bb956bf22f5c2d60ad4cb51adbb706fe8590f0ca4134e53dafa1d3348a084d" + }, + { + "identifier": 10, + "participant_share": "e4b3f419a05fb517759eddfdd64e6b66877074bb7b21415881730cf6a0d5fc85" + }, + { + "identifier": 11, + "participant_share": "e0ac52c74e900e018a906f4691c11fc6105b58862c014dcbc536781a0d21f0bd" + }, + { + "identifier": 12, + "participant_share": "dca4b174fcc066eb9f82008f4d33d42599463c50dce15a3f08f9e33d796de4f5" + }, + { + "identifier": 13, + "participant_share": "d89d1022aaf0bfd5b47391d808a688852231201b8dc166b24cbd4e60e5b9d92d" + }, + { + "identifier": 14, + "participant_share": "d4956ed0592118bfc9652320c4193ce4ab1c03e63ea173259080b9845205cd65" + }, + { + "identifier": 15, + "participant_share": "d08dcd7e075171a9de56b4697f8bf1443406e7b0ef817f98d44424a7be51c19d" + }, + { + "identifier": 16, + "participant_share": "cc862c2bb581ca93f34845b23afea5a3bcf1cb7ba0618c0c18078fcb2a9db5d5" + }, + { + "identifier": 17, + "participant_share": "c87e8ad963b2237e0839d6faf6715a0345dcaf465141987f5bcafaee96e9aa0d" + }, + { + "identifier": 18, + "participant_share": "c476e98711e27c681d2b6843b1e40e62cec793110221a4f29f8e661203359e45" + }, + { + "identifier": 19, + "participant_share": "c06f4834c012d552321cf98c6d56c2c257b276dbb301b165e351d1356f81927d" + }, + { + "identifier": 20, + "participant_share": "bc67a6e26e432e3c470e8ad528c97721e09d5aa663e1bdd927153c58dbcd86b5" + }, + { + "identifier": 21, + "participant_share": "b86005901c7387265c001c1de43c2b8169883e7114c1ca4c6ad8a77c48197aed" + }, + { + "identifier": 22, + "participant_share": "b458643dcaa3e01070f1ad669faedfe0f273223bc5a1d6bfae9c129fb4656f25" + }, + { + "identifier": 23, + "participant_share": "b050c2eb78d438fa85e33eaf5b2194407b5e06067681e332f25f7dc320b1635d" + }, + { + "identifier": 24, + "participant_share": "ac492199270491e49ad4cff8169448a00448e9d12761efa63622e8e68cfd5795" + }, + { + "identifier": 25, + "participant_share": "a8418046d534eaceafc66140d206fcff8d33cd9bd841fc1979e65409f9494bcd" + }, + { + "identifier": 26, + "participant_share": "a439def4836543b8c4b7f2898d79b15f161eb1668922088cbda9bf2d65954005" + }, + { + "identifier": 27, + "participant_share": "a0323da231959ca2d9a983d248ec65be9f0995313a021500016d2a50d1e1343d" + }, + { + "identifier": 28, + "participant_share": "9c2a9c4fdfc5f58cee9b151b045f1a1e27f478fbeae22173453095743e2d2875" + }, + { + "identifier": 29, + "participant_share": "9822fafd8df64e77038ca663bfd1ce7db0df5cc69bc22de688f40097aa791cad" + }, + { + "identifier": 30, + "participant_share": "941b59ab3c26a761187e37ac7b4482dd39ca40914ca23a59ccb76bbb16c510e5" + }, + { + "identifier": 31, + "participant_share": "9013b858ea57004b2d6fc8f536b7373cc2b5245bfd8246cd107ad6de8311051d" + }, + { + "identifier": 32, + "participant_share": "8c0c17069887593542615a3df229eb9c4ba00826ae625340543e4201ef5cf955" + }, + { + "identifier": 33, + "participant_share": "880475b446b7b21f5752eb86ad9c9ffbd48aebf15f425fb39801ad255ba8ed8d" + }, + { + "identifier": 34, + "participant_share": "83fcd461f4e80b096c447ccf690f545b5d75cfbc10226c26dbc51848c7f4e1c5" + }, + { + "identifier": 35, + "participant_share": "7ff5330fa31863f381360e18248208bae660b386c102789a1f88836c3440d5fd" + }, + { + "identifier": 36, + "participant_share": "7bed91bd5148bcdd96279f60dff4bd1a6f4b975171e2850d634bee8fa08cca35" + }, + { + "identifier": 37, + "participant_share": "77e5f06aff7915c7ab1930a99b677179f8367b1c22c29180a70f59b30cd8be6d" + }, + { + "identifier": 38, + "participant_share": "73de4f18ada96eb1c00ac1f256da25d981215ee6d3a29df3ead2c4d67924b2a5" + }, + { + "identifier": 39, + "participant_share": "6fd6adc65bd9c79bd4fc533b124cda390a0c42b18482aa672e962ff9e570a6dd" + }, + { + "identifier": 40, + "participant_share": "6bcf0c740a0a2085e9ede483cdbf8e9892f7267c3562b6da72599b1d51bc9b15" + }, + { + "identifier": 41, + "participant_share": "67c76b21b83a796ffedf75cc893242f81be20a46e642c34db61d0640be088f4d" + }, + { + "identifier": 42, + "participant_share": "63bfc9cf666ad25a13d1071544a4f757a4ccee119722cfc0f9e071642a548385" + }, + { + "identifier": 43, + "participant_share": "5fb8287d149b2b4428c2985e0017abb72db7d1dc4802dc343da3dc8796a077bd" + }, + { + "identifier": 44, + "participant_share": "5bb0872ac2cb842e3db429a6bb8a6016b6a2b5a6f8e2e8a7816747ab02ec6bf5" + }, + { + "identifier": 45, + "participant_share": "57a8e5d870fbdd1852a5baef76fd14763f8d9971a9c2f51ac52ab2ce6f38602d" + }, + { + "identifier": 46, + "participant_share": "53a144861f2c360267974c38326fc8d5c8787d3c5aa3018e08ee1df1db845465" + }, + { + "identifier": 47, + "participant_share": "4f99a333cd5c8eec7c88dd80ede27d35516361070b830e014cb1891547d0489d" + }, + { + "identifier": 48, + "participant_share": "4b9201e17b8ce7d6917a6ec9a9553194da4e44d1bc631a749074f438b41c3cd5" + }, + { + "identifier": 49, + "participant_share": "478a608f29bd40c0a66c001264c7e5f46339289c6d4326e7d4385f5c2068310d" + }, + { + "identifier": 50, + "participant_share": "4382bf3cd7ed99aabb5d915b203a9a53ec240c671e23335b17fbca7f8cb42545" + }, + { + "identifier": 51, + "participant_share": "3f7b1dea861df294d04f22a3dbad4eb3750ef031cf033fce5bbf35a2f900197d" + }, + { + "identifier": 52, + "participant_share": "3b737c98344e4b7ee540b3ec97200312fdf9d3fc7fe34c419f82a0c6654c0db5" + }, + { + "identifier": 53, + "participant_share": "376bdb45e27ea468fa3245355292b77286e4b7c730c358b4e3460be9d19801ed" + }, + { + "identifier": 54, + "participant_share": "336439f390aefd530f23d67e0e056bd20fcf9b91e1a365282709770d3de3f625" + }, + { + "identifier": 55, + "participant_share": "2f5c98a13edf563d241567c6c978203198ba7f5c9283719b6acce230aa2fea5d" + }, + { + "identifier": 56, + "participant_share": "2b54f74eed0faf273906f90f84ead49121a5632743637e0eae904d54167bde95" + }, + { + "identifier": 57, + "participant_share": "274d55fc9b4008114df88a58405d88f0aa9046f1f4438a81f253b87782c7d2cd" + }, + { + "identifier": 58, + "participant_share": "2345b4aa497060fb62ea1ba0fbd03d50337b2abca52396f53617239aef13c705" + }, + { + "identifier": 59, + "participant_share": "1f3e1357f7a0b9e577dbace9b742f1afbc660e875603a36879da8ebe5b5fbb3d" + }, + { + "identifier": 60, + "participant_share": "1b367205a5d112cf8ccd3e3272b5a60f4550f25206e3afdbbd9df9e1c7abaf75" + }, + { + "identifier": 61, + "participant_share": "172ed0b354016bb9a1becf7b2e285a6ece3bd61cb7c3bc4f0161650533f7a3ad" + }, + { + "identifier": 62, + "participant_share": "13272f610231c4a3b6b060c3e99b0ece5726b9e768a3c8c24524d028a04397e5" + }, + { + "identifier": 63, + "participant_share": "0f1f8e0eb0621d8dcba1f20ca50dc32de0119db21983d53588e83b4c0c8f8c1d" + }, + { + "identifier": 64, + "participant_share": "0b17ecbc5e927677e09383556080778d68fc817cca63e1a8ccaba66f78db8055" + }, + { + "identifier": 65, + "participant_share": "07104b6a0cc2cf61f585149e1bf32becf1e765477b43ee1c106f1192e527748d" + }, + { + "identifier": 66, + "participant_share": "0308aa17baf3284c0a76a5e6d765e04c7ad249122c23fa8f54327cb6517368c5" + }, + { + "identifier": 67, + "participant_share": "ff0108c5692381361f68372f92d894aabe6c09c38c4ca73e57c846668df59e3e" + }, + { + "identifier": 68, + "participant_share": "faf967731753da203459c8784e4b490a4756ed8e3d2cb3b19b8bb189fa419276" + }, + { + "identifier": 69, + "participant_share": "f6f1c620c584330a494b59c109bdfd69d041d158ee0cc024df4f1cad668d86ae" + }, + { + "identifier": 70, + "participant_share": "f2ea24ce73b48bf45e3ceb09c530b1c9592cb5239eeccc98231287d0d2d97ae6" + }, + { + "identifier": 71, + "participant_share": "eee2837c21e4e4de732e7c5280a36628e21798ee4fccd90b66d5f2f43f256f1e" + }, + { + "identifier": 72, + "participant_share": "eadae229d0153dc888200d9b3c161a886b027cb900ace57eaa995e17ab716356" + }, + { + "identifier": 73, + "participant_share": "e6d340d77e4596b29d119ee3f788cee7f3ed6083b18cf1f1ee5cc93b17bd578e" + }, + { + "identifier": 74, + "participant_share": "e2cb9f852c75ef9cb203302cb2fb83477cd8444e626cfe653220345e84094bc6" + }, + { + "identifier": 75, + "participant_share": "dec3fe32daa64886c6f4c1756e6e37a705c32819134d0ad875e39f81f0553ffe" + }, + { + "identifier": 76, + "participant_share": "dabc5ce088d6a170dbe652be29e0ec068eae0be3c42d174bb9a70aa55ca13436" + }, + { + "identifier": 77, + "participant_share": "d6b4bb8e3706fa5af0d7e406e553a0661798efae750d23befd6a75c8c8ed286e" + }, + { + "identifier": 78, + "participant_share": "d2ad1a3be537534505c9754fa0c654c5a083d37925ed3032412de0ec35391ca6" + }, + { + "identifier": 79, + "participant_share": "cea578e99367ac2f1abb06985c390925296eb743d6cd3ca584f14c0fa18510de" + }, + { + "identifier": 80, + "participant_share": "ca9dd797419805192fac97e117abbd84b2599b0e87ad4918c8b4b7330dd10516" + }, + { + "identifier": 81, + "participant_share": "c6963644efc85e03449e2929d31e71e43b447ed9388d558c0c7822567a1cf94e" + }, + { + "identifier": 82, + "participant_share": "c28e94f29df8b6ed598fba728e912643c42f62a3e96d61ff503b8d79e668ed86" + }, + { + "identifier": 83, + "participant_share": "be86f3a04c290fd76e814bbb4a03daa34d1a466e9a4d6e7293fef89d52b4e1be" + }, + { + "identifier": 84, + "participant_share": "ba7f524dfa5968c18372dd0405768f02d6052a394b2d7ae5d7c263c0bf00d5f6" + }, + { + "identifier": 85, + "participant_share": "b677b0fba889c1ab98646e4cc0e943625ef00e03fc0d87591b85cee42b4cca2e" + }, + { + "identifier": 86, + "participant_share": "b2700fa956ba1a95ad55ff957c5bf7c1e7daf1ceaced93cc5f493a079798be66" + }, + { + "identifier": 87, + "participant_share": "ae686e5704ea737fc24790de37ceac2170c5d5995dcda03fa30ca52b03e4b29e" + }, + { + "identifier": 88, + "participant_share": "aa60cd04b31acc69d7392226f3416080f9b0b9640eadacb2e6d0104e7030a6d6" + }, + { + "identifier": 89, + "participant_share": "a6592bb2614b2553ec2ab36faeb414e0829b9d2ebf8db9262a937b71dc7c9b0e" + }, + { + "identifier": 90, + "participant_share": "a2518a600f7b7e3e011c44b86a26c9400b8680f9706dc5996e56e69548c88f46" + }, + { + "identifier": 91, + "participant_share": "9e49e90dbdabd728160dd60125997d9f947164c4214dd20cb21a51b8b514837e" + }, + { + "identifier": 92, + "participant_share": "9a4247bb6bdc30122aff6749e10c31ff1d5c488ed22dde7ff5ddbcdc216077b6" + }, + { + "identifier": 93, + "participant_share": "963aa6691a0c88fc3ff0f8929c7ee65ea6472c59830deaf339a127ff8dac6bee" + }, + { + "identifier": 94, + "participant_share": "92330516c83ce1e654e289db57f19abe2f32102433edf7667d649322f9f86026" + }, + { + "identifier": 95, + "participant_share": "8e2b63c4766d3ad069d41b2413644f1db81cf3eee4ce03d9c127fe466644545e" + }, + { + "identifier": 96, + "participant_share": "8a23c272249d93ba7ec5ac6cced7037d4107d7b995ae104d04eb6969d2904896" + }, + { + "identifier": 97, + "participant_share": "861c211fd2cdeca493b73db58a49b7dcc9f2bb84468e1cc048aed48d3edc3cce" + }, + { + "identifier": 98, + "participant_share": "82147fcd80fe458ea8a8cefe45bc6c3c52dd9f4ef76e29338c723fb0ab283106" + }, + { + "identifier": 99, + "participant_share": "7e0cde7b2f2e9e78bd9a6047012f209bdbc88319a84e35a6d035aad41774253e" + }, + { + "identifier": 100, + "participant_share": "7a053d28dd5ef762d28bf18fbca1d4fb64b366e4592e421a13f915f783c01976" + }, + { + "identifier": 101, + "participant_share": "75fd9bd68b8f504ce77d82d87814895aed9e4aaf0a0e4e8d57bc811af00c0dae" + }, + { + "identifier": 102, + "participant_share": "71f5fa8439bfa936fc6f142133873dba76892e79baee5b009b7fec3e5c5801e6" + }, + { + "identifier": 103, + "participant_share": "6dee5931e7f002211160a569eef9f219ff7412446bce6773df435761c8a3f61e" + }, + { + "identifier": 104, + "participant_share": "69e6b7df96205b0b265236b2aa6ca679885ef60f1cae73e72306c28534efea56" + }, + { + "identifier": 105, + "participant_share": "65df168d4450b3f53b43c7fb65df5ad91149d9d9cd8e805a66ca2da8a13bde8e" + }, + { + "identifier": 106, + "participant_share": "61d7753af2810cdf5035594421520f389a34bda47e6e8ccdaa8d98cc0d87d2c6" + }, + { + "identifier": 107, + "participant_share": "5dcfd3e8a0b165c96526ea8cdcc4c398231fa16f2f4e9940ee5103ef79d3c6fe" + }, + { + "identifier": 108, + "participant_share": "59c832964ee1beb37a187bd5983777f7ac0a8539e02ea5b432146f12e61fbb36" + }, + { + "identifier": 109, + "participant_share": "55c09143fd12179d8f0a0d1e53aa2c5734f56904910eb22775d7da36526baf6e" + }, + { + "identifier": 110, + "participant_share": "51b8eff1ab427087a3fb9e670f1ce0b6bde04ccf41eebe9ab99b4559beb7a3a6" + }, + { + "identifier": 111, + "participant_share": "4db14e9f5972c971b8ed2fafca8f951646cb3099f2cecb0dfd5eb07d2b0397de" + }, + { + "identifier": 112, + "participant_share": "49a9ad4d07a3225bcddec0f886024975cfb61464a3aed78141221ba0974f8c16" + }, + { + "identifier": 113, + "participant_share": "45a20bfab5d37b45e2d052414174fdd558a0f82f548ee3f484e586c4039b804e" + }, + { + "identifier": 114, + "participant_share": "419a6aa86403d42ff7c1e389fce7b234e18bdbfa056ef067c8a8f1e76fe77486" + }, + { + "identifier": 115, + "participant_share": "3d92c95612342d1a0cb374d2b85a66946a76bfc4b64efcdb0c6c5d0adc3368be" + }, + { + "identifier": 116, + "participant_share": "398b2803c064860421a5061b73cd1af3f361a38f672f094e502fc82e487f5cf6" + }, + { + "identifier": 117, + "participant_share": "358386b16e94deee369697642f3fcf537c4c875a180f15c193f33351b4cb512e" + }, + { + "identifier": 118, + "participant_share": "317be55f1cc537d84b8828aceab283b305376b24c8ef2234d7b69e7521174566" + }, + { + "identifier": 119, + "participant_share": "2d74440ccaf590c26079b9f5a62538128e224eef79cf2ea81b7a09988d63399e" + }, + { + "identifier": 120, + "participant_share": "296ca2ba7925e9ac756b4b3e6197ec72170d32ba2aaf3b1b5f3d74bbf9af2dd6" + }, + { + "identifier": 121, + "participant_share": "25650168275642968a5cdc871d0aa0d19ff81684db8f478ea300dfdf65fb220e" + }, + { + "identifier": 122, + "participant_share": "215d6015d5869b809f4e6dcfd87d553128e2fa4f8c6f5401e6c44b02d2471646" + }, + { + "identifier": 123, + "participant_share": "1d55bec383b6f46ab43fff1893f00990b1cdde1a3d4f60752a87b6263e930a7e" + }, + { + "identifier": 124, + "participant_share": "194e1d7131e74d54c93190614f62bdf03ab8c1e4ee2f6ce86e4b2149aadefeb6" + }, + { + "identifier": 125, + "participant_share": "15467c1ee017a63ede2321aa0ad5724fc3a3a5af9f0f795bb20e8c6d172af2ee" + }, + { + "identifier": 126, + "participant_share": "113edacc8e47ff28f314b2f2c64826af4c8e897a4fef85cef5d1f7908376e726" + }, + { + "identifier": 127, + "participant_share": "0d37397a3c7858130806443b81badb0ed5796d4500cf9242399562b3efc2db5e" + }, + { + "identifier": 128, + "participant_share": "092f9827eaa8b0fd1cf7d5843d2d8f6e5e64510fb1af9eb57d58cdd75c0ecf96" + }, + { + "identifier": 129, + "participant_share": "0527f6d598d909e731e966ccf8a043cde74f34da628fab28c11c38fac85ac3ce" + }, + { + "identifier": 130, + "participant_share": "01205583470962d146daf815b412f82d703a18a5136fb79c04dfa41e34a6b806" + }, + { + "identifier": 131, + "participant_share": "fd18b430f539bbbb5bcc895e6f85ac8bb3d3d9567398644b08756dce7128ed7f" + }, + { + "identifier": 132, + "participant_share": "f91112dea36a14a570be1aa72af860eb3cbebd21247870be4c38d8f1dd74e1b7" + }, + { + "identifier": 133, + "participant_share": "f509718c519a6d8f85afabefe66b154ac5a9a0ebd5587d318ffc441549c0d5ef" + }, + { + "identifier": 134, + "participant_share": "f101d039ffcac6799aa13d38a1ddc9aa4e9484b6863889a4d3bfaf38b60cca27" + }, + { + "identifier": 135, + "participant_share": "ecfa2ee7adfb1f63af92ce815d507e09d77f68813718961817831a5c2258be5f" + }, + { + "identifier": 136, + "participant_share": "e8f28d955c2b784dc4845fca18c33269606a4c4be7f8a28b5b46857f8ea4b297" + }, + { + "identifier": 137, + "participant_share": "e4eaec430a5bd137d975f112d435e6c8e955301698d8aefe9f09f0a2faf0a6cf" + }, + { + "identifier": 138, + "participant_share": "e0e34af0b88c2a21ee67825b8fa89b28724013e149b8bb71e2cd5bc6673c9b07" + }, + { + "identifier": 139, + "participant_share": "dcdba99e66bc830c035913a44b1b4f87fb2af7abfa98c7e52690c6e9d3888f3f" + }, + { + "identifier": 140, + "participant_share": "d8d4084c14ecdbf6184aa4ed068e03e78415db76ab78d4586a54320d3fd48377" + }, + { + "identifier": 141, + "participant_share": "d4cc66f9c31d34e02d3c3635c200b8470d00bf415c58e0cbae179d30ac2077af" + }, + { + "identifier": 142, + "participant_share": "d0c4c5a7714d8dca422dc77e7d736ca695eba30c0d38ed3ef1db0854186c6be7" + }, + { + "identifier": 143, + "participant_share": "ccbd24551f7de6b4571f58c738e621061ed686d6be18f9b2359e737784b8601f" + }, + { + "identifier": 144, + "participant_share": "c8b58302cdae3f9e6c10ea0ff458d565a7c16aa16ef906257961de9af1045457" + }, + { + "identifier": 145, + "participant_share": "c4ade1b07bde988881027b58afcb89c530ac4e6c1fd91298bd2549be5d50488f" + }, + { + "identifier": 146, + "participant_share": "c0a6405e2a0ef17295f40ca16b3e3e24b9973236d0b91f0c00e8b4e1c99c3cc7" + }, + { + "identifier": 147, + "participant_share": "bc9e9f0bd83f4a5caae59dea26b0f2844282160181992b7f44ac200535e830ff" + }, + { + "identifier": 148, + "participant_share": "b896fdb9866fa346bfd72f32e223a6e3cb6cf9cc327937f2886f8b28a2342537" + }, + { + "identifier": 149, + "participant_share": "b48f5c67349ffc30d4c8c07b9d965b435457dd96e3594465cc32f64c0e80196f" + }, + { + "identifier": 150, + "participant_share": "b087bb14e2d0551ae9ba51c459090fa2dd42c161943950d90ff6616f7acc0da7" + }, + { + "identifier": 151, + "participant_share": "ac8019c29100ae04feabe30d147bc402662da52c45195d4c53b9cc92e71801df" + }, + { + "identifier": 152, + "participant_share": "a87878703f3106ef139d7455cfee7861ef1888f6f5f969bf977d37b65363f617" + }, + { + "identifier": 153, + "participant_share": "a470d71ded615fd9288f059e8b612cc178036cc1a6d97632db40a2d9bfafea4f" + }, + { + "identifier": 154, + "participant_share": "a06935cb9b91b8c33d8096e746d3e12100ee508c57b982a61f040dfd2bfbde87" + }, + { + "identifier": 155, + "participant_share": "9c61947949c211ad527228300246958089d9345708998f1962c779209847d2bf" + }, + { + "identifier": 156, + "participant_share": "9859f326f7f26a976763b978bdb949e012c41821b9799b8ca68ae4440493c6f7" + }, + { + "identifier": 157, + "participant_share": "945251d4a622c3817c554ac1792bfe3f9baefbec6a59a7ffea4e4f6770dfbb2f" + }, + { + "identifier": 158, + "participant_share": "904ab08254531c6b9146dc0a349eb29f2499dfb71b39b4732e11ba8add2baf67" + }, + { + "identifier": 159, + "participant_share": "8c430f3002837555a6386d52f01166fead84c381cc19c0e671d525ae4977a39f" + }, + { + "identifier": 160, + "participant_share": "883b6dddb0b3ce3fbb29fe9bab841b5e366fa74c7cf9cd59b59890d1b5c397d7" + }, + { + "identifier": 161, + "participant_share": "8433cc8b5ee42729d01b8fe466f6cfbdbf5a8b172dd9d9ccf95bfbf5220f8c0f" + }, + { + "identifier": 162, + "participant_share": "802c2b390d148013e50d212d2269841d48456ee1deb9e6403d1f67188e5b8047" + }, + { + "identifier": 163, + "participant_share": "7c2489e6bb44d8fdf9feb275dddc387cd13052ac8f99f2b380e2d23bfaa7747f" + }, + { + "identifier": 164, + "participant_share": "781ce894697531e80ef043be994eecdc5a1b36774079ff26c4a63d5f66f368b7" + }, + { + "identifier": 165, + "participant_share": "7415474217a58ad223e1d50754c1a13be3061a41f15a0b9a0869a882d33f5cef" + }, + { + "identifier": 166, + "participant_share": "700da5efc5d5e3bc38d366501034559b6bf0fe0ca23a180d4c2d13a63f8b5127" + }, + { + "identifier": 167, + "participant_share": "6c06049d74063ca64dc4f798cba709faf4dbe1d7531a24808ff07ec9abd7455f" + }, + { + "identifier": 168, + "participant_share": "67fe634b2236959062b688e18719be5a7dc6c5a203fa30f3d3b3e9ed18233997" + }, + { + "identifier": 169, + "participant_share": "63f6c1f8d066ee7a77a81a2a428c72ba06b1a96cb4da3d6717775510846f2dcf" + }, + { + "identifier": 170, + "participant_share": "5fef20a67e9747648c99ab72fdff27198f9c8d3765ba49da5b3ac033f0bb2207" + }, + { + "identifier": 171, + "participant_share": "5be77f542cc7a04ea18b3cbbb971db7918877102169a564d9efe2b575d07163f" + }, + { + "identifier": 172, + "participant_share": "57dfde01daf7f938b67cce0474e48fd8a17254ccc77a62c0e2c1967ac9530a77" + }, + { + "identifier": 173, + "participant_share": "53d83caf89285222cb6e5f4d305744382a5d3897785a6f342685019e359efeaf" + }, + { + "identifier": 174, + "participant_share": "4fd09b5d3758ab0ce05ff095ebc9f897b3481c62293a7ba76a486cc1a1eaf2e7" + }, + { + "identifier": 175, + "participant_share": "4bc8fa0ae58903f6f55181dea73cacf73c33002cda1a881aae0bd7e50e36e71f" + }, + { + "identifier": 176, + "participant_share": "47c158b893b95ce10a43132762af6156c51de3f78afa948df1cf43087a82db57" + }, + { + "identifier": 177, + "participant_share": "43b9b76641e9b5cb1f34a4701e2215b64e08c7c23bdaa1013592ae2be6cecf8f" + }, + { + "identifier": 178, + "participant_share": "3fb21613f01a0eb5342635b8d994ca15d6f3ab8cecbaad747956194f531ac3c7" + }, + { + "identifier": 179, + "participant_share": "3baa74c19e4a679f4917c70195077e755fde8f579d9ab9e7bd198472bf66b7ff" + }, + { + "identifier": 180, + "participant_share": "37a2d36f4c7ac0895e09584a507a32d4e8c973224e7ac65b00dcef962bb2ac37" + }, + { + "identifier": 181, + "participant_share": "339b321cfaab197372fae9930bece73471b456ecff5ad2ce44a05ab997fea06f" + }, + { + "identifier": 182, + "participant_share": "2f9390caa8db725d87ec7adbc75f9b93fa9f3ab7b03adf418863c5dd044a94a7" + }, + { + "identifier": 183, + "participant_share": "2b8bef78570bcb479cde0c2482d24ff3838a1e82611aebb4cc273100709688df" + }, + { + "identifier": 184, + "participant_share": "27844e26053c2431b1cf9d6d3e4504530c75024d11faf8280fea9c23dce27d17" + }, + { + "identifier": 185, + "participant_share": "237cacd3b36c7d1bc6c12eb5f9b7b8b2955fe617c2db049b53ae0747492e714f" + }, + { + "identifier": 186, + "participant_share": "1f750b81619cd605dbb2bffeb52a6d121e4ac9e273bb110e9771726ab57a6587" + }, + { + "identifier": 187, + "participant_share": "1b6d6a2f0fcd2eeff0a45147709d2171a735adad249b1d81db34dd8e21c659bf" + }, + { + "identifier": 188, + "participant_share": "1765c8dcbdfd87da0595e2902c0fd5d130209177d57b29f51ef848b18e124df7" + }, + { + "identifier": 189, + "participant_share": "135e278a6c2de0c41a8773d8e7828a30b90b7542865b366862bbb3d4fa5e422f" + }, + { + "identifier": 190, + "participant_share": "0f5686381a5e39ae2f790521a2f53e9041f6590d373b42dba67f1ef866aa3667" + }, + { + "identifier": 191, + "participant_share": "0b4ee4e5c88e9298446a966a5e67f2efcae13cd7e81b4f4eea428a1bd2f62a9f" + }, + { + "identifier": 192, + "participant_share": "0747439376beeb82595c27b319daa74f53cc20a298fb5bc22e05f53f3f421ed7" + }, + { + "identifier": 193, + "participant_share": "033fa24124ef446c6e4db8fbd54d5baedcb7046d49db683571c96062ab8e130f" + }, + { + "identifier": 194, + "participant_share": "ff3800eed31f9d56833f4a4490c0100d2050c51eaa0414e4755f2a12e8104888" + }, + { + "identifier": 195, + "participant_share": "fb305f9c814ff6409830db8d4c32c46ca93ba8e95ae42157b9229536545c3cc0" + }, + { + "identifier": 196, + "participant_share": "f728be4a2f804f2aad226cd607a578cc32268cb40bc42dcafce60059c0a830f8" + }, + { + "identifier": 197, + "participant_share": "f3211cf7ddb0a814c213fe1ec3182d2bbb11707ebca43a3e40a96b7d2cf42530" + }, + { + "identifier": 198, + "participant_share": "ef197ba58be100fed7058f677e8ae18b43fc54496d8446b1846cd6a099401968" + }, + { + "identifier": 199, + "participant_share": "eb11da533a1159e8ebf720b039fd95eacce738141e645324c83041c4058c0da0" + }, + { + "identifier": 200, + "participant_share": "e70a3900e841b2d300e8b1f8f5704a4a55d21bdecf445f980bf3ace771d801d8" + }, + { + "identifier": 201, + "participant_share": "e30297ae96720bbd15da4341b0e2fea9debcffa980246c0b4fb7180ade23f610" + }, + { + "identifier": 202, + "participant_share": "defaf65c44a264a72acbd48a6c55b30967a7e3743104787e937a832e4a6fea48" + }, + { + "identifier": 203, + "participant_share": "daf35509f2d2bd913fbd65d327c86768f092c73ee1e484f1d73dee51b6bbde80" + }, + { + "identifier": 204, + "participant_share": "d6ebb3b7a103167b54aef71be33b1bc8797dab0992c491651b0159752307d2b8" + }, + { + "identifier": 205, + "participant_share": "d2e412654f336f6569a088649eadd02802688ed443a49dd85ec4c4988f53c6f0" + }, + { + "identifier": 206, + "participant_share": "cedc7112fd63c84f7e9219ad5a2084878b53729ef484aa4ba2882fbbfb9fbb28" + }, + { + "identifier": 207, + "participant_share": "cad4cfc0ab9421399383aaf6159338e7143e5669a564b6bee64b9adf67ebaf60" + }, + { + "identifier": 208, + "participant_share": "c6cd2e6e59c47a23a8753c3ed105ed469d293a345644c3322a0f0602d437a398" + }, + { + "identifier": 209, + "participant_share": "c2c58d1c07f4d30dbd66cd878c78a1a626141dff0724cfa56dd27126408397d0" + }, + { + "identifier": 210, + "participant_share": "bebdebc9b6252bf7d2585ed047eb5605aeff01c9b804dc18b195dc49accf8c08" + }, + { + "identifier": 211, + "participant_share": "bab64a77645584e1e749f019035e0a6537e9e59468e4e88bf559476d191b8040" + }, + { + "identifier": 212, + "participant_share": "b6aea9251285ddcbfc3b8161bed0bec4c0d4c95f19c4f4ff391cb29085677478" + }, + { + "identifier": 213, + "participant_share": "b2a707d2c0b636b6112d12aa7a43732449bfad29caa501727ce01db3f1b368b0" + }, + { + "identifier": 214, + "participant_share": "ae9f66806ee68fa0261ea3f335b62783d2aa90f47b850de5c0a388d75dff5ce8" + }, + { + "identifier": 215, + "participant_share": "aa97c52e1d16e88a3b10353bf128dbe35b9574bf2c651a590466f3faca4b5120" + }, + { + "identifier": 216, + "participant_share": "a69023dbcb4741745001c684ac9b9042e4805889dd4526cc482a5f1e36974558" + }, + { + "identifier": 217, + "participant_share": "a288828979779a5e64f357cd680e44a26d6b3c548e25333f8bedca41a2e33990" + }, + { + "identifier": 218, + "participant_share": "9e80e13727a7f34879e4e9162380f901f656201f3f053fb2cfb135650f2f2dc8" + }, + { + "identifier": 219, + "participant_share": "9a793fe4d5d84c328ed67a5edef3ad617f4103e9efe54c261374a0887b7b2200" + }, + { + "identifier": 220, + "participant_share": "96719e928408a51ca3c80ba79a6661c1082be7b4a0c5589957380babe7c71638" + }, + { + "identifier": 221, + "participant_share": "9269fd403238fe06b8b99cf055d916209116cb7f51a5650c9afb76cf54130a70" + }, + { + "identifier": 222, + "participant_share": "8e625bede06956f0cdab2e39114bca801a01af4a0285717fdebee1f2c05efea8" + }, + { + "identifier": 223, + "participant_share": "8a5aba9b8e99afdae29cbf81ccbe7edfa2ec9314b3657df322824d162caaf2e0" + }, + { + "identifier": 224, + "participant_share": "865319493cca08c4f78e50ca8831333f2bd776df64458a666645b83998f6e718" + }, + { + "identifier": 225, + "participant_share": "824b77f6eafa61af0c7fe21343a3e79eb4c25aaa152596d9aa09235d0542db50" + }, + { + "identifier": 226, + "participant_share": "7e43d6a4992aba992171735bff169bfe3dad3e74c605a34cedcc8e80718ecf88" + }, + { + "identifier": 227, + "participant_share": "7a3c3552475b1383366304a4ba89505dc698223f76e5afc0318ff9a3dddac3c0" + }, + { + "identifier": 228, + "participant_share": "763493fff58b6c6d4b5495ed75fc04bd4f83060a27c5bc33755364c74a26b7f8" + }, + { + "identifier": 229, + "participant_share": "722cf2ada3bbc55760462736316eb91cd86de9d4d8a5c8a6b916cfeab672ac30" + }, + { + "identifier": 230, + "participant_share": "6e25515b51ec1e417537b87eece16d7c6158cd9f8985d519fcda3b0e22bea068" + }, + { + "identifier": 231, + "participant_share": "6a1db009001c772b8a2949c7a85421dbea43b16a3a65e18d409da6318f0a94a0" + }, + { + "identifier": 232, + "participant_share": "66160eb6ae4cd0159f1adb1063c6d63b732e9534eb45ee0084611154fb5688d8" + }, + { + "identifier": 233, + "participant_share": "620e6d645c7d28ffb40c6c591f398a9afc1978ff9c25fa73c8247c7867a27d10" + }, + { + "identifier": 234, + "participant_share": "5e06cc120aad81e9c8fdfda1daac3efa85045cca4d0606e70be7e79bd3ee7148" + }, + { + "identifier": 235, + "participant_share": "59ff2abfb8dddad3ddef8eea961ef35a0def4094fde6135a4fab52bf403a6580" + }, + { + "identifier": 236, + "participant_share": "55f7896d670e33bdf2e120335191a7b996da245faec61fcd936ebde2ac8659b8" + }, + { + "identifier": 237, + "participant_share": "51efe81b153e8ca807d2b17c0d045c191fc5082a5fa62c40d732290618d24df0" + }, + { + "identifier": 238, + "participant_share": "4de846c8c36ee5921cc442c4c8771078a8afebf5108638b41af59429851e4228" + }, + { + "identifier": 239, + "participant_share": "49e0a576719f3e7c31b5d40d83e9c4d8319acfbfc16645275eb8ff4cf16a3660" + }, + { + "identifier": 240, + "participant_share": "45d904241fcf976646a765563f5c7937ba85b38a7246519aa27c6a705db62a98" + }, + { + "identifier": 241, + "participant_share": "41d162d1cdfff0505b98f69efacf2d974370975523265e0de63fd593ca021ed0" + }, + { + "identifier": 242, + "participant_share": "3dc9c17f7c30493a708a87e7b641e1f6cc5b7b1fd4066a812a0340b7364e1308" + }, + { + "identifier": 243, + "participant_share": "39c2202d2a60a224857c193071b4965655465eea84e676f46dc6abdaa29a0740" + }, + { + "identifier": 244, + "participant_share": "35ba7edad890fb0e9a6daa792d274ab5de3142b535c68367b18a16fe0ee5fb78" + }, + { + "identifier": 245, + "participant_share": "31b2dd8886c153f8af5f3bc1e899ff15671c267fe6a68fdaf54d82217b31efb0" + }, + { + "identifier": 246, + "participant_share": "2dab3c3634f1ace2c450cd0aa40cb374f0070a4a97869c4e3910ed44e77de3e8" + }, + { + "identifier": 247, + "participant_share": "29a39ae3e32205ccd9425e535f7f67d478f1ee154866a8c17cd4586853c9d820" + }, + { + "identifier": 248, + "participant_share": "259bf99191525eb6ee33ef9c1af21c3401dcd1dff946b534c097c38bc015cc58" + }, + { + "identifier": 249, + "participant_share": "2194583f3f82b7a1032580e4d664d0938ac7b5aaaa26c1a8045b2eaf2c61c090" + }, + { + "identifier": 250, + "participant_share": "1d8cb6ecedb3108b1817122d91d784f313b299755b06ce1b481e99d298adb4c8" + }, + { + "identifier": 251, + "participant_share": "1985159a9be369752d08a3764d4a39529c9d7d400be6da8e8be204f604f9a900" + }, + { + "identifier": 252, + "participant_share": "157d74484a13c25f41fa34bf08bcedb22588610abcc6e701cfa5701971459d38" + }, + { + "identifier": 253, + "participant_share": "1175d2f5f8441b4956ebc607c42fa211ae7344d56da6f3751368db3cdd919170" + }, + { + "identifier": 254, + "participant_share": "0d6e31a3a67474336bdd57507fa25671375e28a01e86ffe8572c466049dd85a8" + }, + { + "identifier": 255, + "participant_share": "0966905154a4cd1d80cee8993b150ad0c0490c6acf670c5b9aefb183b62979e0" + }, + { + "identifier": 256, + "participant_share": "055eeeff02d5260795c079e1f687bf304933f035804718cedeb31ca722756e18" + }, + { + "identifier": 257, + "participant_share": "01574dacb1057ef1aab20b2ab1fa738fd21ed40031272542227687ca8ec16250" + } + ] + }, + "round_one_outputs": { + "outputs": [ + { + "identifier": 129, + "hiding_nonce_randomness": "8a8821b1b7d3bb29e7c2ed156a17fbdb9d4e036e3c0f31ade4d853c40531526d", + "binding_nonce_randomness": "b5964e34921c184757523ebfce17f8c8d9a4f8b1a9a6b3ce2c62e75a79e217e8", + "hiding_nonce": "135aa1e7e51aa2b0b284f76c3aa60ff8103a1fd408c0f36ca77edae5b7b70812", + "binding_nonce": "6d068b279ded38490227376e39f399d111f99870e82a6bb1502de20c0aeb6b47", + "hiding_nonce_commitment": "03e783e37cbef36a95e5bb2b46e1010a8399c08b642ca6689e2921319a74b1799e", + "binding_nonce_commitment": "03670f66137edada9052a20e36315f36e76c28488765aa7d3d4f14025cc1f85989", + "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a59da64154cd8963aee316804acf6d77f644ad0a0f512be8c7fb5b8daa396490190000000000000000000000000000000000000000000000000000000000000081", + "binding_factor": "59e9ae0db32dee8c28daadd3ea91b1cf17fa9c9245e60b690193b7d740168666" + }, + { + "identifier": 256, + "hiding_nonce_randomness": "1227d0c4838dbda8f7871bb64cb49545655d65e1da44da38c9062d39e591f290", + "binding_nonce_randomness": "bbab58cdb56bcc90a9cde2778a79cfa786372100d60e6a17dd60349ccceb4c88", + "hiding_nonce": "435ceb2e65e00a57ef672058fd60e31dd752d7e069762419b5f2e45e29b4fdd6", + "binding_nonce": "c6c8aea0d31fdb102dda41795283d2e89474cf5f320786c2dd95c6a42b5e8c77", + "hiding_nonce_commitment": "03df58d07ff0865fb3f4cbfc077e1fa8cffc700536afdeab94bb4a1b74f86c43cb", + "binding_nonce_commitment": "029f423064a974151cfbde1c22a318573148213da913cfdf4660e6f1b4e01c3e60", + "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a59da64154cd8963aee316804acf6d77f644ad0a0f512be8c7fb5b8daa396490190000000000000000000000000000000000000000000000000000000000000100", + "binding_factor": "3590ce03a0f269349c8c4b7d7ff2f0e14fc4e7818a779685656bc2e6f547b97a" + }, + { + "identifier": 257, + "hiding_nonce_randomness": "8e8a7c08ce9f812a140c5f1b65300e65c63314e2ea00a8a7c8992d8e9baa7151", + "binding_nonce_randomness": "5dba45da45f873fd04ddd5f359ee52c8657028429f5819a7d8605b1181bfb3b5", + "hiding_nonce": "f90ffc60826a77fcdcc97e1f9901d22efe27c89a6f24098453ecf7a755232dae", + "binding_nonce": "59cfe70faeb536c8c358fbcf41b55487ef95fb7ae94ad317d9cffd217e352b07", + "hiding_nonce_commitment": "0286950f46bf1d4152c64e0c26eb66711a9a6b7dd7f0ef640782b0d9f014113bf1", + "binding_nonce_commitment": "026756d50a598d377947118be51da1262dbc263fac85eb422c14904ee3cc6195e6", + "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a59da64154cd8963aee316804acf6d77f644ad0a0f512be8c7fb5b8daa396490190000000000000000000000000000000000000000000000000000000000000101", + "binding_factor": "8280f8dd82bc7485824a53ee781bcec7228696004537b28338ecc77a2d123281" + } + ] + }, + "round_two_outputs": { + "outputs": [ + { + "identifier": 129, + "sig_share": "f01e946f27156b55a714d8872a31c860a379e10e305a20bbd39e4f509b78a7ab" + }, + { + "identifier": 256, + "sig_share": "f4ca85e6e1bf8bd0d16cb3bab9a9b223e97fdcff73ecc650706a4b54b4573368" + }, + { + "identifier": 257, + "sig_share": "0b9b540faed82f39a9cee154dc998297865f1b9096f89e87c5cd3163997b1165" + } + ] + }, + "final_output": { + "sig": "0354997f922511dba38d16973092a331f4039477bf4a2d77b438a317862795b267afb5d03fef0520d7b6ef541cbf9c252e51c11b989950ff64f750cb65ac9d1277" + } +} diff --git a/frost-secp256k1-tr/tests/helpers/vectors.json b/frost-secp256k1-tr/tests/helpers/vectors.json new file mode 100644 index 00000000..57eaaf90 --- /dev/null +++ b/frost-secp256k1-tr/tests/helpers/vectors.json @@ -0,0 +1,77 @@ +{ + "config": { + "MAX_PARTICIPANTS": "3", + "NUM_PARTICIPANTS": "2", + "MIN_PARTICIPANTS": "2", + "name": "FROST(secp256k1, SHA-256)", + "group": "secp256k1", + "hash": "SHA-256" + }, + "inputs": { + "participant_list": [ + 1, + 3 + ], + "group_secret_key": "0d004150d27c3bf2a42f312683d35fac7394b1e9e318249c1bfe7f0795a83114", + "verifying_key_key": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4f", + "message": "74657374", + "share_polynomial_coefficients": [ + "fbf85eadae3058ea14f19148bb72b45e4399c0b16028acaf0395c9b03c823579" + ], + "participant_shares": [ + { + "identifier": 1, + "participant_share": "08f89ffe80ac94dcb920c26f3f46140bfc7f95b493f8310f5fc1ea2b01f4254c" + }, + { + "identifier": 2, + "participant_share": "04f0feac2edcedc6ce1253b7fab8c86b856a797f44d83d82a385554e6e401984" + }, + { + "identifier": 3, + "participant_share": "00e95d59dd0d46b0e303e500b62b7ccb0e555d49f5b849f5e748c071da8c0dbc" + } + ] + }, + "round_one_outputs": { + "outputs": [ + { + "identifier": 1, + "hiding_nonce_randomness": "bda8e748e599187762cff956f03dc6ea13fc8e04491a0427b7e6e78600f41c52", + "binding_nonce_randomness": "2ca682429bf05df435b9927b8edb1d748278f3e42fa11ef358e49bbf4a1b780d", + "hiding_nonce": "58cd30723da418156fe9b71870a118e0bbc3d0353ba7c760f9bbc8d60c3dab29", + "binding_nonce": "c22289cc43b82ed938d4b2288efb7381c405fb59f5d43bddc543d98838c60b19", + "hiding_nonce_commitment": "024e34ab3a7ad6b4563dbfe97e9f1206b3378cceb2502491ed0fb709765e1e5ba8", + "binding_nonce_commitment": "03d4b1f3a61dc67e64dfb4abfccabb712f1f6914a6ec9b67749d171370453192cb", + "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a5a875faacc4377f0b6e4638c16db01b5f88070873ee789f5e9e6acf91f52fdd030000000000000000000000000000000000000000000000000000000000000001", + "binding_factor": "55a3e44879db6daf00c81eb28e828869560c0901f347baff524f1c91a6669604" + }, + { + "identifier": 3, + "hiding_nonce_randomness": "70818dd5170672c4a4285fd593d4f222417f941f3118e1244955e7a1098a35d8", + "binding_nonce_randomness": "74ca2da071ed4a2a6cad5087d6758b48a558ab5861c61117fee05757e4b1309e", + "hiding_nonce": "a4109db0a5db30fac8cd1f4e272ff02e08258928f067d82c63d97279b114514a", + "binding_nonce": "ce3837bd963f0d81002279f7bb9eefceac64435f638885c2beae6f1dd881fd9e", + "hiding_nonce_commitment": "02d768658a1b94225645401a1512b803657770c7a21bf9ccccccfa09930a44951b", + "binding_nonce_commitment": "034570a4e5217ee8770a28401185f50b4fce4d3f3933a3af9df7ab39b42381d0eb", + "binding_factor_input": "02f37c34b66ced1fb51c34a90bdae006901f10625cc06c4f64663b0eae87d87b4fc709887b880e002210593616e086c2a0652d18bad338a3d3987251602e45e0a5a875faacc4377f0b6e4638c16db01b5f88070873ee789f5e9e6acf91f52fdd030000000000000000000000000000000000000000000000000000000000000003", + "binding_factor": "444c1cc7cfe48f4577cce65488f337a9cc8c33dea4cfa986eb590bd8e2b1fa2d" + } + ] + }, + "round_two_outputs": { + "outputs": [ + { + "identifier": 1, + "sig_share": "d6641c4136ee5bf7135272c2cae2ffc1170f9ee44562cf1a1f0ab65c14cfcc4e" + }, + { + "identifier": 3, + "sig_share": "e79e49ff66968247cb345da0f43d9ca83522e455478612602ac69e004b93e476" + } + ] + }, + "final_output": { + "sig": "030c776a9516a77808b70a31e74f1464814a6fcf897fb3a6bd84c7a9a9a7a5bcb8c0828f771deb8b8ab07fbfc2138ddbda6ff32bd9f7a673459538bc96d1f72e70" + } +} diff --git a/frost-secp256k1-tr/tests/integration_tests.rs b/frost-secp256k1-tr/tests/integration_tests.rs new file mode 100644 index 00000000..9ad7338b --- /dev/null +++ b/frost-secp256k1-tr/tests/integration_tests.rs @@ -0,0 +1,233 @@ +use frost_secp256k1_tr::*; +use lazy_static::lazy_static; +use rand::thread_rng; +use serde_json::Value; + +#[test] +fn check_zero_key_fails() { + frost_core::tests::ciphersuite_generic::check_zero_key_fails::(); +} + +#[test] +fn check_sign_with_dkg() { + let rng = thread_rng(); + + frost_core::tests::ciphersuite_generic::check_sign_with_dkg::(rng); +} + +#[test] +fn check_dkg_part1_fails_with_invalid_signers_min_signers() { + let rng = thread_rng(); + + let min_signers = 1; + let max_signers = 3; + let error = Error::InvalidMinSigners; + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_dkg_part1_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + + let min_signers = 3; + let max_signers = 2; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_dkg_part1_fails_with_invalid_signers_max_signers() { + let rng = thread_rng(); + + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_rts() { + let rng = thread_rng(); + + frost_core::tests::repairable::check_rts::(rng); +} + +#[test] +fn check_sign_with_dealer() { + let rng = thread_rng(); + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer::(rng); +} + +#[test] +fn check_sign_with_dealer_fails_with_invalid_min_signers() { + let rng = thread_rng(); + + let min_signers = 1; + let max_signers = 3; + let error = Error::InvalidMinSigners; + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_sign_with_dealer_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + + let min_signers = 3; + let max_signers = 2; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_sign_with_dealer_fails_with_invalid_max_signers() { + let rng = thread_rng(); + + let min_signers = 3; + let max_signers = 1; + let error = Error::InvalidMaxSigners; + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +/// This is testing that Shamir's secret sharing to compute and arbitrary +/// value is working. +#[test] +fn check_share_generation_secp256k1_sha256() { + let rng = thread_rng(); + frost_core::tests::ciphersuite_generic::check_share_generation::(rng); +} + +#[test] +fn check_share_generation_fails_with_invalid_min_signers() { + let rng = thread_rng(); + + let min_signers = 0; + let max_signers = 3; + let error = Error::InvalidMinSigners; + + frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_share_generation_fails_with_min_signers_greater_than_max() { + let rng = thread_rng(); + + let min_signers = 3; + let max_signers = 2; + let error: frost_core::Error = Error::InvalidMinSigners; + + frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +#[test] +fn check_share_generation_fails_with_invalid_max_signers() { + let rng = thread_rng(); + + let min_signers = 3; + let max_signers = 0; + let error = Error::InvalidMaxSigners; + + frost_core::tests::ciphersuite_generic::check_share_generation_fails_with_invalid_signers::< + Secp256K1Sha256, + _, + >(min_signers, max_signers, error, rng); +} + +lazy_static! { + pub static ref VECTORS: Value = + serde_json::from_str(include_str!("../tests/helpers/vectors.json").trim()) + .expect("Test vector is valid JSON"); + pub static ref VECTORS_BIG_IDENTIFIER: Value = + serde_json::from_str(include_str!("../tests/helpers/vectors-big-identifier.json").trim()) + .expect("Test vector is valid JSON"); +} + +#[test] +fn check_sign_with_test_vectors() { + frost_core::tests::vectors::check_sign_with_test_vectors::(&VECTORS); +} + +#[test] +fn check_sign_with_test_vectors_with_big_identifiers() { + frost_core::tests::vectors::check_sign_with_test_vectors::( + &VECTORS_BIG_IDENTIFIER, + ); +} + +#[test] +fn check_error_culprit() { + frost_core::tests::ciphersuite_generic::check_error_culprit::(); +} + +#[test] +fn check_identifier_derivation() { + frost_core::tests::ciphersuite_generic::check_identifier_derivation::(); +} + +// Explicit test which is used in a documentation snippet +#[test] +#[allow(unused_variables)] +fn check_identifier_generation() -> Result<(), Error> { + // ANCHOR: dkg_identifier + let participant_identifier = Identifier::try_from(7u16)?; + let participant_identifier = Identifier::derive("alice@example.com".as_bytes())?; + // ANCHOR_END: dkg_identifier + Ok(()) +} + +#[test] +fn check_sign_with_dealer_and_identifiers() { + let rng = thread_rng(); + + frost_core::tests::ciphersuite_generic::check_sign_with_dealer_and_identifiers::< + Secp256K1Sha256, + _, + >(rng); +} + +#[test] +fn check_sign_with_missing_identifier() { + let rng = thread_rng(); + frost_core::tests::ciphersuite_generic::check_sign_with_missing_identifier::( + rng, + ); +} + +#[test] +fn check_sign_with_incorrect_commitments() { + let rng = thread_rng(); + frost_core::tests::ciphersuite_generic::check_sign_with_incorrect_commitments::< + Secp256K1Sha256, + _, + >(rng); +} diff --git a/frost-secp256k1-tr/tests/recreation_tests.rs b/frost-secp256k1-tr/tests/recreation_tests.rs new file mode 100644 index 00000000..5d0150e4 --- /dev/null +++ b/frost-secp256k1-tr/tests/recreation_tests.rs @@ -0,0 +1,123 @@ +//! Test for recreating packages from their components, which shows that they +//! can be serialized and deserialized as the user wishes. + +use frost_secp256k1_tr::{ + keys::{ + dkg::{round1, round2}, + KeyPackage, PublicKeyPackage, SecretShare, + }, + round1::SigningCommitments, + round2::SignatureShare, + SigningPackage, +}; + +mod helpers; + +use helpers::samples; + +/// Check if SigningCommitments can be recreated. +#[test] +fn check_signing_commitments_recreation() { + let commitments = samples::signing_commitments(); + let hiding = commitments.hiding(); + let binding = commitments.binding(); + let new_commitments = SigningCommitments::new(*hiding, *binding); + assert!(commitments == new_commitments); +} + +/// Check if SigningPackage can be recreated. +#[test] +fn check_signing_package_recreation() { + let signing_package = samples::signing_package(); + + let commitments = signing_package.signing_commitments(); + let message = signing_package.message(); + + let new_signing_package = SigningPackage::new(commitments.clone(), message); + assert!(signing_package == new_signing_package); +} + +/// Check if SignatureShare can be recreated. +#[test] +fn check_signature_share_recreation() { + let signature_share = samples::signature_share(); + + let encoded = signature_share.serialize(); + + let new_signature_share = SignatureShare::deserialize(encoded).unwrap(); + assert!(signature_share == new_signature_share); +} + +/// Check if SecretShare can be recreated. +#[test] +fn check_secret_share_recreation() { + let secret_share = samples::secret_share(); + + let identifier = secret_share.identifier(); + let value = secret_share.signing_share(); + let commitment = secret_share.commitment(); + + let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone()); + + assert!(secret_share == new_secret_share); +} + +/// Check if KeyPackage can be recreated. +#[test] +fn check_key_package_recreation() { + let key_package = samples::key_package(); + + let identifier = key_package.identifier(); + let signing_share = key_package.signing_share(); + let verifying_share = key_package.verifying_share(); + let verifying_key = key_package.verifying_key(); + let min_signers = key_package.min_signers(); + + let new_key_package = KeyPackage::new( + *identifier, + *signing_share, + *verifying_share, + *verifying_key, + *min_signers, + ); + + assert!(key_package == new_key_package); +} + +/// Check if PublicKeyPackage can be recreated. +#[test] +fn check_public_key_package_recreation() { + let public_key_package = samples::public_key_package(); + + let verifying_shares = public_key_package.verifying_shares(); + let verifying_key = public_key_package.verifying_key(); + + let new_public_key_package = PublicKeyPackage::new(verifying_shares.clone(), *verifying_key); + + assert!(public_key_package == new_public_key_package); +} + +/// Check if round1::Package can be recreated. +#[test] +fn check_round1_package_recreation() { + let round1_package = samples::round1_package(); + + let vss_commitment = round1_package.commitment(); + let signature = round1_package.proof_of_knowledge(); + + let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature); + + assert!(round1_package == new_round1_package); +} + +/// Check if round2::Package can be recreated. +#[test] +fn check_round2_package_recreation() { + let round2_package = samples::round2_package(); + + let signing_share = round2_package.signing_share(); + + let new_round2_package = round2::Package::new(*signing_share); + + assert!(round2_package == new_round2_package); +} diff --git a/frost-secp256k1-tr/tests/rerandomized_tests.rs b/frost-secp256k1-tr/tests/rerandomized_tests.rs new file mode 100644 index 00000000..79bb5aa3 --- /dev/null +++ b/frost-secp256k1-tr/tests/rerandomized_tests.rs @@ -0,0 +1,10 @@ +use frost_secp256k1_tr::Secp256K1Sha256; +use rand::thread_rng; + +#[test] +fn check_randomized_sign_with_dealer() { + let rng = thread_rng(); + + let (_msg, _group_signature, _group_pubkey) = + frost_rerandomized::tests::check_randomized_sign_with_dealer::(rng); +} diff --git a/frost-secp256k1-tr/tests/serde_tests.rs b/frost-secp256k1-tr/tests/serde_tests.rs new file mode 100644 index 00000000..4b88dfdf --- /dev/null +++ b/frost-secp256k1-tr/tests/serde_tests.rs @@ -0,0 +1,632 @@ +#![cfg(feature = "serde")] + +mod helpers; + +use frost_secp256k1_tr::{ + keys::{ + dkg::{round1, round2}, + KeyPackage, PublicKeyPackage, SecretShare, + }, + round1::SigningCommitments, + round2::SignatureShare, + SigningPackage, +}; + +use helpers::samples; + +#[test] +fn check_signing_commitments_serialization() { + let commitments = samples::signing_commitments(); + + let json = serde_json::to_string_pretty(&commitments).unwrap(); + println!("{}", json); + + let decoded_commitments: SigningCommitments = serde_json::from_str(&json).unwrap(); + assert!(commitments == decoded_commitments); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + }"#; + let decoded_commitments: SigningCommitments = serde_json::from_str(json).unwrap(); + assert!(commitments == decoded_commitments); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Wrong ciphersuite + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST(Wrong, SHA-512)" + }, + "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "foo": "0000000000000000000000000000000000000000000000000000000000000000", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST(Ed25519, SHA-512)" + }, + "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + "extra": 1 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_signing_package_serialization() { + let signing_package = samples::signing_package(); + + let json = serde_json::to_string_pretty(&signing_package).unwrap(); + println!("{}", json); + + let decoded_signing_package: SigningPackage = serde_json::from_str(&json).unwrap(); + assert!(signing_package == decoded_signing_package); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_commitments": { + "000000000000000000000000000000000000000000000000000000000000002a": { + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + } + }, + "message": "68656c6c6f20776f726c64" + }"#; + let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap(); + assert!(signing_package == decoded_signing_package); + + // Invalid identifier + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_commitments": { + "0000000000000000000000000000000000000000000000000000000000000000": { + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + } + }, + "message": "68656c6c6f20776f726c64" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_commitments": { + "000000000000000000000000000000000000000000000000000000000000002a": { + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + } + }, + "message": "68656c6c6f20776f726c64" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_commitments": { + "000000000000000000000000000000000000000000000000000000000000002a": { + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + } + }, + "message": "68656c6c6f20776f726c64" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_commitments": { + "000000000000000000000000000000000000000000000000000000000000002a": { + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" + } + }, + "message": "68656c6c6f20776f726c64", + "extra": 1 + } + "#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_signature_share_serialization() { + let signature_share = samples::signature_share(); + + let json = serde_json::to_string_pretty(&signature_share).unwrap(); + println!("{}", json); + + let decoded_signature_share: SignatureShare = serde_json::from_str(&json).unwrap(); + assert!(signature_share == decoded_signature_share); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" + }"#; + let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap(); + assert!(signature_share == decoded_commitments); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + } + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "extra": 1 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_secret_share_serialization() { + let secret_share = samples::secret_share(); + + let json = serde_json::to_string_pretty(&secret_share).unwrap(); + println!("{}", json); + + let decoded_secret_share: SecretShare = serde_json::from_str(&json).unwrap(); + assert!(secret_share == decoded_secret_share); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ] + }"#; + let decoded_secret_share: SecretShare = serde_json::from_str(json).unwrap(); + assert!(secret_share == decoded_secret_share); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid identifier + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "0000000000000000000000000000000000000000000000000000000000000000", + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ] + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ] + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ] + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ] + "extra": 1, + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_key_package_serialization() { + let key_package = samples::key_package(); + + let json = serde_json::to_string_pretty(&key_package).unwrap(); + println!("{}", json); + + let decoded_key_package: KeyPackage = serde_json::from_str(&json).unwrap(); + assert!(key_package == decoded_key_package); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 + }"#; + let decoded_key_package: KeyPackage = serde_json::from_str(json).unwrap(); + assert!(key_package == decoded_key_package); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid identifier + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "0000000000000000000000000000000000000000000000000000000000000000", + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "verifying_share": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "extra_field": 1 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid version + let invalid_json = r#"{ + "header": { + "version": 1, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "identifier": "000000000000000000000000000000000000000000000000000000000000002a", + "secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "public": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "group_public": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "min_signers": 2 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_public_key_package_serialization() { + let public_key_package = samples::public_key_package(); + + let json = serde_json::to_string_pretty(&public_key_package).unwrap(); + println!("{}", json); + + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(&json).unwrap(); + assert!(public_key_package == decoded_public_key_package); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap(); + assert!(public_key_package == decoded_public_key_package); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid identifier + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "0000000000000000000000000000000000000000000000000000000000000000": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + } + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "verifying_shares": { + "000000000000000000000000000000000000000000000000000000000000002a": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + }, + "verifying_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "extra": 1 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_round1_package_serialization() { + let round1_package = samples::round1_package(); + + let json = serde_json::to_string_pretty(&round1_package).unwrap(); + println!("{}", json); + + let decoded_round1_package: round1::Package = serde_json::from_str(&json).unwrap(); + assert!(round1_package == decoded_round1_package); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ], + "proof_of_knowledge": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" + }"#; + let decoded_round1_package: round1::Package = serde_json::from_str(json).unwrap(); + assert!(round1_package == decoded_round1_package); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ], + "foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ] + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "commitment": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + ], + "proof_of_knowledge": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "extra": 1 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} + +#[test] +fn check_round2_package_serialization() { + let round2_package = samples::round2_package(); + + let json = serde_json::to_string_pretty(&round2_package).unwrap(); + println!("{}", json); + + let decoded_round2_package: round2::Package = serde_json::from_str(&json).unwrap(); + assert!(round2_package == decoded_round2_package); + + let json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" + }"#; + let decoded_round2_package: round2::Package = serde_json::from_str(json).unwrap(); + assert!(round2_package == decoded_round2_package); + + let invalid_json = "{}"; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Invalid field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81" + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Missing field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + } + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); + + // Extra field + let invalid_json = r#"{ + "header": { + "version": 0, + "ciphersuite": "FROST-secp256k1-SHA256-TR-v1" + }, + "signing_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81", + "extra": 1 + }"#; + assert!(serde_json::from_str::(invalid_json).is_err()); +} diff --git a/frost-secp256k1-tr/tests/serialization_tests.rs b/frost-secp256k1-tr/tests/serialization_tests.rs new file mode 100644 index 00000000..8378e6e7 --- /dev/null +++ b/frost-secp256k1-tr/tests/serialization_tests.rs @@ -0,0 +1,94 @@ +#![cfg(feature = "serialization")] + +mod helpers; + +use frost_secp256k1_tr::{ + keys::{ + dkg::{round1, round2}, + KeyPackage, PublicKeyPackage, SecretShare, + }, + round1::SigningCommitments, + round2::SignatureShare, + SigningPackage, +}; + +use helpers::samples; +use insta::assert_snapshot; + +#[test] +fn check_signing_commitments_postcard_serialization() { + let commitments = samples::signing_commitments(); + let bytes: Vec<_> = commitments.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + commitments, + SigningCommitments::deserialize(&bytes).unwrap() + ); +} + +#[test] +fn check_signing_package_postcard_serialization() { + let signing_package = samples::signing_package(); + let bytes: Vec<_> = signing_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + signing_package, + SigningPackage::deserialize(&bytes).unwrap() + ); +} + +#[test] +fn check_signature_share_postcard_serialization() { + let signature_share = samples::signature_share(); + let bytes = signature_share.serialize(); + assert_snapshot!(hex::encode(bytes)); + assert_eq!(signature_share, SignatureShare::deserialize(bytes).unwrap()); +} +#[test] +fn check_secret_share_postcard_serialization() { + let secret_share = samples::secret_share(); + let bytes: Vec<_> = secret_share.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!(secret_share, SecretShare::deserialize(&bytes).unwrap()); +} + +#[test] +fn check_key_package_postcard_serialization() { + let key_package = samples::key_package(); + let bytes: Vec<_> = key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!(key_package, KeyPackage::deserialize(&bytes).unwrap()); +} + +#[test] +fn check_public_key_package_postcard_serialization() { + let public_key_package = samples::public_key_package(); + let bytes: Vec<_> = public_key_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + public_key_package, + PublicKeyPackage::deserialize(&bytes).unwrap() + ); +} + +#[test] +fn check_round1_package_postcard_serialization() { + let round1_package = samples::round1_package(); + let bytes: Vec<_> = round1_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + round1_package, + round1::Package::deserialize(&bytes).unwrap() + ); +} + +#[test] +fn check_round2_package_postcard_serialization() { + let round2_package = samples::round2_package(); + let bytes: Vec<_> = round2_package.serialize().unwrap(); + assert_snapshot!(hex::encode(&bytes)); + assert_eq!( + round2_package, + round2::Package::deserialize(&bytes).unwrap() + ); +} diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap new file mode 100644 index 00000000..ca169f4c --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_key_package_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab3000000000000000000000000000000000000000000000000000000000000002aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b810279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap new file mode 100644 index 00000000..5600403f --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_public_key_package_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab301000000000000000000000000000000000000000000000000000000000000002a0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap new file mode 100644 index 00000000..684f7683 --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round1_package_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab3010279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798410279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap new file mode 100644 index 00000000..218294fb --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_round2_package_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap new file mode 100644 index 00000000..82e3585a --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_secret_share_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab3000000000000000000000000000000000000000000000000000000000000002aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81010279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap new file mode 100644 index 00000000..aa7a5030 --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signature_share_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(bytes)" +--- +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap new file mode 100644 index 00000000..66962d3c --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_commitments_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5 diff --git a/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap new file mode 100644 index 00000000..b398e5e1 --- /dev/null +++ b/frost-secp256k1-tr/tests/snapshots/serialization_tests__check_signing_package_postcard_serialization.snap @@ -0,0 +1,5 @@ +--- +source: frost-secp256k1-tr/tests/serialization_tests.rs +expression: "hex::encode(&bytes)" +--- +00230f8ab301000000000000000000000000000000000000000000000000000000000000002a00230f8ab30279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50b68656c6c6f20776f726c64 diff --git a/gencode/src/main.rs b/gencode/src/main.rs index 0806901e..512d92e9 100644 --- a/gencode/src/main.rs +++ b/gencode/src/main.rs @@ -290,6 +290,19 @@ fn main() -> ExitCode { "", ], ), + ( + "frost-secp256k1-tr", + &[ + "Secp256K1Sha256", + "secp256k1 curve", + "Secp256K1", + "FROST(secp256k1, SHA-256)", + "FROST-secp256k1-SHA256-TR-v1", + "secp256k1_sha256", + "secp256k1", + "", + ], + ), ] { // Some test use "sample" values. To make these tests work for another ciphersuites, // these values must be replaced. To make it cleaner, the strings are