Skip to content

Commit

Permalink
generate Randomizer by hashing SigningPackage
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg committed Sep 21, 2023
1 parent a5dc479 commit b7204e0
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 19 deletions.
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

0 comments on commit b7204e0

Please sign in to comment.