mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 12:49:23 +00:00
Add Ed25519 to FROST and remove expand_xmd for elliptic_curve's
Doesn't fully utilize ec's hash2curve module as k256 Scalar doesn't have FromOkm for some reason. The previously present bigint reduction is preserved. Updates ff/group to 0.12. Premised on https://github.com/cfrg/draft-irtf-cfrg-frost/pull/205 being merged, as while this Ed25519 is vector compliant, it's technically not spec compliant due to that conflict.
This commit is contained in:
@@ -16,16 +16,15 @@ rand = "0.8"
|
||||
rand_distr = "0.4"
|
||||
|
||||
tiny-keccak = { version = "2", features = ["keccak"] }
|
||||
blake2 = "0.10"
|
||||
blake2 = { version = "0.10", optional = true }
|
||||
|
||||
curve25519-dalek = { version = "3", features = ["std"] }
|
||||
|
||||
ff = { version = "0.11", optional = true }
|
||||
group = { version = "0.11", optional = true }
|
||||
group = { version = "0.12", optional = true }
|
||||
|
||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true }
|
||||
transcript = { path = "../../crypto/transcript", optional = true }
|
||||
frost = { path = "../../crypto/frost", optional = true }
|
||||
frost = { path = "../../crypto/frost", features = ["ed25519"], optional = true }
|
||||
|
||||
monero = "0.16"
|
||||
|
||||
@@ -37,7 +36,7 @@ reqwest = { version = "0.11", features = ["json"] }
|
||||
|
||||
[features]
|
||||
experimental = []
|
||||
multisig = ["ff", "group", "rand_chacha", "transcript", "frost", "dalek-ff-group"]
|
||||
multisig = ["rand_chacha", "blake2", "group", "dalek-ff-group", "transcript", "frost"]
|
||||
|
||||
[dev-dependencies]
|
||||
sha2 = "0.10"
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
use core::{convert::TryInto, fmt::{Formatter, Debug}};
|
||||
use std::marker::PhantomData;
|
||||
use core::convert::TryInto;
|
||||
|
||||
use thiserror::Error;
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use blake2::{digest::{generic_array::typenum::U64, Digest}, Blake2b512};
|
||||
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_TABLE as DTable,
|
||||
scalar::Scalar as DScalar,
|
||||
edwards::EdwardsPoint as DPoint
|
||||
};
|
||||
|
||||
use ff::PrimeField;
|
||||
use group::Group;
|
||||
|
||||
use transcript::{Transcript as TranscriptTrait, DigestTranscript};
|
||||
use frost::{CurveError, Curve};
|
||||
use frost::Curve;
|
||||
pub use frost::curves::ed25519::Ed25519;
|
||||
use dalek_ff_group as dfg;
|
||||
|
||||
use crate::random_scalar;
|
||||
@@ -33,109 +28,6 @@ pub enum MultisigError {
|
||||
InvalidKeyImage(u16)
|
||||
}
|
||||
|
||||
// Accept a parameterized hash function in order to check against the FROST vectors while still
|
||||
// allowing Blake2b to be used with wide reduction in practice
|
||||
pub struct Ed25519Internal<D: Digest<OutputSize = U64>, const WIDE: bool> {
|
||||
_digest: PhantomData<D>
|
||||
}
|
||||
|
||||
// Removed requirements for D to have all of these
|
||||
impl<D: Digest<OutputSize = U64>, const WIDE: bool> Clone for Ed25519Internal<D, WIDE> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
impl<D: Digest<OutputSize = U64>, const WIDE: bool> Copy for Ed25519Internal<D, WIDE> {}
|
||||
impl<D: Digest<OutputSize = U64>, const WIDE: bool> PartialEq for Ed25519Internal<D, WIDE> {
|
||||
fn eq(&self, _: &Self) -> bool { true }
|
||||
}
|
||||
impl<D: Digest<OutputSize = U64>, const WIDE: bool> Eq for Ed25519Internal<D, WIDE> {}
|
||||
impl<D: Digest<OutputSize = U64>, const WIDE: bool> Debug for Ed25519Internal<D, WIDE> {
|
||||
fn fmt(&self, _: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { Ok(()) }
|
||||
}
|
||||
|
||||
impl<D: Digest<OutputSize = U64>, const WIDE: bool> Curve for Ed25519Internal<D, WIDE> {
|
||||
type F = dfg::Scalar;
|
||||
type G = dfg::EdwardsPoint;
|
||||
type T = &'static dfg::EdwardsBasepointTable;
|
||||
|
||||
const ID: &'static [u8] = b"edwards25519";
|
||||
|
||||
const GENERATOR: Self::G = dfg::ED25519_BASEPOINT_POINT;
|
||||
const GENERATOR_TABLE: Self::T = &dfg::ED25519_BASEPOINT_TABLE;
|
||||
|
||||
const LITTLE_ENDIAN: bool = true;
|
||||
|
||||
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
|
||||
let mut seed = vec![0; 32];
|
||||
rng.fill_bytes(&mut seed);
|
||||
seed.extend(&secret.to_bytes());
|
||||
Self::hash_to_F(b"nonce", &seed)
|
||||
}
|
||||
|
||||
fn hash_msg(msg: &[u8]) -> Vec<u8> {
|
||||
D::digest(msg).to_vec()
|
||||
}
|
||||
|
||||
fn hash_binding_factor(binding: &[u8]) -> Self::F {
|
||||
Self::hash_to_F(b"rho", binding)
|
||||
}
|
||||
|
||||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
||||
let digest = D::new().chain_update(dst).chain_update(msg);
|
||||
if WIDE {
|
||||
dfg::Scalar::from_hash(digest)
|
||||
} else {
|
||||
dfg::Scalar::from_bytes_mod_order(digest.finalize()[32 ..].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn F_len() -> usize {
|
||||
32
|
||||
}
|
||||
|
||||
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 scalar.is_some().unwrap_u8() == 0 {
|
||||
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 = dfg::CompressedEdwardsY::new(bytes).decompress();
|
||||
|
||||
if let Some(point) = point {
|
||||
// Ban identity and torsioned points
|
||||
if point.is_identity().into() || (!bool::from(point.is_torsion_free())) {
|
||||
Err(CurveError::InvalidPoint)?;
|
||||
}
|
||||
// Ban points which weren't canonically encoded
|
||||
if point.compress().to_bytes() != bytes {
|
||||
Err(CurveError::InvalidPoint)?;
|
||||
}
|
||||
Ok(point)
|
||||
} else {
|
||||
Err(CurveError::InvalidPoint)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
pub type Ed25519 = Ed25519Internal<Blake2b512, true>;
|
||||
|
||||
// Used to prove legitimacy of key images and nonces which both involve other basepoints
|
||||
#[derive(Clone)]
|
||||
pub struct DLEqProof {
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
use dalek_ff_group as dfg;
|
||||
use frost::{
|
||||
Curve,
|
||||
algorithm::Hram,
|
||||
tests::{curve::test_curve, schnorr::test_schnorr, vectors::{Vectors, vectors}}
|
||||
};
|
||||
|
||||
use crate::frost::{Ed25519, Ed25519Internal};
|
||||
|
||||
#[test]
|
||||
fn frost_ed25519_curve() {
|
||||
test_curve::<_, Ed25519>(&mut OsRng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frost_ed25519_schnorr() {
|
||||
test_schnorr::<_, Ed25519>(&mut OsRng);
|
||||
}
|
||||
|
||||
// Not spec-compliant, as this shouldn't use wide reduction
|
||||
// Is vectors compliant, which is why the below tests pass
|
||||
// See https://github.com/cfrg/draft-irtf-cfrg-frost/issues/204
|
||||
//type TestEd25519 = Ed25519Internal<Sha512, false>;
|
||||
// If this is kept, we can remove WIDE
|
||||
type TestEd25519 = Ed25519Internal<Sha512, true>;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct IetfEd25519Hram {}
|
||||
impl Hram<TestEd25519> for IetfEd25519Hram {
|
||||
#[allow(non_snake_case)]
|
||||
fn hram(R: &dfg::EdwardsPoint, A: &dfg::EdwardsPoint, m: &[u8]) -> dfg::Scalar {
|
||||
TestEd25519::hash_to_F(
|
||||
b"",
|
||||
&[&R.compress().to_bytes(), &A.compress().to_bytes(), m].concat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frost_ed25519_vectors() {
|
||||
vectors::<TestEd25519, IetfEd25519Hram>(
|
||||
Vectors {
|
||||
threshold: 2,
|
||||
shares: &[
|
||||
"929dcc590407aae7d388761cddb0c0db6f5627aea8e217f4a033f2ec83d93509",
|
||||
"a91e66e012e4364ac9aaa405fcafd370402d9859f7b6685c07eed76bf409e80d",
|
||||
"d3cb090a075eb154e82fdb4b3cb507f110040905468bb9c46da8bdea643a9a02"
|
||||
],
|
||||
group_secret: "7b1c33d3f5291d85de664833beb1ad469f7fb6025a0ec78b3a790c6e13a98304",
|
||||
group_key: "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673",
|
||||
|
||||
msg: "74657374",
|
||||
included: &[1, 3],
|
||||
nonces: &[
|
||||
[
|
||||
"8c76af04340e83bb5fc427c117d38347fc8ef86d5397feea9aa6412d96c05b0a",
|
||||
"14a37ddbeae8d9e9687369e5eb3c6d54f03dc19d76bb54fb5425131bc37a600b"
|
||||
],
|
||||
[
|
||||
"5ca39ebab6874f5e7b5089f3521819a2aa1e2cf738bae6974ee80555de2ef70e",
|
||||
"0afe3650c4815ff37becd3c6948066e906e929ea9b8f546c74e10002dbcc150c"
|
||||
]
|
||||
],
|
||||
sig_shares: &[
|
||||
"4369474a398aa10357b60d683da91ea6a767dcf53fd541a8ed6b4d780827ea0a",
|
||||
"32fcc690d926075e45d2dfb746bab71447943cddbefe80d122c39174aa2e1004"
|
||||
],
|
||||
sig: "2b8d9c6995333c5990e3a3dd6568785539d3322f7f0376452487ea35cfda587b".to_owned() +
|
||||
"75650edb12b1a8619c88ed1f8463d6baeefb18d3fed3c279102fdfecb255fa0e"
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1 @@
|
||||
#[cfg(feature = "multisig")]
|
||||
mod frost;
|
||||
|
||||
mod clsag;
|
||||
|
||||
Reference in New Issue
Block a user