generate Randomizer by hashing SigningPackage (#542)
This commit is contained in:
parent
ba3ef7dbb8
commit
c0c57f4b4b
|
@ -26,6 +26,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
curve25519-dalek = { version = "=4.1.0", features = ["rand_core"] }
|
||||
document-features = "0.2.7"
|
||||
frost-core = { path = "../frost-core", version = "0.7.0" }
|
||||
frost-rerandomized = { path = "../frost-rerandomized", version = "0.7.0" }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use curve25519_dalek::{
|
|||
scalar::Scalar,
|
||||
traits::Identity,
|
||||
};
|
||||
use frost_rerandomized::RandomizedCiphersuite;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{Digest, Sha512};
|
||||
|
||||
|
@ -209,6 +210,16 @@ impl Ciphersuite for Ed25519Sha512 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RandomizedCiphersuite for Ed25519Sha512 {
|
||||
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||
Some(hash_to_scalar(&[
|
||||
CONTEXT_STRING.as_bytes(),
|
||||
b"randomizer",
|
||||
m,
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
type E = Ed25519Sha512;
|
||||
|
||||
/// A FROST(Ed25519, SHA-512) participant identifier.
|
||||
|
|
|
@ -25,6 +25,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
document-features = "0.2.7"
|
||||
ed448-goldilocks = { version = "0.9.0" }
|
||||
frost-core = { path = "../frost-core", version = "0.7.0" }
|
||||
frost-rerandomized = { path = "../frost-rerandomized", version = "0.7.0" }
|
||||
rand_core = "0.6"
|
||||
sha3 = "0.10.6"
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use ed448_goldilocks::{
|
|||
curve::{edwards::CompressedEdwardsY, ExtendedPoint},
|
||||
Scalar,
|
||||
};
|
||||
use frost_rerandomized::RandomizedCiphersuite;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha3::{
|
||||
digest::{ExtendableOutput, Update, XofReader},
|
||||
|
@ -204,6 +205,16 @@ impl Ciphersuite for Ed448Shake256 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RandomizedCiphersuite for Ed448Shake256 {
|
||||
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||
Some(hash_to_scalar(&[
|
||||
CONTEXT_STRING.as_bytes(),
|
||||
b"randomizer",
|
||||
m,
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
type E = Ed448Shake256;
|
||||
|
||||
/// A FROST(Ed448, SHAKE256) participant identifier.
|
||||
|
|
|
@ -26,6 +26,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
document-features = "0.2.7"
|
||||
p256 = { version = "0.13.0", features = ["hash2curve"] }
|
||||
frost-core = { path = "../frost-core", version = "0.7.0" }
|
||||
frost-rerandomized = { path = "../frost-rerandomized", version = "0.7.0" }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use frost_rerandomized::RandomizedCiphersuite;
|
||||
use p256::{
|
||||
elliptic_curve::{
|
||||
hash2curve::{hash_to_field, ExpandMsgXmd},
|
||||
|
@ -235,6 +236,15 @@ impl Ciphersuite for P256Sha256 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RandomizedCiphersuite for P256Sha256 {
|
||||
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||
Some(hash_to_scalar(
|
||||
(CONTEXT_STRING.to_owned() + "randomizer").as_bytes(),
|
||||
m,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Shorthand alias for the ciphersuite
|
||||
type P = P256Sha256;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! To sign with re-randomized FROST:
|
||||
//!
|
||||
//! - Do Round 1 the same way as regular FROST;
|
||||
//! - The Coordinator should generate a [`RandomizedParams`] and send
|
||||
//! - The Coordinator should call [`RandomizedParams::new()`] and send
|
||||
//! the [`RandomizedParams::randomizer`] to all participants, using a
|
||||
//! confidential channel, along with the regular [`frost::SigningPackage`];
|
||||
//! - Each participant should call [`sign`] and send the resulting
|
||||
|
@ -23,6 +23,7 @@ use frost_core::{
|
|||
frost::{
|
||||
self,
|
||||
keys::{KeyPackage, PublicKeyPackage, SigningShare, VerifyingShare},
|
||||
SigningPackage,
|
||||
},
|
||||
Ciphersuite, Error, Field, Group, Scalar, VerifyingKey,
|
||||
};
|
||||
|
@ -45,6 +46,12 @@ trait Randomize<C> {
|
|||
C: Ciphersuite;
|
||||
}
|
||||
|
||||
/// A Ciphersuite that supports rerandomization.
|
||||
pub trait RandomizedCiphersuite: Ciphersuite {
|
||||
/// A hash function that hashes into a randomizer scalar.
|
||||
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar>;
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> Randomize<C> for KeyPackage<C> {
|
||||
/// Randomize the given [`KeyPackage`] for usage in a re-randomized FROST signing,
|
||||
/// using the given [`RandomizedParams`].
|
||||
|
@ -113,7 +120,7 @@ impl<C: Ciphersuite> Randomize<C> for PublicKeyPackage<C> {
|
|||
/// be sent from the Coordinator using a confidential channel.
|
||||
///
|
||||
/// See [`frost::round2::sign`] for documentation on the other parameters.
|
||||
pub fn sign<C: Ciphersuite>(
|
||||
pub fn sign<C: RandomizedCiphersuite>(
|
||||
signing_package: &frost::SigningPackage<C>,
|
||||
signer_nonces: &frost::round1::SigningNonces<C>,
|
||||
key_package: &frost::keys::KeyPackage<C>,
|
||||
|
@ -158,15 +165,52 @@ pub struct Randomizer<C: Ciphersuite>(Scalar<C>);
|
|||
|
||||
impl<C> Randomizer<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
C: RandomizedCiphersuite,
|
||||
{
|
||||
/// Create a new random Randomizer.
|
||||
pub fn new<R: RngCore + CryptoRng>(mut rng: R) -> Self {
|
||||
let randomizer = <<C::Group as Group>::Field as Field>::random(&mut rng);
|
||||
Self(randomizer)
|
||||
///
|
||||
/// The [`SigningPackage`] must be the signing package being used in the
|
||||
/// current FROST signing run. It is hashed into the randomizer calculation,
|
||||
/// which binds it to that specific package.
|
||||
pub fn new<R: RngCore + CryptoRng>(
|
||||
mut rng: R,
|
||||
signing_package: &SigningPackage<C>,
|
||||
) -> Result<Self, Error<C>> {
|
||||
let rng_randomizer = <<C::Group as Group>::Field as Field>::random(&mut rng);
|
||||
Self::from_randomizer_and_signing_package(rng_randomizer, signing_package)
|
||||
}
|
||||
|
||||
/// Create a new Randomizer from the given scalar. It MUST be randomly generated.
|
||||
/// Create a final Randomizer from a random Randomizer and a SigningPackage.
|
||||
/// Function refactored out for testing, should always be private.
|
||||
fn from_randomizer_and_signing_package(
|
||||
rng_randomizer: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
|
||||
signing_package: &SigningPackage<C>,
|
||||
) -> Result<Randomizer<C>, Error<C>>
|
||||
where
|
||||
C: RandomizedCiphersuite,
|
||||
{
|
||||
let randomizer = C::hash_randomizer(
|
||||
&[
|
||||
<<C::Group as Group>::Field>::serialize(&rng_randomizer).as_ref(),
|
||||
&signing_package.serialize()?,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
.ok_or(Error::SerializationError)?;
|
||||
Ok(Self(randomizer))
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Randomizer<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new Randomizer from the given scalar. It MUST be randomly
|
||||
/// generated.
|
||||
///
|
||||
/// It is not recommended to use this method unless for compatibility
|
||||
/// reasons with specifications on how the randomizer must be generated. Use
|
||||
/// [`Randomizer::new()`] instead.
|
||||
pub fn from_scalar(scalar: Scalar<C>) -> Self {
|
||||
Self(scalar)
|
||||
}
|
||||
|
@ -221,14 +265,26 @@ pub struct RandomizedParams<C: Ciphersuite> {
|
|||
|
||||
impl<C> RandomizedParams<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
C: RandomizedCiphersuite,
|
||||
{
|
||||
/// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and
|
||||
/// the given `participants`.
|
||||
pub fn new<R: RngCore + CryptoRng>(group_verifying_key: &VerifyingKey<C>, rng: R) -> Self {
|
||||
Self::from_randomizer(group_verifying_key, Randomizer::new(rng))
|
||||
pub fn new<R: RngCore + CryptoRng>(
|
||||
group_verifying_key: &VerifyingKey<C>,
|
||||
signing_package: &SigningPackage<C>,
|
||||
rng: R,
|
||||
) -> Result<Self, Error<C>> {
|
||||
Ok(Self::from_randomizer(
|
||||
group_verifying_key,
|
||||
Randomizer::new(rng, signing_package)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> RandomizedParams<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`RandomizedParams`] for the given [`VerifyingKey`] and the
|
||||
/// given `participants` for the given `randomizer`. The `randomizer` MUST
|
||||
/// be generated uniformly at random! Use [`RandomizedParams::new()`] which
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::{frost_core::frost, frost_core::Ciphersuite, RandomizedParams, Randomizer};
|
||||
use frost_core::{Signature, VerifyingKey};
|
||||
use crate::{frost_core::frost, RandomizedCiphersuite, RandomizedParams, Randomizer};
|
||||
use frost_core::{frost::SigningPackage, Field, Group, Signature, VerifyingKey};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
/// Test re-randomized FROST signing with trusted dealer with a Ciphersuite.
|
||||
/// Returns the signed message, generated signature, and the randomized public key
|
||||
/// so that the caller can verify the signature with their own implementation.
|
||||
pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
||||
pub fn check_randomized_sign_with_dealer<C: RandomizedCiphersuite, R: RngCore + CryptoRng>(
|
||||
mut rng: R,
|
||||
) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -39,10 +39,6 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
|
|||
let mut commitments: BTreeMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
|
||||
BTreeMap::new();
|
||||
|
||||
check_from_randomizer(&pubkeys, &mut rng);
|
||||
let randomizer_params = RandomizedParams::new(pubkeys.verifying_key(), &mut rng);
|
||||
let randomizer = randomizer_params.randomizer();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 1: generating nonces and signing commitments for each participant
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -70,6 +66,11 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
|
|||
let message = "message to sign".as_bytes();
|
||||
let signing_package = frost::SigningPackage::new(commitments, message);
|
||||
|
||||
check_randomizer(&pubkeys, &signing_package, &mut rng);
|
||||
let randomizer_params =
|
||||
RandomizedParams::new(pubkeys.verifying_key(), &signing_package, &mut rng).unwrap();
|
||||
let randomizer = randomizer_params.randomizer();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 2: each participant generates their signature share
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -119,13 +120,56 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
|
|||
)
|
||||
}
|
||||
|
||||
fn check_from_randomizer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
||||
fn check_randomizer<C: RandomizedCiphersuite, R: RngCore + CryptoRng>(
|
||||
pubkeys: &frost::keys::PublicKeyPackage<C>,
|
||||
rng: &mut R,
|
||||
signing_package: &frost::SigningPackage<C>,
|
||||
mut rng: &mut R,
|
||||
) {
|
||||
let randomizer = Randomizer::new(rng);
|
||||
check_from_randomizer(&mut rng, signing_package, pubkeys);
|
||||
|
||||
check_from_randomizer_and_signing_package(&mut rng, signing_package);
|
||||
}
|
||||
|
||||
fn check_from_randomizer<C: RandomizedCiphersuite, R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
signing_package: &SigningPackage<C>,
|
||||
pubkeys: &frost::keys::PublicKeyPackage<C>,
|
||||
) {
|
||||
let randomizer = Randomizer::new(rng, signing_package).unwrap();
|
||||
|
||||
let randomizer_params = RandomizedParams::from_randomizer(pubkeys.verifying_key(), randomizer);
|
||||
|
||||
assert!(*randomizer_params.randomizer() == randomizer);
|
||||
}
|
||||
|
||||
fn check_from_randomizer_and_signing_package<C: RandomizedCiphersuite, R: RngCore + CryptoRng>(
|
||||
mut rng: &mut R,
|
||||
signing_package: &SigningPackage<C>,
|
||||
) {
|
||||
let rng_randomizer1 = <<C::Group as Group>::Field as Field>::random(&mut rng);
|
||||
let rng_randomizer2 = <<C::Group as Group>::Field as Field>::random(&mut rng);
|
||||
|
||||
let randomizer1 =
|
||||
Randomizer::from_randomizer_and_signing_package(rng_randomizer1, signing_package);
|
||||
let randomizer2 =
|
||||
Randomizer::from_randomizer_and_signing_package(rng_randomizer1, signing_package);
|
||||
|
||||
// Make sure same inputs lead to same randomizer (and that equality works)
|
||||
assert!(randomizer1 == randomizer2);
|
||||
|
||||
let randomizer2 =
|
||||
Randomizer::from_randomizer_and_signing_package(rng_randomizer2, signing_package);
|
||||
|
||||
// Make sure that different rng_randomizers lead to different randomizers
|
||||
assert!(randomizer1 != randomizer2);
|
||||
|
||||
let signing_package2 = SigningPackage::new(
|
||||
signing_package.signing_commitments().clone(),
|
||||
"fresh new message".as_bytes(),
|
||||
);
|
||||
let randomizer2 =
|
||||
Randomizer::from_randomizer_and_signing_package(rng_randomizer1, &signing_package2);
|
||||
|
||||
// Make sure that different packages lead to different randomizers
|
||||
assert!(randomizer1 != randomizer2);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
curve25519-dalek = { version = "=4.1.0", features = ["serde", "rand_core"] }
|
||||
document-features = "0.2.7"
|
||||
frost-core = { path = "../frost-core", version = "0.7.0" }
|
||||
frost-rerandomized = { path = "../frost-rerandomized", version = "0.7.0" }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use curve25519_dalek::{
|
|||
scalar::Scalar,
|
||||
traits::Identity,
|
||||
};
|
||||
use frost_rerandomized::RandomizedCiphersuite;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{Digest, Sha512};
|
||||
|
||||
|
@ -195,6 +196,16 @@ impl Ciphersuite for Ristretto255Sha512 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RandomizedCiphersuite for Ristretto255Sha512 {
|
||||
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||
Some(hash_to_scalar(&[
|
||||
CONTEXT_STRING.as_bytes(),
|
||||
b"randomizer",
|
||||
m,
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
type R = Ristretto255Sha512;
|
||||
|
||||
/// A FROST(ristretto255, SHA-512) participant identifier.
|
||||
|
|
|
@ -24,6 +24,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
[dependencies]
|
||||
document-features = "0.2.7"
|
||||
frost-core = { path = "../frost-core", version = "0.7.0" }
|
||||
frost-rerandomized = { path = "../frost-rerandomized", version = "0.7.0" }
|
||||
k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"] }
|
||||
rand_core = "0.6"
|
||||
sha2 = "0.10.2"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use frost_rerandomized::RandomizedCiphersuite;
|
||||
use k256::{
|
||||
elliptic_curve::{
|
||||
group::prime::PrimeCurveAffine,
|
||||
|
@ -235,6 +236,15 @@ impl Ciphersuite for Secp256K1Sha256 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RandomizedCiphersuite for Secp256K1Sha256 {
|
||||
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||
Some(hash_to_scalar(
|
||||
(CONTEXT_STRING.to_owned() + "randomizer").as_bytes(),
|
||||
m,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
type S = Secp256K1Sha256;
|
||||
|
||||
/// A FROST(secp256k1, SHA-256) participant identifier.
|
||||
|
|
Loading…
Reference in New Issue