Create Coordinator CLI (#59)
* Add coordinator demo (#48) * Add test for step_3 in coordinator (#48) * Add validation for participant selection in coordinator demo (#48) * Add tests for validation in step_1 for coordinator (#48) * Improve error handling in Coordinator (#48) * Fix clippy error (#48) * Improve usability for coordinator demo (#48) Fix test values Improve identifier input so it doesn't need to be in quotes Remove unecessary text
This commit is contained in:
parent
2c26bf99ab
commit
58abe8d3e3
|
@ -153,6 +153,20 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
|
||||
[[package]]
|
||||
name = "coordinator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"exitcode",
|
||||
"eyre",
|
||||
"frost-ed25519",
|
||||
"hex",
|
||||
"itertools 0.11.0",
|
||||
"rand",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.9"
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
members = [
|
||||
"participant",
|
||||
"trusted-dealer",
|
||||
"dkg"
|
||||
"dkg",
|
||||
"coordinator"
|
||||
]
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "coordinator"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
eyre = "0.6.8"
|
||||
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
|
||||
hex = { version = "0.4", features = ["serde"] }
|
||||
thiserror = "1.0"
|
||||
rand = "0.8"
|
||||
serde_json = "1.0"
|
||||
itertools = "0.11.0"
|
||||
exitcode = "1.1.2"
|
|
@ -0,0 +1,55 @@
|
|||
# FROST Coordinator Demo
|
||||
|
||||
TODO
|
||||
|
||||
This will be part of a set of demos and a proof of concept application that uses the FROST libraries and reference implementation. The purpose of these demos is to:
|
||||
|
||||
1. identify gaps in our documentation
|
||||
2. provide usage examples for developer facing documentation
|
||||
3. provide reference implementations for developers wanting to use FROST in a “real world” scenario.
|
||||
|
||||
This demo uses the (Ed25519, SHA-512) ciphersuite. The crate can be found [here](https://crates.io/crates/frost-ed25519).
|
||||
|
||||
## About FROST (Flexible Round-Optimised Schnorr Threshold signatures)
|
||||
|
||||
Unlike signatures in a single-party setting, threshold signatures require cooperation among a threshold number of signers, each holding a share of a common private key. The security of threshold
|
||||
schemes in general assume that an adversary can corrupt strictly fewer than a threshold number of participants.
|
||||
|
||||
[Two-Round Threshold Schnorr Signatures with FROST](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/) presents a variant of a Flexible Round-Optimized Schnorr Threshold (FROST) signature scheme originally defined in [FROST20](https://eprint.iacr.org/2020/852.pdf). FROST reduces network overhead during threshold
|
||||
signing operations while employing a novel technique to protect against forgery attacks applicable to prior Schnorr-based threshold signature constructions. This variant of FROST requires two rounds to compute a signature, and implements signing efficiency improvements described by [Schnorr21](https://eprint.iacr.org/2021/1375.pdf). Single-round signing with FROST is not implemented here.
|
||||
|
||||
## Status ⚠
|
||||
|
||||
The Coordinator Demo is a WIP
|
||||
|
||||
## Usage
|
||||
|
||||
NOTE: This is for demo purposes only and should not be used in production.
|
||||
|
||||
You will need to have [Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) installed.
|
||||
|
||||
To run:
|
||||
1. Clone the repo. Run `git clone https://github.com/ZcashFoundation/frost-zcash-demo.git`
|
||||
2. Run `cargo install`
|
||||
3. Run `cargo run`
|
||||
|
||||
TODO
|
||||
|
||||
## Using the output
|
||||
|
||||
TODO
|
||||
|
||||
## Developer information
|
||||
|
||||
TODO
|
||||
|
||||
### Pre-commit checks
|
||||
|
||||
1. Run `cargo make all`
|
||||
|
||||
### Coverage
|
||||
|
||||
Test coverage checks are performed in the pipeline. This is configured here: `.github/workflows/coverage.yaml`
|
||||
To run these locally:
|
||||
1. Install coverage tool by running `cargo install cargo-llvm-cov`
|
||||
2. Run `cargo make cov` (you may be asked if you want to install `llvm-tools-preview`, if so type `Y`)
|
|
@ -0,0 +1,29 @@
|
|||
use std::io::{BufRead, Write};
|
||||
|
||||
use crate::step_1::step_1;
|
||||
use crate::step_2::step_2;
|
||||
use crate::step_3::step_3;
|
||||
|
||||
pub fn cli(
|
||||
reader: &mut impl BufRead,
|
||||
logger: &mut impl Write,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
writeln!(logger, "\n=== STEP 1: CHOOSE PARTICIPANTS ===\n")?;
|
||||
|
||||
let participants_config = step_1(reader, logger)?;
|
||||
|
||||
writeln!(
|
||||
logger,
|
||||
"=== STEP 2: CHOOSE MESSAGE AND GENERATE COMMITMENT PACKAGE ===\n"
|
||||
)?;
|
||||
|
||||
let signing_package = step_2(reader, logger, participants_config.participants.clone())?;
|
||||
|
||||
writeln!(logger, "=== STEP 3: BUILD GROUP SIGNATURE ===\n")?;
|
||||
|
||||
step_3(reader, logger, participants_config, signing_package);
|
||||
|
||||
writeln!(logger, "=== END ===")?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
pub mod cli;
|
||||
pub mod step_1;
|
||||
mod step_2;
|
||||
mod step_3;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
|
@ -0,0 +1,19 @@
|
|||
use std::io;
|
||||
|
||||
use coordinator::cli::cli;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut reader = Box::new(io::stdin().lock());
|
||||
let mut logger = io::stdout();
|
||||
cli(&mut reader, &mut logger)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Choose participants -> send message to those participants - gen message to send
|
||||
|
||||
// Choose message - receive commitments - build commitment list - send to participants
|
||||
|
||||
// Receive signature shares - aggregate - send to participants. signautre shares must be validated first
|
||||
|
||||
// Verify group signature
|
|
@ -0,0 +1,169 @@
|
|||
use frost_ed25519 as frost;
|
||||
|
||||
use frost::{keys::PublicKeyPackage, Error, Identifier};
|
||||
|
||||
use eyre::eyre;
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
pub struct ParticipantsConfig {
|
||||
pub participants: Vec<Identifier>,
|
||||
pub pub_key_package: PublicKeyPackage,
|
||||
}
|
||||
|
||||
// TODO: needs to include the coordinator's keys!
|
||||
pub fn step_1(
|
||||
reader: &mut impl BufRead,
|
||||
logger: &mut dyn Write,
|
||||
) -> Result<ParticipantsConfig, Error> {
|
||||
let participants = choose_participants(reader, logger)?;
|
||||
print_participants(logger, &participants.participants);
|
||||
Ok(participants)
|
||||
}
|
||||
|
||||
fn validate(
|
||||
id: Identifier,
|
||||
key_package: PublicKeyPackage,
|
||||
id_list: &[Identifier],
|
||||
) -> Result<(), Error> {
|
||||
if !key_package.signer_pubkeys().contains_key(&id) {
|
||||
return Err(Error::MalformedIdentifier);
|
||||
}; // TODO: Error is actually that the identifier does not exist
|
||||
if id_list.contains(&id) {
|
||||
return Err(Error::DuplicatedIdentifier);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: validate min num of participants
|
||||
|
||||
pub fn read_identifier(input: &mut impl BufRead) -> Result<Identifier, Box<dyn std::error::Error>> {
|
||||
let mut identifier_input = String::new();
|
||||
input.read_line(&mut identifier_input)?;
|
||||
let bytes = hex::decode(identifier_input.trim())?;
|
||||
let serialization = bytes.try_into().map_err(|_| eyre!("Invalid Identifier"))?;
|
||||
let identifier = Identifier::deserialize(&serialization)?;
|
||||
Ok(identifier)
|
||||
}
|
||||
|
||||
// Input required:
|
||||
// 1. public key package
|
||||
// 2. number of participants
|
||||
// 3. identifiers for all participants
|
||||
fn choose_participants(
|
||||
input: &mut impl BufRead,
|
||||
logger: &mut dyn Write,
|
||||
) -> Result<ParticipantsConfig, Error> {
|
||||
writeln!(logger, "Paste the JSON public key package: ").unwrap();
|
||||
let mut key_package = String::new();
|
||||
input.read_line(&mut key_package).unwrap();
|
||||
let pub_key_package: PublicKeyPackage = serde_json::from_str(&key_package).unwrap();
|
||||
|
||||
writeln!(logger, "The number of participants: ").unwrap();
|
||||
|
||||
let mut participants = String::new();
|
||||
input.read_line(&mut participants).unwrap();
|
||||
let num_of_participants = participants.trim().parse::<u16>().unwrap();
|
||||
|
||||
let mut participants = Vec::new();
|
||||
|
||||
for i in 1..=num_of_participants {
|
||||
let package = pub_key_package.clone();
|
||||
writeln!(logger, "Identifier for participant {:?} (hex encoded):", i).unwrap();
|
||||
|
||||
let id_value = read_identifier(input).unwrap();
|
||||
|
||||
validate(id_value, package, &participants)?;
|
||||
|
||||
participants.push(id_value)
|
||||
}
|
||||
Ok(ParticipantsConfig {
|
||||
participants,
|
||||
pub_key_package,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn print_participants(logger: &mut dyn Write, participants: &Vec<Identifier>) {
|
||||
writeln!(logger, "Selected participants:",).unwrap();
|
||||
|
||||
for p in participants {
|
||||
writeln!(logger, "{}", serde_json::to_string(p).unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use frost::{
|
||||
keys::{PublicKeyPackage, VerifyingShare},
|
||||
Error, Identifier, VerifyingKey,
|
||||
};
|
||||
use frost_ed25519 as frost;
|
||||
use hex::FromHex;
|
||||
|
||||
use crate::step_1::validate;
|
||||
|
||||
const PUBLIC_KEY_1: &str = "fc2c9b8e335c132d9ebe0403c9317aac480bbbf8cbdb1bc3730bb68eb60dadf9";
|
||||
const PUBLIC_KEY_2: &str = "2cff4148a2f965801fb1f25f1d2a4e5df2f75b3a57cd06f30471c2c774419a41";
|
||||
const GROUP_PUBLIC_KEY: &str =
|
||||
"15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673";
|
||||
|
||||
fn build_pub_key_package() -> PublicKeyPackage {
|
||||
let id_1 = Identifier::try_from(1).unwrap();
|
||||
let id_2 = Identifier::try_from(2).unwrap();
|
||||
|
||||
let mut signer_pubkeys = HashMap::new();
|
||||
signer_pubkeys.insert(
|
||||
id_1,
|
||||
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY_1).unwrap()).unwrap(),
|
||||
);
|
||||
signer_pubkeys.insert(
|
||||
id_2,
|
||||
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY_2).unwrap()).unwrap(),
|
||||
);
|
||||
|
||||
let group_public = VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap();
|
||||
|
||||
PublicKeyPackage::new(signer_pubkeys, group_public)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_validate() {
|
||||
let id_1 = Identifier::try_from(1).unwrap();
|
||||
let id_2 = Identifier::try_from(2).unwrap();
|
||||
|
||||
let id_list = [id_1];
|
||||
let key_package = build_pub_key_package();
|
||||
|
||||
let validated = validate(id_2, key_package, &id_list);
|
||||
|
||||
assert!(validated.is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_validation_errors_for_missing_identifiers() {
|
||||
let id_1 = Identifier::try_from(1).unwrap();
|
||||
let id_2 = Identifier::try_from(2).unwrap();
|
||||
let id_3 = Identifier::try_from(3).unwrap();
|
||||
|
||||
let id_list = [id_1, id_2];
|
||||
let key_package = build_pub_key_package();
|
||||
|
||||
let validated = validate(id_3, key_package, &id_list);
|
||||
assert!(validated.is_err());
|
||||
assert!(validated == Err(Error::MalformedIdentifier))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_validation_errors_for_duplicate_identifiers() {
|
||||
let id_1 = Identifier::try_from(1).unwrap();
|
||||
let id_2 = Identifier::try_from(2).unwrap();
|
||||
|
||||
let id_list = [id_1, id_2];
|
||||
let key_package = build_pub_key_package();
|
||||
|
||||
let validated = validate(id_1, key_package, &id_list);
|
||||
assert!(validated.is_err());
|
||||
assert!(validated == Err(Error::DuplicatedIdentifier))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
use frost::{round1::SigningCommitments, Identifier, SigningPackage};
|
||||
|
||||
use frost_ed25519 as frost;
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
io::{BufRead, Write},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct CommitmentsConfig {
|
||||
pub message: Vec<u8>,
|
||||
pub signer_commitments: BTreeMap<Identifier, SigningCommitments>,
|
||||
}
|
||||
|
||||
pub fn step_2(
|
||||
input: &mut impl BufRead,
|
||||
logger: &mut dyn Write,
|
||||
participants: Vec<Identifier>,
|
||||
) -> Result<SigningPackage, Box<dyn std::error::Error>> {
|
||||
let signing_package = request_inputs_commitments(input, logger, participants)?;
|
||||
print_commitments(logger, &signing_package);
|
||||
Ok(signing_package)
|
||||
}
|
||||
|
||||
// Input required:
|
||||
// 1. message
|
||||
// 2. number of signers
|
||||
// 3. commitments for all signers
|
||||
fn request_inputs_commitments(
|
||||
input: &mut impl BufRead,
|
||||
logger: &mut dyn Write,
|
||||
participants: Vec<Identifier>,
|
||||
) -> Result<SigningPackage, Box<dyn std::error::Error>> {
|
||||
writeln!(logger, "The message to be signed (hex encoded)")?;
|
||||
|
||||
let mut msg = String::new();
|
||||
input.read_line(&mut msg)?;
|
||||
|
||||
let message = hex::decode(msg.trim())?;
|
||||
|
||||
let mut commitments_list: BTreeMap<Identifier, SigningCommitments> = BTreeMap::new();
|
||||
|
||||
for p in participants {
|
||||
writeln!(
|
||||
logger,
|
||||
"Please enter JSON encoded commitments for participant {:#?}:",
|
||||
p
|
||||
)?; // TODO: improve printing
|
||||
|
||||
let mut commitments_input = String::new();
|
||||
input.read_line(&mut commitments_input)?;
|
||||
let commitments = serde_json::from_str(&commitments_input)?;
|
||||
commitments_list.insert(p, commitments);
|
||||
}
|
||||
|
||||
let signing_package = SigningPackage::new(commitments_list, &message);
|
||||
|
||||
Ok(signing_package)
|
||||
}
|
||||
|
||||
fn print_commitments(logger: &mut dyn Write, signing_package: &SigningPackage) {
|
||||
writeln!(
|
||||
logger,
|
||||
"Signing Package: {}",
|
||||
serde_json::to_string(&signing_package).unwrap()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
use frost::{round2::SignatureShare, Identifier, Signature, SigningPackage};
|
||||
|
||||
use frost_ed25519 as frost;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{BufRead, Write},
|
||||
};
|
||||
|
||||
use crate::step_1::ParticipantsConfig;
|
||||
|
||||
pub fn step_3(
|
||||
input: &mut impl BufRead,
|
||||
logger: &mut dyn Write,
|
||||
participants: ParticipantsConfig,
|
||||
signing_package: SigningPackage,
|
||||
) {
|
||||
let group_signature =
|
||||
request_inputs_signature_shares(input, logger, participants, signing_package).unwrap();
|
||||
print_signature(logger, group_signature);
|
||||
}
|
||||
|
||||
// Input required:
|
||||
// 1. number of signers (TODO: maybe pass this in?)
|
||||
// 2. signatures for all signers
|
||||
fn request_inputs_signature_shares(
|
||||
input: &mut impl BufRead,
|
||||
logger: &mut dyn Write,
|
||||
participants: ParticipantsConfig,
|
||||
signing_package: SigningPackage,
|
||||
) -> Result<Signature, Box<dyn std::error::Error>> {
|
||||
let mut signatures_list: HashMap<Identifier, SignatureShare> = HashMap::new();
|
||||
|
||||
for p in participants.participants {
|
||||
writeln!(
|
||||
logger,
|
||||
"Please enter JSON encoded signatures for participant {:?}:",
|
||||
p
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut signature_input = String::new();
|
||||
input.read_line(&mut signature_input)?;
|
||||
let signatures = serde_json::from_str(&signature_input)?;
|
||||
signatures_list.insert(p, signatures);
|
||||
}
|
||||
|
||||
let group_signature = frost::aggregate(
|
||||
&signing_package,
|
||||
&signatures_list,
|
||||
&participants.pub_key_package,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(group_signature)
|
||||
}
|
||||
|
||||
fn print_signature(logger: &mut dyn Write, group_signature: Signature) {
|
||||
writeln!(
|
||||
logger,
|
||||
"Group signature: {}",
|
||||
serde_json::to_string(&group_signature).unwrap()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
mod steps;
|
|
@ -0,0 +1,107 @@
|
|||
// Test values from https://github.com/ZcashFoundation/frost/blob/main/frost-ed25519/tests/helpers/vectors.json
|
||||
|
||||
// // Input required:
|
||||
// // 1. public key package
|
||||
// // 2. number of signparticipantsers
|
||||
// // 3. identifiers for all signers
|
||||
// #[test]
|
||||
// fn check_step_1() {
|
||||
// }
|
||||
|
||||
// // Input required:
|
||||
// // 1. message
|
||||
// // 2. number of signers
|
||||
// // 3. commitments for all signers
|
||||
// #[test]
|
||||
// fn check_step_2() {
|
||||
// }
|
||||
|
||||
use crate::{step_1::ParticipantsConfig, step_3::step_3};
|
||||
use frost::{
|
||||
keys::{PublicKeyPackage, VerifyingShare},
|
||||
round1::{NonceCommitment, SigningCommitments},
|
||||
Identifier, SigningPackage, VerifyingKey,
|
||||
};
|
||||
use frost_ed25519 as frost;
|
||||
use hex::FromHex;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
io::BufWriter,
|
||||
};
|
||||
|
||||
// // Input required:
|
||||
// // 1. number of signers (TODO: maybe pass this in?)
|
||||
// // 2. signatures for all signers
|
||||
#[test]
|
||||
fn check_step_3() {
|
||||
let mut buf = BufWriter::new(Vec::new());
|
||||
|
||||
let id_1 = Identifier::try_from(1).unwrap();
|
||||
let id_3 = Identifier::try_from(3).unwrap();
|
||||
|
||||
const PUBLIC_KEY_1: &str = "fc2c9b8e335c132d9ebe0403c9317aac480bbbf8cbdb1bc3730bb68eb60dadf9";
|
||||
const PUBLIC_KEY_3: &str = "2cff4148a2f965801fb1f25f1d2a4e5df2f75b3a57cd06f30471c2c774419a41";
|
||||
const GROUP_PUBLIC_KEY: &str =
|
||||
"15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673";
|
||||
|
||||
let mut signer_pubkeys = HashMap::new();
|
||||
signer_pubkeys.insert(
|
||||
id_1,
|
||||
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY_1).unwrap()).unwrap(),
|
||||
);
|
||||
signer_pubkeys.insert(
|
||||
id_3,
|
||||
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY_3).unwrap()).unwrap(),
|
||||
);
|
||||
|
||||
let group_public = VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap();
|
||||
|
||||
let signature_1 = "{\"share\":\"b97409beff18861f0959530db091a64b812e3fefaa87e1e3d2c039f11d96cc09\",\"ciphersuite\":\"FROST(Ed25519, SHA-512)\"}";
|
||||
let signature_3 = "{\"share\":\"9816a14e7cdecfcb240976f564cf98c5640e596b6ddf270379efbef4e9f7db0b\",\"ciphersuite\":\"FROST(Ed25519, SHA-512)\"}";
|
||||
|
||||
let input = format!("{}\n{}\n", signature_1, signature_3);
|
||||
|
||||
let mut valid_input = input.as_bytes();
|
||||
|
||||
let participants_config = ParticipantsConfig {
|
||||
participants: vec![id_1, id_3],
|
||||
pub_key_package: PublicKeyPackage::new(signer_pubkeys, group_public),
|
||||
};
|
||||
const HIDING_COMMITMENT_1: &str =
|
||||
"5078f5c6d679654bb88a8887242d49cc21a553ed26caed4d52570c6656fb9b92";
|
||||
const BINDING_COMMITMENT_1: &str =
|
||||
"936b660d3008d8298b0a7220a327a0813ffedd9d07604bdc73d7cffef63c0da0";
|
||||
const HIDING_COMMITMENT_3: &str =
|
||||
"91c2469b501fe5af8493f9ae77c8f57999460af317f2d9f2d4378ae0e665860e";
|
||||
const BINDING_COMMITMENT_3: &str =
|
||||
"c225618accff2266a45d87dc3219b04c774ca26c8629c4fa483e7e87da820007";
|
||||
|
||||
let signer_commitments_1 = SigningCommitments::new(
|
||||
NonceCommitment::deserialize(<[u8; 32]>::from_hex(HIDING_COMMITMENT_1).unwrap()).unwrap(),
|
||||
NonceCommitment::deserialize(<[u8; 32]>::from_hex(BINDING_COMMITMENT_1).unwrap()).unwrap(),
|
||||
);
|
||||
let signer_commitments_3 = SigningCommitments::new(
|
||||
NonceCommitment::deserialize(<[u8; 32]>::from_hex(HIDING_COMMITMENT_3).unwrap()).unwrap(),
|
||||
NonceCommitment::deserialize(<[u8; 32]>::from_hex(BINDING_COMMITMENT_3).unwrap()).unwrap(),
|
||||
);
|
||||
|
||||
let mut signing_commitments = BTreeMap::new();
|
||||
signing_commitments.insert(id_1, signer_commitments_1);
|
||||
signing_commitments.insert(id_3, signer_commitments_3);
|
||||
|
||||
let signing_package = SigningPackage::new(signing_commitments, b"test");
|
||||
|
||||
step_3(
|
||||
&mut valid_input,
|
||||
&mut buf,
|
||||
participants_config,
|
||||
signing_package,
|
||||
);
|
||||
|
||||
let expected = "Please enter JSON encoded signatures for participant Identifier(\"0100000000000000000000000000000000000000000000000000000000000000\"):\nPlease enter JSON encoded signatures for participant Identifier(\"0300000000000000000000000000000000000000000000000000000000000000\"):\nGroup signature: \"72c948a63797c693e8e978fdb703a1f5a7590472a539da13b71dd6c2b8c1b2a664b7b4af6194439357c5d15f366760fce53c985a186709e74bb0f8e5078ea805\"\n";
|
||||
|
||||
let (_, res) = &buf.into_parts();
|
||||
let actual = hex::encode(res.as_ref().unwrap());
|
||||
|
||||
assert_eq!(hex::encode(expected), actual)
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
# Helper values for manual testing
|
||||
|
||||
Public key package:
|
||||
|
||||
```
|
||||
{"signer_pubkeys":{"0100000000000000000000000000000000000000000000000000000000000000":"fc2c9b8e335c132d9ebe0403c9317aac480bbbf8cbdb1bc3730bb68eb60dadf9", "0300000000000000000000000000000000000000000000000000000000000000":"2cff4148a2f965801fb1f25f1d2a4e5df2f75b3a57cd06f30471c2c774419a41", "0200000000000000000000000000000000000000000000000000000000000000":"f7c3031debffbaf121022409d057e6e1034a532636301d12e26beddff58d05c7"}, "group_public":"15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673", "ciphersuite":"FROST(Ed25519, SHA-512)"}
|
||||
```
|
||||
|
||||
num of participants:
|
||||
```
|
||||
2
|
||||
```
|
||||
|
||||
Identifier for participant 1:
|
||||
|
||||
```
|
||||
0100000000000000000000000000000000000000000000000000000000000000
|
||||
```
|
||||
|
||||
Identifier for participant 3:
|
||||
|
||||
```
|
||||
0300000000000000000000000000000000000000000000000000000000000000
|
||||
```
|
||||
|
||||
Message to be signed:
|
||||
|
||||
```
|
||||
74657374
|
||||
```
|
||||
|
||||
Commitment for Identifier 1:
|
||||
|
||||
```
|
||||
{"hiding": "5078f5c6d679654bb88a8887242d49cc21a553ed26caed4d52570c6656fb9b92", "binding": "936b660d3008d8298b0a7220a327a0813ffedd9d07604bdc73d7cffef63c0da0", "ciphersuite":"FROST(Ed25519, SHA-512)" }
|
||||
```
|
||||
|
||||
Commitment for Identifier 3:
|
||||
|
||||
```
|
||||
{"hiding":"91c2469b501fe5af8493f9ae77c8f57999460af317f2d9f2d4378ae0e665860e","binding":"c225618accff2266a45d87dc3219b04c774ca26c8629c4fa483e7e87da820007","ciphersuite":"FROST(Ed25519, SHA-512)"}
|
||||
```
|
||||
|
||||
Signing package (expected):
|
||||
|
||||
```
|
||||
{"signing_commitments":{"0100000000000000000000000000000000000000000000000000000000000000":{"hiding":"5078f5c6d679654bb88a8887242d49cc21a553ed26caed4d52570c6656fb9b92","binding":"936b660d3008d8298b0a7220a327a0813ffedd9d07604bdc73d7cffef63c0da0","ciphersuite":"FROST(Ed25519, SHA-512)"},"0300000000000000000000000000000000000000000000000000000000000000":{"hiding":"91c2469b501fe5af8493f9ae77c8f57999460af317f2d9f2d4378ae0e665860e","binding":"c225618accff2266a45d87dc3219b04c774ca26c8629c4fa483e7e87da820007","ciphersuite":"FROST(Ed25519, SHA-512)"}},"message":"74657374","ciphersuite":"FROST(Ed25519, SHA-512)"}
|
||||
```
|
||||
Signature share for ID 1:
|
||||
|
||||
```
|
||||
{"share":"b97409beff18861f0959530db091a64b812e3fefaa87e1e3d2c039f11d96cc09","ciphersuite":"FROST(Ed25519, SHA-512)"}
|
||||
```
|
||||
|
||||
Signature share for ID 2:
|
||||
|
||||
```
|
||||
{"share":"9816a14e7cdecfcb240976f564cf98c5640e596b6ddf270379efbef4e9f7db0b","ciphersuite":"FROST(Ed25519, SHA-512)"}
|
||||
```
|
||||
|
||||
Group signature share (expected):
|
||||
|
||||
```
|
||||
"72c948a63797c693e8e978fdb703a1f5a7590472a539da13b71dd6c2b8c1b2a664b7b4af6194439357c5d15f366760fce53c985a186709e74bb0f8e5078ea805"
|
||||
```
|
|
@ -0,0 +1,2 @@
|
|||
#[cfg(test)]
|
||||
mod integration_tests;
|
|
@ -0,0 +1,96 @@
|
|||
// use frost::keys::{IdentifierList, KeyPackage, SecretShare};
|
||||
// use frost::round1::{SigningCommitments, SigningNonces};
|
||||
// use frost::round2::SignatureShare;
|
||||
// use frost::{Identifier, SigningPackage};
|
||||
// use frost_ed25519 as frost;
|
||||
|
||||
// use rand::rngs::ThreadRng;
|
||||
// use rand::thread_rng;
|
||||
// use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
// // #[test]
|
||||
// fn check_keygen_with_dealer() {
|
||||
// let mut rng = thread_rng();
|
||||
// let (shares, pubkeys) =
|
||||
// frost::keys::generate_with_dealer(3, 2, IdentifierList::Default, &mut rng).unwrap();
|
||||
// let key_packages = key_package(&shares);
|
||||
// let (nonces, commitments) = round_1(2, &mut rng, &key_packages);
|
||||
// let message = "i am a message".as_bytes();
|
||||
|
||||
// let signing_packages = step_2()
|
||||
|
||||
// let signature_shares = round_2(nonces, &key_packages, signing_packages);
|
||||
|
||||
// let signing_packages = step_2()
|
||||
|
||||
// // Coordinator
|
||||
|
||||
// let config = Config {
|
||||
// message,
|
||||
// signing_package,
|
||||
// signature_shares,
|
||||
// pubkeys,
|
||||
// };
|
||||
|
||||
// // let group_signature = aggregate_and_verify(config);
|
||||
|
||||
// // let expected = aggregate(
|
||||
// // config.signing_package,
|
||||
// // config.signature_shares,
|
||||
// // config.pubkeys,
|
||||
// // )
|
||||
// // .unwrap();
|
||||
|
||||
// // assert!(group_signature.is_ok());
|
||||
// // assert!(group_signature == expected)
|
||||
// }
|
||||
|
||||
// fn key_package(shares: &HashMap<Identifier, SecretShare>) -> HashMap<Identifier, KeyPackage> {
|
||||
// let mut key_packages: HashMap<_, _> = HashMap::new();
|
||||
|
||||
// for (identifier, secret_share) in shares {
|
||||
// let key_package = frost::keys::KeyPackage::try_from(secret_share.clone()).unwrap();
|
||||
// key_packages.insert(*identifier, key_package);
|
||||
// }
|
||||
|
||||
// key_packages
|
||||
// }
|
||||
|
||||
// fn round_1(
|
||||
// min_signers: u16,
|
||||
// mut rng: &mut ThreadRng,
|
||||
// key_packages: &HashMap<Identifier, KeyPackage>,
|
||||
// ) -> (
|
||||
// HashMap<Identifier, SigningNonces>,
|
||||
// BTreeMap<Identifier, SigningCommitments>,
|
||||
// ) {
|
||||
// // Participant Round 1
|
||||
|
||||
// let mut nonces_map = HashMap::new();
|
||||
// let mut commitments_map = BTreeMap::new();
|
||||
|
||||
// for participant_index in 1..(min_signers + 1) {
|
||||
// let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
// let key_package = &key_packages[&participant_identifier];
|
||||
// let (nonces, commitments) = frost::round1::commit(key_package.secret_share(), &mut rng);
|
||||
// nonces_map.insert(participant_identifier, nonces);
|
||||
// commitments_map.insert(participant_identifier, commitments);
|
||||
// }
|
||||
// (nonces_map, commitments_map)
|
||||
// }
|
||||
|
||||
// fn round_2(
|
||||
// nonces_map: HashMap<Identifier, SigningNonces>,
|
||||
// key_packages: &HashMap<Identifier, KeyPackage>,
|
||||
// signing_package: SigningPackage,
|
||||
// ) -> HashMap<Identifier, SignatureShare> {
|
||||
// let mut signature_shares = HashMap::new();
|
||||
// for participant_identifier in nonces_map.keys() {
|
||||
// let key_package = &key_packages[participant_identifier];
|
||||
|
||||
// let nonces = &nonces_map[participant_identifier];
|
||||
// let signature_share = frost::round2::sign(&signing_package, nonces, key_package).unwrap();
|
||||
// signature_shares.insert(*participant_identifier, signature_share);
|
||||
// }
|
||||
// signature_shares
|
||||
// }
|
Loading…
Reference in New Issue