add Ed25519 ciphersuite (#164)
* add Ed25519 ciphersuite * clippy fixes * fixes after rebase * update share generation test name * use max/min_signers * expand authors Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
parent
201d6adc4d
commit
ac5f44ade8
|
@ -5,4 +5,6 @@ members = [
|
|||
#"frost-redjubjub",
|
||||
"frost-ristretto255",
|
||||
"frost-p256",
|
||||
"frost-ed25519",
|
||||
"gendoc"
|
||||
]
|
||||
|
|
|
@ -33,4 +33,7 @@ pub enum Error {
|
|||
/// This element MUST NOT be the identity.
|
||||
#[error("Invalid for this element to be the identity.")]
|
||||
InvalidIdentityElement,
|
||||
/// This element MUST have (large) prime order.
|
||||
#[error("Invalid for this element to not have large prime order.")]
|
||||
InvalidNonPrimeOrderElement,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
[package]
|
||||
name = "frost-ed25519"
|
||||
edition = "2021"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url
|
||||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>",
|
||||
"Conrado Gouvea <conradoplg@gmail.com>"]
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/ZcashFoundation/frost"
|
||||
categories = ["cryptography"]
|
||||
keywords = ["cryptography", "crypto", "ristretto", "threshold", "signature"]
|
||||
description = "A Schnorr signature scheme over the prime-order Ristretto group that supports FROST ."
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["nightly"]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4"
|
||||
curve25519-dalek = { version = "4.0.0-pre.1", features = ["serde"] }
|
||||
digest = "0.10"
|
||||
frost-core = { path = "../frost-core", features = ["test-impl"] }
|
||||
frost-ristretto255 = { path = "../frost-ristretto255" }
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
rand_core = "0.6"
|
||||
serde = { version = "1", optional = true, features = ["derive"] }
|
||||
sha2 = "0.10.2"
|
||||
thiserror = "1.0"
|
||||
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
bincode = "1"
|
||||
criterion = "0.4"
|
||||
ed25519-dalek = "1.0.1"
|
||||
ed25519-zebra = "3.0.0"
|
||||
lazy_static = "1.4"
|
||||
proptest = "1.0"
|
||||
proptest-derive = "0.3"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
serde_json = "1.0"
|
||||
|
||||
[features]
|
||||
nightly = []
|
||||
default = ["serde"]
|
|
@ -0,0 +1,32 @@
|
|||
An implementation of Schnorr signatures on the Ristretto group for both single and threshold numbers
|
||||
of signers (FROST).
|
||||
|
||||
## Examples
|
||||
|
||||
Creating a `Signature` with a single signer, serializing and deserializing it, and verifying the
|
||||
signature:
|
||||
|
||||
```rust
|
||||
use rand::thread_rng;
|
||||
use frost_ed25519::*;
|
||||
|
||||
let msg = b"Hello!";
|
||||
|
||||
// Generate a secret key and sign the message
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let sig = sk.sign(thread_rng(), msg);
|
||||
|
||||
// Types can be converted to raw byte arrays using `from_bytes`/`to_bytes`
|
||||
let sig_bytes = sig.to_bytes();
|
||||
let pk_bytes = VerifyingKey::from(&sk).to_bytes();
|
||||
|
||||
// Deserialize and verify the signature.
|
||||
let sig = Signature::from_bytes(sig_bytes)?;
|
||||
|
||||
assert!(
|
||||
VerifyingKey::from_bytes(pk_bytes)
|
||||
.and_then(|pk| pk.verify(msg, &sig))
|
||||
.is_ok()
|
||||
);
|
||||
# Ok::<(), Error>(())
|
||||
```
|
|
@ -0,0 +1,304 @@
|
|||
#![allow(non_snake_case)]
|
||||
#![deny(missing_docs)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_POINT,
|
||||
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||
scalar::Scalar,
|
||||
traits::Identity,
|
||||
};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{digest::Update, Digest, Sha512};
|
||||
|
||||
use frost_core::{frost, Ciphersuite, Field, Group};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use frost_core::Error;
|
||||
|
||||
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite scalar field.
|
||||
pub type Ed25519ScalarField = frost_ristretto255::RistrettoScalarField;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite group.
|
||||
pub struct Ed25519Group;
|
||||
|
||||
impl Group for Ed25519Group {
|
||||
type Field = Ed25519ScalarField;
|
||||
|
||||
type Element = EdwardsPoint;
|
||||
|
||||
type Serialization = [u8; 32];
|
||||
|
||||
fn cofactor() -> <Self::Field as Field>::Scalar {
|
||||
Scalar::one()
|
||||
}
|
||||
|
||||
fn identity() -> Self::Element {
|
||||
EdwardsPoint::identity()
|
||||
}
|
||||
|
||||
fn generator() -> Self::Element {
|
||||
ED25519_BASEPOINT_POINT
|
||||
}
|
||||
|
||||
fn serialize(element: &Self::Element) -> Self::Serialization {
|
||||
element.compress().to_bytes()
|
||||
}
|
||||
|
||||
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
|
||||
match CompressedEdwardsY::from_slice(buf.as_ref()).decompress() {
|
||||
Some(point) => {
|
||||
if point == Self::identity() {
|
||||
Err(Error::InvalidIdentityElement)
|
||||
} else if point.is_torsion_free() {
|
||||
Ok(point)
|
||||
} else {
|
||||
Err(Error::InvalidNonPrimeOrderElement)
|
||||
}
|
||||
}
|
||||
None => Err(Error::MalformedElement),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context string 'FROST-RISTRETTO255-SHA512-v5' from the ciphersuite in the [spec]
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-1
|
||||
const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v11";
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite.
|
||||
pub struct Ed25519Sha512;
|
||||
|
||||
impl Ciphersuite for Ed25519Sha512 {
|
||||
type Group = Ed25519Group;
|
||||
|
||||
type HashOutput = [u8; 64];
|
||||
|
||||
type SignatureSerialization = [u8; 64];
|
||||
|
||||
/// H1 for FROST(Ed25519, SHA-512)
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.1
|
||||
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
let h = Sha512::new()
|
||||
.chain(CONTEXT_STRING.as_bytes())
|
||||
.chain("rho")
|
||||
.chain(m);
|
||||
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(h.finalize().as_slice());
|
||||
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
|
||||
}
|
||||
|
||||
/// H2 for FROST(Ed25519, SHA-512)
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.2
|
||||
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
let h = Sha512::new().chain(m);
|
||||
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(h.finalize().as_slice());
|
||||
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
|
||||
}
|
||||
|
||||
/// H3 for FROST(Ed25519, SHA-512)
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.3
|
||||
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
|
||||
let h = Sha512::new()
|
||||
.chain(CONTEXT_STRING.as_bytes())
|
||||
.chain("nonce")
|
||||
.chain(m);
|
||||
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(h.finalize().as_slice());
|
||||
<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output)
|
||||
}
|
||||
|
||||
/// H4 for FROST(Ed25519, SHA-512)
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.4
|
||||
fn H4(m: &[u8]) -> Self::HashOutput {
|
||||
let h = Sha512::new()
|
||||
.chain(CONTEXT_STRING.as_bytes())
|
||||
.chain("msg")
|
||||
.chain(m);
|
||||
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(h.finalize().as_slice());
|
||||
output
|
||||
}
|
||||
|
||||
/// H5 for FROST(Ed25519, SHA-512)
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-6.1-2.2.2.5
|
||||
fn H5(m: &[u8]) -> Self::HashOutput {
|
||||
let h = Sha512::new()
|
||||
.chain(CONTEXT_STRING.as_bytes())
|
||||
.chain("com")
|
||||
.chain(m);
|
||||
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(h.finalize().as_slice());
|
||||
output
|
||||
}
|
||||
|
||||
/// HDKG for FROST(Ed25519, SHA-512)
|
||||
fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||
let h = Sha512::new()
|
||||
.chain(CONTEXT_STRING.as_bytes())
|
||||
.chain("dkg")
|
||||
.chain(m);
|
||||
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(h.finalize().as_slice());
|
||||
Some(<<Self::Group as Group>::Field as Field>::Scalar::from_bytes_mod_order_wide(&output))
|
||||
}
|
||||
}
|
||||
|
||||
type E = Ed25519Sha512;
|
||||
|
||||
/// A FROST(Ed25519, SHA-512) participant identifier.
|
||||
pub type Identifier = frost::Identifier<E>;
|
||||
|
||||
/// FROST(Ed25519, SHA-512) keys, key generation, key shares.
|
||||
pub mod keys {
|
||||
use super::*;
|
||||
|
||||
/// Allows all participants' keys to be generated using a central, trusted
|
||||
/// dealer.
|
||||
pub fn keygen_with_dealer<RNG: RngCore + CryptoRng>(
|
||||
max_signers: u16,
|
||||
min_signers: u16,
|
||||
mut rng: RNG,
|
||||
) -> Result<(Vec<SecretShare>, PublicKeyPackage), &'static str> {
|
||||
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
|
||||
}
|
||||
|
||||
/// Secret and public key material generated by a dealer performing
|
||||
/// [`keygen_with_dealer`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// To derive a FROST(Ed25519, SHA-512) keypair, the receiver of the [`SecretShare`] *must* call
|
||||
/// .into(), which under the hood also performs validation.
|
||||
pub type SecretShare = frost::keys::SecretShare<E>;
|
||||
|
||||
/// A FROST(Ed25519, SHA-512) keypair, which can be generated either by a trusted dealer or using
|
||||
/// a DKG.
|
||||
///
|
||||
/// When using a central dealer, [`SecretShare`]s are distributed to
|
||||
/// participants, who then perform verification, before deriving
|
||||
/// [`KeyPackage`]s, which they store to later use during signing.
|
||||
pub type KeyPackage = frost::keys::KeyPackage<E>;
|
||||
|
||||
/// Public data that contains all the signers' public keys as well as the
|
||||
/// group public key.
|
||||
///
|
||||
/// Used for verification purposes before publishing a signature.
|
||||
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<E>;
|
||||
}
|
||||
|
||||
/// FROST(Ed25519, SHA-512) Round 1 functionality and types.
|
||||
pub mod round1 {
|
||||
use frost_core::frost::keys::SigningShare;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Comprised of FROST(Ed25519, SHA-512) hiding and binding nonces.
|
||||
///
|
||||
/// Note that [`SigningNonces`] must be used *only once* for a signing
|
||||
/// operation; re-using nonces will result in leakage of a signer's long-lived
|
||||
/// signing key.
|
||||
pub type SigningNonces = frost::round1::SigningNonces<E>;
|
||||
|
||||
/// Published by each participant in the first round of the signing protocol.
|
||||
///
|
||||
/// This step can be batched if desired by the implementation. Each
|
||||
/// SigningCommitment can be used for exactly *one* signature.
|
||||
pub type SigningCommitments = frost::round1::SigningCommitments<E>;
|
||||
|
||||
/// Performed once by each participant selected for the signing operation.
|
||||
///
|
||||
/// Generates the signing nonces and commitments to be used in the signing
|
||||
/// operation.
|
||||
pub fn commit<RNG>(
|
||||
participant_identifier: frost::Identifier<E>,
|
||||
secret: &SigningShare<E>,
|
||||
rng: &mut RNG,
|
||||
) -> (SigningNonces, SigningCommitments)
|
||||
where
|
||||
RNG: CryptoRng + RngCore,
|
||||
{
|
||||
frost::round1::commit::<E, RNG>(participant_identifier, secret, rng)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated by the coordinator of the signing operation and distributed to
|
||||
/// each signing party.
|
||||
pub type SigningPackage = frost::SigningPackage<E>;
|
||||
|
||||
/// FROST(Ed25519, SHA-512) Round 2 functionality and types, for signature share generation.
|
||||
pub mod round2 {
|
||||
use super::*;
|
||||
|
||||
/// A FROST(Ed25519, SHA-512) participant's signature share, which the Coordinator will aggregate with all other signer's
|
||||
/// shares into the joint signature.
|
||||
pub type SignatureShare = frost::round2::SignatureShare<E>;
|
||||
|
||||
/// Generated by the coordinator of the signing operation and distributed to
|
||||
/// each signing party
|
||||
pub type SigningPackage = frost::SigningPackage<E>;
|
||||
|
||||
/// Performed once by each participant selected for the signing operation.
|
||||
///
|
||||
/// Receives the message to be signed and a set of signing commitments and a set
|
||||
/// of randomizing commitments to be used in that signing operation, including
|
||||
/// that for this participant.
|
||||
///
|
||||
/// Assumes the participant has already determined which nonce corresponds with
|
||||
/// the commitment that was assigned by the coordinator in the SigningPackage.
|
||||
pub fn sign(
|
||||
signing_package: &SigningPackage,
|
||||
signer_nonces: &round1::SigningNonces,
|
||||
key_package: &keys::KeyPackage,
|
||||
) -> Result<SignatureShare, &'static str> {
|
||||
frost::round2::sign(signing_package, signer_nonces, key_package)
|
||||
}
|
||||
}
|
||||
|
||||
/// A Schnorr signature on FROST(Ed25519, SHA-512).
|
||||
pub type Signature = frost_core::Signature<E>;
|
||||
|
||||
/// Verifies each FROST(Ed25519, SHA-512) participant's signature share, and if all are valid,
|
||||
/// aggregates the shares into a signature to publish.
|
||||
///
|
||||
/// Resulting signature is compatible with verification of a plain Schnorr
|
||||
/// signature.
|
||||
///
|
||||
/// This operation is performed by a coordinator that can communicate with all
|
||||
/// the signing participants before publishing the final signature. The
|
||||
/// coordinator can be one of the participants or a semi-trusted third party
|
||||
/// (who is trusted to not perform denial of service attacks, but does not learn
|
||||
/// any secret information). Note that because the coordinator is trusted to
|
||||
/// report misbehaving parties in order to avoid publishing an invalid
|
||||
/// signature, if the coordinator themselves is a signer and misbehaves, they
|
||||
/// can avoid that step. However, at worst, this results in a denial of
|
||||
/// service attack due to publishing an invalid signature.
|
||||
pub fn aggregate(
|
||||
signing_package: &round2::SigningPackage,
|
||||
signature_shares: &[round2::SignatureShare],
|
||||
pubkeys: &keys::PublicKeyPackage,
|
||||
) -> Result<Signature, &'static str> {
|
||||
frost::aggregate(signing_package, signature_shares, pubkeys)
|
||||
}
|
||||
|
||||
/// A signing key for a Schnorr signature on FROST(Ed25519, SHA-512).
|
||||
pub type SigningKey = frost_core::SigningKey<E>;
|
||||
|
||||
/// A valid verifying key for Schnorr signatures on FROST(Ed25519, SHA-512).
|
||||
pub type VerifyingKey = frost_core::VerifyingKey<E>;
|
|
@ -0,0 +1,24 @@
|
|||
use lazy_static::lazy_static;
|
||||
use rand::thread_rng;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::*;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref ED25519_SHA512: Value =
|
||||
serde_json::from_str(include_str!("tests/vectors.json").trim())
|
||||
.expect("Test vector is valid JSON");
|
||||
}
|
||||
|
||||
/// This is testing that Shamir's secret sharing to compute and arbitrary
|
||||
/// value is working.
|
||||
#[test]
|
||||
fn check_share_generation_ed25519_sha512() {
|
||||
let rng = thread_rng();
|
||||
frost_core::tests::check_share_generation::<Ed25519Sha512, _>(rng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_sign_with_test_vectors() {
|
||||
frost_core::tests::vectors::check_sign_with_test_vectors::<Ed25519Sha512>(&ED25519_SHA512)
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"config": {
|
||||
"MAX_PARTICIPANTS": "3",
|
||||
"NUM_PARTICIPANTS": "2",
|
||||
"MIN_PARTICIPANTS": "2",
|
||||
"name": "FROST(Ed25519, SHA-512)",
|
||||
"group": "ed25519",
|
||||
"hash": "SHA-512"
|
||||
},
|
||||
"inputs": {
|
||||
"group_secret_key": "7b1c33d3f5291d85de664833beb1ad469f7fb6025a0ec78b3a790c6e13a98304",
|
||||
"group_public_key": "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673",
|
||||
"message": "74657374",
|
||||
"share_polynomial_coefficients": [
|
||||
"178199860edd8c62f5212ee91eff1295d0d670ab4ed4506866bae57e7030b204"
|
||||
],
|
||||
"participants": {
|
||||
"1": {
|
||||
"participant_share": "929dcc590407aae7d388761cddb0c0db6f5627aea8e217f4a033f2ec83d93509"
|
||||
},
|
||||
"2": {
|
||||
"participant_share": "a91e66e012e4364ac9aaa405fcafd370402d9859f7b6685c07eed76bf409e80d"
|
||||
},
|
||||
"3": {
|
||||
"participant_share": "d3cb090a075eb154e82fdb4b3cb507f110040905468bb9c46da8bdea643a9a02"
|
||||
}
|
||||
}
|
||||
},
|
||||
"round_one_outputs": {
|
||||
"participant_list": "1,3",
|
||||
"participants": {
|
||||
"1": {
|
||||
"hiding_nonce_randomness": "9d06a6381c7a4493929761a73692776772b274236fb5cfcc7d1b48ac3a9c249f",
|
||||
"binding_nonce_randomness": "db184d7bc01a3417fe1f2eb3cf5479bb027145e6369a5f879f32d334ab256b23",
|
||||
"hiding_nonce": "70652da3e8d7533a0e4b9e9104f01b48c396b5b553717784ed8d05c6a36b9609",
|
||||
"binding_nonce": "4f9e1ad260b5c0e4fe0e0719c6324f89fecd053758f77c957f56967e634a710e",
|
||||
"hiding_nonce_commitment": "44105304351ceddc58e15ddea35b2cb48e60ced54ceb22c3b0e5d42d098aa1d8",
|
||||
"binding_nonce_commitment": "b8274b18a12f2cef74ae42f876cec1e31daab5cb162f95a56cd2487409c9d1dd",
|
||||
"binding_factor_input": "c5b95020cba31a9035835f074f718d0c3af02a318d6b4723bbd1c088f4889dd7b9ff8e79f9a67a9d27605144259a7af18b7cca2539ffa5c4f1366a98645da8f4e077d604fff64f20e2377a37e5a10ce152194d62fe856ef4cd935d4f1cb0088c2083a2722ad3f5a84d778e257da0df2a7cadb004b1f5528352af778b94ee1c2a0100000000000000000000000000000000000000000000000000000000000000",
|
||||
"binding_factor": "2d5630c36d33258b1208c4205fa759b762d09bfa06b29cf792cf98758c0b3305"
|
||||
},
|
||||
"3": {
|
||||
"hiding_nonce_randomness": "31ca9b07936d6b342a43d97f23b7bec5a5f5a09575a075393868dd8df5c05a54",
|
||||
"binding_nonce_randomness": "c1db96a85d8b593e14fdb869c0955625478afa6a987ad217e7f2261dcab26819",
|
||||
"hiding_nonce": "233adcb0ec0eddba5f1cc5268f3f4e6fc1dd97fb1e4a1754e6ddc92ed834ca0b",
|
||||
"binding_nonce": "b59fc8a32fe02ec0a44c4671f3d1f82ea3924b7c7c0179398fc9137e82757803",
|
||||
"hiding_nonce_commitment": "d31bd81ce216b1c83912803a574a0285796275cb8b14f6dc92c8b09a6951f0a2",
|
||||
"binding_nonce_commitment": "e1c863cfd08df775b6747ef2456e9bf9a03cc281a479a95261dc39137fcf0967",
|
||||
"binding_factor_input": "c5b95020cba31a9035835f074f718d0c3af02a318d6b4723bbd1c088f4889dd7b9ff8e79f9a67a9d27605144259a7af18b7cca2539ffa5c4f1366a98645da8f4e077d604fff64f20e2377a37e5a10ce152194d62fe856ef4cd935d4f1cb0088c2083a2722ad3f5a84d778e257da0df2a7cadb004b1f5528352af778b94ee1c2a0300000000000000000000000000000000000000000000000000000000000000",
|
||||
"binding_factor": "1137be5cdf3d18e44367acee8485e9a66c3164077af80619b6291e3943bbef04"
|
||||
}
|
||||
}
|
||||
},
|
||||
"round_two_outputs": {
|
||||
"participant_list": "1,3",
|
||||
"participants": {
|
||||
"1": {
|
||||
"sig_share": "c4b26af1e91fbc8440a0dad253e72620da624553c5b625fd51e6ea179fc09f05"
|
||||
},
|
||||
"3": {
|
||||
"sig_share": "9369640967d0cb98f4dedfde58a845e0e18e0a7164396358439060ed282b4e08"
|
||||
}
|
||||
}
|
||||
},
|
||||
"final_output": {
|
||||
"sig": "ae11c539fdc709b78fef5ee1f5a2250297e3e1b62a86a86c26d93c389934ba0e571ccffa50f0871d357fbab1ac8f6c00bcf14fc429f0885595764b05c8ebed0d"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
use curve25519_dalek::{edwards::EdwardsPoint, traits::Identity};
|
||||
use frost_core::{Ciphersuite, Group};
|
||||
use frost_ed25519::*;
|
||||
use rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn check_sign_with_dealer() {
|
||||
let rng = thread_rng();
|
||||
|
||||
frost_core::tests::check_sign_with_dealer::<Ed25519Sha512, _>(rng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_sign_with_dkg() {
|
||||
let rng = thread_rng();
|
||||
|
||||
frost_core::tests::check_sign_with_dkg::<Ed25519Sha512, _>(rng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_batch_verify() {
|
||||
let rng = thread_rng();
|
||||
|
||||
frost_core::tests::batch::batch_verify::<Ed25519Sha512, _>(rng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_bad_batch_verify() {
|
||||
let rng = thread_rng();
|
||||
|
||||
frost_core::tests::batch::bad_batch_verify::<Ed25519Sha512, _>(rng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_deserialize_identity() {
|
||||
let encoded_identity = EdwardsPoint::identity().compress().to_bytes();
|
||||
|
||||
let r = <<Ed25519Sha512 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
|
||||
assert_eq!(r, Err(Error::InvalidIdentityElement));
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
use frost_core::tests::proptests::{tweak_strategy, SignatureCase};
|
||||
use frost_ed25519::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
use rand_chacha::ChaChaRng;
|
||||
use rand_core::SeedableRng;
|
||||
|
||||
proptest! {
|
||||
|
||||
#[test]
|
||||
fn tweak_signature(
|
||||
tweaks in prop::collection::vec(tweak_strategy(), (0,5)),
|
||||
rng_seed in prop::array::uniform32(any::<u8>()),
|
||||
) {
|
||||
// Use a deterministic RNG so that test failures can be reproduced.
|
||||
// Seeding with 64 bits of entropy is INSECURE and this code should
|
||||
// not be copied outside of this test!
|
||||
let mut rng = ChaChaRng::from_seed(rng_seed);
|
||||
|
||||
// Create a test case for each signature type.
|
||||
let msg = b"test message for proptests";
|
||||
let mut sig = SignatureCase::<Ed25519Sha512>::new(&mut rng, msg.to_vec());
|
||||
|
||||
// Apply tweaks to each case.
|
||||
for t in &tweaks {
|
||||
sig.apply_tweak(t);
|
||||
}
|
||||
|
||||
assert!(sig.check());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -6,7 +6,8 @@ edition = "2021"
|
|||
# - Update CHANGELOG.md
|
||||
# - Create git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>"]
|
||||
authors = ["Deirdre Connolly <durumcrustulum@gmail.com>", "Chelsea Komlo <me@chelseakomlo.com>",
|
||||
"Conrado Gouvea <conradoplg@gmail.com>"]
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/ZcashFoundation/frost"
|
||||
|
|
|
@ -263,7 +263,7 @@ pub mod keys {
|
|||
/// # Security
|
||||
///
|
||||
/// To derive a FROST(P-256, SHA-256) keypair, the receiver of the [`SecretShare`] *must* call
|
||||
/// .try_into(), which under the hood also performs validation.
|
||||
/// .into(), which under the hood also performs validation.
|
||||
pub type SecretShare = frost::keys::SecretShare<P>;
|
||||
|
||||
/// A FROST(P-256, SHA-256) keypair, which can be generated either by a trusted dealer or using
|
||||
|
|
|
@ -117,7 +117,7 @@ impl Group for RistrettoGroup {
|
|||
}
|
||||
}
|
||||
|
||||
/// Context string 'FROST-RISTRETTO255-SHA512-v5' from the ciphersuite in the [spec]
|
||||
/// Context string from the ciphersuite in the [spec].
|
||||
///
|
||||
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.2-1
|
||||
const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v11";
|
||||
|
|
|
@ -79,7 +79,7 @@ fn read_docs(filename: &str, suite_names_code: &[&str]) -> Vec<(String, String,
|
|||
/// new_suite_names_doc: replacements to use in the documentation of the given file
|
||||
/// for each reference in `old_suite_names_doc`.
|
||||
fn write_docs(
|
||||
docs: Vec<(String, String, usize, usize)>,
|
||||
docs: &[(String, String, usize, usize)],
|
||||
filename: &str,
|
||||
suite_names_code: &[&str],
|
||||
old_suite_names_doc: &[&str],
|
||||
|
@ -90,10 +90,14 @@ fn write_docs(
|
|||
|
||||
// To be able to replace the documentation properly, start from the end, which
|
||||
// will keep the string positions consistent
|
||||
for ((old_name, _, old_start, old_end), (new_name, new_doc, _, _)) in
|
||||
for ((_old_name, _, old_start, old_end), (_new_name, new_doc, _, _)) in
|
||||
zip(old_docs.iter().rev(), docs.iter().rev())
|
||||
{
|
||||
assert_eq!(old_name, new_name, "source code does not match");
|
||||
// This is a sanity check to test if we're replacing the right comment.
|
||||
// It was commented out due to an exception (Ed25519 scalar is defined
|
||||
// as the Ristretto25519 scalar instead of its own struct)
|
||||
// assert_eq!(old_name, new_name, "source code does not match");
|
||||
|
||||
// Replaces ciphersuite-references in documentation
|
||||
let mut new_doc = new_doc.to_string();
|
||||
for (old_n, new_n) in zip(old_suite_names_doc.iter(), new_suite_names_doc.iter()) {
|
||||
|
@ -114,10 +118,18 @@ fn main() {
|
|||
// To add a new ciphersuite, just copy this call and replace the required strings.
|
||||
|
||||
write_docs(
|
||||
docs,
|
||||
&docs,
|
||||
"frost-p256/src/lib.rs",
|
||||
&["P256Sha256", "P256", "<P>"],
|
||||
old_suite_names_doc,
|
||||
&["FROST(P-256, SHA-256)"],
|
||||
)
|
||||
);
|
||||
|
||||
write_docs(
|
||||
&docs,
|
||||
"frost-ed25519/src/lib.rs",
|
||||
&["Ed25519Sha512", "Ed25519", "<E>"],
|
||||
old_suite_names_doc,
|
||||
&["FROST(Ed25519, SHA-512)"],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue