Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generate Randomizer by hashing SigningPackage #542

Merged
merged 1 commit into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frost-ed25519/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
11 changes: 11 additions & 0 deletions frost-ed25519/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use curve25519_dalek::{
scalar::Scalar,
traits::Identity,
};
use frost_rerandomized::RandomizedCiphersuite;
use rand_core::{CryptoRng, RngCore};
use sha2::{Digest, Sha512};

Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions frost-ed448/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
11 changes: 11 additions & 0 deletions frost-ed448/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions frost-p256/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
10 changes: 10 additions & 0 deletions frost-p256/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use std::collections::HashMap;

use frost_rerandomized::RandomizedCiphersuite;
use p256::{
elliptic_curve::{
hash2curve::{hash_to_field, ExpandMsgXmd},
Expand Down Expand Up @@ -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;

Expand Down
76 changes: 66 additions & 10 deletions frost-rerandomized/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,6 +23,7 @@ use frost_core::{
frost::{
self,
keys::{KeyPackage, PublicKeyPackage, SigningShare, VerifyingShare},
SigningPackage,
},
Ciphersuite, Error, Field, Group, Scalar, VerifyingKey,
};
Expand All @@ -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`].
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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 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))
}
}

/// Create a new Randomizer from the given scalar. It MUST be randomly generated.
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)
}
Expand Down Expand Up @@ -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
Expand Down
62 changes: 53 additions & 9 deletions frost-rerandomized/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

use std::collections::{BTreeMap, HashMap};

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>) {
////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -38,10 +38,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
////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -69,6 +65,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
////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -118,13 +119,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>,
signing_package: &frost::SigningPackage<C>,
mut rng: &mut R,
) {
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);
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);
}
1 change: 1 addition & 0 deletions frost-ristretto255/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
11 changes: 11 additions & 0 deletions frost-ristretto255/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use curve25519_dalek::{
scalar::Scalar,
traits::Identity,
};
use frost_rerandomized::RandomizedCiphersuite;
use rand_core::{CryptoRng, RngCore};
use sha2::{Digest, Sha512};

Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions frost-secp256k1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 10 additions & 0 deletions frost-secp256k1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use std::collections::HashMap;

use frost_rerandomized::RandomizedCiphersuite;
use k256::{
elliptic_curve::{
group::prime::PrimeCurveAffine,
Expand Down Expand Up @@ -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.
Expand Down