diff --git a/coins/monero/src/frost.rs b/coins/monero/src/frost.rs index 49d4e2da..69c4747e 100644 --- a/coins/monero/src/frost.rs +++ b/coins/monero/src/frost.rs @@ -3,6 +3,8 @@ use core::convert::TryInto; use thiserror::Error; use rand_core::{RngCore, CryptoRng}; +use group::GroupEncoding; + use curve25519_dalek::{ constants::ED25519_BASEPOINT_TABLE as DTable, scalar::Scalar as DScalar, @@ -10,7 +12,6 @@ use curve25519_dalek::{ }; use transcript::{Transcript, RecommendedTranscript}; -use frost::curve::{Curve, Ed25519}; use dalek_ff_group as dfg; use crate::random_scalar; @@ -118,18 +119,26 @@ impl DLEqProof { } #[allow(non_snake_case)] -pub fn read_dleq( +pub(crate) fn read_dleq( serialized: &[u8], start: usize, H: &DPoint, l: u16, xG: &DPoint ) -> Result { - // Not using G_from_slice here would enable non-canonical points and break blame - // This does also ban identity points, yet those should never be a concern - let other = ::G_from_slice( - &serialized[(start + 0) .. (start + 32)] - ).map_err(|_| MultisigError::InvalidDLEqProof(l))?; + if serialized.len() < start + 96 { + Err(MultisigError::InvalidDLEqProof(l))?; + } + + let bytes = (&serialized[(start + 0) .. (start + 32)]).try_into().unwrap(); + // dfg ensures the point is torsion free + let other = Option::::from( + dfg::EdwardsPoint::from_bytes(&bytes)).ok_or(MultisigError::InvalidDLEqProof(l) + )?; + // Ensure this is a canonical point + if other.to_bytes() != bytes { + Err(MultisigError::InvalidDLEqProof(l))?; + } DLEqProof::deserialize(&serialized[(start + 32) .. (start + 96)]) .ok_or(MultisigError::InvalidDLEqProof(l))? diff --git a/coins/monero/src/wallet/send/multisig.rs b/coins/monero/src/wallet/send/multisig.rs index c2de702c..1bf30d96 100644 --- a/coins/monero/src/wallet/send/multisig.rs +++ b/coins/monero/src/wallet/send/multisig.rs @@ -226,6 +226,11 @@ impl SignMachine for TransactionSignMachine { // FROST commitments, image, H commitments, and their proofs let clsag_len = 64 + ClsagMultisig::serialized_len(); + for (l, commitments) in &commitments { + if commitments.len() != (self.clsags.len() * clsag_len) { + Err(FrostError::InvalidCommitment(*l))?; + } + } // Convert the unified commitments to a Vec of the individual commitments let mut commitments = (0 .. self.clsags.len()).map(|_| commitments.iter_mut().map( diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index c919fea9..eea21cfb 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -27,7 +27,7 @@ use dalek::{ } }; -use group::{ff::{Field, PrimeField}, Group}; +use group::{ff::{Field, PrimeField}, Group, GroupEncoding, prime::PrimeGroup}; macro_rules! deref_borrow { ($Source: ident, $Target: ident) => { @@ -192,6 +192,7 @@ macro_rules! dalek_group { ( $Point: ident, $DPoint: ident, + $torsion_free: expr, $Table: ident, $DTable: ident, @@ -225,6 +226,29 @@ macro_rules! dalek_group { fn double(&self) -> Self { *self + self } } + impl GroupEncoding for $Point { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + if let Some(point) = $DCompressed(*bytes).decompress() { + if $torsion_free(point) { + return CtOption::new($Point(point), Choice::from(1)); + } + } + CtOption::new($Point::identity(), Choice::from(0)) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + $Point::from_bytes(bytes) + } + + fn to_bytes(&self) -> Self::Repr { + self.0.compress().to_bytes() + } + } + + impl PrimeGroup for $Point {} + pub struct $Compressed(pub $DCompressed); deref_borrow!($Compressed, $DCompressed); impl $Compressed { @@ -261,6 +285,7 @@ macro_rules! dalek_group { dalek_group!( EdwardsPoint, DEdwardsPoint, + |point: DEdwardsPoint| point.is_torsion_free(), EdwardsBasepointTable, DEdwardsBasepointTable, @@ -272,15 +297,10 @@ dalek_group!( ED25519_BASEPOINT_TABLE ); -impl EdwardsPoint { - pub fn is_torsion_free(&self) -> bool { - self.0.is_torsion_free() - } -} - dalek_group!( RistrettoPoint, DRistrettoPoint, + |_| true, RistrettoBasepointTable, DRistrettoBasepointTable, diff --git a/crypto/frost/src/curve/dalek.rs b/crypto/frost/src/curve/dalek.rs index 362a9614..07515eee 100644 --- a/crypto/frost/src/curve/dalek.rs +++ b/crypto/frost/src/curve/dalek.rs @@ -1,34 +1,27 @@ -use core::convert::TryInto; - use rand_core::{RngCore, CryptoRng}; use sha2::{Digest, Sha512}; -use group::{ff::PrimeField, Group}; - use dalek_ff_group::Scalar; -use crate::{curve::{CurveError, Curve}, algorithm::Hram}; +use crate::{curve::Curve, algorithm::Hram}; macro_rules! dalek_curve { ( $Curve: ident, $Hram: ident, $Point: ident, - $Compressed: ident, $Table: ident, $POINT: ident, $TABLE: ident, - $torsioned: expr, - $ID: literal, $CONTEXT: literal, $chal: literal, $digest: literal, ) => { - use dalek_ff_group::{$Point, $Compressed, $Table, $POINT, $TABLE}; + use dalek_ff_group::{$Point, $Table, $POINT, $TABLE}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct $Curve; @@ -75,43 +68,6 @@ macro_rules! dalek_curve { fn G_len() -> usize { 32 } - - fn F_from_slice(slice: &[u8]) -> Result { - let scalar = Self::F::from_repr( - slice.try_into().map_err(|_| CurveError::InvalidLength(32, slice.len()))? - ); - if !bool::from(scalar.is_some()) { - Err(CurveError::InvalidScalar)?; - } - Ok(scalar.unwrap()) - } - - fn G_from_slice(slice: &[u8]) -> Result { - let bytes = slice.try_into().map_err(|_| CurveError::InvalidLength(32, slice.len()))?; - let point = $Compressed::new(bytes).decompress().ok_or(CurveError::InvalidPoint)?; - - // Ban identity - if point.is_identity().into() { - Err(CurveError::InvalidPoint)?; - } - // Ban torsioned points to meet the prime order group requirement - if $torsioned(point) { - Err(CurveError::InvalidPoint)?; - } - // Ban points which weren't canonically encoded - if point.compress().to_bytes() != bytes { - Err(CurveError::InvalidPoint)?; - } - Ok(point) - } - - fn F_to_bytes(f: &Self::F) -> Vec { - f.to_repr().to_vec() - } - - fn G_to_bytes(g: &Self::G) -> Vec { - g.compress().to_bytes().to_vec() - } } #[derive(Copy, Clone)] @@ -130,11 +86,9 @@ dalek_curve!( Ristretto, IetfRistrettoHram, RistrettoPoint, - CompressedRistretto, RistrettoBasepointTable, RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_TABLE, - |_| false, b"ristretto", b"FROST-RISTRETTO255-SHA512-v5", b"chal", @@ -146,11 +100,9 @@ dalek_curve!( Ed25519, IetfEd25519Hram, EdwardsPoint, - CompressedEdwardsY, EdwardsBasepointTable, ED25519_BASEPOINT_POINT, ED25519_BASEPOINT_TABLE, - |point: EdwardsPoint| !bool::from(point.is_torsion_free()), b"edwards25519", b"", b"", diff --git a/crypto/frost/src/curve/kp256.rs b/crypto/frost/src/curve/kp256.rs index 1b762978..278e4eaa 100644 --- a/crypto/frost/src/curve/kp256.rs +++ b/crypto/frost/src/curve/kp256.rs @@ -1,14 +1,12 @@ -use core::convert::TryInto; - use rand_core::{RngCore, CryptoRng}; use sha2::{digest::Update, Digest, Sha256}; -use group::{ff::{Field, PrimeField}, Group, GroupEncoding}; +use group::{ff::Field, GroupEncoding}; use elliptic_curve::{bigint::{Encoding, U384}, hash2curve::{Expander, ExpandMsg, ExpandMsgXmd}}; -use crate::{curve::{CurveError, Curve}, algorithm::Hram}; +use crate::{curve::{Curve, F_from_slice}, algorithm::Hram}; macro_rules! kp_curve { ( @@ -65,7 +63,7 @@ macro_rules! kp_curve { let mut modulus = vec![0; 16]; modulus.extend((Self::F::zero() - Self::F::one()).to_bytes()); let modulus = U384::from_be_slice(&modulus).wrapping_add(&U384::ONE); - Self::F_from_slice( + F_from_slice::( &U384::from_be_slice(&{ let mut bytes = [0; 48]; ExpandMsgXmd::::expand_message( @@ -85,38 +83,6 @@ macro_rules! kp_curve { fn G_len() -> usize { 33 } - - fn F_from_slice(slice: &[u8]) -> Result { - let bytes: [u8; 32] = slice.try_into() - .map_err(|_| CurveError::InvalidLength(32, slice.len()))?; - - let scalar = Self::F::from_repr(bytes.into()); - if scalar.is_none().into() { - Err(CurveError::InvalidScalar)?; - } - - Ok(scalar.unwrap()) - } - - fn G_from_slice(slice: &[u8]) -> Result { - let bytes: [u8; 33] = slice.try_into() - .map_err(|_| CurveError::InvalidLength(33, slice.len()))?; - - let point = Self::G::from_bytes(&bytes.into()); - if point.is_none().into() || point.unwrap().is_identity().into() { - Err(CurveError::InvalidPoint)?; - } - - Ok(point.unwrap()) - } - - fn F_to_bytes(f: &Self::F) -> Vec { - f.to_bytes().to_vec() - } - - fn G_to_bytes(g: &Self::G) -> Vec { - g.to_bytes().to_vec() - } } #[derive(Clone)] @@ -126,7 +92,7 @@ macro_rules! kp_curve { fn hram(R: &$lib::ProjectivePoint, A: &$lib::ProjectivePoint, m: &[u8]) -> $lib::Scalar { $Curve::hash_to_F( &[$CONTEXT as &[u8], b"chal"].concat(), - &[&$Curve::G_to_bytes(R), &$Curve::G_to_bytes(A), m].concat() + &[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat() ) } } diff --git a/crypto/frost/src/curve/mod.rs b/crypto/frost/src/curve/mod.rs index f6ad5cf9..2de31a2a 100644 --- a/crypto/frost/src/curve/mod.rs +++ b/crypto/frost/src/curve/mod.rs @@ -4,7 +4,7 @@ use thiserror::Error; use rand_core::{RngCore, CryptoRng}; -use group::{ff::PrimeField, Group, GroupOps}; +use group::{ff::PrimeField, Group, GroupOps, prime::PrimeGroup}; #[cfg(any(test, feature = "dalek"))] mod dalek; @@ -42,7 +42,7 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug { // This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses type F: PrimeField; /// Group element type - type G: Group + GroupOps; + type G: Group + GroupOps + PrimeGroup; /// Precomputed table type type T: Mul; @@ -99,23 +99,31 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug { // that is on them #[allow(non_snake_case)] fn G_len() -> usize; - - /// Field element from slice. Preferred to be canonical yet does not have to be - // Required due to the lack of standardized encoding functions provided by ff/group - // While they do technically exist, their usage of Self::Repr breaks all potential library usage - // without helper functions like this - #[allow(non_snake_case)] - fn F_from_slice(slice: &[u8]) -> Result; - - /// Group element from slice. Must require canonicity or risks differing binding factors - #[allow(non_snake_case)] - fn G_from_slice(slice: &[u8]) -> Result; - - /// Obtain a vector of the byte encoding of F - #[allow(non_snake_case)] - fn F_to_bytes(f: &Self::F) -> Vec; - - /// Obtain a vector of the byte encoding of G - #[allow(non_snake_case)] - fn G_to_bytes(g: &Self::G) -> Vec; +} + +/// Field element from slice +#[allow(non_snake_case)] +pub(crate) fn F_from_slice(slice: &[u8]) -> Result { + let mut encoding = F::Repr::default(); + encoding.as_mut().copy_from_slice(slice); + + let point = Option::::from(F::from_repr(encoding)).ok_or(CurveError::InvalidScalar)?; + if point.to_repr().as_ref() != slice { + Err(CurveError::InvalidScalar)?; + } + Ok(point) +} + +/// Group element from slice +#[allow(non_snake_case)] +pub(crate) fn G_from_slice(slice: &[u8]) -> Result { + let mut encoding = G::Repr::default(); + encoding.as_mut().copy_from_slice(slice); + + let point = Option::::from(G::from_bytes(&encoding)).ok_or(CurveError::InvalidPoint)?; + // Ban the identity, per the FROST spec, and non-canonical points + if (point.is_identity().into()) || (point.to_bytes().as_ref() != slice) { + Err(CurveError::InvalidPoint)?; + } + Ok(point) } diff --git a/crypto/frost/src/key_gen.rs b/crypto/frost/src/key_gen.rs index aeb18ca0..4f2832e5 100644 --- a/crypto/frost/src/key_gen.rs +++ b/crypto/frost/src/key_gen.rs @@ -2,12 +2,12 @@ use std::{marker::PhantomData, collections::HashMap}; use rand_core::{RngCore, CryptoRng}; -use group::ff::{Field, PrimeField}; +use group::{ff::{Field, PrimeField}, GroupEncoding}; use multiexp::{multiexp_vartime, BatchVerifier}; use crate::{ - curve::Curve, + curve::{Curve, F_from_slice, G_from_slice}, FrostError, FrostParams, FrostKeys, schnorr::{self, SchnorrSignature}, validate_map @@ -43,7 +43,7 @@ fn generate_key_r1( // Step 3: Generate public commitments commitments.push(C::GENERATOR_TABLE * coefficients[i]); // Serialize them for publication - serialized.extend(&C::G_to_bytes(&commitments[i])); + serialized.extend(commitments[i].to_bytes().as_ref()); } // Step 2: Provide a proof of knowledge @@ -59,7 +59,7 @@ fn generate_key_r1( challenge::( context, params.i(), - &C::G_to_bytes(&(C::GENERATOR_TABLE * r)), + (C::GENERATOR_TABLE * r).to_bytes().as_ref(), &serialized ) ).serialize() @@ -90,11 +90,11 @@ fn verify_r1( #[allow(non_snake_case)] let R_bytes = |l| &serialized[&l][commitments_len .. commitments_len + C::G_len()]; #[allow(non_snake_case)] - let R = |l| C::G_from_slice(R_bytes(l)).map_err(|_| FrostError::InvalidProofOfKnowledge(l)); + 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| C::F_from_slice( + let s = |l| F_from_slice::( &serialized[&l][commitments_len + C::G_len() ..] ).map_err(|_| FrostError::InvalidProofOfKnowledge(l)); @@ -103,7 +103,7 @@ fn verify_r1( let mut these_commitments = vec![]; for c in 0 .. usize::from(params.t()) { these_commitments.push( - C::G_from_slice( + G_from_slice::( &serialized[&l][(c * C::G_len()) .. ((c + 1) * C::G_len())] ).map_err(|_| FrostError::InvalidCommitment(l.try_into().unwrap()))? ); @@ -166,7 +166,7 @@ fn generate_key_r2( continue; } - res.insert(l, C::F_to_bytes(&polynomial(&coefficients, l))); + res.insert(l, polynomial(&coefficients, l).to_repr().as_ref().to_vec()); } // Calculate our own share @@ -199,13 +199,13 @@ fn complete_r2( validate_map( &mut serialized, &(1 ..= params.n()).into_iter().collect::>(), - (params.i(), C::F_to_bytes(&secret_share)) + (params.i(), secret_share.to_repr().as_ref().to_vec()) )?; // Step 2. Verify each share let mut shares = HashMap::new(); for (l, share) in serialized { - shares.insert(l, C::F_from_slice(&share).map_err(|_| FrostError::InvalidShare(l))?); + shares.insert(l, F_from_slice::(&share).map_err(|_| FrostError::InvalidShare(l))?); } // Calculate the exponent for a given participant and apply it to a series of commitments diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index e337b70e..ca64b96f 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -3,12 +3,12 @@ use std::collections::HashMap; use thiserror::Error; -use group::ff::{Field, PrimeField}; +use group::{ff::{Field, PrimeField}, GroupEncoding}; mod schnorr; pub mod curve; -use curve::Curve; +use curve::{Curve, F_from_slice, G_from_slice}; pub mod key_gen; pub mod algorithm; pub mod sign; @@ -213,10 +213,10 @@ impl FrostKeys { serialized.extend(&self.params.t.to_be_bytes()); serialized.extend(&self.params.n.to_be_bytes()); serialized.extend(&self.params.i.to_be_bytes()); - serialized.extend(&C::F_to_bytes(&self.secret_share)); - serialized.extend(&C::G_to_bytes(&self.group_key)); + serialized.extend(self.secret_share.to_repr().as_ref()); + serialized.extend(self.group_key.to_bytes().as_ref()); for l in 1 ..= self.params.n.into() { - serialized.extend(&C::G_to_bytes(&self.verification_shares[&l])); + serialized.extend(self.verification_shares[&l].to_bytes().as_ref()); } serialized } @@ -253,10 +253,10 @@ impl FrostKeys { let i = u16::from_be_bytes(serialized[cursor .. (cursor + 2)].try_into().unwrap()); cursor += 2; - let secret_share = C::F_from_slice(&serialized[cursor .. (cursor + C::F_len())]) + let secret_share = F_from_slice::(&serialized[cursor .. (cursor + C::F_len())]) .map_err(|_| FrostError::InternalError("invalid secret share".to_string()))?; cursor += C::F_len(); - let group_key = C::G_from_slice(&serialized[cursor .. (cursor + C::G_len())]) + let group_key = G_from_slice::(&serialized[cursor .. (cursor + C::G_len())]) .map_err(|_| FrostError::InternalError("invalid group key".to_string()))?; cursor += C::G_len(); @@ -264,7 +264,7 @@ impl FrostKeys { for l in 1 ..= n { verification_shares.insert( l, - C::G_from_slice(&serialized[cursor .. (cursor + C::G_len())]) + G_from_slice::(&serialized[cursor .. (cursor + C::G_len())]) .map_err(|_| FrostError::InternalError("invalid verification share".to_string()))? ); cursor += C::G_len(); diff --git a/crypto/frost/src/schnorr.rs b/crypto/frost/src/schnorr.rs index 22361173..9424fd28 100644 --- a/crypto/frost/src/schnorr.rs +++ b/crypto/frost/src/schnorr.rs @@ -1,6 +1,6 @@ use rand_core::{RngCore, CryptoRng}; -use group::ff::Field; +use group::{ff::{Field, PrimeField}, GroupEncoding}; use multiexp::BatchVerifier; @@ -16,8 +16,8 @@ pub struct SchnorrSignature { impl SchnorrSignature { pub fn serialize(&self) -> Vec { let mut res = Vec::with_capacity(C::G_len() + C::F_len()); - res.extend(C::G_to_bytes(&self.R)); - res.extend(C::F_to_bytes(&self.s)); + res.extend(self.R.to_bytes().as_ref()); + res.extend(self.s.to_repr().as_ref()); res } } diff --git a/crypto/frost/src/sign.rs b/crypto/frost/src/sign.rs index 902607f7..ba2b8203 100644 --- a/crypto/frost/src/sign.rs +++ b/crypto/frost/src/sign.rs @@ -3,12 +3,12 @@ use std::{sync::Arc, collections::HashMap}; use rand_core::{RngCore, CryptoRng}; -use group::ff::Field; +use group::{ff::{Field, PrimeField}, GroupEncoding}; use transcript::Transcript; use crate::{ - curve::Curve, + curve::{Curve, F_from_slice, G_from_slice}, FrostError, FrostParams, FrostKeys, FrostView, algorithm::Algorithm, @@ -85,8 +85,8 @@ fn preprocess>( C::random_nonce(params.view().secret_share(), &mut *rng) ]; let commitments = [C::GENERATOR_TABLE * nonces[0], C::GENERATOR_TABLE * nonces[1]]; - let mut serialized = C::G_to_bytes(&commitments[0]); - serialized.extend(&C::G_to_bytes(&commitments[1])); + let mut serialized = commitments[0].to_bytes().as_ref().to_vec(); + serialized.extend(commitments[1].to_bytes().as_ref()); serialized.extend( ¶ms.algorithm.preprocess_addendum( @@ -129,7 +129,7 @@ fn sign_with_share>( transcript.domain_separate(b"FROST"); // Include the offset, if one exists if let Some(offset) = params.keys.offset { - transcript.append_message(b"offset", &C::F_to_bytes(&offset)); + transcript.append_message(b"offset", offset.to_repr().as_ref()); } } @@ -148,7 +148,7 @@ fn sign_with_share>( let mut read_commitment = |c, label| { let commitment = &commitments[c .. (c + C::G_len())]; transcript.append_message(label, commitment); - C::G_from_slice(commitment).map_err(|_| FrostError::InvalidCommitment(*l)) + G_from_slice::(commitment).map_err(|_| FrostError::InvalidCommitment(*l)) }; #[allow(non_snake_case)] @@ -176,15 +176,13 @@ fn sign_with_share>( let R = { B.values().map(|B| B[0]).sum::() + (B.values().map(|B| B[1]).sum::() * binding) }; - let share = C::F_to_bytes( - ¶ms.algorithm.sign_share( - ¶ms.view, - R, - binding, - our_preprocess.nonces[0] + (our_preprocess.nonces[1] * binding), - msg - ) - ); + let share = params.algorithm.sign_share( + ¶ms.view, + R, + binding, + our_preprocess.nonces[0] + (our_preprocess.nonces[1] * binding), + msg + ).to_repr().as_ref().to_vec(); Ok((Package { B, binding, R, share: share.clone() }, share)) } @@ -203,7 +201,7 @@ fn complete>( let mut responses = HashMap::new(); let mut sum = C::F::zero(); for l in &sign_params.view.included { - let part = C::F_from_slice(&shares[l]).map_err(|_| FrostError::InvalidShare(*l))?; + let part = F_from_slice::(&shares[l]).map_err(|_| FrostError::InvalidShare(*l))?; sum += part; responses.insert(*l, part); } diff --git a/crypto/frost/src/tests/schnorr.rs b/crypto/frost/src/tests/schnorr.rs index 7ef1806a..6450a845 100644 --- a/crypto/frost/src/tests/schnorr.rs +++ b/crypto/frost/src/tests/schnorr.rs @@ -2,7 +2,7 @@ use std::{marker::PhantomData, sync::Arc, collections::HashMap}; use rand_core::{RngCore, CryptoRng}; -use group::ff::Field; +use group::{ff::Field, GroupEncoding}; use crate::{ Curve, FrostKeys, schnorr::{self, SchnorrSignature}, algorithm::{Hram, Schnorr}, @@ -96,7 +96,7 @@ pub struct TestHram { impl Hram for TestHram { #[allow(non_snake_case)] fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F { - C::hash_to_F(b"challenge", &[&C::G_to_bytes(R), &C::G_to_bytes(A), m].concat()) + C::hash_to_F(b"challenge", &[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat()) } } diff --git a/crypto/frost/src/tests/vectors.rs b/crypto/frost/src/tests/vectors.rs index 00f745fb..7a5d1af5 100644 --- a/crypto/frost/src/tests/vectors.rs +++ b/crypto/frost/src/tests/vectors.rs @@ -2,8 +2,10 @@ use std::{sync::Arc, collections::HashMap}; use rand_core::{RngCore, CryptoRng}; +use group::{ff::PrimeField, GroupEncoding}; + use crate::{ - Curve, FrostKeys, + curve::{Curve, F_from_slice, G_from_slice}, FrostKeys, algorithm::{Schnorr, Hram}, sign::{PreprocessPackage, SignMachine, SignatureMachine, AlgorithmMachine}, tests::{curve::test_curve, schnorr::test_schnorr, recover} @@ -25,7 +27,7 @@ pub struct Vectors { // Load these vectors into FrostKeys using a custom serialization it'll deserialize fn vectors_to_multisig_keys(vectors: &Vectors) -> HashMap> { let shares = vectors.shares.iter().map( - |secret| C::F_from_slice(&hex::decode(secret).unwrap()).unwrap() + |secret| F_from_slice::(&hex::decode(secret).unwrap()).unwrap() ).collect::>(); let verification_shares = shares.iter().map( |secret| C::GENERATOR * secret @@ -39,10 +41,10 @@ fn vectors_to_multisig_keys(vectors: &Vectors) -> HashMap::deserialize(&serialized).unwrap(); @@ -50,7 +52,7 @@ fn vectors_to_multisig_keys(vectors: &Vectors) -> HashMap(&vectors); - let group_key = C::G_from_slice(&hex::decode(vectors.group_key).unwrap()).unwrap(); + let group_key = G_from_slice::(&hex::decode(vectors.group_key).unwrap()).unwrap(); assert_eq!( - C::GENERATOR * C::F_from_slice(&hex::decode(vectors.group_secret).unwrap()).unwrap(), + C::GENERATOR * F_from_slice::(&hex::decode(vectors.group_secret).unwrap()).unwrap(), group_key ); assert_eq!( recover(&keys), - C::F_from_slice(&hex::decode(vectors.group_secret).unwrap()).unwrap() + F_from_slice::(&hex::decode(vectors.group_secret).unwrap()).unwrap() ); let mut machines = vec![]; @@ -94,13 +96,13 @@ pub fn test_with_vectors< let mut c = 0; let mut machines = machines.drain(..).map(|(i, machine)| { let nonces = [ - C::F_from_slice(&hex::decode(vectors.nonces[c][0]).unwrap()).unwrap(), - C::F_from_slice(&hex::decode(vectors.nonces[c][1]).unwrap()).unwrap() + F_from_slice::(&hex::decode(vectors.nonces[c][0]).unwrap()).unwrap(), + F_from_slice::(&hex::decode(vectors.nonces[c][1]).unwrap()).unwrap() ]; c += 1; - let mut serialized = C::G_to_bytes(&(C::GENERATOR * nonces[0])); - serialized.extend(&C::G_to_bytes(&(C::GENERATOR * nonces[1]))); + let mut serialized = (C::GENERATOR * nonces[0]).to_bytes().as_ref().to_vec(); + serialized.extend((C::GENERATOR * nonces[1]).to_bytes().as_ref()); let (machine, serialized) = machine.unsafe_override_preprocess( PreprocessPackage { nonces, serialized: serialized.clone() } @@ -127,8 +129,8 @@ pub fn test_with_vectors< for (_, machine) in machines.drain() { let sig = machine.complete(shares.clone()).unwrap(); - let mut serialized = C::G_to_bytes(&sig.R); - serialized.extend(C::F_to_bytes(&sig.s)); + let mut serialized = sig.R.to_bytes().as_ref().to_vec(); + serialized.extend(sig.s.to_repr().as_ref()); assert_eq!(hex::encode(serialized), vectors.sig); } } diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 131b3106..cf2eb7a3 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -18,6 +18,8 @@ serde_json = "1.0" curve25519-dalek = { version = "3", features = ["std"] } blake2 = "0.10" +group = "0.12" + transcript = { package = "flexible-transcript", path = "../crypto/transcript", features = ["recommended"] } dalek-ff-group = { path = "../crypto/dalek-ff-group" } frost = { package = "modular-frost", path = "../crypto/frost" } diff --git a/processor/src/wallet.rs b/processor/src/wallet.rs index 79458ce1..2b9bbf37 100644 --- a/processor/src/wallet.rs +++ b/processor/src/wallet.rs @@ -2,8 +2,9 @@ use std::{sync::Arc, collections::HashMap}; use rand_core::OsRng; -use transcript::{Transcript, RecommendedTranscript}; +use group::GroupEncoding; +use transcript::{Transcript, RecommendedTranscript}; use frost::{curve::Curve, FrostKeys, sign::{PreprocessMachine, SignMachine, SignatureMachine}}; use crate::{coin::{CoinError, Output, Coin}, SignError, Network}; @@ -31,7 +32,7 @@ impl WalletKeys { let mut transcript = RecommendedTranscript::new(DST); transcript.append_message(b"chain", chain); transcript.append_message(b"curve", C::ID); - transcript.append_message(b"group_key", &C::G_to_bytes(&self.keys.group_key())); + transcript.append_message(b"group_key", self.keys.group_key().to_bytes().as_ref()); self.keys.offset(C::hash_to_F(DST, &transcript.challenge(b"offset"))) } }