mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Smash the singular Ciphersuite trait into multiple
This helps identify where the various functionalities are used, or rather, not used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating the entire FCMP++ tree, only requires the markers _and_ canonical point decoding. I've opened a PR to upstream such a trait into `group` (https://github.com/zkcrypto/group/pull/68). `WrappedGroup` is still justified for as long as `Group::generator` exists. Moving `::generator()` to its own trait, on an independent structure (upstream) would be massively appreciated. @tarcieri also wanted to update from `fn generator()` to `const GENERATOR`, which would encourage further discussion on https://github.com/zkcrypto/group/issues/32 and https://github.com/zkcrypto/group/issues/45, which have been stagnant. The `Id` trait is occasionally used yet really should be first off the chopping block. Finally, `WithPreferredHash` is only actually used around a third of the time, which more than justifies it being a separate trait. --- Updates `dalek_ff_group::Scalar` to directly re-export `curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint` also could be replaced with an export of `curve25519_dalek::RistrettoPoint`, yet the coordinator relies on how we implemented `Hash` on it for the hell of it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't implement `zeroize`, `subtle` traits within a released, non-yanked version. Relevance to https://github.com/serai-dex/serai/issues/201 and https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746. Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over `SHA2-512`. In order to maintain compliance with FROST's IETF standard, `modular-frost` defines its own ciphersuite for Ristretto which still uses `SHA2-512`.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dalek-ff-group"
|
||||
version = "0.4.6"
|
||||
version = "0.5.0"
|
||||
description = "ff/group bindings around curve25519-dalek"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group"
|
||||
@@ -22,15 +22,13 @@ subtle = { version = "^2.4", default-features = false }
|
||||
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
|
||||
digest = { version = "0.10", default-features = false }
|
||||
sha2 = { version = "0.11.0-rc.0", default-features = false }
|
||||
sha2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
|
||||
blake2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
|
||||
|
||||
prime-field = { path = "../prime-field", default-features = false }
|
||||
ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false }
|
||||
|
||||
crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] }
|
||||
|
||||
curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["zeroize", "digest", "group", "precomputed-tables"] }
|
||||
curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["zeroize", "digest", "group-bits", "precomputed-tables"] }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4"
|
||||
@@ -38,6 +36,6 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||
ff-group-tests = { path = "../ff-group-tests" }
|
||||
|
||||
[features]
|
||||
alloc = ["zeroize/alloc", "digest/alloc", "prime-field/alloc", "ciphersuite/alloc", "crypto-bigint/alloc", "curve25519-dalek/alloc"]
|
||||
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "digest/std", "prime-field/std", "ciphersuite/std"]
|
||||
alloc = ["zeroize/alloc", "prime-field/alloc", "ciphersuite/alloc", "curve25519-dalek/alloc"]
|
||||
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "prime-field/std", "ciphersuite/std"]
|
||||
default = ["std"]
|
||||
|
||||
@@ -1,49 +1,48 @@
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use sha2::Sha512;
|
||||
use blake2::Blake2b512;
|
||||
|
||||
use ciphersuite::{group::Group, Ciphersuite};
|
||||
use ::ciphersuite::{group::Group, *};
|
||||
|
||||
use crate::Scalar;
|
||||
|
||||
macro_rules! dalek_curve {
|
||||
(
|
||||
$feature: literal,
|
||||
|
||||
$Ciphersuite: ident,
|
||||
$Point: ident,
|
||||
$ID: literal
|
||||
) => {
|
||||
use crate::$Point;
|
||||
|
||||
impl Ciphersuite for $Ciphersuite {
|
||||
type F = Scalar;
|
||||
type G = $Point;
|
||||
type H = Sha512;
|
||||
|
||||
const ID: &'static [u8] = $ID;
|
||||
|
||||
fn generator() -> Self::G {
|
||||
$Point::generator()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
use crate::*;
|
||||
|
||||
/// Ciphersuite for Ristretto.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct Ristretto;
|
||||
dalek_curve!("ristretto", Ristretto, RistrettoPoint, b"ristretto");
|
||||
#[test]
|
||||
fn test_ristretto() {
|
||||
ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng);
|
||||
impl WrappedGroup for Ristretto {
|
||||
type F = Scalar;
|
||||
type G = RistrettoPoint;
|
||||
fn generator() -> Self::G {
|
||||
<RistrettoPoint as Group>::generator()
|
||||
}
|
||||
}
|
||||
impl Id for Ristretto {
|
||||
const ID: &[u8] = b"ristretto";
|
||||
}
|
||||
impl WithPreferredHash for Ristretto {
|
||||
type H = Blake2b512;
|
||||
}
|
||||
impl GroupCanonicalEncoding for Ristretto {
|
||||
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
|
||||
Self::G::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ciphersuite for Ed25519, inspired by RFC-8032.
|
||||
/// Ciphersuite for Ed25519.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct Ed25519;
|
||||
dalek_curve!("ed25519", Ed25519, EdwardsPoint, b"edwards25519");
|
||||
#[test]
|
||||
fn test_ed25519() {
|
||||
ff_group_tests::group::test_prime_group_bits::<_, EdwardsPoint>(&mut rand_core::OsRng);
|
||||
impl WrappedGroup for Ed25519 {
|
||||
type F = Scalar;
|
||||
type G = EdwardsPoint;
|
||||
fn generator() -> Self::G {
|
||||
<EdwardsPoint as Group>::generator()
|
||||
}
|
||||
}
|
||||
impl Id for Ed25519 {
|
||||
const ID: &[u8] = b"ed25519";
|
||||
}
|
||||
impl WithPreferredHash for Ed25519 {
|
||||
type H = Sha512;
|
||||
}
|
||||
impl GroupCanonicalEncoding for Ed25519 {}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use core::{
|
||||
borrow::Borrow,
|
||||
ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
||||
iter::{Iterator, Sum, Product},
|
||||
iter::{Iterator, Sum},
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
@@ -15,25 +15,16 @@ use zeroize::Zeroize;
|
||||
use subtle::{ConstantTimeEq, ConditionallySelectable};
|
||||
|
||||
use rand_core::RngCore;
|
||||
use digest::{consts::U64, Digest, HashMarker};
|
||||
|
||||
use subtle::{Choice, CtOption};
|
||||
|
||||
pub use curve25519_dalek as dalek;
|
||||
|
||||
use dalek::{
|
||||
constants::{self, BASEPOINT_ORDER},
|
||||
scalar::Scalar as DScalar,
|
||||
edwards::{EdwardsPoint as DEdwardsPoint, EdwardsBasepointTable, CompressedEdwardsY},
|
||||
ristretto::{RistrettoPoint as DRistrettoPoint, RistrettoBasepointTable, CompressedRistretto},
|
||||
use curve25519_dalek::{
|
||||
edwards::{EdwardsPoint as DEdwardsPoint, CompressedEdwardsY},
|
||||
ristretto::{RistrettoPoint as DRistrettoPoint, CompressedRistretto},
|
||||
};
|
||||
pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE};
|
||||
pub use curve25519_dalek::Scalar;
|
||||
|
||||
use ::ciphersuite::group::{
|
||||
ff::{Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes},
|
||||
Group, GroupEncoding,
|
||||
prime::PrimeGroup,
|
||||
};
|
||||
use ::ciphersuite::group::{Group, GroupEncoding, prime::PrimeGroup};
|
||||
|
||||
mod ciphersuite;
|
||||
pub use crate::ciphersuite::{Ed25519, Ristretto};
|
||||
@@ -97,7 +88,41 @@ macro_rules! constant_time {
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use constant_time;
|
||||
|
||||
macro_rules! math_op_without_wrapping {
|
||||
(
|
||||
$Value: ident,
|
||||
$Other: ident,
|
||||
$Op: ident,
|
||||
$op_fn: ident,
|
||||
$Assign: ident,
|
||||
$assign_fn: ident,
|
||||
$function: expr
|
||||
) => {
|
||||
impl $Op<$Other> for $Value {
|
||||
type Output = $Value;
|
||||
fn $op_fn(self, other: $Other) -> Self::Output {
|
||||
Self($function(self.0, other))
|
||||
}
|
||||
}
|
||||
impl $Assign<$Other> for $Value {
|
||||
fn $assign_fn(&mut self, other: $Other) {
|
||||
self.0 = $function(self.0, other);
|
||||
}
|
||||
}
|
||||
impl<'a> $Op<&'a $Other> for $Value {
|
||||
type Output = $Value;
|
||||
fn $op_fn(self, other: &'a $Other) -> Self::Output {
|
||||
Self($function(self.0, other))
|
||||
}
|
||||
}
|
||||
impl<'a> $Assign<&'a $Other> for $Value {
|
||||
fn $assign_fn(&mut self, other: &'a $Other) {
|
||||
self.0 = $function(self.0, other);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! math_op {
|
||||
(
|
||||
@@ -133,20 +158,12 @@ macro_rules! math_op {
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use math_op;
|
||||
|
||||
macro_rules! math {
|
||||
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
|
||||
math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
|
||||
math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
|
||||
math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
|
||||
};
|
||||
}
|
||||
pub(crate) use math;
|
||||
|
||||
macro_rules! math_neg {
|
||||
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
|
||||
math!($Value, $Factor, $add, $sub, $mul);
|
||||
math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
|
||||
math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
|
||||
math_op_without_wrapping!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
|
||||
|
||||
impl Neg for $Value {
|
||||
type Output = Self;
|
||||
@@ -157,187 +174,6 @@ macro_rules! math_neg {
|
||||
};
|
||||
}
|
||||
|
||||
/// Wrapper around the dalek Scalar type.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
|
||||
pub struct Scalar(pub DScalar);
|
||||
deref_borrow!(Scalar, DScalar);
|
||||
constant_time!(Scalar, DScalar);
|
||||
math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul);
|
||||
|
||||
macro_rules! from_wrapper {
|
||||
($uint: ident) => {
|
||||
impl From<$uint> for Scalar {
|
||||
fn from(a: $uint) -> Scalar {
|
||||
Scalar(DScalar::from(a))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_wrapper!(u8);
|
||||
from_wrapper!(u16);
|
||||
from_wrapper!(u32);
|
||||
from_wrapper!(u64);
|
||||
from_wrapper!(u128);
|
||||
|
||||
impl Scalar {
|
||||
pub fn pow(&self, other: Scalar) -> Scalar {
|
||||
let mut table = [Scalar::ONE; 16];
|
||||
table[1] = *self;
|
||||
for i in 2 .. 16 {
|
||||
table[i] = table[i - 1] * self;
|
||||
}
|
||||
|
||||
let mut res = Scalar::ONE;
|
||||
let mut bits = 0;
|
||||
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||
bits <<= 1;
|
||||
let mut bit = u8_from_bool(&mut bit);
|
||||
bits |= bit;
|
||||
bit.zeroize();
|
||||
|
||||
if ((i + 1) % 4) == 0 {
|
||||
if i != 3 {
|
||||
for _ in 0 .. 4 {
|
||||
res *= res;
|
||||
}
|
||||
}
|
||||
|
||||
let mut scale_by = Scalar::ONE;
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0 .. 16 {
|
||||
#[allow(clippy::cast_possible_truncation)] // Safe since 0 .. 16
|
||||
{
|
||||
scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8)));
|
||||
}
|
||||
}
|
||||
res *= scale_by;
|
||||
bits = 0;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Perform wide reduction on a 64-byte array to create a Scalar without bias.
|
||||
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
|
||||
Self(DScalar::from_bytes_mod_order_wide(bytes))
|
||||
}
|
||||
|
||||
/// Derive a Scalar without bias from a digest via wide reduction.
|
||||
pub fn from_hash<D: Digest<OutputSize = U64> + HashMarker>(hash: D) -> Scalar {
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(&hash.finalize());
|
||||
let res = Scalar(DScalar::from_bytes_mod_order_wide(&output));
|
||||
output.zeroize();
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Scalar {
|
||||
const ZERO: Scalar = Scalar(DScalar::ZERO);
|
||||
const ONE: Scalar = Scalar(DScalar::ONE);
|
||||
|
||||
fn random(rng: impl RngCore) -> Self {
|
||||
Self(<DScalar as Field>::random(rng))
|
||||
}
|
||||
|
||||
fn square(&self) -> Self {
|
||||
Self(self.0.square())
|
||||
}
|
||||
fn double(&self) -> Self {
|
||||
Self(self.0.double())
|
||||
}
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
<DScalar as Field>::invert(&self.0).map(Self)
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
self.0.sqrt().map(Self)
|
||||
}
|
||||
|
||||
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
|
||||
let (choice, res) = DScalar::sqrt_ratio(num, div);
|
||||
(choice, Self(res))
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeField for Scalar {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
const MODULUS: &'static str = <DScalar as PrimeField>::MODULUS;
|
||||
|
||||
const NUM_BITS: u32 = <DScalar as PrimeField>::NUM_BITS;
|
||||
const CAPACITY: u32 = <DScalar as PrimeField>::CAPACITY;
|
||||
|
||||
const TWO_INV: Scalar = Scalar(<DScalar as PrimeField>::TWO_INV);
|
||||
|
||||
const MULTIPLICATIVE_GENERATOR: Scalar =
|
||||
Scalar(<DScalar as PrimeField>::MULTIPLICATIVE_GENERATOR);
|
||||
const S: u32 = <DScalar as PrimeField>::S;
|
||||
|
||||
const ROOT_OF_UNITY: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY);
|
||||
const ROOT_OF_UNITY_INV: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY_INV);
|
||||
|
||||
const DELTA: Scalar = Scalar(<DScalar as PrimeField>::DELTA);
|
||||
|
||||
fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
|
||||
<DScalar as PrimeField>::from_repr(bytes).map(Scalar)
|
||||
}
|
||||
fn to_repr(&self) -> [u8; 32] {
|
||||
self.0.to_repr()
|
||||
}
|
||||
|
||||
fn is_odd(&self) -> Choice {
|
||||
self.0.is_odd()
|
||||
}
|
||||
|
||||
fn from_u128(num: u128) -> Self {
|
||||
Scalar(DScalar::from_u128(num))
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeFieldBits for Scalar {
|
||||
type ReprBits = [u8; 32];
|
||||
|
||||
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
||||
self.to_repr().into()
|
||||
}
|
||||
|
||||
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||
BASEPOINT_ORDER.to_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromUniformBytes<64> for Scalar {
|
||||
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
|
||||
Self::from_bytes_mod_order_wide(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum<Scalar> for Scalar {
|
||||
fn sum<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
||||
Self(DScalar::sum(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a Scalar> for Scalar {
|
||||
fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
||||
Self(DScalar::sum(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl Product<Scalar> for Scalar {
|
||||
fn product<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
||||
Self(DScalar::product(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Product<&'a Scalar> for Scalar {
|
||||
fn product<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
||||
Self(DScalar::product(iter))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! dalek_group {
|
||||
(
|
||||
$Point: ident,
|
||||
@@ -347,9 +183,6 @@ macro_rules! dalek_group {
|
||||
$Table: ident,
|
||||
|
||||
$DCompressed: ident,
|
||||
|
||||
$BASEPOINT_POINT: ident,
|
||||
$BASEPOINT_TABLE: ident
|
||||
) => {
|
||||
/// Wrapper around the dalek Point type.
|
||||
///
|
||||
@@ -363,9 +196,6 @@ macro_rules! dalek_group {
|
||||
constant_time!($Point, $DPoint);
|
||||
math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul);
|
||||
|
||||
/// The basepoint for this curve.
|
||||
pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT);
|
||||
|
||||
impl Sum<$Point> for $Point {
|
||||
fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point {
|
||||
Self($DPoint::sum(iter))
|
||||
@@ -396,7 +226,7 @@ macro_rules! dalek_group {
|
||||
Self($DPoint::identity())
|
||||
}
|
||||
fn generator() -> Self {
|
||||
$BASEPOINT_POINT
|
||||
Self(<$DPoint as Group>::generator())
|
||||
}
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.0.ct_eq(&$DPoint::identity())
|
||||
@@ -430,13 +260,6 @@ macro_rules! dalek_group {
|
||||
|
||||
impl PrimeGroup for $Point {}
|
||||
|
||||
impl Mul<Scalar> for &$Table {
|
||||
type Output = $Point;
|
||||
fn mul(self, b: Scalar) -> $Point {
|
||||
$Point(&b.0 * self)
|
||||
}
|
||||
}
|
||||
|
||||
// Support being used as a key in a table
|
||||
// While it is expensive as a key, due to the field operations required, there's frequently
|
||||
// use cases for public key -> value lookups
|
||||
@@ -456,24 +279,14 @@ dalek_group!(
|
||||
|point: DEdwardsPoint| point.is_torsion_free(),
|
||||
EdwardsBasepointTable,
|
||||
CompressedEdwardsY,
|
||||
ED25519_BASEPOINT_POINT,
|
||||
ED25519_BASEPOINT_TABLE
|
||||
);
|
||||
|
||||
impl EdwardsPoint {
|
||||
pub fn mul_by_cofactor(&self) -> EdwardsPoint {
|
||||
EdwardsPoint(self.0.mul_by_cofactor())
|
||||
}
|
||||
}
|
||||
|
||||
dalek_group!(
|
||||
RistrettoPoint,
|
||||
DRistrettoPoint,
|
||||
|_| true,
|
||||
RistrettoBasepointTable,
|
||||
CompressedRistretto,
|
||||
RISTRETTO_BASEPOINT_POINT,
|
||||
RISTRETTO_BASEPOINT_TABLE
|
||||
);
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user