diff --git a/crypto/frost/src/curve/dalek.rs b/crypto/frost/src/curve/dalek.rs index 40e6c252..9c0bf65d 100644 --- a/crypto/frost/src/curve/dalek.rs +++ b/crypto/frost/src/curve/dalek.rs @@ -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(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"", diff --git a/crypto/frost/src/curve/kp256.rs b/crypto/frost/src/curve/kp256.rs index 9b1874d8..bb3e02ed 100644 --- a/crypto/frost/src/curve/kp256.rs +++ b/crypto/frost/src/curve/kp256.rs @@ -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(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)] diff --git a/crypto/frost/src/curve/mod.rs b/crypto/frost/src/curve/mod.rs index e08e2faf..32b8fef0 100644 --- a/crypto/frost/src/curve/mod.rs +++ b/crypto/frost/src/curve/mod.rs @@ -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 + GroupOps + PrimeGroup; - /// Precomputed table type - type T: Mul; /// 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(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() -> usize { + ::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() -> usize { + ::Repr::default().as_ref().len() } /// Field element from slice diff --git a/crypto/frost/src/key_gen.rs b/crypto/frost/src/key_gen.rs index e5b0f76f..84f4a590 100644 --- a/crypto/frost/src/key_gen.rs +++ b/crypto/frost/src/key_gen.rs @@ -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( 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::() * t) + G_len::() + F_len::()); 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( challenge::( 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( (params.i(), our_commitments) )?; - let commitments_len = usize::from(params.t()) * C::G_len(); + let commitments_len = usize::from(params.t()) * G_len::(); 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::()]; #[allow(non_snake_case)] let R = |l| G_from_slice::(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::( - &serialized[&l][commitments_len + C::G_len() ..] + &serialized[&l][commitments_len + G_len::() ..] ).map_err(|_| FrostError::InvalidProofOfKnowledge(l)); let mut signatures = Vec::with_capacity(usize::from(params.n() - 1)); @@ -104,7 +104,7 @@ fn verify_r1( for c in 0 .. usize::from(params.t()) { these_commitments.push( G_from_slice::( - &serialized[&l][(c * C::G_len()) .. ((c + 1) * C::G_len())] + &serialized[&l][(c * G_len::()) .. ((c + 1) * G_len::())] ).map_err(|_| FrostError::InvalidCommitment(l.try_into().unwrap()))? ); } @@ -257,7 +257,7 @@ fn complete_r2( 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 diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index ca64b96f..40363153 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -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 FrostKeys { // 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 FrostKeys { verification_shares: self.verification_shares.iter().map( |(l, share)| ( *l, - (*share * lagrange::(*l, &included)) + (C::GENERATOR_TABLE * offset_share) + (*share * lagrange::(*l, &included)) + (C::GENERATOR * offset_share) ) ).collect(), included: included.to_vec(), @@ -203,7 +203,7 @@ impl FrostKeys { } 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::() + G_len::() + (usize::from(n) * G_len::()) } pub fn serialize(&self) -> Vec { @@ -253,21 +253,21 @@ impl FrostKeys { let i = u16::from_be_bytes(serialized[cursor .. (cursor + 2)].try_into().unwrap()); cursor += 2; - let secret_share = F_from_slice::(&serialized[cursor .. (cursor + C::F_len())]) + let secret_share = F_from_slice::(&serialized[cursor .. (cursor + F_len::())]) .map_err(|_| FrostError::InternalError("invalid secret share".to_string()))?; - cursor += C::F_len(); - let group_key = G_from_slice::(&serialized[cursor .. (cursor + C::G_len())]) + cursor += F_len::(); + let group_key = G_from_slice::(&serialized[cursor .. (cursor + G_len::())]) .map_err(|_| FrostError::InternalError("invalid group key".to_string()))?; - cursor += C::G_len(); + cursor += G_len::(); let mut verification_shares = HashMap::new(); for l in 1 ..= n { verification_shares.insert( l, - G_from_slice::(&serialized[cursor .. (cursor + C::G_len())]) + G_from_slice::(&serialized[cursor .. (cursor + G_len::())]) .map_err(|_| FrostError::InternalError("invalid verification share".to_string()))? ); - cursor += C::G_len(); + cursor += G_len::(); } Ok( diff --git a/crypto/frost/src/schnorr.rs b/crypto/frost/src/schnorr.rs index af9ff808..dafc8251 100644 --- a/crypto/frost/src/schnorr.rs +++ b/crypto/frost/src/schnorr.rs @@ -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 { impl SchnorrSignature { pub fn serialize(&self) -> Vec { - let mut res = Vec::with_capacity(C::G_len() + C::F_len()); + let mut res = Vec::with_capacity(G_len::() + F_len::()); 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( challenge: C::F ) -> SchnorrSignature { SchnorrSignature { - R: C::GENERATOR_TABLE * nonce, + R: C::GENERATOR * nonce, s: nonce + (private_key * challenge) } } @@ -38,7 +38,7 @@ pub(crate) fn verify( challenge: C::F, signature: &SchnorrSignature ) -> 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( diff --git a/crypto/frost/src/sign.rs b/crypto/frost/src/sign.rs index c01dbe63..c05e4a89 100644 --- a/crypto/frost/src/sign.rs +++ b/crypto/frost/src/sign.rs @@ -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>( 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>( 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::())]; transcript.append_message(label, commitment); G_from_slice::(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::(), 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::() * 2) ..].to_vec()); } // Append the message to the transcript diff --git a/crypto/frost/src/tests/mod.rs b/crypto/frost/src/tests/mod.rs index 87f2bf83..3c982cbf 100644 --- a/crypto/frost/src/tests/mod.rs +++ b/crypto/frost/src/tests/mod.rs @@ -98,7 +98,7 @@ pub fn recover(keys: &HashMap>) -> C::F { C::F::zero(), |accum, (i, keys)| accum + (keys.secret_share() * lagrange::(*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 } diff --git a/crypto/frost/src/tests/schnorr.rs b/crypto/frost/src/tests/schnorr.rs index 6450a845..d27e5e5d 100644 --- a/crypto/frost/src/tests/schnorr.rs +++ b/crypto/frost/src/tests/schnorr.rs @@ -15,7 +15,7 @@ pub(crate) fn core_sign(rng: &mut R) { let challenge = C::F::random(rng); // Doesn't bother to craft an HRAM assert!( schnorr::verify::( - 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(rng: &mut R) { pub(crate) fn core_verify(rng: &mut R) { assert!( !schnorr::verify::( - 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(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::>(); schnorr::batch_verify(rng, &triplets).unwrap(); @@ -113,7 +113,7 @@ fn sign_with_offset(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); }