mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-16 17:07:35 +00:00
Use a non-constant generator in FROST
This commit is contained in:
parent
6f776ff004
commit
885d816309
12 changed files with 29 additions and 24 deletions
|
@ -119,7 +119,7 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nonces(&self) -> Vec<Vec<C::G>> {
|
fn nonces(&self) -> Vec<Vec<C::G>> {
|
||||||
vec![vec![C::GENERATOR]]
|
vec![vec![C::generator()]]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
||||||
|
|
|
@ -28,7 +28,9 @@ macro_rules! dalek_curve {
|
||||||
type G = $Point;
|
type G = $Point;
|
||||||
|
|
||||||
const ID: &'static [u8] = $ID;
|
const ID: &'static [u8] = $ID;
|
||||||
const GENERATOR: Self::G = $POINT;
|
fn generator() -> Self::G {
|
||||||
|
$POINT
|
||||||
|
}
|
||||||
|
|
||||||
fn hash_msg(msg: &[u8]) -> Vec<u8> {
|
fn hash_msg(msg: &[u8]) -> Vec<u8> {
|
||||||
Sha512::new()
|
Sha512::new()
|
||||||
|
|
|
@ -31,7 +31,9 @@ macro_rules! kp_curve {
|
||||||
type G = $lib::ProjectivePoint;
|
type G = $lib::ProjectivePoint;
|
||||||
|
|
||||||
const ID: &'static [u8] = $ID;
|
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> {
|
fn hash_msg(msg: &[u8]) -> Vec<u8> {
|
||||||
(&Sha256::new().chain($CONTEXT).chain(b"digest").chain(msg).finalize()).to_vec()
|
(&Sha256::new().chain($CONTEXT).chain(b"digest").chain(msg).finalize()).to_vec()
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug + Zeroize {
|
||||||
|
|
||||||
/// Generator for the group
|
/// Generator for the group
|
||||||
// While group does provide this in its API, privacy coins may want to use a custom basepoint
|
// 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
|
/// 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
|
// This doesn't actually need to be part of Curve as it does nothing with the curve
|
||||||
|
|
|
@ -50,7 +50,7 @@ fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
|
||||||
// Step 1: Generate t random values to form a polynomial with
|
// Step 1: Generate t random values to form a polynomial with
|
||||||
coefficients.push(C::F::random(&mut *rng));
|
coefficients.push(C::F::random(&mut *rng));
|
||||||
// Step 3: Generate public commitments
|
// Step 3: Generate public commitments
|
||||||
commitments.push(C::GENERATOR * coefficients[i]);
|
commitments.push(C::generator() * coefficients[i]);
|
||||||
// Serialize them for publication
|
// Serialize them for publication
|
||||||
serialized.extend(commitments[i].to_bytes().as_ref());
|
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
|
// There's no reason to spend the time and effort to make this deterministic besides a
|
||||||
// general obsession with canonicity and determinism though
|
// general obsession with canonicity and determinism though
|
||||||
r,
|
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(),
|
.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
|
// 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
|
// per sender and not as an aggregate of all senders, which also enables blame
|
||||||
let mut values = exponential(params.i, &commitments[l]);
|
let mut values = exponential(params.i, &commitments[l]);
|
||||||
values.push((-*share, C::GENERATOR));
|
values.push((-*share, C::generator()));
|
||||||
share.zeroize();
|
share.zeroize();
|
||||||
|
|
||||||
batch.queue(rng, *l, values);
|
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)));
|
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)
|
// 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[¶ms.i()]);
|
debug_assert_eq!(C::generator() * secret_share, verification_shares[¶ms.i()]);
|
||||||
|
|
||||||
Ok(FrostCore { params, secret_share, group_key: stripes[0], verification_shares })
|
Ok(FrostCore { params, secret_share, group_key: stripes[0], verification_shares })
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,7 +330,7 @@ impl<C: Curve> FrostKeys<C> {
|
||||||
|
|
||||||
/// Returns the group key with any offset applied
|
/// Returns the group key with any offset applied
|
||||||
pub fn group_key(&self) -> C::G {
|
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
|
/// 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) *
|
let offset_share = self.offset.unwrap_or_else(C::F::zero) *
|
||||||
C::F::from(included.len().try_into().unwrap()).invert().unwrap();
|
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 {
|
Ok(FrostView {
|
||||||
group_key: self.group_key(),
|
group_key: self.group_key(),
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub(crate) fn sign<C: Curve>(
|
||||||
mut nonce: C::F,
|
mut nonce: C::F,
|
||||||
challenge: C::F,
|
challenge: C::F,
|
||||||
) -> SchnorrSignature<C> {
|
) -> 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();
|
private_key.zeroize();
|
||||||
nonce.zeroize();
|
nonce.zeroize();
|
||||||
res
|
res
|
||||||
|
@ -44,14 +44,14 @@ pub(crate) fn verify<C: Curve>(
|
||||||
challenge: C::F,
|
challenge: C::F,
|
||||||
signature: &SchnorrSignature<C>,
|
signature: &SchnorrSignature<C>,
|
||||||
) -> bool {
|
) -> 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>(
|
pub(crate) fn batch_verify<C: Curve, R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
triplets: &[(u16, C::G, C::F, SchnorrSignature<C>)],
|
triplets: &[(u16, C::G, C::F, SchnorrSignature<C>)],
|
||||||
) -> Result<(), u16> {
|
) -> 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());
|
let mut batch = BatchVerifier::new(triplets.len());
|
||||||
for triple in triplets {
|
for triple in triplets {
|
||||||
// s = r + ca
|
// s = r + ca
|
||||||
|
|
|
@ -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
|
// 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
|
// communicated as a point along with only a single party applying the offset), this means it
|
||||||
// wouldn't require a transcript change as well
|
// 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
|
// Generate the per-signer binding factors
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub fn test_curve<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||||
let mut sum = C::G::identity();
|
let mut sum = C::G::identity();
|
||||||
for _ in 0 .. 10 {
|
for _ in 0 .. 10 {
|
||||||
for _ in 0 .. 100 {
|
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;
|
sum += pairs[pairs.len() - 1].1 * pairs[pairs.len() - 1].0;
|
||||||
}
|
}
|
||||||
assert_eq!(multiexp::multiexp(&pairs), sum);
|
assert_eq!(multiexp::multiexp(&pairs), sum);
|
||||||
|
|
|
@ -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)| {
|
let group_private = keys.iter().fold(C::F::zero(), |accum, (i, keys)| {
|
||||||
accum + (keys.secret_share() * lagrange::<C::F>(*i, &included))
|
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
|
group_private
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 nonce = C::F::random(&mut *rng);
|
||||||
let challenge = C::F::random(rng); // Doesn't bother to craft an HRAM
|
let challenge = C::F::random(rng); // Doesn't bother to craft an HRAM
|
||||||
assert!(schnorr::verify::<C>(
|
assert!(schnorr::verify::<C>(
|
||||||
C::GENERATOR * private_key,
|
C::generator() * private_key,
|
||||||
challenge,
|
challenge,
|
||||||
&schnorr::sign(private_key, nonce, 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
|
// random
|
||||||
pub(crate) fn core_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
pub(crate) fn core_verify<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
||||||
assert!(!schnorr::verify::<C>(
|
assert!(!schnorr::verify::<C>(
|
||||||
C::GENERATOR * C::F::random(&mut *rng),
|
C::generator() * C::F::random(&mut *rng),
|
||||||
C::F::random(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
|
// Batch verify
|
||||||
let triplets = (0 .. 5)
|
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<_>>();
|
.collect::<Vec<_>>();
|
||||||
schnorr::batch_verify(rng, &triplets).unwrap();
|
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() {
|
for i in 1 ..= u16::try_from(keys.len()).unwrap() {
|
||||||
keys.insert(i, keys[&i].offset(offset));
|
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);
|
sign_core(rng, offset_key, &keys);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, FrostKe
|
||||||
.iter()
|
.iter()
|
||||||
.map(|secret| C::read_F(&mut Cursor::new(hex::decode(secret).unwrap())).unwrap())
|
.map(|secret| C::read_F(&mut Cursor::new(hex::decode(secret).unwrap())).unwrap())
|
||||||
.collect::<Vec<_>>();
|
.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();
|
let mut keys = HashMap::new();
|
||||||
for i in 1 ..= u16::try_from(shares.len()).unwrap() {
|
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 keys = vectors_to_multisig_keys::<C>(&vectors);
|
||||||
let group_key = C::read_G(&mut Cursor::new(hex::decode(vectors.group_key).unwrap())).unwrap();
|
let group_key = C::read_G(&mut Cursor::new(hex::decode(vectors.group_key).unwrap())).unwrap();
|
||||||
assert_eq!(
|
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
|
group_key
|
||||||
);
|
);
|
||||||
assert_eq!(
|
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::read_F(&mut Cursor::new(hex::decode(vectors.nonces[c][1]).unwrap())).unwrap(),
|
||||||
];
|
];
|
||||||
c += 1;
|
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 {
|
let machine = machine.unsafe_override_preprocess(PreprocessPackage {
|
||||||
nonces: vec![nonces],
|
nonces: vec![nonces],
|
||||||
commitments: vec![these_commitments.clone()],
|
commitments: vec![these_commitments.clone()],
|
||||||
|
|
Loading…
Reference in a new issue