Use a non-constant generator in FROST

This commit is contained in:
Luke Parker 2022-08-13 05:07:07 -04:00
parent 6f776ff004
commit 885d816309
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
12 changed files with 29 additions and 24 deletions

View file

@ -119,7 +119,7 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
}
fn nonces(&self) -> Vec<Vec<C::G>> {
vec![vec![C::GENERATOR]]
vec![vec![C::generator()]]
}
fn preprocess_addendum<R: RngCore + CryptoRng>(

View file

@ -28,7 +28,9 @@ macro_rules! dalek_curve {
type G = $Point;
const ID: &'static [u8] = $ID;
const GENERATOR: Self::G = $POINT;
fn generator() -> Self::G {
$POINT
}
fn hash_msg(msg: &[u8]) -> Vec<u8> {
Sha512::new()

View file

@ -31,7 +31,9 @@ macro_rules! kp_curve {
type G = $lib::ProjectivePoint;
const ID: &'static [u8] = $ID;
const GENERATOR: Self::G = $lib::ProjectivePoint::GENERATOR;
fn generator() -> Self::G {
$lib::ProjectivePoint::GENERATOR
}
fn hash_msg(msg: &[u8]) -> Vec<u8> {
(&Sha256::new().chain($CONTEXT).chain(b"digest").chain(msg).finalize()).to_vec()

View file

@ -53,7 +53,7 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug + Zeroize {
/// Generator for the group
// While group does provide this in its API, privacy coins may want to use a custom basepoint
const GENERATOR: Self::G;
fn generator() -> Self::G;
/// Hash the message for the binding factor. H3 from the IETF draft
// This doesn't actually need to be part of Curve as it does nothing with the curve

View file

@ -50,7 +50,7 @@ fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
// Step 1: Generate t random values to form a polynomial with
coefficients.push(C::F::random(&mut *rng));
// Step 3: Generate public commitments
commitments.push(C::GENERATOR * coefficients[i]);
commitments.push(C::generator() * coefficients[i]);
// Serialize them for publication
serialized.extend(commitments[i].to_bytes().as_ref());
}
@ -65,7 +65,7 @@ fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
// There's no reason to spend the time and effort to make this deterministic besides a
// general obsession with canonicity and determinism though
r,
challenge::<C>(context, params.i(), (C::GENERATOR * r).to_bytes().as_ref(), &serialized),
challenge::<C>(context, params.i(), (C::generator() * r).to_bytes().as_ref(), &serialized),
)
.serialize(),
);
@ -224,7 +224,7 @@ fn complete_r2<Re: Read, R: RngCore + CryptoRng, C: Curve>(
// ensure that malleability isn't present is to use this n * t algorithm, which runs
// per sender and not as an aggregate of all senders, which also enables blame
let mut values = exponential(params.i, &commitments[l]);
values.push((-*share, C::GENERATOR));
values.push((-*share, C::generator()));
share.zeroize();
batch.queue(rng, *l, values);
@ -246,7 +246,7 @@ fn complete_r2<Re: Read, R: RngCore + CryptoRng, C: Curve>(
verification_shares.insert(i, multiexp_vartime(&exponential(i, &stripes)));
}
// Removing this check would enable optimizing the above from t + (n * t) to t + ((n - 1) * t)
debug_assert_eq!(C::GENERATOR * secret_share, verification_shares[&params.i()]);
debug_assert_eq!(C::generator() * secret_share, verification_shares[&params.i()]);
Ok(FrostCore { params, secret_share, group_key: stripes[0], verification_shares })
}

View file

@ -330,7 +330,7 @@ impl<C: Curve> FrostKeys<C> {
/// Returns the group key with any offset applied
pub fn group_key(&self) -> C::G {
self.core.group_key + (C::GENERATOR * self.offset.unwrap_or_else(C::F::zero))
self.core.group_key + (C::generator() * self.offset.unwrap_or_else(C::F::zero))
}
/// Returns all participants' verification shares without any offsetting
@ -354,7 +354,7 @@ impl<C: Curve> FrostKeys<C> {
let offset_share = self.offset.unwrap_or_else(C::F::zero) *
C::F::from(included.len().try_into().unwrap()).invert().unwrap();
let offset_verification_share = C::GENERATOR * offset_share;
let offset_verification_share = C::generator() * offset_share;
Ok(FrostView {
group_key: self.group_key(),

View file

@ -32,7 +32,7 @@ pub(crate) fn sign<C: Curve>(
mut nonce: C::F,
challenge: C::F,
) -> SchnorrSignature<C> {
let res = SchnorrSignature { R: C::GENERATOR * nonce, s: nonce + (private_key * challenge) };
let res = SchnorrSignature { R: C::generator() * nonce, s: nonce + (private_key * challenge) };
private_key.zeroize();
nonce.zeroize();
res
@ -44,14 +44,14 @@ pub(crate) fn verify<C: Curve>(
challenge: C::F,
signature: &SchnorrSignature<C>,
) -> bool {
(C::GENERATOR * signature.s) == (signature.R + (public_key * challenge))
(C::generator() * signature.s) == (signature.R + (public_key * challenge))
}
pub(crate) fn batch_verify<C: Curve, R: RngCore + CryptoRng>(
rng: &mut R,
triplets: &[(u16, C::G, C::F, SchnorrSignature<C>)],
) -> Result<(), u16> {
let mut values = [(C::F::one(), C::GENERATOR); 3];
let mut values = [(C::F::one(), C::generator()); 3];
let mut batch = BatchVerifier::new(triplets.len());
for triple in triplets {
// s = r + ca

View file

@ -267,7 +267,7 @@ fn sign_with_share<Re: Read, C: Curve, A: Algorithm<C>>(
// While further code edits would still be required for such a model (having the offset
// communicated as a point along with only a single party applying the offset), this means it
// wouldn't require a transcript change as well
rho_transcript.append_message(b"offset", (C::GENERATOR * offset).to_bytes().as_ref());
rho_transcript.append_message(b"offset", (C::generator() * offset).to_bytes().as_ref());
}
// Generate the per-signer binding factors

View file

@ -30,7 +30,7 @@ pub fn test_curve<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
let mut sum = C::G::identity();
for _ in 0 .. 10 {
for _ in 0 .. 100 {
pairs.push((C::F::random(&mut *rng), C::GENERATOR * C::F::random(&mut *rng)));
pairs.push((C::F::random(&mut *rng), C::generator() * C::F::random(&mut *rng)));
sum += pairs[pairs.len() - 1].1 * pairs[pairs.len() - 1].0;
}
assert_eq!(multiexp::multiexp(&pairs), sum);

View file

@ -99,7 +99,7 @@ pub fn recover<C: Curve>(keys: &HashMap<u16, FrostKeys<C>>) -> C::F {
let group_private = keys.iter().fold(C::F::zero(), |accum, (i, keys)| {
accum + (keys.secret_share() * lagrange::<C::F>(*i, &included))
});
assert_eq!(C::GENERATOR * group_private, first.group_key(), "failed to recover keys");
assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys");
group_private
}

View file

@ -16,7 +16,7 @@ pub(crate) fn core_sign<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
let nonce = C::F::random(&mut *rng);
let challenge = C::F::random(rng); // Doesn't bother to craft an HRAM
assert!(schnorr::verify::<C>(
C::GENERATOR * private_key,
C::generator() * private_key,
challenge,
&schnorr::sign(private_key, nonce, challenge)
));
@ -27,9 +27,9 @@ pub(crate) fn core_sign<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
// random
pub(crate) fn core_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
assert!(!schnorr::verify::<C>(
C::GENERATOR * C::F::random(&mut *rng),
C::generator() * C::F::random(&mut *rng),
C::F::random(rng),
&SchnorrSignature { R: C::GENERATOR * C::F::zero(), s: C::F::zero() }
&SchnorrSignature { R: C::identity(), s: C::F::zero() }
));
}
@ -46,7 +46,7 @@ pub(crate) fn core_batch_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
// Batch verify
let triplets = (0 .. 5)
.map(|i| (u16::try_from(i + 1).unwrap(), C::GENERATOR * keys[i], challenges[i], sigs[i]))
.map(|i| (u16::try_from(i + 1).unwrap(), C::generator() * keys[i], challenges[i], sigs[i]))
.collect::<Vec<_>>();
schnorr::batch_verify(rng, &triplets).unwrap();
@ -111,7 +111,7 @@ fn sign_with_offset<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
for i in 1 ..= u16::try_from(keys.len()).unwrap() {
keys.insert(i, keys[&i].offset(offset));
}
let offset_key = group_key + (C::GENERATOR * offset);
let offset_key = group_key + (C::generator() * offset);
sign_core(rng, offset_key, &keys);
}

View file

@ -32,7 +32,7 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, FrostKe
.iter()
.map(|secret| C::read_F(&mut Cursor::new(hex::decode(secret).unwrap())).unwrap())
.collect::<Vec<_>>();
let verification_shares = shares.iter().map(|secret| C::GENERATOR * secret).collect::<Vec<_>>();
let verification_shares = shares.iter().map(|secret| C::generator() * secret).collect::<Vec<_>>();
let mut keys = HashMap::new();
for i in 1 ..= u16::try_from(shares.len()).unwrap() {
@ -71,7 +71,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
let keys = vectors_to_multisig_keys::<C>(&vectors);
let group_key = C::read_G(&mut Cursor::new(hex::decode(vectors.group_key).unwrap())).unwrap();
assert_eq!(
C::GENERATOR * C::read_F(&mut Cursor::new(hex::decode(vectors.group_secret).unwrap())).unwrap(),
C::generator() *
C::read_F(&mut Cursor::new(hex::decode(vectors.group_secret).unwrap())).unwrap(),
group_key
);
assert_eq!(
@ -102,7 +103,7 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
C::read_F(&mut Cursor::new(hex::decode(vectors.nonces[c][1]).unwrap())).unwrap(),
];
c += 1;
let these_commitments = vec![[C::GENERATOR * nonces[0], C::GENERATOR * nonces[1]]];
let these_commitments = vec![[C::generator() * nonces[0], C::generator() * nonces[1]]];
let machine = machine.unsafe_override_preprocess(PreprocessPackage {
nonces: vec![nonces],
commitments: vec![these_commitments.clone()],