diff --git a/src/frost/redjubjub/dkg.md b/src/frost/redjubjub/dkg.md index b077d68..93cd0c8 100644 --- a/src/frost/redjubjub/dkg.md +++ b/src/frost/redjubjub/dkg.md @@ -3,7 +3,7 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from +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 @@ -25,6 +25,7 @@ they can proceed to sign messages with FROST. ## Example ```rust +# // ANCHOR: dkg_import use rand::thread_rng; use std::collections::HashMap; @@ -32,13 +33,14 @@ use reddsa::frost::redjubjub as frost; let mut rng = thread_rng(); +let max_signers = 5; +let min_signers = 3; +# // ANCHOR_END: dkg_import + //////////////////////////////////////////////////////////////////////////// // Key generation, Round 1 //////////////////////////////////////////////////////////////////////////// -let max_signers = 5; -let min_signers = 3; - // 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. @@ -53,16 +55,18 @@ let mut received_round1_packages = HashMap::new(); // 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 (secret_package, round1_package) = frost::keys::dkg::part1( + # // 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, secret_package); + 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 HashMap; in practice this will be @@ -76,8 +80,8 @@ for participant_index in 1..=max_signers { .expect("should be nonzero"); received_round1_packages .entry(receiver_participant_identifier) - .or_insert_with(Vec::new) - .push(round1_package.clone()); + .or_insert_with(HashMap::new) + .insert(participant_identifier, round1_package.clone()); } } @@ -99,12 +103,14 @@ let mut received_round2_packages = HashMap::new(); // 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_packages) = frost::keys::dkg::part2( - round1_secret_packages - .remove(&participant_identifier) - .unwrap(), - &received_round1_packages[&participant_identifier], - )?; + 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. @@ -115,11 +121,11 @@ for participant_index in 1..=max_signers { // sent through some communication channel. // Note that, in contrast to the previous part, here each other participant // gets its own specific package. - for round2_package in round2_packages { + for (receiver_identifier, round2_package) in round2_packages { received_round2_packages - .entry(round2_package.receiver_identifier) - .or_insert_with(Vec::new) - .push(round2_package); + .entry(receiver_identifier) + .or_insert_with(HashMap::new) + .insert(participant_identifier, round2_package); } } @@ -142,13 +148,18 @@ let mut pubkey_packages = HashMap::new(); // 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 (key_package, pubkey_package_for_participant) = frost::keys::dkg::part3( - &round2_secret_packages[&participant_identifier], - &received_round1_packages[&participant_identifier], - &received_round2_packages[&participant_identifier], + 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_for_participant); + pubkey_packages.insert(participant_identifier, pubkey_package); } // With its own key package and the pubkey package, each participant can now proceed diff --git a/src/frost/redjubjub/keys/dkg.rs b/src/frost/redjubjub/keys/dkg.rs index 4a93fe4..bf25e22 100644 --- a/src/frost/redjubjub/keys/dkg.rs +++ b/src/frost/redjubjub/keys/dkg.rs @@ -64,8 +64,8 @@ pub fn part1( /// must be sent to other participants. pub fn part2( secret_package: round1::SecretPackage, - round1_packages: &[round1::Package], -) -> Result<(round2::SecretPackage, Vec), Error> { + round1_packages: &HashMap, +) -> Result<(round2::SecretPackage, HashMap), Error> { frost::keys::dkg::part2(secret_package, round1_packages) } @@ -80,8 +80,8 @@ pub fn part2( /// signatures. pub fn part3( round2_secret_package: &round2::SecretPackage, - round1_packages: &[round1::Package], - round2_packages: &[round2::Package], + round1_packages: &HashMap, + round2_packages: &HashMap, ) -> Result<(KeyPackage, PublicKeyPackage), Error> { frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages) } diff --git a/src/frost/redpallas/dkg.md b/src/frost/redpallas/dkg.md index e6362d7..93abd4d 100644 --- a/src/frost/redpallas/dkg.md +++ b/src/frost/redpallas/dkg.md @@ -3,7 +3,7 @@ The DKG module supports generating FROST key shares in a distributed manner, without a trusted dealer. -Before starting, each participant needs a unique identifier, which can be built from +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 @@ -25,6 +25,7 @@ they can proceed to sign messages with FROST. ## Example ```rust +# // ANCHOR: dkg_import use rand::thread_rng; use std::collections::HashMap; @@ -32,13 +33,14 @@ use reddsa::frost::redpallas as frost; let mut rng = thread_rng(); +let max_signers = 5; +let min_signers = 3; +# // ANCHOR_END: dkg_import + //////////////////////////////////////////////////////////////////////////// // Key generation, Round 1 //////////////////////////////////////////////////////////////////////////// -let max_signers = 5; -let min_signers = 3; - // 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. @@ -53,16 +55,18 @@ let mut received_round1_packages = HashMap::new(); // 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 (secret_package, round1_package) = frost::keys::dkg::part1( + # // 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, secret_package); + 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 HashMap; in practice this will be @@ -76,8 +80,8 @@ for participant_index in 1..=max_signers { .expect("should be nonzero"); received_round1_packages .entry(receiver_participant_identifier) - .or_insert_with(Vec::new) - .push(round1_package.clone()); + .or_insert_with(HashMap::new) + .insert(participant_identifier, round1_package.clone()); } } @@ -99,12 +103,14 @@ let mut received_round2_packages = HashMap::new(); // 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_packages) = frost::keys::dkg::part2( - round1_secret_packages - .remove(&participant_identifier) - .unwrap(), - &received_round1_packages[&participant_identifier], - )?; + 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. @@ -115,11 +121,11 @@ for participant_index in 1..=max_signers { // sent through some communication channel. // Note that, in contrast to the previous part, here each other participant // gets its own specific package. - for round2_package in round2_packages { + for (receiver_identifier, round2_package) in round2_packages { received_round2_packages - .entry(round2_package.receiver_identifier) - .or_insert_with(Vec::new) - .push(round2_package); + .entry(receiver_identifier) + .or_insert_with(HashMap::new) + .insert(participant_identifier, round2_package); } } @@ -142,13 +148,18 @@ let mut pubkey_packages = HashMap::new(); // 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 (key_package, pubkey_package_for_participant) = frost::keys::dkg::part3( - &round2_secret_packages[&participant_identifier], - &received_round1_packages[&participant_identifier], - &received_round2_packages[&participant_identifier], + 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_for_participant); + pubkey_packages.insert(participant_identifier, pubkey_package); } // With its own key package and the pubkey package, each participant can now proceed diff --git a/src/frost/redpallas/keys/dkg.rs b/src/frost/redpallas/keys/dkg.rs index f0703ac..cb638f3 100644 --- a/src/frost/redpallas/keys/dkg.rs +++ b/src/frost/redpallas/keys/dkg.rs @@ -64,8 +64,8 @@ pub fn part1( /// must be sent to other participants. pub fn part2( secret_package: round1::SecretPackage, - round1_packages: &[round1::Package], -) -> Result<(round2::SecretPackage, Vec), Error> { + round1_packages: &HashMap, +) -> Result<(round2::SecretPackage, HashMap), Error> { frost::keys::dkg::part2(secret_package, round1_packages) } @@ -80,8 +80,8 @@ pub fn part2( /// signatures. pub fn part3( round2_secret_package: &round2::SecretPackage, - round1_packages: &[round1::Package], - round2_packages: &[round2::Package], + round1_packages: &HashMap, + round2_packages: &HashMap, ) -> Result<(KeyPackage, PublicKeyPackage), Error> { frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages) } diff --git a/src/orchard/tests.rs b/src/orchard/tests.rs index 4918fa5..1461ed2 100644 --- a/src/orchard/tests.rs +++ b/src/orchard/tests.rs @@ -1,6 +1,7 @@ use std::println; use crate::scalar_mul::{self, VartimeMultiscalarMul}; +use crate::{orchard, Signature, SigningKey, VerificationKey, VerificationKeyBytes}; use alloc::vec::Vec; use group::ff::Field; use group::{ff::PrimeField, GroupEncoding}; @@ -9,6 +10,47 @@ use rand::thread_rng; use pasta_curves::arithmetic::CurveExt; use pasta_curves::pallas; +#[test] +fn orchard_sign() { + let msg = + hex::decode("8ca86a5e2f89da4dd6b8f26f740f360667ec1526cdb0ac7719ddd1c4a1e62981").unwrap(); + + // Generate a secret key and sign the message + let sk_bytes: [u8; 32] = + hex::decode("6a0df875bb9747883d518dd12223c986bb8166468263f0ab27f235c90d07db30") + .unwrap() + .try_into() + .unwrap(); + let sk = SigningKey::::try_from(sk_bytes).unwrap(); + let ak: VerificationKey<_> = (&sk).into(); + let ak: VerificationKeyBytes<_> = ak.into(); + let ak: [u8; 32] = ak.into(); + println!("ak: {}", hex::encode(ak)); + + let randomizer_bytes: [u8; 32] = + hex::decode("10e10752b172b0bfbce1fcc577da34023b67749aa37c50845a35fdc04dc4d51f") + .unwrap() + .try_into() + .unwrap(); + let randomizer = pasta_curves::pallas::Scalar::from_repr(randomizer_bytes).unwrap(); + + let sk = sk.randomize(&randomizer); + + let sig = sk.sign(thread_rng(), &msg); + + // Types can be converted to raw byte arrays using From/Into + let sig_bytes: [u8; 64] = sig.into(); + println!("Signature: {}", hex::encode(sig_bytes)); + let pk: VerificationKey = (&sk).into(); + let pk_bytes: [u8; 32] = pk.into(); + + // Deserialize and verify the signature. + let sig: Signature = sig_bytes.into(); + assert!(VerificationKey::try_from(pk_bytes) + .and_then(|pk| pk.verify(&msg, &sig)) + .is_ok()); +} + #[test] fn orchard_spendauth_basepoint() { use super::ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES;