Implement eVRF traits, all the way up to the DKG, for secp256k1/ed25519

This commit is contained in:
Luke Parker
2024-07-28 15:20:52 -04:00
parent 681010f422
commit a6775d7dc5
13 changed files with 118 additions and 17 deletions

14
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"]

View File

@@ -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<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
let mut repr = <C::G as GroupEncoding>::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(

View File

@@ -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;

View File

@@ -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"

View File

@@ -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;

View File

@@ -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<Sum<Self::ScalarBits, U1>, U2>;
type XCoefficientsMinusOne = Diff<Self::XCoefficients, U1>;
type YxCoefficients = Diff<Quot<Sum<Self::ScalarBits, U1>, U2>, U2>;
}

View File

@@ -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,

View File

@@ -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"

View File

@@ -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;

View File

@@ -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<Sum<Self::ScalarBits, U1>, U2>;
type XCoefficientsMinusOne = Diff<Self::XCoefficients, U1>;
type YxCoefficients = Diff<Quot<Sum<Self::ScalarBits, U1>, U2>, U2>;
}

View File

@@ -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,

View File

@@ -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"]