Add initial implementation of socket communications to the demo (phase 1) - Coordinator (#89)

* move commitments to step_1
* halfway through making things async; need to fix handling input/output
* async step_1
* async step_3
* started SocketComms
* SocketComms compiling
* Finished SocketComms; untested
* fixed existing tests
* update frost-rerandomized; skip tests if redpallas enabled
* ci: use nightly, and overall cleanup
* point frost to 1.0.0-rc.0
This commit is contained in:
Conrado Gouvea 2023-11-21 11:56:29 -03:00 committed by GitHub
parent 3cd81382e5
commit 0a9e830fa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1170 additions and 876 deletions

View File

@ -27,11 +27,8 @@ jobs:
with:
persist-credentials: false
- uses: actions-rs/toolchain@v1.0.7
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: stable
override: true
profile: minimal
components: llvm-tools-preview
- name: Install cargo-llvm-cov cargo command

View File

@ -10,14 +10,26 @@ on:
jobs:
build_redpallas:
name: Build with redpallas
# We're using nightly for the async traits.
# TODO: Revert back to stable when that is stabilized.
test_ed25519:
name: Test with ed25519
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
- uses: dtolnay/rust-toolchain@stable
- run: cargo build --features redpallas
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test
test_redpallas:
name: Test with redpallas
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.0
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test --features redpallas
clippy:
name: Clippy
@ -27,34 +39,10 @@ jobs:
- uses: actions/checkout@v4.1.1
with:
persist-credentials: false
- uses: actions-rs/toolchain@v1.0.7
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: stable
override: true
- name: Check workflow permissions
id: check_permissions
uses: scherermichael-oss/action-has-permission@1.0.6
with:
required-permission: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run clippy action to produce annotations
uses: actions-rs/clippy-check@v1.0.7
if: ${{ steps.check_permissions.outputs.has-permission }}
with:
# GitHub displays the clippy job and its results as separate entries
name: Clippy (stable) Results
token: ${{ secrets.GITHUB_TOKEN }}
# Notet that we don't use --all-features because we go against Rust
# convention and have a non-additive "redpallas" feature, and the
# tests only work without it currently.
args: --all-targets -- -D warnings
components: rustfmt, clippy
- name: Run clippy manually without annotations
if: ${{ !steps.check_permissions.outputs.has-permission }}
run: cargo clippy --all-targets -- -D warnings
fmt:
@ -65,19 +53,12 @@ jobs:
- uses: actions/checkout@v4.1.1
with:
persist-credentials: false
- uses: actions-rs/toolchain@v1.0.7
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: stable
components: rustfmt
override: true
- uses: Swatinem/rust-cache@v2
- uses: actions-rs/cargo@v1.0.3
with:
command: fmt
args: --all -- --check
- name: Run rustfmt
run: cargo fmt --all -- --check
actionlint:
runs-on: ubuntu-latest

1166
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,17 @@ edition = "2021"
[dependencies]
eyre = "0.6.9"
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "5ea1293bb8df90c8a19dedf7ab5510903650dda9", features = ["frost"] }
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "6e55be4da9fa7beec9f744d49a1bad844318a932", features = ["frost", "serde"] }
hex = { version = "0.4", features = ["serde"] }
thiserror = "1.0"
rand = "0.8"
serde_json = "1.0"
itertools = "0.12.0"
exitcode = "1.1.2"
clap = { version = "4.4.7", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
message-io = "0.18"
[features]
redpallas = []

19
coordinator/src/args.rs Normal file
View File

@ -0,0 +1,19 @@
use clap::Parser;
#[derive(Parser, Debug, Default)]
#[command(author, version, about, long_about = None)]
pub struct Args {
/// Public key package to use. Can be a file with a JSON-encoded
/// package, or "-". If the file does not exist or if "-" is specified,
/// then it will be read from standard input.
#[arg(short = 'P', long, default_value = "public-key-package.json")]
pub public_key_package: String,
/// IP to bind to, if using online comms
#[arg(short, long, default_value = "0.0.0.0")]
pub ip: String,
/// Port to bind to, if using online comms
#[arg(short, long, default_value_t = 2744)]
pub port: u16,
}

View File

@ -1,5 +1,7 @@
use std::io::{BufRead, Write};
use crate::args::Args;
use crate::comms::cli::CLIComms;
use crate::step_1::step_1;
use crate::step_2::step_2;
use crate::step_3::step_3;
@ -7,20 +9,23 @@ use crate::step_3::step_3;
#[cfg(feature = "redpallas")]
use crate::step_3::request_randomizer;
pub fn cli(
pub async fn cli(
args: &Args,
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)?;
let mut comms = CLIComms {};
let participants_config = step_1(args, &mut comms, reader, logger).await?;
writeln!(
logger,
"=== STEP 2: CHOOSE MESSAGE AND GENERATE COMMITMENT PACKAGE ===\n"
)?;
let signing_package = step_2(reader, logger, participants_config.participants.clone())?;
let signing_package = step_2(reader, logger, participants_config.commitments.clone())?;
#[cfg(feature = "redpallas")]
let randomizer = request_randomizer(reader, logger)?;
@ -28,13 +33,15 @@ pub fn cli(
writeln!(logger, "=== STEP 3: BUILD GROUP SIGNATURE ===\n")?;
step_3(
&mut comms,
reader,
logger,
participants_config,
signing_package,
&signing_package,
#[cfg(feature = "redpallas")]
randomizer,
);
)
.await?;
writeln!(logger, "=== END ===")?;

52
coordinator/src/comms.rs Normal file
View File

@ -0,0 +1,52 @@
pub mod cli;
pub mod socket;
#[cfg(not(feature = "redpallas"))]
use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use frost::{
keys::PublicKeyPackage,
round1::SigningCommitments,
round2::SignatureShare,
serde::{self, Deserialize, Serialize},
Identifier, SigningPackage,
};
use std::{
collections::BTreeMap,
error::Error,
io::{BufRead, Write},
};
#[derive(Serialize, Deserialize)]
#[serde(crate = "self::serde")]
#[allow(clippy::large_enum_variant)]
pub enum Message {
IdentifiedCommitments {
identifier: Identifier,
commitments: SigningCommitments,
},
SigningPackage(SigningPackage),
SignatureShare(SignatureShare),
}
#[allow(async_fn_in_trait)]
pub trait Comms {
async fn get_signing_commitments(
&mut self,
input: &mut dyn BufRead,
output: &mut dyn Write,
pub_key_package: &PublicKeyPackage,
num_of_participants: u16,
) -> Result<BTreeMap<Identifier, SigningCommitments>, Box<dyn Error>>;
async fn get_signature_shares(
&mut self,
input: &mut dyn BufRead,
output: &mut dyn Write,
signing_package: &SigningPackage,
#[cfg(feature = "redpallas")] randomizer: frost::round2::Randomizer,
) -> Result<BTreeMap<Identifier, SignatureShare>, Box<dyn Error>>;
}

View File

@ -0,0 +1,101 @@
//! Command line interface implementation of the Comms trait.
#[cfg(not(feature = "redpallas"))]
use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use eyre::eyre;
use frost::{
keys::PublicKeyPackage, round1::SigningCommitments, round2::SignatureShare, Identifier,
SigningPackage,
};
use std::{
collections::BTreeMap,
error::Error,
io::{BufRead, Write},
};
use super::Comms;
pub struct CLIComms {}
impl Comms for CLIComms {
async fn get_signing_commitments(
&mut self,
input: &mut dyn BufRead,
output: &mut dyn Write,
pub_key_package: &PublicKeyPackage,
num_of_participants: u16,
) -> Result<BTreeMap<Identifier, SigningCommitments>, Box<dyn Error>> {
let mut participants_list = Vec::new();
let mut commitments_list: BTreeMap<Identifier, SigningCommitments> = BTreeMap::new();
for i in 1..=num_of_participants {
writeln!(output, "Identifier for participant {:?} (hex encoded): ", i)?;
let id_value = read_identifier(input)?;
validate(id_value, pub_key_package, &participants_list)?;
participants_list.push(id_value);
writeln!(
output,
"Please enter JSON encoded commitments for participant {}:",
hex::encode(id_value.serialize())
)?;
let mut commitments_input = String::new();
input.read_line(&mut commitments_input)?;
let commitments = serde_json::from_str(&commitments_input)?;
commitments_list.insert(id_value, commitments);
}
Ok(commitments_list)
}
async fn get_signature_shares(
&mut self,
input: &mut dyn BufRead,
output: &mut dyn Write,
signing_package: &SigningPackage,
#[cfg(feature = "redpallas")] _randomizer: frost::round2::Randomizer,
) -> Result<BTreeMap<Identifier, SignatureShare>, Box<dyn Error>> {
let mut signatures_list: BTreeMap<Identifier, SignatureShare> = BTreeMap::new();
for p in signing_package.signing_commitments().keys() {
writeln!(
output,
"Please enter JSON encoded signature shares for participant {}:",
hex::encode(p.serialize())
)?;
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);
}
Ok(signatures_list)
}
}
pub fn read_identifier(input: &mut dyn BufRead) -> Result<Identifier, Box<dyn 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)
}
pub fn validate(
id: Identifier,
key_package: &PublicKeyPackage,
id_list: &[Identifier],
) -> Result<(), frost::Error> {
if !key_package.verifying_shares().contains_key(&id) {
return Err(frost::Error::MalformedIdentifier);
}; // TODO: Error is actually that the identifier does not exist
if id_list.contains(&id) {
return Err(frost::Error::DuplicatedIdentifier);
};
Ok(())
}

View File

@ -0,0 +1,142 @@
//! Socket implementation of the Comms trait, using message-io.
#[cfg(not(feature = "redpallas"))]
use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use eyre::eyre;
use message_io::{
network::{Endpoint, NetEvent, Transport},
node::{self, NodeHandler, NodeListener},
};
use tokio::sync::mpsc::{self, Receiver, Sender};
use frost::{
keys::PublicKeyPackage, round1::SigningCommitments, round2::SignatureShare, Identifier,
SigningPackage,
};
use std::{
collections::BTreeMap,
error::Error,
io::{BufRead, Write},
};
use super::{Comms, Message};
use crate::args::Args;
pub struct SocketComms {
input_rx: Receiver<(Endpoint, Vec<u8>)>,
endpoints: BTreeMap<Identifier, Endpoint>,
handler: NodeHandler<()>,
}
impl SocketComms {
pub fn new(args: &Args) -> Self {
let (handler, listener) = node::split::<()>();
let addr = format!("{}:{}", args.ip, args.port);
let (tx, rx) = mpsc::channel(100);
handler
.network()
.listen(Transport::FramedTcp, addr)
.unwrap();
let socket_comm = Self {
input_rx: rx,
endpoints: BTreeMap::new(),
handler,
};
// TODO: save handle
let _handle = tokio::spawn(async move { Self::run(listener, tx) });
socket_comm
}
fn run(listener: NodeListener<()>, input_tx: Sender<(Endpoint, Vec<u8>)>) {
// Read incoming network events.
listener.for_each(|event| match event.network() {
NetEvent::Connected(_, _) => unreachable!(), // Used for explicit connections.
NetEvent::Accepted(_endpoint, _listener) => println!("Client connected"), // Tcp or Ws
NetEvent::Message(endpoint, data) => {
println!("Received: {}", String::from_utf8_lossy(data));
// TODO: handle error
let _ = input_tx.try_send((endpoint, data.to_vec()));
}
NetEvent::Disconnected(_endpoint) => println!("Client disconnected"), //Tcp or Ws
});
}
}
impl Comms for SocketComms {
async fn get_signing_commitments(
&mut self,
_input: &mut dyn BufRead,
_output: &mut dyn Write,
_pub_key_package: &PublicKeyPackage,
num_of_participants: u16,
) -> Result<BTreeMap<Identifier, SigningCommitments>, Box<dyn Error>> {
self.endpoints = BTreeMap::new();
let mut signing_commitments = BTreeMap::new();
for _ in 0..num_of_participants {
let (endpoint, data) = self
.input_rx
.recv()
.await
.ok_or(eyre!("Did not receive all commitments"))?;
let message: Message = serde_json::from_slice(&data)?;
if let Message::IdentifiedCommitments {
identifier,
commitments,
} = message
{
self.endpoints.insert(identifier, endpoint);
signing_commitments.insert(identifier, commitments);
} else {
Err(eyre!("Expected IdentifiedCommitments message"))?;
}
}
Ok(signing_commitments)
}
async fn get_signature_shares(
&mut self,
_input: &mut dyn BufRead,
_output: &mut dyn Write,
signing_package: &SigningPackage,
#[cfg(feature = "redpallas")] _randomizer: frost::round2::Randomizer,
) -> Result<BTreeMap<Identifier, SignatureShare>, Box<dyn Error>> {
// Send SigningPackage to all participants
let data = serde_json::to_vec(&Message::SigningPackage(signing_package.clone()))?;
for identifier in signing_package.signing_commitments().keys() {
let endpoint = self
.endpoints
.get(identifier)
.ok_or(eyre!("unknown identifier"))?;
self.handler.network().send(*endpoint, &data);
}
// Read SignatureShare from all participants
let mut signature_shares = BTreeMap::new();
for _ in 0..signing_package.signing_commitments().len() {
let (endpoint, data) = self
.input_rx
.recv()
.await
.ok_or(eyre!("Did not receive all commitments"))?;
let message: Message = serde_json::from_slice(&data)?;
if let Message::SignatureShare(signature_share) = message {
let identifier = self
.endpoints
.iter()
.find_map(|(i, e)| if *e == endpoint { Some(i) } else { None })
.ok_or(eyre!("Unknown participant"))?;
signature_shares.insert(*identifier, signature_share);
} else {
Err(eyre!("Expected IdentifiedCommitments message"))?;
}
}
Ok(signature_shares)
}
}

46
coordinator/src/input.rs Normal file
View File

@ -0,0 +1,46 @@
use std::{
error::Error,
fs,
io::{BufRead, Write},
path::Path,
};
/// Read the contents of a file or from a stdin.
/// If `object_name` is "-" or a file that does not exist, then it reads from
/// stdin.
/// `object_name` is used for printing prompts and it should describe what
/// is being read.
pub fn read_from_file_or_stdin(
input: &mut dyn BufRead,
output: &mut dyn Write,
object_name: &str,
file_path: &str,
) -> Result<String, Box<dyn Error>> {
let file_path = {
if file_path == "-" {
None
} else {
let p = Path::new(&file_path);
if p.exists() {
writeln!(output, "Reading {} from {}", object_name, file_path)?;
Some(p)
} else {
writeln!(
output,
"File not found: {}\nWill read from stdin",
file_path
)?;
None
}
}
};
match file_path {
Some(file_path) => Ok(fs::read_to_string(file_path)?),
None => {
writeln!(output, "Paste the {}: ", object_name)?;
let mut key_package = String::new();
input.read_line(&mut key_package)?;
Ok(key_package)
}
}
}

View File

@ -1,5 +1,8 @@
pub mod args;
pub mod cli;
pub mod comms;
pub mod input;
pub mod step_1;
pub mod step_2;
pub mod step_3;

View File

@ -1,11 +1,16 @@
use std::io;
use coordinator::cli::cli;
use clap::Parser;
use coordinator::{args::Args, cli::cli};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
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)?;
cli(&args, &mut reader, &mut logger).await?;
Ok(())
}

View File

@ -3,100 +3,83 @@ use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use frost::{keys::PublicKeyPackage, Error, Identifier};
use frost::{keys::PublicKeyPackage, round1::SigningCommitments, Identifier};
use eyre::eyre;
use std::io::{BufRead, Write};
use std::{
collections::BTreeMap,
io::{BufRead, Write},
};
use crate::{args::Args, comms::Comms, input::read_from_file_or_stdin};
#[derive(PartialEq, Debug)]
pub struct ParticipantsConfig {
pub participants: Vec<Identifier>,
pub commitments: BTreeMap<Identifier, SigningCommitments>,
pub pub_key_package: PublicKeyPackage,
}
// TODO: needs to include the coordinator's keys!
pub fn step_1(
reader: &mut impl BufRead,
pub async fn step_1(
args: &Args,
comms: &mut impl Comms,
reader: &mut dyn BufRead,
logger: &mut dyn Write,
) -> Result<ParticipantsConfig, Error> {
let participants = choose_participants(reader, logger)?;
print_participants(logger, &participants.participants);
) -> Result<ParticipantsConfig, Box<dyn std::error::Error>> {
let participants = read_commitments(args, comms, reader, logger).await?;
print_participants(logger, &participants.commitments);
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,
async fn read_commitments(
args: &Args,
comms: &mut impl Comms,
input: &mut dyn 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();
) -> Result<ParticipantsConfig, Box<dyn std::error::Error>> {
let pub_key_package = read_from_file_or_stdin(
input,
logger,
"public key package",
&args.public_key_package,
)?;
let pub_key_package: PublicKeyPackage = serde_json::from_str(&pub_key_package)?;
writeln!(logger, "The number of participants: ").unwrap();
writeln!(logger, "The number of participants: ")?;
let mut participants = String::new();
input.read_line(&mut participants).unwrap();
let num_of_participants = participants.trim().parse::<u16>().unwrap();
input.read_line(&mut participants)?;
let num_of_participants = participants.trim().parse::<u16>()?;
let mut participants_list = Vec::new();
let commitments_list = comms
.get_signing_commitments(input, logger, &pub_key_package, num_of_participants)
.await?;
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_list)?;
participants_list.push(id_value)
}
Ok(ParticipantsConfig {
participants: participants_list,
commitments: commitments_list,
pub_key_package,
})
}
pub fn print_participants(logger: &mut dyn Write, participants: &Vec<Identifier>) {
pub fn print_participants(
logger: &mut dyn Write,
participants: &BTreeMap<Identifier, SigningCommitments>,
) {
writeln!(logger, "Selected participants: ",).unwrap();
for p in participants {
for p in participants.keys() {
writeln!(logger, "{}", serde_json::to_string(p).unwrap()).unwrap();
}
}
#[cfg(test)]
#[cfg(all(test, not(feature = "redpallas")))]
mod tests {
use std::collections::HashMap;
use std::collections::BTreeMap;
use frost::{
keys::{PublicKeyPackage, VerifyingShare},
@ -105,7 +88,7 @@ mod tests {
use frost_ed25519 as frost;
use hex::FromHex;
use crate::step_1::validate;
use crate::comms::cli::validate;
const PUBLIC_KEY_1: &str = "fc2c9b8e335c132d9ebe0403c9317aac480bbbf8cbdb1bc3730bb68eb60dadf9";
const PUBLIC_KEY_2: &str = "2cff4148a2f965801fb1f25f1d2a4e5df2f75b3a57cd06f30471c2c774419a41";
@ -116,7 +99,7 @@ mod tests {
let id_1 = Identifier::try_from(1).unwrap();
let id_2 = Identifier::try_from(2).unwrap();
let mut signer_pubkeys = HashMap::new();
let mut signer_pubkeys = BTreeMap::new();
signer_pubkeys.insert(
id_1,
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY_1).unwrap()).unwrap(),
@ -126,7 +109,8 @@ mod tests {
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY_2).unwrap()).unwrap(),
);
let group_public = VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap();
let group_public =
VerifyingKey::deserialize(<[u8; 32]>::from_hex(GROUP_PUBLIC_KEY).unwrap()).unwrap();
PublicKeyPackage::new(signer_pubkeys, group_public)
}
@ -139,7 +123,7 @@ mod tests {
let id_list = [id_1];
let key_package = build_pub_key_package();
let validated = validate(id_2, key_package, &id_list);
let validated = validate(id_2, &key_package, &id_list);
assert!(validated.is_ok())
}
@ -153,7 +137,7 @@ mod tests {
let id_list = [id_1, id_2];
let key_package = build_pub_key_package();
let validated = validate(id_3, key_package, &id_list);
let validated = validate(id_3, &key_package, &id_list);
assert!(validated.is_err());
assert!(validated == Err(Error::MalformedIdentifier))
}
@ -166,7 +150,7 @@ mod tests {
let id_list = [id_1, id_2];
let key_package = build_pub_key_package();
let validated = validate(id_1, key_package, &id_list);
let validated = validate(id_1, &key_package, &id_list);
assert!(validated.is_err());
assert!(validated == Err(Error::DuplicatedIdentifier))
}

View File

@ -19,21 +19,19 @@ pub struct CommitmentsConfig {
pub fn step_2(
input: &mut impl BufRead,
logger: &mut dyn Write,
participants: Vec<Identifier>,
commitments: BTreeMap<Identifier, SigningCommitments>,
) -> Result<SigningPackage, Box<dyn std::error::Error>> {
let signing_package = request_inputs_commitments(input, logger, participants)?;
print_commitments(logger, &signing_package);
let signing_package = request_message(input, logger, commitments)?;
print_signing_package(logger, &signing_package);
Ok(signing_package)
}
// Input required:
// 1. message
// 2. number of signers
// 3. commitments for all signers
fn request_inputs_commitments(
fn request_message(
input: &mut impl BufRead,
logger: &mut dyn Write,
participants: Vec<Identifier>,
commitments: BTreeMap<Identifier, SigningCommitments>,
) -> Result<SigningPackage, Box<dyn std::error::Error>> {
writeln!(logger, "The message to be signed (hex encoded)")?;
@ -42,27 +40,12 @@ fn request_inputs_commitments(
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 {}:",
hex::encode(p.serialize())
)?; // 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);
let signing_package = SigningPackage::new(commitments, &message);
Ok(signing_package)
}
fn print_commitments(logger: &mut dyn Write, signing_package: &SigningPackage) {
fn print_signing_package(logger: &mut dyn Write, signing_package: &SigningPackage) {
writeln!(
logger,
"Signing Package:\n{}",

View File

@ -3,14 +3,11 @@ use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use frost::{round2::SignatureShare, Identifier, Signature, SigningPackage};
use frost::{Signature, SigningPackage};
use std::{
collections::HashMap,
io::{BufRead, Write},
};
use std::io::{BufRead, Write};
use crate::step_1::ParticipantsConfig;
use crate::{comms::Comms, step_1::ParticipantsConfig};
#[cfg(feature = "redpallas")]
pub fn request_randomizer(
@ -29,14 +26,16 @@ pub fn request_randomizer(
)?)
}
pub fn step_3(
input: &mut impl BufRead,
pub async fn step_3(
comms: &mut impl Comms,
input: &mut dyn BufRead,
logger: &mut dyn Write,
participants: ParticipantsConfig,
signing_package: SigningPackage,
signing_package: &SigningPackage,
#[cfg(feature = "redpallas")] randomizer: frost::round2::Randomizer,
) -> Signature {
) -> Result<Signature, Box<dyn std::error::Error>> {
let group_signature = request_inputs_signature_shares(
comms,
input,
logger,
participants,
@ -44,45 +43,40 @@ pub fn step_3(
#[cfg(feature = "redpallas")]
randomizer,
)
.unwrap();
.await?;
print_signature(logger, group_signature);
group_signature
Ok(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,
async fn request_inputs_signature_shares(
comms: &mut impl Comms,
input: &mut dyn BufRead,
logger: &mut dyn Write,
participants: ParticipantsConfig,
signing_package: SigningPackage,
signing_package: &SigningPackage,
#[cfg(feature = "redpallas")] randomizer: frost::round2::Randomizer,
) -> Result<Signature, Box<dyn std::error::Error>> {
let mut signatures_list: HashMap<Identifier, SignatureShare> = HashMap::new();
for p in participants.participants {
writeln!(
let signatures_list = comms
.get_signature_shares(
input,
logger,
"Please enter JSON encoded signature shares for participant {}:",
hex::encode(p.serialize())
signing_package,
#[cfg(feature = "redpallas")]
randomizer,
)
.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);
}
.await?;
#[cfg(feature = "redpallas")]
let randomizer_params = frost::RandomizedParams::from_randomizer(
participants.pub_key_package.group_public(),
participants.pub_key_package.verifying_key(),
randomizer,
);
let group_signature = frost::aggregate(
&signing_package,
signing_package,
&signatures_list,
&participants.pub_key_package,
#[cfg(feature = "redpallas")]

View File

@ -7,8 +7,8 @@ edition = "2021"
[dependencies]
eyre = "0.6.9"
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "5ea1293bb8df90c8a19dedf7ab5510903650dda9", features = ["frost"] }
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "6e55be4da9fa7beec9f744d49a1bad844318a932", features = ["frost"] }
hex = { version = "0.4", features = ["serde"] }
thiserror = "1.0"
rand = "0.8"

View File

@ -5,10 +5,8 @@ use reddsa::frost::redpallas as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas::keys::PositiveY;
use frost::keys::dkg::{round1, round2};
use frost::Identifier;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::io::{BufRead, Write};
use crate::inputs::{read_round1_package, read_round2_package, request_inputs};
@ -44,7 +42,7 @@ pub fn cli(
"Input Round 1 Packages from the other {} participants.\n",
config.max_signers - 1,
)?;
let mut received_round1_packages: HashMap<Identifier, round1::Package> = HashMap::new();
let mut received_round1_packages = BTreeMap::new();
for _ in 0..config.max_signers - 1 {
let (identifier, round1_package) = read_round1_package(reader, logger)?;
received_round1_packages.insert(identifier, round1_package);
@ -73,7 +71,7 @@ pub fn cli(
"Input Round 2 Packages from the other {} participants.\n",
config.max_signers - 1,
)?;
let mut received_round2_packages: HashMap<Identifier, round2::Package> = HashMap::new();
let mut received_round2_packages = BTreeMap::new();
for _ in 0..config.max_signers - 1 {
let (identifier, round2_package) = read_round2_package(reader, logger)?;
received_round2_packages.insert(identifier, round2_package);

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "redpallas"))]
use std::io::BufWriter;
use crate::inputs::{request_inputs, Config};

View File

@ -1,11 +1,16 @@
#[cfg(not(feature = "redpallas"))]
use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use dkg::cli::cli;
use std::collections::HashMap;
use std::io::{BufRead, Write};
use std::thread;
use frost_ed25519::keys::{KeyPackage, PublicKeyPackage};
use frost_ed25519::Identifier;
use frost::keys::{KeyPackage, PublicKeyPackage};
use frost::Identifier;
// Read a single line from the given reader.
fn read_line(mut reader: impl BufRead) -> Result<String, std::io::Error> {

View File

@ -6,10 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "5ea1293bb8df90c8a19dedf7ab5510903650dda9", features = ["frost"] }
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "6e55be4da9fa7beec9f744d49a1bad844318a932", features = ["frost"] }
hex = "0.4"
rand = "0.8"
eyre = "0.6.8"
exitcode = "1.1.2"
serde_json = "1.0"

View File

@ -20,7 +20,7 @@ pub fn cli(
writeln!(logger, "Key Package succesfully created.")?;
let mut rng = thread_rng();
let (nonces, commitments) = frost::round1::commit(key_package.secret_share(), &mut rng);
let (nonces, commitments) = frost::round1::commit(key_package.signing_share(), &mut rng);
print_values(commitments, logger)?;
@ -36,7 +36,7 @@ pub fn cli(
let group_signature = request_signature(input, logger)?;
key_package
.group_public()
.verifying_key()
.verify(config_message.signing_package.message(), &group_signature)?;
writeln!(logger, "Group Signature verified.")?;

View File

@ -2,7 +2,7 @@ mod cli;
mod round1;
mod round2;
#[cfg(test)]
#[cfg(all(test, not(feature = "redpallas")))]
mod tests;
use cli::cli;

View File

@ -2,11 +2,14 @@ use std::io::BufWriter;
use crate::cli::cli;
#[test]
// TODO: to restore this test, we need to intercept that generated commitments
// to put them inside the SigningPackage
// #[test]
#[allow(unused)]
fn check_cli() {
let key_package = r#"{"identifier":"0100000000000000000000000000000000000000000000000000000000000000","value":"ee4a66fec3ced53cac04b0abc309bb57f03f8d7dede033e4ae7b6ef57630120f","commitment":["21446705fa7da298998a567a3c2fdd7274903a886dcde9a77f615d915feb6764","56ce223ffbde8ce5971be587cbb0b8b31aa2bc220a6803b9ce73c63f9f432514","6dcc10da9443ef2c9bbd5fc6a9c3bcd4c5ede8048cc0b1342b091fd1ff6dc53c"],"ciphersuite":"FROST(Ed25519, SHA-512)"}"#;
let key_package = r#"{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"identifier":"0100000000000000000000000000000000000000000000000000000000000000","signing_share":"ee4a66fec3ced53cac04b0abc309bb57f03f8d7dede033e4ae7b6ef57630120f","commitment":["21446705fa7da298998a567a3c2fdd7274903a886dcde9a77f615d915feb6764","56ce223ffbde8ce5971be587cbb0b8b31aa2bc220a6803b9ce73c63f9f432514","6dcc10da9443ef2c9bbd5fc6a9c3bcd4c5ede8048cc0b1342b091fd1ff6dc53c"]}"#;
let signing_package = r#"{"signing_commitments":{"0100000000000000000000000000000000000000000000000000000000000000":{"hiding":"710a280fcedbcbe626fff055f682e4a525c31f157dd6071ef2c04ea0ecbe8de9","binding":"6dc707cdf26a589b3e2de4f6bae09b94d5d3bb939937b52bc6b16bdecd0b041f","ciphersuite":"FROST(Ed25519, SHA-512)"},"0200000000000000000000000000000000000000000000000000000000000000":{"hiding":"777f011bf695e27ce62474747a9c110cc3b827268047913a21030c3eba0e1eed","binding":"67f051035284cd619f0e7fc583eb3cb0c88d993aad621c856edc0f995f4588b2","ciphersuite":"FROST(Ed25519, SHA-512)"},"0300000000000000000000000000000000000000000000000000000000000000":{"hiding":"c052599bb7a52911b6b58e7c20747f12d45d23aab4aec98aaecdc7909dc6aff3","binding":"b3fbefc67070b1b56203ef875a2c7caf24802dbc943bdc62decac33287b63b23","ciphersuite":"FROST(Ed25519, SHA-512)"}},"message":"74657374","ciphersuite":"FROST(Ed25519, SHA-512)"}"#;
let signing_package = r#"{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"signing_commitments":{"0100000000000000000000000000000000000000000000000000000000000000":{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"hiding":"710a280fcedbcbe626fff055f682e4a525c31f157dd6071ef2c04ea0ecbe8de9","binding":"6dc707cdf26a589b3e2de4f6bae09b94d5d3bb939937b52bc6b16bdecd0b041f"},"0200000000000000000000000000000000000000000000000000000000000000":{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"hiding":"777f011bf695e27ce62474747a9c110cc3b827268047913a21030c3eba0e1eed","binding":"67f051035284cd619f0e7fc583eb3cb0c88d993aad621c856edc0f995f4588b2"},"0300000000000000000000000000000000000000000000000000000000000000":{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"hiding":"c052599bb7a52911b6b58e7c20747f12d45d23aab4aec98aaecdc7909dc6aff3","binding":"b3fbefc67070b1b56203ef875a2c7caf24802dbc943bdc62decac33287b63b23"}},"message":"74657374"}"#;
let group_signature = "\"daae8e867c1c3000687a819262099c44e4799853729d87738b4811637a659f3075829c4ee6c5f6767e11b937e18dce20886b0d3f015caaf4ccdb76d4d185910c\"";
let mut buf = BufWriter::new(Vec::new());
@ -17,5 +20,9 @@ fn check_cli() {
);
let signature = cli(&mut input.as_bytes(), &mut buf);
assert!(signature.is_ok());
assert!(
signature.is_ok(),
"invalid signature: {}",
signature.unwrap_err()
);
}

View File

@ -15,14 +15,15 @@ use rand::thread_rng;
const PUBLIC_KEY: &str = "adf6ab1f882d04988eadfaa52fb175bf37b6247785d7380fde3fb9d68032470d";
const GROUP_PUBLIC_KEY: &str = "087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e";
const SIGNING_SHARE: &str = "ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104";
const SECRET_SHARE_JSON: &str = r#"{"identifier":"0100000000000000000000000000000000000000000000000000000000000000","value":"ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104","commitment":["087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e","926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2", "291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc"],"ciphersuite":"FROST(Ed25519, SHA-512)"}"#;
const SECRET_SHARE_JSON: &str = r#"{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"identifier":"0100000000000000000000000000000000000000000000000000000000000000","signing_share":"ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104","commitment":["087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e","926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2", "291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc"]}"#;
fn build_key_package() -> KeyPackage {
KeyPackage::new(
Identifier::try_from(1).unwrap(),
SigningShare::deserialize(<[u8; 32]>::from_hex(SIGNING_SHARE).unwrap()).unwrap(),
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY).unwrap()).unwrap(),
VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap(),
VerifyingKey::deserialize(<[u8; 32]>::from_hex(GROUP_PUBLIC_KEY).unwrap()).unwrap(),
3,
)
}
@ -77,7 +78,7 @@ fn check_invalid_length_signing_share() {
#[test]
fn check_invalid_round_1_inputs() {
let input = r#"{"value":"ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104","commitment":["087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e","926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2", "291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc"],"ciphersuite":"FROST(Ed25519, SHA-512)"}"#;
let input = r#"{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"signing_share":"ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104","commitment":["087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e","926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2", "291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc"]}"#;
let mut buf = BufWriter::new(Vec::new());
@ -115,7 +116,7 @@ fn check_print_values() {
let out = String::from_utf8(buf.into_inner().unwrap()).unwrap();
let log = format!("=== Round 1 ===\nSigningNonces were generated and stored in memory\nSigningCommitments:\n{{\"hiding\":\"{}\",\"binding\":\"{}\",\"ciphersuite\":\"FROST(Ed25519, SHA-512)\"}}\n=== Round 1 Completed ===\nPlease send your SigningCommitments to the coordinator\n", &hex::encode(commitments.hiding().serialize()), &hex::encode(commitments.binding().serialize()));
let log = format!("=== Round 1 ===\nSigningNonces were generated and stored in memory\nSigningCommitments:\n{{\"header\":{{\"version\":0,\"ciphersuite\":\"FROST-ED25519-SHA512-v1\"}},\"hiding\":\"{}\",\"binding\":\"{}\"}}\n=== Round 1 Completed ===\nPlease send your SigningCommitments to the coordinator\n", &hex::encode(commitments.hiding().serialize()), &hex::encode(commitments.binding().serialize()));
assert_eq!(out, log)
}

View File

@ -53,7 +53,7 @@ fn check_valid_round_2_inputs() {
let message = <[u8; 32]>::from_hex(MESSAGE).unwrap();
let signing_package = r#"{"signing_commitments":{"0100000000000000000000000000000000000000000000000000000000000000":{"hiding":"beb81feb53ed75a2695b07f377b464a88c4c2824e7d7b63911b745df01dc2d87","binding":"d2102c5f8b8abb7ad2f1706f47a4aab3be6ede28e408f3e74baeff1f6fbcd5c0","ciphersuite":"FROST(Ed25519, SHA-512)"},"0200000000000000000000000000000000000000000000000000000000000000":{"hiding":"cc9e9503921cdd3f4d64f2c9e7b22c9ab6d7c940111ce36f84e4a114331c6edd","binding":"b0e13794eaf00be2e430b16ec7f72ab0b6579e52ca604d17406a4fd1597afd66","ciphersuite":"FROST(Ed25519, SHA-512)"}},"message":"15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673","ciphersuite":"FROST(Ed25519, SHA-512)"}"#;
let signing_package = r#"{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"signing_commitments":{"0100000000000000000000000000000000000000000000000000000000000000":{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"hiding":"beb81feb53ed75a2695b07f377b464a88c4c2824e7d7b63911b745df01dc2d87","binding":"d2102c5f8b8abb7ad2f1706f47a4aab3be6ede28e408f3e74baeff1f6fbcd5c0"},"0200000000000000000000000000000000000000000000000000000000000000":{"header":{"version":0,"ciphersuite":"FROST-ED25519-SHA512-v1"},"hiding":"cc9e9503921cdd3f4d64f2c9e7b22c9ab6d7c940111ce36f84e4a114331c6edd","binding":"b0e13794eaf00be2e430b16ec7f72ab0b6579e52ca604d17406a4fd1597afd66"}},"message":"15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673"}"#;
let expected = Round2Config {
signing_package: SigningPackage::new(signer_commitments, &message),
@ -81,7 +81,8 @@ fn check_sign() {
Identifier::try_from(1).unwrap(),
SigningShare::deserialize(<[u8; 32]>::from_hex(SIGNING_SHARE).unwrap()).unwrap(),
VerifyingShare::deserialize(<[u8; 32]>::from_hex(PUBLIC_KEY).unwrap()).unwrap(),
VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap(),
VerifyingKey::deserialize(<[u8; 32]>::from_hex(GROUP_PUBLIC_KEY).unwrap()).unwrap(),
2,
);
// let config = Round1Config {
@ -91,8 +92,10 @@ fn check_sign() {
let mut rng = thread_rng();
// TODO: Nonce doesn't seem to be exported. Look into this to improve these tests
let (nonces, my_commitments) =
round1::commit(&SigningShare::from_hex(SIGNING_SHARE).unwrap(), &mut rng);
let (nonces, my_commitments) = round1::commit(
&SigningShare::deserialize(<[u8; 32]>::from_hex(SIGNING_SHARE).unwrap()).unwrap(),
&mut rng,
);
let signer_commitments_2 = SigningCommitments::new(
NonceCommitment::deserialize(<[u8; 32]>::from_hex(HIDING_COMMITMENT_2).unwrap()).unwrap(),
@ -127,9 +130,7 @@ fn check_print_values_round_2() {
print_values_round_2(signature_response, &mut buf).unwrap();
let log = "Please send the following to the Coordinator\n".to_owned() +
"SignatureShare:\n{\"share\":\"44055c54d0604cbd006f0d1713a22474d7735c5e8816b1878f62ca94bf105900\",\"ciphersuite\":\"FROST(Ed25519, SHA-512)\"}\n" +
"=== End of Round 2 ===\n";
let log = "Please send the following to the Coordinator\nSignatureShare:\n{\"header\":{\"version\":0,\"ciphersuite\":\"FROST-ED25519-SHA512-v1\"},\"share\":\"44055c54d0604cbd006f0d1713a22474d7735c5e8816b1878f62ca94bf105900\"}\n=== End of Round 2 ===\n";
let out = String::from_utf8(buf.into_inner().unwrap()).unwrap();

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "redpallas"))]
use std::collections::{BTreeMap, HashMap};
use frost::keys::IdentifierList;
@ -25,7 +27,7 @@ fn check_participant() {
let mut commitments = BTreeMap::new();
for i in shares.keys() {
let (nonce, commitment) = frost::round1::commit(key_packages[&i].secret_share(), &mut rng);
let (nonce, commitment) = frost::round1::commit(key_packages[&i].signing_share(), &mut rng);
nonces.insert(*i, nonce);
commitments.insert(*i, commitment);
}
@ -36,7 +38,7 @@ fn check_participant() {
// Round 2
let mut signature_shares = HashMap::new();
let mut signature_shares = BTreeMap::new();
for participant_identifier in nonces.keys() {
let config = Round2Config {
@ -56,7 +58,7 @@ fn check_participant() {
let signing_package = SigningPackage::new(commitments, &message);
let group_signature = aggregate(&signing_package, &signature_shares, &pubkeys).unwrap();
let verify_signature = pubkeys.group_public().verify(&message, &group_signature);
let verify_signature = pubkeys.verifying_key().verify(&message, &group_signature);
assert!(verify_signature.is_ok());
}

View File

@ -4,17 +4,22 @@ version = "0.1.0"
edition = "2021"
[dependencies]
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "5ea1293bb8df90c8a19dedf7ab5510903650dda9", features = ["frost"] }
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "6e55be4da9fa7beec9f744d49a1bad844318a932", features = ["frost"] }
hex = "0.4"
rand = "0.8"
exitcode = "1.1.2"
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
[dev-dependencies]
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
dkg = { path = "../dkg"}
trusted-dealer = { path = "../trusted-dealer"}
participant = { path = "../participant"}
coordinator = { path = "../coordinator"}
rand = "0.8"
[features]
redpallas = []
default = []

View File

@ -1,3 +1,7 @@
#![cfg(not(feature = "redpallas"))]
use coordinator::args::Args;
use coordinator::comms::cli::CLIComms;
use frost_ed25519 as frost;
use frost::keys::IdentifierList;
@ -17,11 +21,14 @@ use participant::{
round1::request_inputs as participant_input_round_1, round2::generate_signature,
};
#[test]
fn trusted_dealer_journey() {
#[tokio::test]
async fn trusted_dealer_journey() {
let mut buf = BufWriter::new(Vec::new());
let mut rng = thread_rng();
let args = Args::default();
let mut comms = CLIComms {};
// Trusted dealer
let dealer_input = "3\n5\n\n";
@ -43,20 +50,6 @@ fn trusted_dealer_journey() {
let participant_id_2 = Identifier::try_from(2).unwrap();
let participant_id_3 = Identifier::try_from(3).unwrap();
let step_1_input = format!(
"{}\n{}\n{}\n{}\n{}\n",
serde_json::to_string(&pubkeys).unwrap(),
num_of_participants,
id_input_1,
id_input_2,
id_input_3
);
let participants_config =
coordinator::step_1::step_1(&mut step_1_input.as_bytes(), &mut buf).unwrap();
// Participants round 1
let mut key_packages: HashMap<_, _> = HashMap::new();
for (identifier, secret_share) in shares {
@ -70,7 +63,7 @@ fn trusted_dealer_journey() {
for participant_index in 1..=3 {
let participant_identifier = Identifier::try_from(participant_index).unwrap();
let share = key_packages[&participant_identifier].secret_share();
let share = key_packages[&participant_identifier].signing_share();
let round_1_input = format!(
"{}\n",
@ -90,25 +83,33 @@ fn trusted_dealer_journey() {
commitments_map.insert(participant_identifier, commitments);
}
let step_1_input = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
serde_json::to_string(&pubkeys).unwrap(),
num_of_participants,
id_input_1,
serde_json::to_string(&commitments_map[&participant_id_1]).unwrap(),
id_input_2,
serde_json::to_string(&commitments_map[&participant_id_2]).unwrap(),
id_input_3,
serde_json::to_string(&commitments_map[&participant_id_3]).unwrap(),
);
let participants_config =
coordinator::step_1::step_1(&args, &mut comms, &mut step_1_input.as_bytes(), &mut buf)
.await
.unwrap();
// Coordinator step 2
let mut signature_shares = HashMap::new();
let message = "74657374";
let step_2_input = format!("{}\n", message);
let step_2_input = format!(
"{}\n{}\n{}\n{}\n",
message,
serde_json::to_string(&commitments_map[&participant_id_1]).unwrap(),
serde_json::to_string(&commitments_map[&participant_id_2]).unwrap(),
serde_json::to_string(&commitments_map[&participant_id_3]).unwrap()
);
let signing_package = coordinator::step_2::step_2(
&mut step_2_input.as_bytes(),
&mut buf,
vec![participant_id_1, participant_id_2, participant_id_3],
)
.unwrap();
let signing_package =
coordinator::step_2::step_2(&mut step_2_input.as_bytes(), &mut buf, commitments_map)
.unwrap();
// Participants round 2
@ -134,16 +135,19 @@ fn trusted_dealer_journey() {
serde_json::to_string(&signature_shares[&participant_id_3]).unwrap()
);
let group_signature = coordinator::step_3::step_3(
&mut comms,
&mut step_3_input.as_bytes(),
&mut buf,
participants_config,
signing_package,
);
&signing_package,
)
.await
.unwrap();
// verify
let is_signature_valid = pubkeys
.group_public()
.verifying_key()
.verify("test".as_bytes(), &group_signature)
.is_ok();
assert!(is_signature_valid);

View File

@ -6,8 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
frost-ed25519 = { version = "0.6.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "5ea1293bb8df90c8a19dedf7ab5510903650dda9", features = ["frost"] }
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "6e55be4da9fa7beec9f744d49a1bad844318a932", features = ["frost"] }
thiserror = "1.0"
rand = "0.8"
hex = "0.4"

View File

@ -9,7 +9,7 @@ use frost::keys::{PublicKeyPackage, SecretShare};
use frost::Error;
use frost::Identifier;
use itertools::Itertools;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::io::{BufRead, Write};
#[derive(Debug, PartialEq, Clone)]
@ -79,7 +79,7 @@ pub fn request_inputs(
}
pub fn print_values(
keys: &HashMap<Identifier, SecretShare>,
keys: &BTreeMap<Identifier, SecretShare>,
pubkeys: &PublicKeyPackage,
logger: &mut dyn Write,
) -> Result<(), Box<dyn std::error::Error>> {

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "redpallas"))]
use std::io::BufWriter;
use frost::Error;

View File

@ -1,16 +1,18 @@
#![cfg(not(feature = "redpallas"))]
use frost::keys::{IdentifierList, PublicKeyPackage, SecretShare};
use frost::Identifier;
use frost_ed25519 as frost;
use itertools::Itertools;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::io::BufWriter;
use trusted_dealer::inputs::print_values;
use trusted_dealer::inputs::Config;
use trusted_dealer::trusted_dealer_keygen::{split_secret, trusted_dealer_keygen};
fn build_output(shares: HashMap<Identifier, SecretShare>, pubkeys: PublicKeyPackage) -> String {
fn build_output(shares: BTreeMap<Identifier, SecretShare>, pubkeys: PublicKeyPackage) -> String {
let pub_key_package = format!(
"Public key package:\n{}",
serde_json::to_string(&pubkeys).unwrap()

View File

@ -6,7 +6,7 @@ use reddsa::frost::redpallas as frost;
use frost::keys::{IdentifierList, PublicKeyPackage, SecretShare};
use frost::{Error, Identifier, SigningKey};
use rand::rngs::ThreadRng;
use std::collections::HashMap;
use std::collections::BTreeMap;
use crate::inputs::Config;
@ -14,7 +14,7 @@ pub fn trusted_dealer_keygen(
config: &Config,
identifiers: IdentifierList,
rng: &mut ThreadRng,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
let (shares, pubkeys) = frost::keys::generate_with_dealer(
config.max_signers,
config.min_signers,
@ -33,7 +33,7 @@ pub fn split_secret(
config: &Config,
identifiers: IdentifierList,
rng: &mut ThreadRng,
) -> Result<(HashMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
let secret_key = SigningKey::deserialize(
config
.secret
@ -56,6 +56,7 @@ pub fn split_secret(
Ok((shares, pubkeys))
}
#[cfg(not(feature = "redpallas"))]
#[cfg(test)]
mod tests {

View File

@ -4,10 +4,10 @@ use frost::round2::SignatureShare;
use frost::{Identifier, SigningPackage};
use frost_ed25519 as frost;
use rand::rngs::ThreadRng;
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
pub fn key_package(shares: &HashMap<Identifier, SecretShare>) -> HashMap<Identifier, KeyPackage> {
let mut key_packages: HashMap<_, _> = HashMap::new();
pub fn key_package(shares: &BTreeMap<Identifier, SecretShare>) -> BTreeMap<Identifier, KeyPackage> {
let mut key_packages: BTreeMap<_, _> = BTreeMap::new();
for (identifier, secret_share) in shares {
let key_package = frost::keys::KeyPackage::try_from(secret_share.clone()).unwrap();
@ -20,20 +20,20 @@ pub fn key_package(shares: &HashMap<Identifier, SecretShare>) -> HashMap<Identif
pub fn round_1(
min_signers: u16,
mut rng: &mut ThreadRng,
key_packages: &HashMap<Identifier, KeyPackage>,
key_packages: &BTreeMap<Identifier, KeyPackage>,
) -> (
HashMap<Identifier, SigningNonces>,
BTreeMap<Identifier, SigningNonces>,
BTreeMap<Identifier, SigningCommitments>,
) {
// Participant Round 1
let mut nonces_map = HashMap::new();
let mut nonces_map = BTreeMap::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);
let (nonces, commitments) = frost::round1::commit(key_package.signing_share(), &mut rng);
nonces_map.insert(participant_identifier, nonces);
commitments_map.insert(participant_identifier, commitments);
}
@ -41,13 +41,13 @@ pub fn round_1(
}
pub fn round_2(
nonces_map: HashMap<Identifier, SigningNonces>,
key_packages: &HashMap<Identifier, KeyPackage>,
nonces_map: BTreeMap<Identifier, SigningNonces>,
key_packages: &BTreeMap<Identifier, KeyPackage>,
commitments_map: BTreeMap<Identifier, SigningCommitments>,
message: &[u8],
) -> (SigningPackage, HashMap<Identifier, SignatureShare>) {
) -> (SigningPackage, BTreeMap<Identifier, SignatureShare>) {
let signing_package = frost::SigningPackage::new(commitments_map, message);
let mut signature_shares = HashMap::new();
let mut signature_shares = BTreeMap::new();
for participant_identifier in nonces_map.keys() {
let key_package = &key_packages[participant_identifier];

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "redpallas"))]
mod helpers;
use frost::aggregate;
@ -26,7 +28,7 @@ fn check_keygen_with_dealer() {
let message = "i am a message".as_bytes();
let (signing_package, signature_shares) = round_2(nonces, &key_packages, commitments, message);
let group_signature = aggregate(&signing_package, &signature_shares, &pubkeys).unwrap();
let verify_signature = pubkeys.group_public().verify(message, &group_signature);
let verify_signature = pubkeys.verifying_key().verify(message, &group_signature);
assert!(verify_signature.is_ok());
}
@ -47,7 +49,7 @@ fn check_keygen_with_dealer_with_large_num_of_signers() {
let message = "i am a message".as_bytes();
let (signing_package, signature_shares) = round_2(nonces, &key_packages, commitments, message);
let group_signature = aggregate(&signing_package, &signature_shares, &pubkeys).unwrap();
let verify_signature = pubkeys.group_public().verify(message, &group_signature);
let verify_signature = pubkeys.verifying_key().verify(message, &group_signature);
assert!(verify_signature.is_ok());
}
@ -72,7 +74,7 @@ fn check_keygen_with_dealer_with_secret() {
let message = "i am a message".as_bytes();
let (signing_package, signature_shares) = round_2(nonces, &key_packages, commitments, message);
let group_signature = aggregate(&signing_package, &signature_shares, &pubkeys).unwrap();
let verify_signature = pubkeys.group_public().verify(message, &group_signature);
let verify_signature = pubkeys.verifying_key().verify(message, &group_signature);
assert!(verify_signature.is_ok());
}
@ -96,7 +98,7 @@ fn check_keygen_with_dealer_with_secret_with_large_num_of_signers() {
let message = "i am a message".as_bytes();
let (signing_package, signature_shares) = round_2(nonces, &key_packages, commitments, message);
let group_signature = aggregate(&signing_package, &signature_shares, &pubkeys).unwrap();
let verify_signature = pubkeys.group_public().verify(message, &group_signature);
let verify_signature = pubkeys.verifying_key().verify(message, &group_signature);
assert!(verify_signature.is_ok());
}