From a6775d7dc58635ff672f60b96c337f31abbba713 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 28 Jul 2024 15:20:52 -0400 Subject: [PATCH] Implement eVRF traits, all the way up to the DKG, for secp256k1/ed25519 --- Cargo.lock | 14 ++++++--- crypto/dkg/Cargo.toml | 5 ++++ crypto/dkg/src/evrf/proof.rs | 13 ++++++++- crypto/evrf/ec-gadgets/src/dlog.rs | 5 ++++ crypto/evrf/embedwards25519/Cargo.toml | 7 +++-- crypto/evrf/embedwards25519/src/backend.rs | 4 ++- crypto/evrf/embedwards25519/src/lib.rs | 34 ++++++++++++++++++++++ crypto/evrf/embedwards25519/src/point.rs | 2 +- crypto/evrf/secq256k1/Cargo.toml | 6 ++-- crypto/evrf/secq256k1/src/backend.rs | 4 ++- crypto/evrf/secq256k1/src/lib.rs | 34 ++++++++++++++++++++++ crypto/evrf/secq256k1/src/point.rs | 2 +- processor/Cargo.toml | 5 ++-- 13 files changed, 118 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1d0ed3e..337cfaab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2134,6 +2134,7 @@ dependencies = [ "ciphersuite", "dleq", "ec-divisors", + "embedwards25519", "flexible-transcript", "generalized-bulletproofs", "generalized-bulletproofs-circuit-abstraction", @@ -2145,6 +2146,7 @@ dependencies = [ "rand_chacha", "rand_core", "schnorr-signatures", + "secq256k1", "std-shims", "subtle", "thiserror", @@ -2332,12 +2334,14 @@ dependencies = [ name = "embedwards25519" version = "0.1.0" dependencies = [ + "blake2", + "ciphersuite", "crypto-bigint", "dalek-ff-group", "ec-divisors", - "ff", "ff-group-tests", - "group", + "generalized-bulletproofs-ec-gadgets", + "generic-array 0.14.7", "hex", "hex-literal", "rand_core", @@ -7915,12 +7919,13 @@ dependencies = [ name = "secq256k1" version = "0.1.0" dependencies = [ + "blake2", + "ciphersuite", "crypto-bigint", "ec-divisors", - "ff", "ff-group-tests", + "generalized-bulletproofs-ec-gadgets", "generic-array 0.14.7", - "group", "hex", "hex-literal", "k256", @@ -8428,6 +8433,7 @@ dependencies = [ "ciphersuite", "const-hex", "dalek-ff-group", + "dkg", "dockertest", "env_logger", "ethereum-serai", diff --git a/crypto/dkg/Cargo.toml b/crypto/dkg/Cargo.toml index 0e9d4ab0..2d8066a6 100644 --- a/crypto/dkg/Cargo.toml +++ b/crypto/dkg/Cargo.toml @@ -46,6 +46,9 @@ ec-divisors = { path = "../evrf/divisors", default-features = false, optional = generalized-bulletproofs-circuit-abstraction = { path = "../evrf/circuit-abstraction", optional = true } generalized-bulletproofs-ec-gadgets = { path = "../evrf/ec-gadgets", optional = true } +secq256k1 = { path = "../evrf/secq256k1", optional = true } +embedwards25519 = { path = "../evrf/embedwards25519", optional = true } + [dev-dependencies] rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } rand = { version = "0.8", default-features = false, features = ["std"] } @@ -90,5 +93,7 @@ evrf = [ "dep:generalized-bulletproofs-circuit-abstraction", "dep:generalized-bulletproofs-ec-gadgets", ] +evrf-secp256k1 = ["evrf", "ciphersuite/secp256k1", "secq256k1"] +evrf-ed25519 = ["evrf", "ciphersuite/ed25519", "embedwards25519"] tests = ["rand_core/getrandom"] default = ["std"] diff --git a/crypto/dkg/src/evrf/proof.rs b/crypto/dkg/src/evrf/proof.rs index 52c72d75..ed9f29d1 100644 --- a/crypto/dkg/src/evrf/proof.rs +++ b/crypto/dkg/src/evrf/proof.rs @@ -33,6 +33,18 @@ pub trait EvrfCurve: Ciphersuite { type EmbeddedCurveParameters: DiscreteLogParameters; } +#[cfg(feature = "evrf-secp256k1")] +impl EvrfCurve for ciphersuite::Secp256k1 { + type EmbeddedCurve = secq256k1::Secq256k1; + type EmbeddedCurveParameters = secq256k1::Secq256k1; +} + +#[cfg(feature = "evrf-ed25519")] +impl EvrfCurve for ciphersuite::Ed25519 { + type EmbeddedCurve = embedwards25519::Embedwards25519; + type EmbeddedCurveParameters = embedwards25519::Embedwards25519; +} + fn sample_point(rng: &mut (impl RngCore + CryptoRng)) -> C::G { let mut repr = ::Repr::default(); loop { @@ -742,7 +754,6 @@ where }) } - // TODO: Dedicated error /// Verify an eVRF proof, returning the commitments output. #[allow(clippy::too_many_arguments)] pub(crate) fn verify( diff --git a/crypto/evrf/ec-gadgets/src/dlog.rs b/crypto/evrf/ec-gadgets/src/dlog.rs index 353efffd..d20c39c8 100644 --- a/crypto/evrf/ec-gadgets/src/dlog.rs +++ b/crypto/evrf/ec-gadgets/src/dlog.rs @@ -10,6 +10,11 @@ use generalized_bulletproofs_circuit_abstraction::*; use crate::*; /// Parameters for a discrete logarithm proof. +/// +/// This isn't required to be implemented by the Field/Group/Ciphersuite, solely a struct, to +/// enable parameterization of discrete log proofs to the bitlength of the discrete logarithm. +/// While that may be F::NUM_BITS, a discrete log proof a for a full scalar, it could also be 64, +/// a discrete log proof for a u64 (such as if opening a Pedersen commitment in-circuit). pub trait DiscreteLogParameters { /// The amount of bits used to represent a scalar. type ScalarBits: ArrayLength; diff --git a/crypto/evrf/embedwards25519/Cargo.toml b/crypto/evrf/embedwards25519/Cargo.toml index 04436212..bbae482b 100644 --- a/crypto/evrf/embedwards25519/Cargo.toml +++ b/crypto/evrf/embedwards25519/Cargo.toml @@ -21,14 +21,15 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] } subtle = { version = "^2.4", default-features = false, features = ["std"] } -ff = { version = "0.13", default-features = false, features = ["std", "bits"] } -group = { version = "0.13", default-features = false } - +generic-array = { version = "0.14", default-features = false } crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] } dalek-ff-group = { path = "../../dalek-ff-group", version = "0.4", default-features = false } +blake2 = { version = "0.10", default-features = false, features = ["std"] } +ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false, features = ["std"] } ec-divisors = { path = "../divisors" } +generalized-bulletproofs-ec-gadgets = { path = "../ec-gadgets" } [dev-dependencies] hex = "0.4" diff --git a/crypto/evrf/embedwards25519/src/backend.rs b/crypto/evrf/embedwards25519/src/backend.rs index bcb36bbc..304fa0bc 100644 --- a/crypto/evrf/embedwards25519/src/backend.rs +++ b/crypto/evrf/embedwards25519/src/backend.rs @@ -90,7 +90,9 @@ macro_rules! field { use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus}; - use ff::{Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic}; + use ciphersuite::group::ff::{ + Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic, + }; use $crate::backend::u8_from_bool; diff --git a/crypto/evrf/embedwards25519/src/lib.rs b/crypto/evrf/embedwards25519/src/lib.rs index 818a9b61..0c9ac6bb 100644 --- a/crypto/evrf/embedwards25519/src/lib.rs +++ b/crypto/evrf/embedwards25519/src/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] +use generic_array::typenum::{Sum, Diff, Quot, U, U1, U2}; +use ciphersuite::group::{ff::PrimeField, Group}; + #[macro_use] mod backend; @@ -11,3 +14,34 @@ pub use dalek_ff_group::Scalar as FieldElement; mod point; pub use point::Point; + +/// Ciphersuite for Embedwards25519. +/// +/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition +/// between the two. This means `dst: b"abc", data: b"def"`, will produce the same scalar as +/// `dst: "abcdef", data: b""`. Please use carefully, not letting dsts be substrings of each other. +#[derive(Clone, Copy, PartialEq, Eq, Debug, zeroize::Zeroize)] +pub struct Embedwards25519; +impl ciphersuite::Ciphersuite for Embedwards25519 { + type F = Scalar; + type G = Point; + type H = blake2::Blake2b512; + + const ID: &'static [u8] = b"embedwards25519"; + + fn generator() -> Self::G { + Point::generator() + } + + fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F { + use blake2::Digest; + Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_slice().try_into().unwrap()) + } +} + +impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameters for Embedwards25519 { + type ScalarBits = U<{ Scalar::NUM_BITS as usize }>; + type XCoefficients = Quot, U2>; + type XCoefficientsMinusOne = Diff; + type YxCoefficients = Diff, U2>, U2>; +} diff --git a/crypto/evrf/embedwards25519/src/point.rs b/crypto/evrf/embedwards25519/src/point.rs index b762ff5d..9d24e88a 100644 --- a/crypto/evrf/embedwards25519/src/point.rs +++ b/crypto/evrf/embedwards25519/src/point.rs @@ -8,7 +8,7 @@ use rand_core::RngCore; use zeroize::Zeroize; use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable}; -use group::{ +use ciphersuite::group::{ ff::{Field, PrimeField, PrimeFieldBits}, Group, GroupEncoding, prime::PrimeGroup, diff --git a/crypto/evrf/secq256k1/Cargo.toml b/crypto/evrf/secq256k1/Cargo.toml index 90682bf3..c363ca4f 100644 --- a/crypto/evrf/secq256k1/Cargo.toml +++ b/crypto/evrf/secq256k1/Cargo.toml @@ -21,15 +21,15 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] } subtle = { version = "^2.4", default-features = false, features = ["std"] } -ff = { version = "0.13", default-features = false, features = ["std", "bits"] } -group = { version = "0.13", default-features = false } - generic-array = { version = "0.14", default-features = false } crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] } k256 = { version = "0.13", default-features = false, features = ["arithmetic"] } +blake2 = { version = "0.10", default-features = false, features = ["std"] } +ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false, features = ["std"] } ec-divisors = { path = "../divisors" } +generalized-bulletproofs-ec-gadgets = { path = "../ec-gadgets" } [dev-dependencies] hex = "0.4" diff --git a/crypto/evrf/secq256k1/src/backend.rs b/crypto/evrf/secq256k1/src/backend.rs index bcb36bbc..304fa0bc 100644 --- a/crypto/evrf/secq256k1/src/backend.rs +++ b/crypto/evrf/secq256k1/src/backend.rs @@ -90,7 +90,9 @@ macro_rules! field { use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus}; - use ff::{Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic}; + use ciphersuite::group::ff::{ + Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic, + }; use $crate::backend::u8_from_bool; diff --git a/crypto/evrf/secq256k1/src/lib.rs b/crypto/evrf/secq256k1/src/lib.rs index f3acc086..8e157844 100644 --- a/crypto/evrf/secq256k1/src/lib.rs +++ b/crypto/evrf/secq256k1/src/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] +use generic_array::typenum::{Sum, Diff, Quot, U, U1, U2}; +use ciphersuite::group::{ff::PrimeField, Group}; + #[macro_use] mod backend; @@ -11,3 +14,34 @@ pub use k256::Scalar as FieldElement; mod point; pub use point::Point; + +/// Ciphersuite for Secq256k1. +/// +/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition +/// between the two. This means `dst: b"abc", data: b"def"`, will produce the same scalar as +/// `dst: "abcdef", data: b""`. Please use carefully, not letting dsts be substrings of each other. +#[derive(Clone, Copy, PartialEq, Eq, Debug, zeroize::Zeroize)] +pub struct Secq256k1; +impl ciphersuite::Ciphersuite for Secq256k1 { + type F = Scalar; + type G = Point; + type H = blake2::Blake2b512; + + const ID: &'static [u8] = b"secq256k1"; + + fn generator() -> Self::G { + Point::generator() + } + + fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F { + use blake2::Digest; + Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_slice().try_into().unwrap()) + } +} + +impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameters for Secq256k1 { + type ScalarBits = U<{ Scalar::NUM_BITS as usize }>; + type XCoefficients = Quot, U2>; + type XCoefficientsMinusOne = Diff; + type YxCoefficients = Diff, U2>, U2>; +} diff --git a/crypto/evrf/secq256k1/src/point.rs b/crypto/evrf/secq256k1/src/point.rs index 8f3a4730..384b68c9 100644 --- a/crypto/evrf/secq256k1/src/point.rs +++ b/crypto/evrf/secq256k1/src/point.rs @@ -10,7 +10,7 @@ use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, Conditio use generic_array::{typenum::U33, GenericArray}; -use group::{ +use ciphersuite::group::{ ff::{Field, PrimeField, PrimeFieldBits}, Group, GroupEncoding, prime::PrimeGroup, diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 5ff7e94d..7b086636 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -37,6 +37,7 @@ serde_json = { version = "1", default-features = false, features = ["std"] } ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std", "ristretto"] } transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std"] } +dkg = { package = "dkg", path = "../crypto/dkg", default-features = false, features = ["std"] } frost = { package = "modular-frost", path = "../crypto/frost", default-features = false, features = ["ristretto"] } frost-schnorrkel = { path = "../crypto/schnorrkel", default-features = false } @@ -81,12 +82,12 @@ dockertest = "0.4" serai-docker-tests = { path = "../tests/docker" } [features] -secp256k1 = ["k256", "frost/secp256k1"] +secp256k1 = ["k256", "dkg/evrf-secp256k1", "frost/secp256k1"] bitcoin = ["dep:secp256k1", "secp256k1", "bitcoin-serai", "serai-client/bitcoin"] ethereum = ["secp256k1", "ethereum-serai/tests"] -ed25519 = ["dalek-ff-group", "frost/ed25519"] +ed25519 = ["dalek-ff-group", "dkg/evrf-ed25519", "frost/ed25519"] monero = ["ed25519", "monero-simple-request-rpc", "monero-wallet", "serai-client/monero"] binaries = ["env_logger", "serai-env", "message-queue"]