Turn several functions into type methods, impl FromHex for some for test vectors
This commit is contained in:
parent
a05286498e
commit
c59d36d508
|
@ -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"] }
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue