Turn several functions into type methods, impl FromHex for some for test vectors

This commit is contained in:
Deirdre Connolly 2022-02-18 22:32:04 -05:00
parent a05286498e
commit c59d36d508
8 changed files with 299 additions and 212 deletions

View File

@ -19,12 +19,12 @@ features = ["nightly"]
[dependencies]
byteorder = "1.4"
curve25519-dalek = "4.0.0-pre.1"
curve25519-dalek = { version = "4.0.0-pre.1", features = ["serde"] }
digest = "0.9"
hex = "0.4.3"
hex = { version = "0.4.3", features = ["serde"] }
rand_core = "0.6"
serde = { version = "1", optional = true, features = ["derive"] }
sha2 = "0.9.0"
sha2 = "0.10.2"
thiserror = "1.0"
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }

View File

@ -27,6 +27,7 @@ use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT, ristretto::RistrettoPoint, scalar::Scalar,
traits::Identity,
};
use hex::{FromHex, ToHex};
use rand_core::{CryptoRng, RngCore};
use zeroize::DefaultIsZeroes;
@ -50,6 +51,19 @@ impl From<Scalar> for Secret {
}
}
impl FromHex for Secret {
type Error = &'static str;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let mut bytes = [0u8; 32];
match hex::decode_to_slice(hex, &mut bytes[..]) {
Ok(()) => Secret::try_from(bytes),
Err(_) => Err("invalid hex"),
}
}
}
impl TryFrom<[u8; 32]> for Secret {
type Error = &'static str;
@ -422,6 +436,36 @@ impl From<(u64, &SigningNonces)> for SigningCommitments {
}
}
/// Encode the list of group signing commitments.
///
/// Inputs:
/// - commitment_list = [(j, D_j, E_j), ...], a list of commitments issued by each signer,
/// where each element in the list indicates the signer index and their
/// two commitment Element values. B MUST be sorted in ascending order
/// by signer index.
///
/// Outputs:
/// - A byte string containing the serialized representation of B.
///
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding>
fn encode_group_commitments(signing_commitments: Vec<SigningCommitments>) -> Vec<u8> {
// B MUST be sorted in ascending order by signer index.
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
let mut sorted_signing_commitments = signing_commitments;
sorted_signing_commitments.sort_by_key(|a| a.index);
let mut bytes = vec![];
for item in sorted_signing_commitments {
bytes.extend_from_slice(&item.index.to_be_bytes()[..]);
bytes.extend_from_slice(&item.hiding.0.compress().to_bytes()[..]);
bytes.extend_from_slice(&item.binding.0.compress().to_bytes()[..]);
}
bytes
}
/// Generated by the coordinator of the signing operation and distributed to
/// each signing party
#[derive(Debug)]
@ -436,6 +480,37 @@ pub struct SigningPackage {
message: Vec<u8>,
}
impl TryFrom<&SigningPackage> for GroupCommitment {
type Error = &'static str;
/// Generates the group commitment which is published as part of the joint
/// Schnorr signature.
// fn generate_group_commitment(
// signing_package: &SigningPackage,
// ) -> Result<GroupCommitment, &'static str> {
fn try_from(signing_package: &SigningPackage) -> Result<GroupCommitment, &'static str> {
let rho: Rho = signing_package.into();
let identity = RistrettoPoint::identity();
let mut accumulator = identity;
// Ala the sorting of B, just always sort by index in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for commitment in signing_package.signing_commitments() {
// The following check prevents a party from accidentally revealing their share.
// Note that the '&&' operator would be sufficient.
if identity == commitment.binding.0 || identity == commitment.hiding.0 {
return Err("Commitment equals the identity.");
}
accumulator += commitment.hiding.0 + (commitment.binding.0 * rho.0)
}
Ok(GroupCommitment(accumulator))
}
}
impl SigningPackage {
/// Create a new `SigingPackage`
///
@ -461,9 +536,36 @@ impl SigningPackage {
pub fn message(&self) -> &Vec<u8> {
&self.message
}
/// Compute the preimage to H3 to compute rho
// We separate this out into its own method so it can be tested
fn rho_preimage(&self) -> Vec<u8> {
let mut preimage = vec![];
preimage.extend_from_slice(&encode_group_commitments(self.signing_commitments.clone())[..]);
preimage.extend_from_slice(&H3(self.message.as_slice()));
preimage
}
}
// TODO(dconnolly): impl From<SigningPackage> for Rho or something
/// The binding factor, alos known as _rho_ (ρ)
///
/// Ensures each signature share is strongly bound to a signing set, specific set
/// of commitments, and a specific message.
///
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
struct Rho(Scalar);
impl From<&SigningPackage> for Rho {
fn from(signing_package: &SigningPackage) -> Rho {
let preimage = signing_package.rho_preimage();
let binding_factor = H1(&preimage[..]);
Rho(Scalar::from_bytes_mod_order_wide(&binding_factor))
}
}
/// A representation of a single signature used in FROST structures and
/// messages.
@ -537,82 +639,6 @@ where
(signing_nonces, signing_commitments)
}
/// Encode the list of group signing commitments.
///
/// Inputs:
/// - commitment_list = [(j, D_j, E_j), ...], a list of commitments issued by each signer,
/// where each element in the list indicates the signer index and their
/// two commitment Element values. B MUST be sorted in ascending order
/// by signer index.
///
/// Outputs:
/// - A byte string containing the serialized representation of B.
///
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding>
fn encode_group_commitments(signing_commitments: Vec<SigningCommitments>) -> Vec<u8> {
// B MUST be sorted in ascending order by signer index.
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
let mut sorted_signing_commitments = signing_commitments;
sorted_signing_commitments.sort_by_key(|a| a.index);
let mut bytes = vec![];
for item in sorted_signing_commitments {
bytes.extend_from_slice(&item.index.to_be_bytes()[..]);
bytes.extend_from_slice(&item.hiding.0.compress().to_bytes()[..]);
bytes.extend_from_slice(&item.binding.0.compress().to_bytes()[..]);
}
bytes
}
/// Generates the binding factor that ensures each signature share is strongly
/// bound to a signing set, specific set of commitments, and a specific message.
// TODO(dconnolly): turn this into `impl From<SigningPackage> for Rho` where `struct Rho(Scalar);`
fn generate_rho(signing_package: &SigningPackage) -> Scalar {
let mut preimage = vec![];
preimage.extend_from_slice(
&encode_group_commitments(signing_package.signing_commitments.clone())[..],
);
preimage.extend_from_slice(&H3(signing_package.message.as_slice()));
let binding_factor = H1(&preimage[..]);
Scalar::from_bytes_mod_order_wide(&binding_factor)
}
/// Generates the group commitment which is published as part of the joint
/// Schnorr signature.
fn generate_group_commitment(
signing_package: &SigningPackage,
bindings: &HashMap<u64, Scalar>,
) -> Result<GroupCommitment, &'static str> {
// TODO(dconnolly): tidy up now that rho is the same for all i's
let identity = RistrettoPoint::identity();
let mut accumulator = identity;
// Ala the sorting of B, just always sort by index in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for commitment in signing_package.signing_commitments() {
// The following check prevents a party from accidentally revealing their share.
// Note that the '&&' operator would be sufficient.
if identity == commitment.binding.0 || identity == commitment.hiding.0 {
return Err("Commitment equals the identity.");
}
let rho_i = bindings
.get(&commitment.index)
.ok_or("No matching commitment index")?;
accumulator += commitment.hiding.0 + (commitment.binding.0 * rho_i)
}
Ok(GroupCommitment(accumulator))
}
/// Generates the lagrange coefficient for the i'th participant.
fn generate_lagrange_coeff(
signer_index: u64,
@ -655,23 +681,9 @@ pub fn sign(
participant_nonces: &SigningNonces,
key_package: &KeyPackage,
) -> Result<SignatureShare, &'static str> {
// TODO(dconnolly): tidy up now that rho is the same for all i's
let mut bindings: HashMap<u64, Scalar> =
HashMap::with_capacity(signing_package.signing_commitments.len());
let rho = generate_rho(signing_package);
// Ala the sorting of B, just always sort by index in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for comm in signing_package.signing_commitments() {
bindings.insert(comm.index, rho);
}
let lambda_i = generate_lagrange_coeff(key_package.index, signing_package)?;
let group_commitment = generate_group_commitment(signing_package, &bindings)?;
let group_commitment = GroupCommitment::try_from(signing_package)?;
let challenge = generate_challenge(
&group_commitment.0.compress().to_bytes(),
@ -679,9 +691,11 @@ pub fn sign(
signing_package.message.as_slice(),
);
let rho: Rho = signing_package.into();
// The Schnorr signature share
let signature: Scalar = participant_nonces.hiding
+ (participant_nonces.binding * rho)
+ (participant_nonces.binding * rho.0)
+ (lambda_i * key_package.secret_share.0 * challenge);
Ok(SignatureShare {
@ -710,18 +724,7 @@ pub fn aggregate(
signing_shares: &[SignatureShare],
pubkeys: &PublicKeyPackage,
) -> Result<Signature, &'static str> {
// TODO(dconnolly): tidy up now that rho is the same for all i's
let mut bindings: HashMap<u64, Scalar> =
HashMap::with_capacity(signing_package.signing_commitments.len());
let rho = generate_rho(signing_package);
for comm in signing_package.signing_commitments.iter() {
bindings.insert(comm.index, rho);
}
let group_commitment = generate_group_commitment(signing_package, &bindings)?;
let group_commitment = GroupCommitment::try_from(signing_package)?;
let challenge = generate_challenge(
&group_commitment.0.compress().to_bytes(),
@ -729,6 +732,8 @@ pub fn aggregate(
signing_package.message.as_slice(),
);
let rho = Rho::from(signing_package);
for signing_share in signing_shares {
let signer_pubkey = pubkeys.signer_pubkeys[&signing_share.index];
let lambda_i = generate_lagrange_coeff(signing_share.index, signing_package)?;
@ -738,8 +743,7 @@ pub fn aggregate(
.find(|comm| comm.index == signing_share.index)
.ok_or("No matching signing commitment for signer")?;
let commitment_i = signer_commitment.hiding.0
+ (signer_commitment.binding.0 * bindings[&signing_share.index]);
let commitment_i = signer_commitment.hiding.0 + (signer_commitment.binding.0 * rho.0);
signing_share.check_is_valid(&signer_pubkey, lambda_i, commitment_i, challenge)?;
}

View File

@ -6,7 +6,7 @@ use crate::frost::{self, *};
mod vectors;
use vectors::RISTRETTO255_SHA512;
use vectors::*;
fn reconstruct_secret(secret_shares: Vec<SecretShare>) -> Result<Scalar, &'static str> {
let numshares = secret_shares.len();
@ -71,41 +71,43 @@ fn check_sign_with_test_vectors() {
// let threshold = 3;
// let (shares, pubkeys) = frost::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
let config = &RISTRETTO255_SHA512["config"];
let inputs = &RISTRETTO255_SHA512["inputs"];
println!("{inputs}");
parse_test_vectors();
assert_eq!(hex::encode("test"), inputs["message"].as_str().unwrap());
// let config = &RISTRETTO255_SHA512["config"];
// let inputs = &RISTRETTO255_SHA512["inputs"];
// println!("{inputs}");
let mut signer_pubkeys: HashMap<u64, Public> =
HashMap::with_capacity(config["NUM_SIGNERS"].as_u64().unwrap() as usize);
// assert_eq!(hex::encode("test"), inputs["message"].as_str().unwrap());
let mut key_packages: Vec<KeyPackage> =
Vec::with_capacity(config["NUM_SIGNERS"].as_u64().unwrap() as usize);
// let mut signer_pubkeys: HashMap<u64, Public> =
// HashMap::with_capacity(config["NUM_SIGNERS"].as_u64().unwrap() as usize);
for (i, secret_share) in RISTRETTO255_SHA512["inputs"]["signers"]
.as_object()
.unwrap()
{
// TODO: parse test vector bytes into `SecretShare`, turn that .into() `SharePackage`
// let mut key_packages: Vec<KeyPackage> =
// Vec::with_capacity(config["NUM_SIGNERS"].as_u64().unwrap() as usize);
println!("{secret_share}");
// for (i, secret_share) in RISTRETTO255_SHA512["inputs"]["signers"]
// // .as_object()
// // .unwrap()
// {
// // TODO: parse test vector bytes into `SecretShare`, turn that .into() `SharePackage`
let secret = Secret::try_from(secret_share["value"].into()).unwrap();
let signer_public = secret.into();
// println!("{:?}", secret_share);
key_packages.push(KeyPackage {
index: u64::from_str(i).unwrap(),
secret_share: secret,
public: signer_public,
group_public: VerificationKey::try_from(<[u8; 32]>::from(
inputs["group_public_key"].as_object(),
))
.unwrap(),
});
// let secret: Secret = serde_json::from_str(secret_share.as_str().unwrap()).unwrap(); // Secret::try_from(secret_share["value"]).unwrap();
// let signer_public = secret.into();
signer_pubkeys.insert(u64::from_str(i).unwrap(), signer_public);
}
// // key_packages.push(KeyPackage {
// // index: u64::from_str(i).unwrap(),
// // secret_share: secret,
// // public: signer_public,
// // group_public: VerificationKey::try_from(<[u8; 32]>::from(
// // inputs["group_public_key"].as_object(),
// // ))
// // .unwrap(),
// // });
// signer_pubkeys.insert(u64::from_str(i).unwrap(), signer_public);
// }
// let mut nonces: HashMap<u64, Vec<frost::SigningNonces>> =
// HashMap::with_capacity(threshold as usize);

View File

@ -0,0 +1,61 @@
{
"config": {
"MAX_SIGNERS": "3",
"NUM_SIGNERS": "2",
"THRESHOLD_LIMIT": "2",
"name": "FROST(ristretto255, SHA-512)",
"group": "ristretto255",
"hash": "SHA-512"
},
"inputs": {
"group_secret_key": "b120be204b5e758960458ca9c4675b56b12a8faff2be9c94891d5e1cd75c880e",
"group_public_key": "563b80013f337deaa2a282af7b281bd70d2f501928a89c1aa48b379a5ac4202b",
"message": "74657374",
"signers": {
"1": {
"signer_share": "94ae65bb90030a89507fa00fff08dfed841cf996de5a0c574f1f4693ddcb6705"
},
"2": {
"signer_share": "641003b3f00bb1e01656ac1818a4419a580e637ecaf67b1915212e0ae43a470c"
},
"3": {
"signer_share": "479eaa4d36b145e00690c07e5245c5312c00cd65b692ebdbda221681eaa92603"
}
}
},
"round_one_outputs": {
"participants": "1,2",
"group_binding_factor_input": "0001824e9eddddf02b2a9caf5859825e999d791ca094f65b814a8bca6013d9cc312774c7e1271d2939a84a9a867e3a06579b4d25659b427439ccf0d745b43f75b76600028013834ff4d48e7d6b76c2e732bc611f54720ef8933c4ca4de7eaaa77ff5cd125e056ecc4f7c4657d3a742354430d768f945db229c335d258e9622ad99f3e7582d07b35bd9849ce4af6ad403090d69a7d0eb88bba669a9f985175d70cd15ad5f1ef5b734c98a32b4aab7b43a57e93fc09281f2e7a207076b31e416ba63f53d9d",
"group_binding_factor": "f00ae6007f2d74a1507c962cf30006be77596106db28f2d5443fd66d755e780c",
"signers": {
"1": {
"hiding_nonce": "349b3bb8464a1d87f7d6b56f4559a3f9a6335261a3266089a9b12d9d6f6ce209",
"binding_nonce": "ce7406016a854be4291f03e7d24fe30e77994c3465de031515a4c116f22ca901",
"hiding_nonce_commitment": "824e9eddddf02b2a9caf5859825e999d791ca094f65b814a8bca6013d9cc3127",
"binding_nonce_commitment": "74c7e1271d2939a84a9a867e3a06579b4d25659b427439ccf0d745b43f75b766"
},
"2": {
"hiding_nonce": "4d66d319f20a728ec3d491cbf260cc6be687bd87cc2b5fdb4d5f528f65fd650d",
"binding_nonce": "278b9b1e04632e6af3f1a3c144d07922ffcf5efd3a341b47abc19c43f48ce306",
"hiding_nonce_commitment": "8013834ff4d48e7d6b76c2e732bc611f54720ef8933c4ca4de7eaaa77ff5cd12",
"binding_nonce_commitment": "5e056ecc4f7c4657d3a742354430d768f945db229c335d258e9622ad99f3e758"
}
}
},
"round_two_outputs": {
"participants": "1,2",
"signers": {
"1": {
"sig_share": "ec6b075f17c5670e80b1fda8f6de1cfe3c79db06a852f8d5650fb71eaad69501",
"group_commitment_share": "bc7e792fce347a15d547935652377c406cc721965c58d3003dbd947a6dfddc0c"
},
"2": {
"sig_share": "87ceccc477069aa9b751b307f25955daaf943a3abc51f214a114781de0f58e03",
"group_commitment_share": "92c4f352ec392ba779271dc2ed09cda37f38d8c283747d4a85b4c9ce7289cb07"
}
}
},
"final_output": {
"sig": "7e92309bf40993141acd5f2c7680a302cc5aa5dd291a833906da8e35bc39b03e733ad4238fcb01b83703b1b0e83872d8ec0d164164a4eaea06242f3c8acc2405"
}
}

View File

@ -1,79 +1,85 @@
use std::collections::HashMap;
use std::str::FromStr;
use hex;
use lazy_static::lazy_static;
use serde_json::Value;
const RISTRETTO255_SHA512_JSON: &str = r#"
{
"config": {
"NUM_SIGNERS": "3",
"THRESHOLD_LIMIT": "2",
"name": "FROST(ristretto255, SHA512)",
"group": "ristretto255",
"hash": "SHA-512"
},
"inputs": {
"group_secret_key": "ca8009d372fa61174ae16d422a216ff503eccfe12348a5a2e4b30e95fdd92909",
"group_public_key": "6acc470751d8954bc4bf3581c3f0d25d4d65ef818318de89f9500d288d8dcf05",
"message": "74657374",
"signers": {
"1": {
"signer_share": "1767555c694baffbb51ed5e75e3c199f35d025a7a0b40124d93d6dc824cf240d"
},
"2": {
"signer_share": "7779ab884539ea874bbf44eab45de43367b47b6c1d215ea5cdc7cbfb4bc41f01"
},
"3": {
"signer_share": "c45ff7113c8a376cb7fcab8fe9788edd9898d1319a8dba26c2512a2f73b91a05"
}
}
},
"round_one_outputs": {
"participants": [
"1",
"2"
],
"commitment_list": "00013294581c296781c6fdb2696b2a8d08f961311e15b4bc3614daec6a19a78cd77a42a3cfa235deb33e7f99a21f4e22b7090d9c278f2613664dff607115cecedf58000208659fe4a31c1775caa4488f669324460b5d972df8983a6cf8e624f79906a639285fd985bb44d53b28b6523aa1c459a94bface28ab2f4fcdbc5215df5d9be234f226c1530c93fbfe1a29f34aa2e13da14ace01b6e6412e36d5e01baba2c78e4921dc1c0b7143210bb0fc42553c3a9490ba011e30250727c0189372a38632591f",
"group_binding_factor": "190c206d7368cc7cdf8539680f45a82836889db034464d9f9cc13c970fc9d20e",
"outputs": {
"1": {
"hiding_nonce": "7e119fcff436f4817fbcd1b09e82d7d2ff6dd433b0f81e012cadd4662282b809",
"binding_nonce": "3b3bbe82babf2a67ded81b308ba45f73b88f6cf3f6aaa4442256b7a0a6a9e20c",
"hiding_nonce_commitment": "3294581c296781c6fdb2696b2a8d08f961311e15b4bc3614daec6a19a78cd77a",
"binding_nonce_commitment": "42a3cfa235deb33e7f99a21f4e22b7090d9c278f2613664dff607115cecedf58"
},
"2": {
"hiding_nonce": "488cfde0a2bba98ba4c3e65645e1b77386eb4063e497801fbbbd112ad1f7d708",
"binding_nonce": "f33b7cd25041000d7935823cb0c99503afac2860b1c099435eb472bb29329e02",
"hiding_nonce_commitment": "08659fe4a31c1775caa4488f669324460b5d972df8983a6cf8e624f79906a639",
"binding_nonce_commitment": "285fd985bb44d53b28b6523aa1c459a94bface28ab2f4fcdbc5215df5d9be234"
}
}
},
"round_two_outputs": {
"participants": [
"1",
"2"
],
"outputs": {
"1": {
"sig_share": "0f03834e01fc2447135f694a5d1d6493f7a21fcf6fc41191118000e8b9a29306",
"group_commitment_share": "76818272e9bd5d71f062dce08687919850aaf4b8cfd135ad70e50e0267d87246"
},
"2": {
"sig_share": "08442979523a3e335cf4c03fe58cdcec25841e64f5a4f2ac656730c1fee43509",
"group_commitment_share": "62e97479cb0df12293aadfa88e5d1caa7f82d548f77eaa2408fe4bca61916439"
}
}
},
"final_output": {
"sig": {
"R": "56604a3b6ca135e56f5d68d2f6496e3e0e9b9ec691a3790f5e8311d24d75ce13",
"z": "1747acc75336637a6f532a8a42aa40801d273e336569043e77e730a9b887c90f"
}
}
}
"#;
use crate::frost::*;
lazy_static! {
pub static ref RISTRETTO255_SHA512: Value =
serde_json::from_str(RISTRETTO255_SHA512_JSON).expect("Test vector is valid JSON");
serde_json::from_str(include_str!("vectors.json").trim())
.expect("Test vector is valid JSON");
}
pub fn parse_test_vectors() {
let config = &RISTRETTO255_SHA512["config"];
let inputs = &RISTRETTO255_SHA512["inputs"];
println!("{inputs}");
assert_eq!(hex::encode("test"), inputs["message"].as_str().unwrap());
let mut signer_pubkeys: HashMap<u64, Public> = HashMap::new();
let mut key_packages: Vec<KeyPackage> = Vec::new();
let possible_signers = RISTRETTO255_SHA512["inputs"]["signers"]
.as_object()
.unwrap()
.iter();
for (i, secret_share) in possible_signers {
let secret = Secret::from_hex(secret_share["signer_share"].as_str().unwrap()).unwrap();
let signer_public = secret.into();
let key_package = KeyPackage {
index: u64::from_str(i).unwrap(),
secret_share: secret,
public: signer_public,
group_public: VerificationKey::from_hex(inputs["group_public_key"].as_str().unwrap())
.unwrap(),
};
key_packages.push(key_package);
signer_pubkeys.insert(u64::from_str(i).unwrap(), signer_public);
}
// let mut nonces: HashMap<u64, Vec<frost::SigningNonces>> =
// HashMap::with_capacity(threshold as usize);
// let mut commitments: Vec<frost::SigningCommitments> = Vec::with_capacity(threshold as usize);
// // Round 1, generating nonces and signing commitments for each participant.
// for participant_index in 1..(threshold + 1) {
// // Generate one (1) nonce and one SigningCommitments instance for each
// // participant, up to _threshold_.
// let (nonce, commitment) = frost::preprocess(1, participant_index as u64, &mut rng);
// nonces.insert(participant_index as u64, nonce);
// commitments.push(commitment[0]);
// }
// // This is what the signature aggregator / coordinator needs to do:
// // - decide what message to sign
// // - take one (unused) commitment per signing participant
// let mut signature_shares: Vec<frost::SignatureShare> = Vec::with_capacity(threshold as usize);
// let message = "message to sign".as_bytes();
// let signing_package = frost::SigningPackage::new(commitments, message.to_vec());
// // Round 2: each participant generates their signature share
// for (participant_index, nonce) in &nonces {
// let share_package = shares
// .iter()
// .find(|share| *participant_index == share.index)
// .unwrap();
// let nonce_to_use = nonce[0];
// // Each participant generates their signature share.
// let signature_share = frost::sign(&signing_package, &nonce_to_use, share_package).unwrap();
// signature_shares.push(signature_share);
// }
// // The aggregator collects the signing shares from all participants and
// // generates the final signature.
// let group_signature_res = frost::aggregate(&signing_package, &signature_shares[..], &pubkeys);
}

View File

@ -12,7 +12,7 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::{digest::Update, scalar::Scalar};
use sha2::{Digest, Sha512};
pub mod batch;

View File

@ -10,7 +10,7 @@
use std::convert::{TryFrom, TryInto};
use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, scalar::Scalar};
use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, digest::Update, scalar::Scalar};
use rand_core::{CryptoRng, RngCore};
use sha2::{Digest, Sha512};

View File

@ -15,6 +15,7 @@ use curve25519_dalek::{
scalar::Scalar,
traits::Identity,
};
use hex::FromHex;
use crate::{Error, Signature};
@ -77,6 +78,19 @@ impl From<VerificationKey> for [u8; 32] {
}
}
impl FromHex for VerificationKey {
type Error = Error;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let mut bytes = [0u8; 32];
match hex::decode_to_slice(hex, &mut bytes[..]) {
Ok(()) => Self::try_from(bytes),
Err(_) => Err(Error::MalformedVerificationKey),
}
}
}
impl TryFrom<VerificationKeyBytes> for VerificationKey {
type Error = Error;