mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-16 17:07:35 +00:00
Remove C::F_len, C::G_len for F_len<C> and G_len<C>
Relies on the ff/group API, instead of the custom Curve type. Also removes GENERATOR_TABLE, only used by dalek, as we should provide our own API for that over ff/group instead. This slows down the FROST tests, under debug, by about 0.2-0.3s. Ed25519 and Ristretto together take ~2.15 seconds now.
This commit is contained in:
parent
4eafbe2a09
commit
133c1222ad
9 changed files with 46 additions and 84 deletions
|
@ -11,29 +11,24 @@ macro_rules! dalek_curve {
|
|||
$Curve: ident,
|
||||
$Hram: ident,
|
||||
$Point: ident,
|
||||
$Table: ident,
|
||||
|
||||
$POINT: ident,
|
||||
$TABLE: ident,
|
||||
|
||||
$ID: literal,
|
||||
$CONTEXT: literal,
|
||||
$chal: literal,
|
||||
$digest: literal,
|
||||
) => {
|
||||
use dalek_ff_group::{$Point, $Table, $POINT, $TABLE};
|
||||
use dalek_ff_group::{$Point, $POINT};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct $Curve;
|
||||
impl Curve for $Curve {
|
||||
type F = Scalar;
|
||||
type G = $Point;
|
||||
type T = &'static $Table;
|
||||
|
||||
const ID: &'static [u8] = $ID;
|
||||
|
||||
const GENERATOR: Self::G = $POINT;
|
||||
const GENERATOR_TABLE: Self::T = &$TABLE;
|
||||
|
||||
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
|
||||
let mut seed = vec![0; 32];
|
||||
|
@ -58,14 +53,6 @@ macro_rules! dalek_curve {
|
|||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
||||
Scalar::from_hash(Sha512::new().chain_update($CONTEXT).chain_update(dst).chain_update(msg))
|
||||
}
|
||||
|
||||
fn F_len() -> usize {
|
||||
32
|
||||
}
|
||||
|
||||
fn G_len() -> usize {
|
||||
32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -84,9 +71,7 @@ dalek_curve!(
|
|||
Ristretto,
|
||||
IetfRistrettoHram,
|
||||
RistrettoPoint,
|
||||
RistrettoBasepointTable,
|
||||
RISTRETTO_BASEPOINT_POINT,
|
||||
RISTRETTO_BASEPOINT_TABLE,
|
||||
b"ristretto",
|
||||
b"FROST-RISTRETTO255-SHA512-v5",
|
||||
b"chal",
|
||||
|
@ -98,9 +83,7 @@ dalek_curve!(
|
|||
Ed25519,
|
||||
IetfEd25519Hram,
|
||||
EdwardsPoint,
|
||||
EdwardsBasepointTable,
|
||||
ED25519_BASEPOINT_POINT,
|
||||
ED25519_BASEPOINT_TABLE,
|
||||
b"edwards25519",
|
||||
b"",
|
||||
b"",
|
||||
|
|
|
@ -22,12 +22,9 @@ macro_rules! kp_curve {
|
|||
impl Curve for $Curve {
|
||||
type F = $lib::Scalar;
|
||||
type G = $lib::ProjectivePoint;
|
||||
type T = $lib::ProjectivePoint;
|
||||
|
||||
const ID: &'static [u8] = $ID;
|
||||
|
||||
const GENERATOR: Self::G = $lib::ProjectivePoint::GENERATOR;
|
||||
const GENERATOR_TABLE: Self::G = $lib::ProjectivePoint::GENERATOR;
|
||||
|
||||
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
|
||||
let mut seed = vec![0; 32];
|
||||
|
@ -73,14 +70,6 @@ macro_rules! kp_curve {
|
|||
}).reduce(&modulus).unwrap().to_be_bytes()[16 ..]
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
fn F_len() -> usize {
|
||||
32
|
||||
}
|
||||
|
||||
fn G_len() -> usize {
|
||||
33
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use core::{ops::Mul, fmt::Debug};
|
||||
use core::fmt::Debug;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use ff::{PrimeField, PrimeFieldBits};
|
||||
use group::{Group, GroupOps, prime::PrimeGroup};
|
||||
use group::{Group, GroupOps, GroupEncoding, prime::PrimeGroup};
|
||||
|
||||
#[cfg(any(test, feature = "dalek"))]
|
||||
mod dalek;
|
||||
|
@ -44,20 +44,14 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
|
|||
type F: PrimeField + PrimeFieldBits;
|
||||
/// Group element type
|
||||
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup;
|
||||
/// Precomputed table type
|
||||
type T: Mul<Self::F, Output = Self::G>;
|
||||
|
||||
/// ID for this curve
|
||||
const ID: &'static [u8];
|
||||
|
||||
/// Generator for the group
|
||||
// While group does provide this in its API, privacy coins will 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;
|
||||
|
||||
/// Table for the generator for the group
|
||||
/// If there isn't a precomputed table available, the generator itself should be used
|
||||
const GENERATOR_TABLE: Self::T;
|
||||
|
||||
/// Securely generate a random nonce. H4 from the IETF draft
|
||||
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F;
|
||||
|
||||
|
@ -83,20 +77,16 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
|
|||
// hash_msg and hash_binding_factor
|
||||
#[allow(non_snake_case)]
|
||||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F;
|
||||
}
|
||||
|
||||
/// Constant size of a serialized scalar field element
|
||||
// The alternative way to grab this would be either serializing a junk element and getting its
|
||||
// length or doing a naive division of its BITS property by 8 and assuming a lack of padding
|
||||
#[allow(non_snake_case)]
|
||||
fn F_len() -> usize;
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn F_len<C: Curve>() -> usize {
|
||||
<C::F as PrimeField>::Repr::default().as_ref().len()
|
||||
}
|
||||
|
||||
/// Constant size of a serialized group element
|
||||
// We could grab the serialization as described above yet a naive developer may use a
|
||||
// non-constant size encoding, proving yet another reason to force this to be a provided constant
|
||||
// A naive developer could still provide a constant for a variable length encoding, yet at least
|
||||
// that is on them
|
||||
#[allow(non_snake_case)]
|
||||
fn G_len() -> usize;
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn G_len<C: Curve>() -> usize {
|
||||
<C::G as GroupEncoding>::Repr::default().as_ref().len()
|
||||
}
|
||||
|
||||
/// Field element from slice
|
||||
|
|
|
@ -7,7 +7,7 @@ use group::{ff::{Field, PrimeField}, GroupEncoding};
|
|||
use multiexp::{multiexp_vartime, BatchVerifier};
|
||||
|
||||
use crate::{
|
||||
curve::{Curve, F_from_slice, G_from_slice},
|
||||
curve::{Curve, F_len, G_len, F_from_slice, G_from_slice},
|
||||
FrostError, FrostParams, FrostKeys,
|
||||
schnorr::{self, SchnorrSignature},
|
||||
validate_map
|
||||
|
@ -35,13 +35,13 @@ fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
|
|||
let t = usize::from(params.t);
|
||||
let mut coefficients = Vec::with_capacity(t);
|
||||
let mut commitments = Vec::with_capacity(t);
|
||||
let mut serialized = Vec::with_capacity((C::G_len() * t) + C::G_len() + C::F_len());
|
||||
let mut serialized = Vec::with_capacity((G_len::<C>() * t) + G_len::<C>() + F_len::<C>());
|
||||
|
||||
for i in 0 .. t {
|
||||
// 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_TABLE * coefficients[i]);
|
||||
commitments.push(C::GENERATOR * coefficients[i]);
|
||||
// Serialize them for publication
|
||||
serialized.extend(commitments[i].to_bytes().as_ref());
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ fn generate_key_r1<R: RngCore + CryptoRng, C: Curve>(
|
|||
challenge::<C>(
|
||||
context,
|
||||
params.i(),
|
||||
(C::GENERATOR_TABLE * r).to_bytes().as_ref(),
|
||||
(C::GENERATOR * r).to_bytes().as_ref(),
|
||||
&serialized
|
||||
)
|
||||
).serialize()
|
||||
|
@ -83,19 +83,19 @@ fn verify_r1<R: RngCore + CryptoRng, C: Curve>(
|
|||
(params.i(), our_commitments)
|
||||
)?;
|
||||
|
||||
let commitments_len = usize::from(params.t()) * C::G_len();
|
||||
let commitments_len = usize::from(params.t()) * G_len::<C>();
|
||||
|
||||
let mut commitments = HashMap::new();
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let R_bytes = |l| &serialized[&l][commitments_len .. commitments_len + C::G_len()];
|
||||
let R_bytes = |l| &serialized[&l][commitments_len .. commitments_len + G_len::<C>()];
|
||||
#[allow(non_snake_case)]
|
||||
let R = |l| G_from_slice::<C::G>(R_bytes(l)).map_err(|_| FrostError::InvalidProofOfKnowledge(l));
|
||||
#[allow(non_snake_case)]
|
||||
let Am = |l| &serialized[&l][0 .. commitments_len];
|
||||
|
||||
let s = |l| F_from_slice::<C::F>(
|
||||
&serialized[&l][commitments_len + C::G_len() ..]
|
||||
&serialized[&l][commitments_len + G_len::<C>() ..]
|
||||
).map_err(|_| FrostError::InvalidProofOfKnowledge(l));
|
||||
|
||||
let mut signatures = Vec::with_capacity(usize::from(params.n() - 1));
|
||||
|
@ -104,7 +104,7 @@ fn verify_r1<R: RngCore + CryptoRng, C: Curve>(
|
|||
for c in 0 .. usize::from(params.t()) {
|
||||
these_commitments.push(
|
||||
G_from_slice::<C::G>(
|
||||
&serialized[&l][(c * C::G_len()) .. ((c + 1) * C::G_len())]
|
||||
&serialized[&l][(c * G_len::<C>()) .. ((c + 1) * G_len::<C>())]
|
||||
).map_err(|_| FrostError::InvalidCommitment(l.try_into().unwrap()))?
|
||||
);
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ fn complete_r2<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_TABLE * secret_share, verification_shares[¶ms.i()]);
|
||||
debug_assert_eq!(C::GENERATOR * secret_share, verification_shares[¶ms.i()]);
|
||||
|
||||
// TODO: Clear serialized and shares
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use group::{ff::{Field, PrimeField}, GroupEncoding};
|
|||
mod schnorr;
|
||||
|
||||
pub mod curve;
|
||||
use curve::{Curve, F_from_slice, G_from_slice};
|
||||
use curve::{Curve, F_len, G_len, F_from_slice, G_from_slice};
|
||||
pub mod key_gen;
|
||||
pub mod algorithm;
|
||||
pub mod sign;
|
||||
|
@ -160,7 +160,7 @@ impl<C: Curve> FrostKeys<C> {
|
|||
// Enables schemes like Monero's subaddresses which have a per-subaddress offset and then a
|
||||
// one-time-key offset
|
||||
res.offset = Some(offset + res.offset.unwrap_or(C::F::zero()));
|
||||
res.group_key += C::GENERATOR_TABLE * offset;
|
||||
res.group_key += C::GENERATOR * offset;
|
||||
res
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ impl<C: Curve> FrostKeys<C> {
|
|||
verification_shares: self.verification_shares.iter().map(
|
||||
|(l, share)| (
|
||||
*l,
|
||||
(*share * lagrange::<C::F>(*l, &included)) + (C::GENERATOR_TABLE * offset_share)
|
||||
(*share * lagrange::<C::F>(*l, &included)) + (C::GENERATOR * offset_share)
|
||||
)
|
||||
).collect(),
|
||||
included: included.to_vec(),
|
||||
|
@ -203,7 +203,7 @@ impl<C: Curve> FrostKeys<C> {
|
|||
}
|
||||
|
||||
pub fn serialized_len(n: u16) -> usize {
|
||||
8 + C::ID.len() + (3 * 2) + C::F_len() + C::G_len() + (usize::from(n) * C::G_len())
|
||||
8 + C::ID.len() + (3 * 2) + F_len::<C>() + G_len::<C>() + (usize::from(n) * G_len::<C>())
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
|
@ -253,21 +253,21 @@ impl<C: Curve> FrostKeys<C> {
|
|||
let i = u16::from_be_bytes(serialized[cursor .. (cursor + 2)].try_into().unwrap());
|
||||
cursor += 2;
|
||||
|
||||
let secret_share = F_from_slice::<C::F>(&serialized[cursor .. (cursor + C::F_len())])
|
||||
let secret_share = F_from_slice::<C::F>(&serialized[cursor .. (cursor + F_len::<C>())])
|
||||
.map_err(|_| FrostError::InternalError("invalid secret share".to_string()))?;
|
||||
cursor += C::F_len();
|
||||
let group_key = G_from_slice::<C::G>(&serialized[cursor .. (cursor + C::G_len())])
|
||||
cursor += F_len::<C>();
|
||||
let group_key = G_from_slice::<C::G>(&serialized[cursor .. (cursor + G_len::<C>())])
|
||||
.map_err(|_| FrostError::InternalError("invalid group key".to_string()))?;
|
||||
cursor += C::G_len();
|
||||
cursor += G_len::<C>();
|
||||
|
||||
let mut verification_shares = HashMap::new();
|
||||
for l in 1 ..= n {
|
||||
verification_shares.insert(
|
||||
l,
|
||||
G_from_slice::<C::G>(&serialized[cursor .. (cursor + C::G_len())])
|
||||
G_from_slice::<C::G>(&serialized[cursor .. (cursor + G_len::<C>())])
|
||||
.map_err(|_| FrostError::InternalError("invalid verification share".to_string()))?
|
||||
);
|
||||
cursor += C::G_len();
|
||||
cursor += G_len::<C>();
|
||||
}
|
||||
|
||||
Ok(
|
||||
|
|
|
@ -4,7 +4,7 @@ use group::{ff::{Field, PrimeField}, GroupEncoding};
|
|||
|
||||
use multiexp::BatchVerifier;
|
||||
|
||||
use crate::Curve;
|
||||
use crate::{Curve, F_len, G_len};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
|
@ -15,7 +15,7 @@ pub struct SchnorrSignature<C: Curve> {
|
|||
|
||||
impl<C: Curve> SchnorrSignature<C> {
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(C::G_len() + C::F_len());
|
||||
let mut res = Vec::with_capacity(G_len::<C>() + F_len::<C>());
|
||||
res.extend(self.R.to_bytes().as_ref());
|
||||
res.extend(self.s.to_repr().as_ref());
|
||||
res
|
||||
|
@ -28,7 +28,7 @@ pub(crate) fn sign<C: Curve>(
|
|||
challenge: C::F
|
||||
) -> SchnorrSignature<C> {
|
||||
SchnorrSignature {
|
||||
R: C::GENERATOR_TABLE * nonce,
|
||||
R: C::GENERATOR * nonce,
|
||||
s: nonce + (private_key * challenge)
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ pub(crate) fn verify<C: Curve>(
|
|||
challenge: C::F,
|
||||
signature: &SchnorrSignature<C>
|
||||
) -> bool {
|
||||
(C::GENERATOR_TABLE * 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>(
|
||||
|
|
|
@ -8,7 +8,7 @@ use group::{ff::{Field, PrimeField}, GroupEncoding};
|
|||
use transcript::Transcript;
|
||||
|
||||
use crate::{
|
||||
curve::{Curve, F_from_slice, G_from_slice},
|
||||
curve::{Curve, G_len, F_from_slice, G_from_slice},
|
||||
FrostError,
|
||||
FrostParams, FrostKeys, FrostView,
|
||||
algorithm::Algorithm,
|
||||
|
@ -84,7 +84,7 @@ fn preprocess<R: RngCore + CryptoRng, C: Curve, A: Algorithm<C>>(
|
|||
C::random_nonce(params.view().secret_share(), &mut *rng),
|
||||
C::random_nonce(params.view().secret_share(), &mut *rng)
|
||||
];
|
||||
let commitments = [C::GENERATOR_TABLE * nonces[0], C::GENERATOR_TABLE * nonces[1]];
|
||||
let commitments = [C::GENERATOR * nonces[0], C::GENERATOR * nonces[1]];
|
||||
let mut serialized = commitments[0].to_bytes().as_ref().to_vec();
|
||||
serialized.extend(commitments[1].to_bytes().as_ref());
|
||||
|
||||
|
@ -146,18 +146,18 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
|
|||
|
||||
let commitments = commitments.remove(l).unwrap();
|
||||
let mut read_commitment = |c, label| {
|
||||
let commitment = &commitments[c .. (c + C::G_len())];
|
||||
let commitment = &commitments[c .. (c + G_len::<C>())];
|
||||
transcript.append_message(label, commitment);
|
||||
G_from_slice::<C::G>(commitment).map_err(|_| FrostError::InvalidCommitment(*l))
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let mut read_D_E = || Ok(
|
||||
[read_commitment(0, b"commitment_D")?, read_commitment(C::G_len(), b"commitment_E")?]
|
||||
[read_commitment(0, b"commitment_D")?, read_commitment(G_len::<C>(), b"commitment_E")?]
|
||||
);
|
||||
|
||||
B.insert(*l, read_D_E()?);
|
||||
addendums.insert(*l, commitments[(C::G_len() * 2) ..].to_vec());
|
||||
addendums.insert(*l, commitments[(G_len::<C>() * 2) ..].to_vec());
|
||||
}
|
||||
|
||||
// Append the message to the transcript
|
||||
|
|
|
@ -98,7 +98,7 @@ pub fn recover<C: Curve>(keys: &HashMap<u16, FrostKeys<C>>) -> C::F {
|
|||
C::F::zero(),
|
||||
|accum, (i, keys)| accum + (keys.secret_share() * lagrange::<C::F>(*i, &included))
|
||||
);
|
||||
assert_eq!(C::GENERATOR_TABLE * group_private, first.group_key(), "failed to recover keys");
|
||||
assert_eq!(C::GENERATOR * group_private, first.group_key(), "failed to recover keys");
|
||||
group_private
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ pub(crate) fn core_sign<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
|||
let challenge = C::F::random(rng); // Doesn't bother to craft an HRAM
|
||||
assert!(
|
||||
schnorr::verify::<C>(
|
||||
C::GENERATOR_TABLE * private_key,
|
||||
C::GENERATOR * private_key,
|
||||
challenge,
|
||||
&schnorr::sign(private_key, nonce, challenge)
|
||||
)
|
||||
|
@ -28,9 +28,9 @@ pub(crate) fn core_sign<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>(
|
||||
C::GENERATOR_TABLE * C::F::random(&mut *rng),
|
||||
C::GENERATOR * C::F::random(&mut *rng),
|
||||
C::F::random(rng),
|
||||
&SchnorrSignature { R: C::GENERATOR_TABLE * C::F::zero(), s: C::F::zero() }
|
||||
&SchnorrSignature { R: C::GENERATOR * C::F::zero(), s: C::F::zero() }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -48,7 +48,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_TABLE * keys[i], challenges[i], sigs[i])
|
||||
|i| (u16::try_from(i + 1).unwrap(), C::GENERATOR * keys[i], challenges[i], sigs[i])
|
||||
).collect::<Vec<_>>();
|
||||
schnorr::batch_verify(rng, &triplets).unwrap();
|
||||
|
||||
|
@ -113,7 +113,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, Arc::new(keys[&i].offset(offset)));
|
||||
}
|
||||
let offset_key = group_key + (C::GENERATOR_TABLE * offset);
|
||||
let offset_key = group_key + (C::GENERATOR * offset);
|
||||
|
||||
sign_core(rng, offset_key, &keys);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue