Serialize round 2 output in Participant demo (#44)

* Add round 2 inputs for participant demo (#20)

* Generate signature in participant demo (#20)

Add integration test

* Refactor rounds into own files in participant demo (#20)

* Refactor rounds tests into own files in participant demo (#20)

* Remove unnecessary test lib file from participant (#20)

* Add test for round 1 output for participant (#20)

* Print values for round 2 participant (#20)

Add back in decode_vss_commitment test

* Add in some errors (#23)

* Fix test for invalid length of vss commitment (#23)

* Improve readability of text output

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Improve readability of text output

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Refactor and rename (#23)

---------

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>
This commit is contained in:
natalie 2023-07-10 23:09:00 +01:00 committed by GitHub
parent a70d0342e0
commit 0831c3053a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 731 additions and 217 deletions

View File

@ -1,15 +1,29 @@
use frost::round1;
use frost_ed25519 as frost;
use participant::{generate_key_package, print_values, request_inputs, Logger};
use participant::round1::{generate_key_package, print_values, request_inputs};
use participant::round2::{generate_signature, print_values_round_2, round_2_request_inputs};
use participant::Logger;
use rand::thread_rng;
use std::io::BufRead;
pub fn cli(input: &mut impl BufRead, logger: &mut dyn Logger) {
let config = request_inputs(input, logger).unwrap(); // TODO: handle error
let _key_package = generate_key_package(&config);
let round_1_config = request_inputs(input, logger).unwrap(); // TODO: handle error
let key_package = generate_key_package(&round_1_config).unwrap();
logger.log("Key Package succesfully created.".to_string());
let mut rng = thread_rng();
let (nonces, commitments) = round1::commit(config.identifier, &config.signing_share, &mut rng);
print_values(nonces, commitments, logger);
let (nonces, commitments) = round1::commit(
round_1_config.identifier,
&round_1_config.signing_share,
&mut rng,
);
print_values(&nonces, commitments, logger);
let round_2_config = round_2_request_inputs(commitments, input, logger).unwrap(); // TODO: handle errors
// Sign
let signature = generate_signature(round_2_config, &key_package, &nonces).unwrap(); // TODO: handle errors
print_values_round_2(signature, logger);
}

View File

@ -1,183 +1,6 @@
use frost::{
keys::{
KeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment, VerifyingShare,
},
round1::{SigningCommitments, SigningNonces},
Error, Identifier, VerifyingKey,
};
use frost_ed25519 as frost;
use hex::FromHex;
use std::io::BufRead;
// TODO: Rethink the types here. They're inconsistent with each other
#[derive(Debug, PartialEq)]
pub struct Config {
pub identifier: Identifier,
pub public_key: VerifyingShare,
pub group_public_key: VerifyingKey,
pub signing_share: SigningShare,
pub vss_commitment: Vec<u8>,
}
pub mod round1;
pub mod round2;
pub trait Logger {
fn log(&mut self, value: String);
}
// TODO: refactor to generate config
pub fn request_inputs(input: &mut impl BufRead, logger: &mut dyn Logger) -> Result<Config, Error> {
logger.log("Your identifier (this should be an integer between 1 and 65535):".to_string());
let mut identifier_input = String::new();
input.read_line(&mut identifier_input).unwrap();
let identifier = identifier_input
.trim()
.parse::<u16>()
.map_err(|_| Error::MalformedIdentifier)?;
logger.log("Your public key:".to_string());
let mut public_key_input = String::new();
input.read_line(&mut public_key_input).unwrap();
// A specific VerifyingShare error does not currently exist in Frost so `MalformedVerifyingKey`
// has been used. This should either be added to Frost or the error handling here can be reconsidered
let public_key = VerifyingShare::from_bytes(
<[u8; 32]>::from_hex(public_key_input.trim()).map_err(|_| Error::MalformedVerifyingKey)?,
)?; //TODO: test error
logger.log("The group public key:".to_string());
let mut group_public_key_input = String::new();
input.read_line(&mut group_public_key_input).unwrap();
let group_public_key = VerifyingKey::from_bytes(
<[u8; 32]>::from_hex(group_public_key_input.trim())
.map_err(|_| Error::MalformedVerifyingKey)?,
)
.map_err(|_| Error::MalformedVerifyingKey)?; // TODO: Add test for correct error to be returned on failing deserialisation
logger.log("Your secret share:".to_string());
let mut signing_share_input = String::new();
input.read_line(&mut signing_share_input).unwrap();
// A specific SigningShare error does not currently exist in Frost so `MalformedSigningKey`
// has been used. This should either be added to Frost or the error handling here can be reconsidered
let signing_share = SigningShare::from_bytes(
<[u8; 32]>::from_hex(signing_share_input.trim()).map_err(|_| Error::MalformedSigningKey)?,
)?; //TODO: test error
logger.log("Your verifiable secret sharing commitment:".to_string());
let mut vss_commitment_input = String::new();
input.read_line(&mut vss_commitment_input).unwrap();
let vss_commitment = hex::decode(vss_commitment_input.trim()).unwrap();
Ok(Config {
identifier: Identifier::try_from(identifier)?,
public_key,
group_public_key,
signing_share,
vss_commitment,
})
}
pub fn generate_key_package(config: &Config) -> Result<KeyPackage, Error> {
let secret_share = SecretShare::new(
config.identifier,
config.signing_share,
decode_vss_commitment(&config.vss_commitment).unwrap(),
);
let key_package = KeyPackage::try_from(secret_share)?;
Ok(key_package)
}
fn decode_vss_commitment(
vss_commitment: &Vec<u8>,
) -> Result<VerifiableSecretSharingCommitment, Error> {
let coeff_commitments_data = vss_commitment[1..vss_commitment.len()].to_vec();
let n = vss_commitment[0] as usize;
let l = coeff_commitments_data.len() / n;
let mut coeff_commitments = Vec::with_capacity(n);
for i in 0..n {
let commitment_value = hex::encode(&coeff_commitments_data[(i * l)..((i * l) + l)]);
let serialized =
<[u8; 32]>::from_hex(commitment_value).map_err(|_| Error::InvalidCoefficients)?; // TODO: Is this the right error? Need to add test
coeff_commitments.push(serialized)
}
let out = VerifiableSecretSharingCommitment::deserialize(coeff_commitments)?; //TODO: test for this error
Ok(out)
}
// The nonces are printed out here for demo purposes only. The hiding and binding nonces are SECRET and not to be shared.
pub fn print_values(
nonces: SigningNonces,
commitments: SigningCommitments,
logger: &mut dyn Logger,
) {
logger.log("=== Round 1 ===".to_string());
logger.log(format!(
"Hiding nonce: {}",
hex::encode(nonces.hiding().to_bytes())
));
logger.log(format!(
"Binding nonce: {}",
hex::encode(nonces.binding().to_bytes())
));
logger.log(format!(
"Hiding commitment: {}",
hex::encode(commitments.hiding().to_bytes())
));
logger.log(format!(
"Binding commitment: {}",
hex::encode(commitments.binding().to_bytes())
));
}
#[cfg(test)]
mod tests {
use frost::keys::VerifiableSecretSharingCommitment;
use frost_ed25519 as frost;
use hex::FromHex;
use crate::decode_vss_commitment;
// TODO: Add details of encoding
#[test]
fn check_decode_vss_commitment() {
let vss_commitment_input = hex::decode("0353e4f0ed77543d021eb12cac53c35d4d99f5fc0fa5c3dfd82a3e1e296fba01bdcad2a298d93b5f0079f5f3874599ca2295482e9a4fa75be6c6deb273b61ee441e30ae9f78c1b56a4648130417247826afe3499c0d80b449740f8c968c64df0a4").unwrap();
let expected = VerifiableSecretSharingCommitment::deserialize(vec![
<[u8; 32]>::from_hex(
"53e4f0ed77543d021eb12cac53c35d4d99f5fc0fa5c3dfd82a3e1e296fba01bd",
)
.unwrap(),
<[u8; 32]>::from_hex(
"cad2a298d93b5f0079f5f3874599ca2295482e9a4fa75be6c6deb273b61ee441",
)
.unwrap(),
<[u8; 32]>::from_hex(
"e30ae9f78c1b56a4648130417247826afe3499c0d80b449740f8c968c64df0a4",
)
.unwrap(),
])
.unwrap();
let actual = decode_vss_commitment(&vss_commitment_input).unwrap();
assert!(expected == actual);
}
}

190
participant/src/round1.rs Normal file
View File

@ -0,0 +1,190 @@
use crate::Logger;
use frost::{
keys::{
KeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment, VerifyingShare,
},
round1::{SigningCommitments, SigningNonces},
Error, GroupError, Identifier, VerifyingKey,
};
use frost_ed25519 as frost;
use hex::FromHex;
use std::io::BufRead;
// TODO: Rethink the types here. They're inconsistent with each other
#[derive(Debug, PartialEq)]
pub struct Round1Config {
pub identifier: Identifier,
pub public_key: VerifyingShare,
pub group_public_key: VerifyingKey,
pub signing_share: SigningShare,
pub vss_commitment: Vec<u8>,
}
// pub trait Logger {
// fn log(&mut self, value: String);
// }
// TODO: refactor to generate config
pub fn request_inputs(
input: &mut impl BufRead,
logger: &mut dyn Logger,
) -> Result<Round1Config, Error> {
logger.log("Your identifier (this should be an integer between 1 and 65535):".to_string());
let mut identifier_input = String::new();
input.read_line(&mut identifier_input).unwrap();
let identifier = identifier_input
.trim()
.parse::<u16>()
.map_err(|_| Error::MalformedIdentifier)?;
logger.log("Your public key:".to_string());
let mut public_key_input = String::new();
input.read_line(&mut public_key_input).unwrap();
// A specific VerifyingShare error does not currently exist in Frost so `MalformedVerifyingKey`
// has been used. This should either be added to Frost or the error handling here can be reconsidered
let public_key = VerifyingShare::from_bytes(
<[u8; 32]>::from_hex(public_key_input.trim()).map_err(|_| Error::MalformedVerifyingKey)?,
)?; //TODO: test error
logger.log("The group public key:".to_string());
let mut group_public_key_input = String::new();
input.read_line(&mut group_public_key_input).unwrap();
let group_public_key = VerifyingKey::from_bytes(
<[u8; 32]>::from_hex(group_public_key_input.trim())
.map_err(|_| Error::MalformedVerifyingKey)?,
)
.map_err(|_| Error::MalformedVerifyingKey)?; // TODO: Add test for correct error to be returned on failing deserialisation
logger.log("Your secret share:".to_string());
let mut signing_share_input = String::new();
input.read_line(&mut signing_share_input).unwrap();
// A specific SigningShare error does not currently exist in Frost so `MalformedSigningKey`
// has been used. This should either be added to Frost or the error handling here can be reconsidered
let signing_share = SigningShare::from_bytes(
<[u8; 32]>::from_hex(signing_share_input.trim()).map_err(|_| Error::MalformedSigningKey)?,
)?; //TODO: test error
logger.log("Your verifiable secret sharing commitment:".to_string());
let mut vss_commitment_input = String::new();
input.read_line(&mut vss_commitment_input).unwrap();
let vss_commitment =
hex::decode(vss_commitment_input.trim()).map_err(|_| GroupError::MalformedElement)?;
Ok(Round1Config {
identifier: Identifier::try_from(identifier)?,
public_key,
group_public_key,
signing_share,
vss_commitment,
})
}
pub fn generate_key_package(config: &Round1Config) -> Result<KeyPackage, Error> {
let secret_share = SecretShare::new(
config.identifier,
config.signing_share,
decode_vss_commitment(&config.vss_commitment)?,
);
let key_package = KeyPackage::try_from(secret_share)?;
Ok(key_package)
}
fn decode_vss_commitment(
vss_commitment: &Vec<u8>,
) -> Result<VerifiableSecretSharingCommitment, Error> {
let coeff_commitments_data = vss_commitment[1..vss_commitment.len()].to_vec();
let n = vss_commitment[0] as usize;
let l = coeff_commitments_data.len() / n;
let mut coeff_commitments = Vec::with_capacity(n);
for i in 0..n {
let commitment_value = hex::encode(&coeff_commitments_data[(i * l)..((i * l) + l)]);
let serialized =
<[u8; 32]>::from_hex(commitment_value).map_err(|_| Error::InvalidCoefficients)?; // TODO: Is this the right error? Need to add test
coeff_commitments.push(serialized)
}
let out = VerifiableSecretSharingCommitment::deserialize(coeff_commitments)?; //TODO: test for this error
Ok(out)
}
// The nonces are printed out here for demo purposes only. The hiding and binding nonces are SECRET and not to be shared.
pub fn print_values(
nonces: &SigningNonces,
commitments: SigningCommitments,
logger: &mut dyn Logger,
) {
logger.log("=== Round 1 ===".to_string());
logger.log(format!(
"Hiding nonce: {}",
hex::encode(nonces.hiding().to_bytes())
));
logger.log(format!(
"Binding nonce: {}",
hex::encode(nonces.binding().to_bytes())
));
logger.log(format!(
"Hiding commitment: {}",
hex::encode(commitments.hiding().to_bytes())
));
logger.log(format!(
"Binding commitment: {}",
hex::encode(commitments.binding().to_bytes())
));
logger.log("=== Round 1 Completed ===".to_string());
logger.log("Please send your Hiding and Binding Commitments to the coordinator".to_string());
}
#[cfg(test)]
mod tests {
use frost::keys::VerifiableSecretSharingCommitment;
use frost_ed25519 as frost;
use hex::FromHex;
use crate::round1::decode_vss_commitment;
// TODO: Add details of encoding
#[test]
fn check_decode_vss_commitment() {
let vss_commitment_input = hex::decode("0353e4f0ed77543d021eb12cac53c35d4d99f5fc0fa5c3dfd82a3e1e296fba01bdcad2a298d93b5f0079f5f3874599ca2295482e9a4fa75be6c6deb273b61ee441e30ae9f78c1b56a4648130417247826afe3499c0d80b449740f8c968c64df0a4").unwrap();
let expected = VerifiableSecretSharingCommitment::deserialize(vec![
<[u8; 32]>::from_hex(
"53e4f0ed77543d021eb12cac53c35d4d99f5fc0fa5c3dfd82a3e1e296fba01bd",
)
.unwrap(),
<[u8; 32]>::from_hex(
"cad2a298d93b5f0079f5f3874599ca2295482e9a4fa75be6c6deb273b61ee441",
)
.unwrap(),
<[u8; 32]>::from_hex(
"e30ae9f78c1b56a4648130417247826afe3499c0d80b449740f8c968c64df0a4",
)
.unwrap(),
])
.unwrap();
let actual = decode_vss_commitment(&vss_commitment_input).unwrap();
assert!(expected == actual);
}
}

135
participant/src/round2.rs Normal file
View File

@ -0,0 +1,135 @@
use crate::Logger;
use frost::{
keys::KeyPackage,
round1::{NonceCommitment, SigningCommitments, SigningNonces},
round2::{self, SignatureShare},
Error, Identifier, SigningPackage,
};
use frost_ed25519 as frost;
use hex::FromHex;
use std::io::BufRead;
// #[derive(Debug)]
pub struct Round2Config {
pub message: Vec<u8>,
pub signer_commitments: Vec<SigningCommitments>,
}
// TODO: refactor to generate config
// TODO: handle errors
pub fn round_2_request_inputs(
signing_commitments: SigningCommitments,
input: &mut impl BufRead,
logger: &mut dyn Logger,
) -> Result<Round2Config, Error> {
logger.log("=== Round 2 ===".to_string());
logger.log("Number of signers:".to_string());
let mut signers_input = String::new();
input.read_line(&mut signers_input).unwrap();
let signers = signers_input.trim().parse::<u16>().unwrap();
logger.log("Enter the message to sign (received from the coordinator):".to_string());
let mut message_input = String::new();
input.read_line(&mut message_input).unwrap();
let message = hex::decode(message_input.trim()).unwrap();
let mut commitments = vec![signing_commitments];
for _ in 2..=signers {
logger.log("Identifier:".to_string());
let mut identifier_input = String::new();
input.read_line(&mut identifier_input).unwrap();
let id_value = identifier_input.trim().parse::<u16>().unwrap();
let identifier = Identifier::try_from(id_value).unwrap();
logger.log(format!("Hiding commitment {}:", id_value));
let mut hiding_commitment_input = String::new();
input.read_line(&mut hiding_commitment_input).unwrap();
let hiding_commitment = NonceCommitment::from_bytes(
<[u8; 32]>::from_hex(hiding_commitment_input.trim()).unwrap(),
)?;
logger.log(format!("Binding commitment {}:", id_value));
let mut binding_commitment_input = String::new();
input.read_line(&mut binding_commitment_input).unwrap();
let binding_commitment = NonceCommitment::from_bytes(
<[u8; 32]>::from_hex(binding_commitment_input.trim()).unwrap(),
)?;
let signer_commitments =
SigningCommitments::new(identifier, hiding_commitment, binding_commitment); // TODO: Add test for correct error to be returned on failing deserialisation
commitments.push(signer_commitments);
}
Ok(Round2Config {
message,
signer_commitments: commitments,
})
}
pub fn generate_signature(
config: Round2Config,
key_package: &KeyPackage,
signing_nonces: &SigningNonces,
) -> Result<SignatureShare, Error> {
let signing_package = SigningPackage::new(config.signer_commitments, &config.message);
let signature = round2::sign(&signing_package, signing_nonces, key_package)?;
Ok(signature)
}
fn encode_signature_response(signature_share: SignatureShare) -> String {
let id = hex::encode(signature_share.identifier().serialize());
let sig = hex::encode(signature_share.signature().to_bytes());
id + &sig
}
pub fn print_values_round_2(signature: SignatureShare, logger: &mut dyn Logger) {
logger.log("Please send the following to the Coordinator".to_string());
logger.log(format!(
"Signature share: {}",
encode_signature_response(signature)
));
logger.log("=== End of Round 2 ===".to_string());
}
#[cfg(test)]
mod tests {
use frost::{
round2::{SignatureResponse, SignatureShare},
Identifier,
};
use frost_ed25519 as frost;
use hex::FromHex;
use crate::round2::encode_signature_response;
// TODO: Add details of encoding
#[test]
fn check_encode_signature_response() {
const SIGNATURE_RESPONSE: &str =
"44055c54d0604cbd006f0d1713a22474d7735c5e8816b1878f62ca94bf105900";
let signature_response =
SignatureResponse::from_bytes(<[u8; 32]>::from_hex(SIGNATURE_RESPONSE).unwrap())
.unwrap();
let signature_share =
SignatureShare::new(Identifier::try_from(1).unwrap(), signature_response);
let expected = "010000000000000000000000000000000000000000000000000000000000000044055c54d0604cbd006f0d1713a22474d7735c5e8816b1878f62ca94bf105900";
let signature = encode_signature_response(signature_share);
assert!(expected == signature)
}
}

View File

@ -1,2 +1,3 @@
mod cli;
mod lib;
mod round1;
mod round2;

View File

@ -1,5 +1,9 @@
use crate::cli::cli;
use frost::{keys::SigningShare, round1, Identifier};
use frost_ed25519 as frost;
use hex::FromHex;
use participant::Logger;
use rand::thread_rng;
pub struct TestLogger(Vec<String>);
@ -11,32 +15,92 @@ impl Logger for TestLogger {
#[test]
fn check_cli() {
let identifier: u16 = 1;
// Round 1 inputs
let identifier = "1";
let pub_key = "470f53fb724502bf5b851471e9f8317616fcc7be9405ccff3347c232a3052ce7";
let group_pub_key = "42ae1baa1bce5a38c130e60aade154ec8775076e729881aba66dabd0c0ac6332";
let signing_share = "1edfa2ebd280cba9a72f0bc027d21c30078c11f92e0c908addb958062c1ac900";
let vss_commitment = "0342ae1baa1bce5a38c130e60aade154ec8775076e729881aba66dabd0c0ac6332393a813a6b47782f0fbe653593cbb7b0e0e13f01b54b801144545cb774c0fe5683d8bee3cd63b10523ccace10044869c56bce8a6061950f9aebd7f2e36249571";
// Round 2 inputs
let min_signers = "3";
const MESSAGE: &str = "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673";
const IDENTIFIER_2: &str = "2";
const HIDING_COMMITMENT_2: &str =
"30f3f03bd739024dc5b1e9d422745a7f32b0971d5cef302106b30bd9f5642d70";
const BINDING_COMMITMENT_2: &str =
"a7ccae3750846fbd7d132efec85e96236a711b2097a6f03b1afa04f6029458cc";
const IDENTIFIER_3: &str = "3";
const HIDING_COMMITMENT_3: &str =
"d31bd81ce216b1c83912803a574a0285796275cb8b14f6dc92c8b09a6951f0a2";
const BINDING_COMMITMENT_3: &str =
"e1c863cfd08df775b6747ef2456e9bf9a03cc281a479a95261dc39137fcf0967";
let input = format!(
"{}\n{}\n{}\n{}\n{}\n",
identifier, pub_key, group_pub_key, signing_share, vss_commitment
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
identifier,
pub_key,
group_pub_key,
signing_share,
vss_commitment,
min_signers,
MESSAGE,
IDENTIFIER_2,
HIDING_COMMITMENT_2,
BINDING_COMMITMENT_2,
IDENTIFIER_3,
HIDING_COMMITMENT_3,
BINDING_COMMITMENT_3
);
let mut reader = input.as_bytes();
let mut test_logger = TestLogger(Vec::new());
cli(&mut reader, &mut test_logger);
// We aren't testing randomness so we are not testing the generation of the nonces and commitments at the top level.
// TODO: mock the round1::commit function. To be improved in a later issue.
let mut rng = thread_rng();
// We aren't testing randomness so this needs to be generated in the tests. TODO: mock the round1::commit function. To be improved in a later issue.
let (nonces, commitments) = round1::commit(
Identifier::try_from(1).unwrap(),
&SigningShare::from_hex(signing_share).unwrap(),
&mut rng,
);
let _hiding_nonce = hex::encode(nonces.hiding().to_bytes());
let _binding_nonce = hex::encode(nonces.binding().to_bytes());
let _hiding_commitment = hex::encode(commitments.hiding().to_bytes());
let _binding_commitment = hex::encode(commitments.binding().to_bytes());
// let signature_share = hex::encode(sig_share.to_bytes());
let log = [
"Your identifier (this should be an integer between 1 and 65535):".to_string(),
"Your public key:".to_string(),
"The group public key:".to_string(),
"Your secret share:".to_string(),
"Your verifiable secret sharing commitment:".to_string(),
"Key Package succesfully created.".to_string(),
"=== Round 1 ===".to_string(),
];
"Your identifier (this should be an integer between 1 and 65535):",
"Your public key:",
"The group public key:",
"Your secret share:",
"Your verifiable secret sharing commitment:",
"Key Package succesfully created.",
"=== Round 1 ===",
"Hiding nonce: {}",
"Binding nonce: {}",
"Hiding commitment: {}",
"Binding commitment: {}",
"=== Round 1 Completed ===",
"Please send your Hiding and Binding Commitments to the coordinator",
"=== Round 2 ===",
"Number of signers:",
"Enter the message to sign (received from the coordinator):",
"Identifier:",
"Hiding commitment 2:",
"Binding commitment 2:",
"Identifier:",
"Hiding commitment 3:",
"Binding commitment 3:",
// "Signature share: {}", // TODO: this tests the signature share value returned is correct. The calculation is done in lib.rs tests.
// "=== Round 2 Completed ==="
]
.to_vec();
assert_eq!(test_logger.0[0..=6], log)
assert_eq!(test_logger.0[0..7], log[0..7]);
assert_eq!(test_logger.0[12..22], log[12..22]);
// TODO: test nonce and commitment values
}

View File

@ -1,14 +1,21 @@
use frost::{
keys::{KeyPackage, SigningShare, VerifyingShare},
VerifyingKey,
round1, VerifyingKey,
};
#[cfg(test)]
use frost::{Error, Identifier};
use frost_ed25519 as frost;
use hex::FromHex;
use participant::{generate_key_package, request_inputs, Config};
use participant::round1::{generate_key_package, print_values, request_inputs, Round1Config};
use crate::Logger;
use participant::Logger;
use rand::thread_rng;
const IDENTIFIER: &str = "1";
const PUBLIC_KEY: &str = "adf6ab1f882d04988eadfaa52fb175bf37b6247785d7380fde3fb9d68032470d";
const GROUP_PUBLIC_KEY: &str = "087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e";
const SIGNING_SHARE: &str = "ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104";
const VSS_COMMITMENT : &str = "03087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc";
pub struct TestLogger(Vec<String>);
@ -18,15 +25,9 @@ impl Logger for TestLogger {
}
}
const IDENTIFIER: &str = "1";
const PUBLIC_KEY: &str = "adf6ab1f882d04988eadfaa52fb175bf37b6247785d7380fde3fb9d68032470d";
const GROUP_PUBLIC_KEY: &str = "087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e";
const SIGNING_SHARE: &str = "ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104";
const VSS_COMMITMENT : &str = "03087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc";
#[test]
fn check_valid_inputs() {
let config = Config {
fn check_valid_round_1_inputs() {
let config = Round1Config {
identifier: Identifier::try_from(1).unwrap(),
public_key: VerifyingShare::from_bytes(<[u8; 32]>::from_hex(PUBLIC_KEY).unwrap()).unwrap(),
group_public_key: VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap(),
@ -130,7 +131,6 @@ fn check_invalid_length_signing_share() {
// TODO: Handle this error differently
#[test]
#[should_panic]
fn check_invalid_length_vss_commitment() {
let mut test_logger = TestLogger(Vec::new());
@ -141,12 +141,13 @@ fn check_invalid_length_vss_commitment() {
);
let mut invalid_input = input.as_bytes();
let _expected = request_inputs(&mut invalid_input, &mut test_logger);
let expected = request_inputs(&mut invalid_input, &mut test_logger);
assert!(expected.is_err())
}
#[test]
fn check_key_package_generation() {
let config = Config {
let config = Round1Config {
identifier: Identifier::try_from(1).unwrap(),
public_key: VerifyingShare::from_bytes(<[u8; 32]>::from_hex(PUBLIC_KEY).unwrap()).unwrap(),
group_public_key: VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap(),
@ -170,7 +171,7 @@ fn check_key_package_generation() {
fn check_key_package_generation_fails_with_invalid_secret_share() {
let incorrect_signing_share =
"afc0ba51fd450297725f9efe714400d51a1180a273177b5dd8ad3b8cba41560d";
let config = Config {
let config = Round1Config {
identifier: Identifier::try_from(1).unwrap(),
public_key: VerifyingShare::from_bytes(<[u8; 32]>::from_hex(PUBLIC_KEY).unwrap()).unwrap(),
group_public_key: VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap(),
@ -180,8 +181,38 @@ fn check_key_package_generation_fails_with_invalid_secret_share() {
.unwrap(),
vss_commitment: hex::decode(VSS_COMMITMENT).unwrap(),
};
let key_package = generate_key_package(&config);
assert!(key_package.is_err());
}
#[test]
fn check_print_values() {
let mut test_logger = TestLogger(Vec::new());
let signing_share =
SigningShare::from_bytes(<[u8; 32]>::from_hex(SIGNING_SHARE).unwrap()).unwrap();
let mut rng = thread_rng();
let (nonces, commitments) =
round1::commit(Identifier::try_from(1).unwrap(), &signing_share, &mut rng);
print_values(&nonces, commitments, &mut test_logger);
let log = [
"=== Round 1 ===".to_string(),
format!("Hiding nonce: {}", hex::encode(nonces.hiding().to_bytes())),
format!(
"Binding nonce: {}",
hex::encode(nonces.binding().to_bytes())
),
format!(
"Hiding commitment: {}",
hex::encode(commitments.hiding().to_bytes())
),
format!(
"Binding commitment: {}",
hex::encode(commitments.binding().to_bytes())
),
"=== Round 1 Completed ===".to_string(),
"Please send your Hiding and Binding Commitments to the coordinator".to_string(),
];
assert_eq!(test_logger.0, log)
}

View File

@ -0,0 +1,172 @@
#[cfg(test)]
use frost::Identifier;
use frost::{
keys::{KeyPackage, SigningShare, VerifyingShare},
round1::{self, NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
VerifyingKey,
};
use frost_ed25519 as frost;
use hex::FromHex;
use participant::round2::{generate_signature, round_2_request_inputs, Round2Config};
use participant::Logger;
use participant::{round1::Round1Config, round2::print_values_round_2};
use rand::thread_rng;
pub struct TestLogger(Vec<String>);
impl Logger for TestLogger {
fn log(&mut self, value: String) {
self.0.push(value);
}
}
const PUBLIC_KEY: &str = "adf6ab1f882d04988eadfaa52fb175bf37b6247785d7380fde3fb9d68032470d";
const GROUP_PUBLIC_KEY: &str = "087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e";
const SIGNING_SHARE: &str = "ceed7dd148a1a1ec2e65b50ecab6a7c453ccbd38c397c3506a540b7cf0dd9104";
const VSS_COMMITMENT : &str = "03087e22f970daf6ac5b07b55bd7fc0af6dea199ab847dc34fc92a6f8641a1bb8e926d5910e146dccb9148ca39dc7607f4f7123ff1c0ffaf109add1d165c568bf2291bb78d7e4ef124f5aa6a36cbcf8c276e70fbb4e208212e916d762fc42c1bbc";
const MESSAGE: &str = "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673";
const MY_HIDING_COMMITMENT: &str =
"44105304351ceddc58e15ddea35b2cb48e60ced54ceb22c3b0e5d42d098aa1d8";
const MY_BINDING_COMMITMENT: &str =
"b8274b18a12f2cef74ae42f876cec1e31daab5cb162f95a56cd2487409c9d1dd";
const IDENTIFIER_2: &str = "2";
const HIDING_COMMITMENT_2: &str =
"30f3f03bd739024dc5b1e9d422745a7f32b0971d5cef302106b30bd9f5642d70";
const BINDING_COMMITMENT_2: &str =
"a7ccae3750846fbd7d132efec85e96236a711b2097a6f03b1afa04f6029458cc";
const IDENTIFIER_3: &str = "3";
const HIDING_COMMITMENT_3: &str =
"d31bd81ce216b1c83912803a574a0285796275cb8b14f6dc92c8b09a6951f0a2";
const BINDING_COMMITMENT_3: &str =
"e1c863cfd08df775b6747ef2456e9bf9a03cc281a479a95261dc39137fcf0967";
#[test]
fn check_valid_round_2_inputs() {
// TODO: refactor
let my_signer_commitments = SigningCommitments::new(
Identifier::try_from(1).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(MY_HIDING_COMMITMENT).unwrap()).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(MY_BINDING_COMMITMENT).unwrap()).unwrap(),
);
let signer_commitments_2 = SigningCommitments::new(
Identifier::try_from(2).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(HIDING_COMMITMENT_2).unwrap()).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(BINDING_COMMITMENT_2).unwrap()).unwrap(),
);
let signer_commitments_3 = SigningCommitments::new(
Identifier::try_from(3).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(HIDING_COMMITMENT_3).unwrap()).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(BINDING_COMMITMENT_3).unwrap()).unwrap(),
);
let config = Round2Config {
message: hex::decode("15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673")
.unwrap(),
signer_commitments: vec![
my_signer_commitments,
signer_commitments_2,
signer_commitments_3,
],
};
let mut test_logger = TestLogger(Vec::new());
let input = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
"3",
MESSAGE,
IDENTIFIER_2,
HIDING_COMMITMENT_2,
BINDING_COMMITMENT_2,
IDENTIFIER_3,
HIDING_COMMITMENT_3,
BINDING_COMMITMENT_3
);
let mut valid_input = input.as_bytes();
let expected =
round_2_request_inputs(my_signer_commitments, &mut valid_input, &mut test_logger).unwrap();
assert_eq!(expected.message, config.message);
// TODO: This is easily resolved in the latest release of Frost which includes the Debug trait
// assert_eq!(expected.signer_commitments[&Identifier::try_from(1).unwrap()], config.signer_commitments[&Identifier::try_from(1).unwrap()]);
}
// TODO: test for invalid inputs
#[test]
fn check_sign() {
let config = Round1Config {
identifier: Identifier::try_from(1).unwrap(),
public_key: VerifyingShare::from_bytes(<[u8; 32]>::from_hex(PUBLIC_KEY).unwrap()).unwrap(),
group_public_key: VerifyingKey::from_hex(GROUP_PUBLIC_KEY).unwrap(),
signing_share: SigningShare::from_bytes(<[u8; 32]>::from_hex(SIGNING_SHARE).unwrap())
.unwrap(),
vss_commitment: hex::decode(VSS_COMMITMENT).unwrap(),
};
let key_package = KeyPackage::new(
config.identifier,
config.signing_share,
config.public_key,
config.group_public_key,
);
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(
Identifier::try_from(1).unwrap(),
&SigningShare::from_hex(SIGNING_SHARE).unwrap(),
&mut rng,
);
let signer_commitments_2 = SigningCommitments::new(
Identifier::try_from(2).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(HIDING_COMMITMENT_2).unwrap()).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(BINDING_COMMITMENT_2).unwrap()).unwrap(),
);
let signer_commitments_3 = SigningCommitments::new(
Identifier::try_from(3).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(HIDING_COMMITMENT_3).unwrap()).unwrap(),
NonceCommitment::from_bytes(<[u8; 32]>::from_hex(BINDING_COMMITMENT_3).unwrap()).unwrap(),
);
let config = Round2Config {
message: hex::decode("15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673")
.unwrap(),
signer_commitments: vec![my_commitments, signer_commitments_2, signer_commitments_3],
};
let signature = generate_signature(config, &key_package, &nonces);
assert!(signature.is_ok()) // TODO: Should be able to test this more specifically when I remove randomness from the test
}
#[test]
fn check_print_values_round_2() {
let mut test_logger = TestLogger(Vec::new());
const IDENTIFIER: &str = "0100000000000000000000000000000000000000000000000000000000000000";
const SIGNATURE_RESPONSE: &str =
"44055c54d0604cbd006f0d1713a22474d7735c5e8816b1878f62ca94bf105900";
let signature_response =
SignatureResponse::from_bytes(<[u8; 32]>::from_hex(SIGNATURE_RESPONSE).unwrap()).unwrap();
let signature = SignatureShare::new(Identifier::try_from(1).unwrap(), signature_response);
print_values_round_2(signature, &mut test_logger);
let log = [
"Please send the following to the Coordinator".to_string(),
format!(
"Signature share: {}",
IDENTIFIER.to_string() + SIGNATURE_RESPONSE
),
"=== End of Round 2 ===".to_string(),
];
assert_eq!(test_logger.0, log);
}

View File

@ -1,2 +1,86 @@
use std::collections::HashMap;
use frost::SigningPackage;
use frost_ed25519 as frost;
use participant::round1::Round1Config;
use participant::round2::{generate_signature, Round2Config};
use rand::thread_rng;
fn encode_commitment_helper(commitment: Vec<[u8; 32]>) -> String {
let len_test = commitment.len() as u8;
let mut out = hex::encode([len_test]);
for c in commitment {
out = out + &hex::encode(c)
}
out
}
#[test]
fn check_test() {}
fn check_participant() {
let mut rng = thread_rng();
let (shares, pubkeys) = frost::keys::generate_with_dealer(3, 2, &mut rng).unwrap();
let mut key_packages: HashMap<_, _> = HashMap::new();
for (k, v) in shares.clone() {
let key_package = frost::keys::KeyPackage::try_from(v).unwrap();
key_packages.insert(k, key_package);
}
// Round 1
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();
for i in shares.keys() {
let config = Round1Config {
identifier: *i,
public_key: pubkeys.signer_pubkeys()[i],
group_public_key: *pubkeys.group_public(),
signing_share: *shares[i].secret(),
vss_commitment: hex::decode(encode_commitment_helper(
shares[i].commitment().serialize(),
))
.unwrap(),
};
let (nonce, commitment) = frost::round1::commit(
config.identifier,
key_packages[&config.identifier].secret_share(),
&mut rng,
);
nonces.insert(config.identifier, nonce);
commitments.insert(config.identifier, commitment);
}
// Coordinator sends message
let message = "a message".as_bytes().to_vec();
// Round 2
let mut signature_shares = Vec::new();
for participant_identifier in nonces.keys() {
let config = Round2Config {
message: message.clone(),
signer_commitments: commitments.values().cloned().collect(),
};
let signature = generate_signature(
config,
&key_packages[participant_identifier],
&nonces[participant_identifier],
)
.unwrap();
signature_shares.push(signature);
}
// Coordinator aggregates signatures
let signing_package = SigningPackage::new(commitments.values().cloned().collect(), &message);
let group_signature =
frost::aggregate(&signing_package, &signature_shares[..], &pubkeys).unwrap();
let verify_signature = pubkeys.group_public().verify(&message, &group_signature);
assert!(verify_signature.is_ok());
}