mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-03 17:40:34 +00:00
Use GroupEncoding instead of Curve's from_slice/to_bytes
Increases usage of standardization while expanding dalek_ff_group. Closes https://github.com/serai-dex/serai/issues/26 by moving dfg::EdwardsPoint to only be for the prime subgroup.
This commit is contained in:
parent
ac17645fc8
commit
3de7a76051
14 changed files with 141 additions and 178 deletions
|
@ -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<dfg::EdwardsPoint, MultisigError> {
|
||||
// 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 = <Ed25519 as Curve>::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::<dfg::EdwardsPoint>::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))?
|
||||
|
|
|
@ -226,6 +226,11 @@ impl SignMachine<Transaction> 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(
|
||||
|
|
|
@ -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<Self> {
|
||||
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<Self> {
|
||||
$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,
|
||||
|
|
|
@ -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<Self::F, CurveError> {
|
||||
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<Self::G, CurveError> {
|
||||
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<u8> {
|
||||
f.to_repr().to_vec()
|
||||
}
|
||||
|
||||
fn G_to_bytes(g: &Self::G) -> Vec<u8> {
|
||||
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"",
|
||||
|
|
|
@ -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::<Self::F>(
|
||||
&U384::from_be_slice(&{
|
||||
let mut bytes = [0; 48];
|
||||
ExpandMsgXmd::<Sha256>::expand_message(
|
||||
|
@ -85,38 +83,6 @@ macro_rules! kp_curve {
|
|||
fn G_len() -> usize {
|
||||
33
|
||||
}
|
||||
|
||||
fn F_from_slice(slice: &[u8]) -> Result<Self::F, CurveError> {
|
||||
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<Self::G, CurveError> {
|
||||
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<u8> {
|
||||
f.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn G_to_bytes(g: &Self::G) -> Vec<u8> {
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Scalar = Self::F> + GroupOps;
|
||||
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup;
|
||||
/// Precomputed table type
|
||||
type T: Mul<Self::F, Output = Self::G>;
|
||||
|
||||
|
@ -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<Self::F, CurveError>;
|
||||
|
||||
/// Group element from slice. Must require canonicity or risks differing binding factors
|
||||
#[allow(non_snake_case)]
|
||||
fn G_from_slice(slice: &[u8]) -> Result<Self::G, CurveError>;
|
||||
|
||||
/// Obtain a vector of the byte encoding of F
|
||||
#[allow(non_snake_case)]
|
||||
fn F_to_bytes(f: &Self::F) -> Vec<u8>;
|
||||
|
||||
/// Obtain a vector of the byte encoding of G
|
||||
#[allow(non_snake_case)]
|
||||
fn G_to_bytes(g: &Self::G) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Field element from slice
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn F_from_slice<F: PrimeField>(slice: &[u8]) -> Result<F, CurveError> {
|
||||
let mut encoding = F::Repr::default();
|
||||
encoding.as_mut().copy_from_slice(slice);
|
||||
|
||||
let point = Option::<F>::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<G: PrimeGroup>(slice: &[u8]) -> Result<G, CurveError> {
|
||||
let mut encoding = G::Repr::default();
|
||||
encoding.as_mut().copy_from_slice(slice);
|
||||
|
||||
let point = Option::<G>::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)
|
||||
}
|
||||
|
|
|
@ -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<R: RngCore + CryptoRng, C: Curve>(
|
|||
// 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<R: RngCore + CryptoRng, C: Curve>(
|
|||
challenge::<C>(
|
||||
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<R: RngCore + CryptoRng, C: Curve>(
|
|||
#[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::<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| C::F_from_slice(
|
||||
let s = |l| F_from_slice::<C::F>(
|
||||
&serialized[&l][commitments_len + C::G_len() ..]
|
||||
).map_err(|_| FrostError::InvalidProofOfKnowledge(l));
|
||||
|
||||
|
@ -103,7 +103,7 @@ fn verify_r1<R: RngCore + CryptoRng, C: Curve>(
|
|||
let mut these_commitments = vec![];
|
||||
for c in 0 .. usize::from(params.t()) {
|
||||
these_commitments.push(
|
||||
C::G_from_slice(
|
||||
G_from_slice::<C::G>(
|
||||
&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<R: RngCore + CryptoRng, C: Curve>(
|
|||
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<R: RngCore + CryptoRng, C: Curve>(
|
|||
validate_map(
|
||||
&mut serialized,
|
||||
&(1 ..= params.n()).into_iter().collect::<Vec<_>>(),
|
||||
(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::<C::F>(&share).map_err(|_| FrostError::InvalidShare(l))?);
|
||||
}
|
||||
|
||||
// Calculate the exponent for a given participant and apply it to a series of commitments
|
||||
|
|
|
@ -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<C: Curve> FrostKeys<C> {
|
|||
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<C: Curve> FrostKeys<C> {
|
|||
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::<C::F>(&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::<C::G>(&serialized[cursor .. (cursor + C::G_len())])
|
||||
.map_err(|_| FrostError::InternalError("invalid group key".to_string()))?;
|
||||
cursor += C::G_len();
|
||||
|
||||
|
@ -264,7 +264,7 @@ impl<C: Curve> FrostKeys<C> {
|
|||
for l in 1 ..= n {
|
||||
verification_shares.insert(
|
||||
l,
|
||||
C::G_from_slice(&serialized[cursor .. (cursor + C::G_len())])
|
||||
G_from_slice::<C::G>(&serialized[cursor .. (cursor + C::G_len())])
|
||||
.map_err(|_| FrostError::InternalError("invalid verification share".to_string()))?
|
||||
);
|
||||
cursor += C::G_len();
|
||||
|
|
|
@ -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<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());
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<R: RngCore + CryptoRng, C: Curve, A: Algorithm<C>>(
|
|||
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<C: Curve, A: Algorithm<C>>(
|
|||
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<C: Curve, A: Algorithm<C>>(
|
|||
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::<C::G>(commitment).map_err(|_| FrostError::InvalidCommitment(*l))
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -176,15 +176,13 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
|
|||
let R = {
|
||||
B.values().map(|B| B[0]).sum::<C::G>() + (B.values().map(|B| B[1]).sum::<C::G>() * 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<C: Curve, A: Algorithm<C>>(
|
|||
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::<C::F>(&shares[l]).map_err(|_| FrostError::InvalidShare(*l))?;
|
||||
sum += part;
|
||||
responses.insert(*l, part);
|
||||
}
|
||||
|
|
|
@ -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<C: Curve> {
|
|||
impl<C: Curve> Hram<C> for TestHram<C> {
|
||||
#[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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<C: Curve>(vectors: &Vectors) -> HashMap<u16, FrostKeys<C>> {
|
||||
let shares = vectors.shares.iter().map(
|
||||
|secret| C::F_from_slice(&hex::decode(secret).unwrap()).unwrap()
|
||||
|secret| F_from_slice::<C::F>(&hex::decode(secret).unwrap()).unwrap()
|
||||
).collect::<Vec<_>>();
|
||||
let verification_shares = shares.iter().map(
|
||||
|secret| C::GENERATOR * secret
|
||||
|
@ -39,10 +41,10 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, FrostKe
|
|||
serialized.extend(vectors.threshold.to_be_bytes());
|
||||
serialized.extend(u16::try_from(shares.len()).unwrap().to_be_bytes());
|
||||
serialized.extend(i.to_be_bytes());
|
||||
serialized.extend(C::F_to_bytes(&shares[usize::from(i) - 1]));
|
||||
serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
|
||||
serialized.extend(&hex::decode(vectors.group_key).unwrap());
|
||||
for share in &verification_shares {
|
||||
serialized.extend(&C::G_to_bytes(share));
|
||||
serialized.extend(share.to_bytes().as_ref());
|
||||
}
|
||||
|
||||
let these_keys = FrostKeys::<C>::deserialize(&serialized).unwrap();
|
||||
|
@ -50,7 +52,7 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, FrostKe
|
|||
assert_eq!(usize::from(these_keys.params().n()), shares.len());
|
||||
assert_eq!(these_keys.params().i(), i);
|
||||
assert_eq!(these_keys.secret_share(), shares[usize::from(i - 1)]);
|
||||
assert_eq!(&hex::encode(&C::G_to_bytes(&these_keys.group_key())), vectors.group_key);
|
||||
assert_eq!(&hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key);
|
||||
keys.insert(i, these_keys);
|
||||
}
|
||||
|
||||
|
@ -68,14 +70,14 @@ pub fn test_with_vectors<
|
|||
|
||||
// Test against the vectors
|
||||
let keys = vectors_to_multisig_keys::<C>(&vectors);
|
||||
let group_key = C::G_from_slice(&hex::decode(vectors.group_key).unwrap()).unwrap();
|
||||
let group_key = G_from_slice::<C::G>(&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::<C::F>(&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::<C::F>(&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::<C::F>(&hex::decode(vectors.nonces[c][0]).unwrap()).unwrap(),
|
||||
F_from_slice::<C::F>(&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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<C: Curve> WalletKeys<C> {
|
|||
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")))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue