From e2dc5db7aa5a22125193b8311c414059552950a1 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 29 Aug 2025 06:14:25 -0400 Subject: [PATCH] Various feature tweaks and updates --- Cargo.lock | 29 +- coordinator/Cargo.toml | 2 +- coordinator/cosign/Cargo.toml | 2 +- coordinator/p2p/libp2p/Cargo.toml | 2 +- coordinator/tributary-sdk/Cargo.toml | 2 +- coordinator/tributary/Cargo.toml | 2 +- crypto/ciphersuite/Cargo.toml | 2 +- crypto/dalek-ff-group/Cargo.toml | 9 +- crypto/dalek-ff-group/src/ciphersuite.rs | 5 +- crypto/dalek-ff-group/src/field.rs | 374 ----------------------- crypto/dalek-ff-group/src/lib.rs | 48 +-- crypto/dkg/evrf/Cargo.toml | 3 +- crypto/dkg/evrf/src/curves.rs | 4 +- crypto/ed448/Cargo.toml | 2 +- crypto/embedwards25519/Cargo.toml | 4 +- crypto/embedwards25519/src/lib.rs | 2 +- crypto/prime-field/src/lib.rs | 62 +++- crypto/secq256k1/Cargo.toml | 5 +- crypto/secq256k1/src/lib.rs | 18 +- deny.toml | 1 + processor/key-gen/Cargo.toml | 2 +- processor/scanner/Cargo.toml | 2 +- processor/signers/Cargo.toml | 2 +- substrate/client/Cargo.toml | 2 +- tests/coordinator/Cargo.toml | 2 +- 25 files changed, 126 insertions(+), 462 deletions(-) delete mode 100644 crypto/dalek-ff-group/src/field.rs diff --git a/Cargo.lock b/Cargo.lock index 4d8359de..ef9430b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2460,12 +2460,10 @@ dependencies = [ "crypto-bigint 0.5.5", "curve25519-dalek", "digest 0.10.7", - "ff", "ff-group-tests", - "group", "hex", + "prime-field", "rand_core 0.6.4", - "rustversion", "sha2 0.11.0-rc.0", "subtle", "zeroize", @@ -2793,7 +2791,7 @@ dependencies = [ name = "dkg-evrf" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "ciphersuite 0.4.2", "ciphersuite-kp256", "dalek-ff-group", @@ -3029,13 +3027,13 @@ dependencies = [ "dalek-ff-group", "ff-group-tests", "generalized-bulletproofs-ec-gadgets", - "generic-array 1.2.0", "hex", "hex-literal", "prime-field", "rand_core 0.6.4", "short-weierstrass", "std-shims", + "typenum", "zeroize", ] @@ -9592,16 +9590,15 @@ dependencies = [ name = "secq256k1" version = "0.1.0" dependencies = [ - "blake2 0.11.0-rc.0", "ciphersuite 0.4.2", "ff-group-tests", "generalized-bulletproofs-ec-gadgets", - "generic-array 1.2.0", "hex", "hex-literal", "k256", "prime-field", "rand_core 0.6.4", + "sha2 0.11.0-rc.0", "short-weierstrass", "std-shims", ] @@ -9745,7 +9742,7 @@ dependencies = [ "async-lock", "bitcoin", "bitvec", - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "ciphersuite 0.4.2", "ciphersuite-kp256", @@ -9807,7 +9804,7 @@ name = "serai-coordinator" version = "0.1.0" dependencies = [ "bitvec", - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "ciphersuite 0.4.2", "dalek-ff-group", @@ -9844,7 +9841,7 @@ name = "serai-coordinator-libp2p-p2p" version = "0.1.0" dependencies = [ "async-trait", - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "futures-util", "hex", @@ -9898,7 +9895,7 @@ dependencies = [ name = "serai-coordinator-tributary" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "ciphersuite 0.4.2", "dalek-ff-group", @@ -9921,7 +9918,7 @@ dependencies = [ name = "serai-cosign" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "log", "parity-scale-codec", @@ -10461,7 +10458,7 @@ dependencies = [ name = "serai-processor-key-gen" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "ciphersuite 0.4.2", "dkg-evrf", @@ -10509,7 +10506,7 @@ dependencies = [ name = "serai-processor-scanner" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "group", "hex", @@ -10541,7 +10538,7 @@ dependencies = [ name = "serai-processor-signers" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "borsh", "ciphersuite 0.4.2", "dalek-ff-group", @@ -12608,7 +12605,7 @@ dependencies = [ name = "tributary-sdk" version = "0.1.0" dependencies = [ - "blake2 0.10.6", + "blake2 0.11.0-rc.0", "ciphersuite 0.4.2", "dalek-ff-group", "flexible-transcript", diff --git a/coordinator/Cargo.toml b/coordinator/Cargo.toml index e55aef91..fdb019d3 100644 --- a/coordinator/Cargo.toml +++ b/coordinator/Cargo.toml @@ -21,7 +21,7 @@ zeroize = { version = "^1.5", default-features = false, features = ["std"] } bitvec = { version = "1", default-features = false, features = ["std"] } rand_core = { version = "0.6", default-features = false, features = ["std"] } -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } schnorrkel = { version = "0.11", default-features = false, features = ["std"] } transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std", "recommended"] } diff --git a/coordinator/cosign/Cargo.toml b/coordinator/cosign/Cargo.toml index 0aa8e230..2154adcc 100644 --- a/coordinator/cosign/Cargo.toml +++ b/coordinator/cosign/Cargo.toml @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } schnorrkel = { version = "0.11", default-features = false, features = ["std"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] } diff --git a/coordinator/p2p/libp2p/Cargo.toml b/coordinator/p2p/libp2p/Cargo.toml index 80d98859..450c0f38 100644 --- a/coordinator/p2p/libp2p/Cargo.toml +++ b/coordinator/p2p/libp2p/Cargo.toml @@ -23,7 +23,7 @@ async-trait = { version = "0.1", default-features = false } rand_core = { version = "0.6", default-features = false, features = ["std"] } zeroize = { version = "^1.5", default-features = false, features = ["std"] } -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } schnorrkel = { version = "0.11", default-features = false, features = ["std"] } hex = { version = "0.4", default-features = false, features = ["std"] } diff --git a/coordinator/tributary-sdk/Cargo.toml b/coordinator/tributary-sdk/Cargo.toml index 1aea3d15..219d3b49 100644 --- a/coordinator/tributary-sdk/Cargo.toml +++ b/coordinator/tributary-sdk/Cargo.toml @@ -24,7 +24,7 @@ zeroize = { version = "^1.5", default-features = false, features = ["std"] } rand = { version = "0.8", default-features = false, features = ["std"] } rand_chacha = { version = "0.3", default-features = false, features = ["std"] } -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["std", "recommended"] } ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", default-features = false, features = ["std"] } diff --git a/coordinator/tributary/Cargo.toml b/coordinator/tributary/Cargo.toml index 54fafd13..af533e64 100644 --- a/coordinator/tributary/Cargo.toml +++ b/coordinator/tributary/Cargo.toml @@ -24,7 +24,7 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] } borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] } dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, features = ["std"] } dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] } diff --git a/crypto/ciphersuite/Cargo.toml b/crypto/ciphersuite/Cargo.toml index c211d033..f17e417f 100644 --- a/crypto/ciphersuite/Cargo.toml +++ b/crypto/ciphersuite/Cargo.toml @@ -38,7 +38,7 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } ff-group-tests = { version = "0.13", path = "../ff-group-tests" } [features] -alloc = ["std-shims", "ff/alloc"] +alloc = ["std-shims", "digest/alloc", "ff/alloc"] std = [ "alloc", diff --git a/crypto/dalek-ff-group/Cargo.toml b/crypto/dalek-ff-group/Cargo.toml index 58a2fdbf..46d32f51 100644 --- a/crypto/dalek-ff-group/Cargo.toml +++ b/crypto/dalek-ff-group/Cargo.toml @@ -17,8 +17,6 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] -rustversion = "1" - zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } subtle = { version = "^2.4", default-features = false } @@ -27,8 +25,7 @@ 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 } -ff = { version = "0.13", default-features = false, features = ["bits"] } -group = { version = "0.13", default-features = false } +prime-field = { path = "../prime-field", default-features = false } ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false } crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] } @@ -41,6 +38,6 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } ff-group-tests = { path = "../ff-group-tests" } [features] -alloc = ["zeroize/alloc", "ciphersuite/alloc", "curve25519-dalek/alloc"] -std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "digest/std", "ciphersuite/std"] +alloc = ["zeroize/alloc", "digest/alloc", "prime-field/alloc", "ciphersuite/alloc", "curve25519-dalek/alloc"] +std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "digest/std", "prime-field/std", "ciphersuite/std"] default = ["std"] diff --git a/crypto/dalek-ff-group/src/ciphersuite.rs b/crypto/dalek-ff-group/src/ciphersuite.rs index 170a3bee..137d032f 100644 --- a/crypto/dalek-ff-group/src/ciphersuite.rs +++ b/crypto/dalek-ff-group/src/ciphersuite.rs @@ -2,10 +2,9 @@ use zeroize::Zeroize; use sha2::Sha512; -use group::Group; -use crate::Scalar; +use ciphersuite::{group::Group, Ciphersuite}; -use ciphersuite::Ciphersuite; +use crate::Scalar; macro_rules! dalek_curve { ( diff --git a/crypto/dalek-ff-group/src/field.rs b/crypto/dalek-ff-group/src/field.rs deleted file mode 100644 index c21496f7..00000000 --- a/crypto/dalek-ff-group/src/field.rs +++ /dev/null @@ -1,374 +0,0 @@ -use core::{ - ops::{Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}, - iter::{Sum, Product}, -}; - -use zeroize::Zeroize; -use rand_core::RngCore; - -use subtle::{ - Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallyNegatable, - ConditionallySelectable, -}; - -use crypto_bigint::{ - Integer, NonZero, Encoding, U256, U512, - modular::constant_mod::{ResidueParams, Residue}, - impl_modulus, -}; - -use group::ff::{Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes}; - -use crate::{u8_from_bool, constant_time, math_op, math}; - -// 2 ** 255 - 19 -// Uses saturating_sub because checked_sub isn't available at compile time -const MODULUS: U256 = U256::from_u8(1).shl_vartime(255).saturating_sub(&U256::from_u8(19)); -const WIDE_MODULUS: U512 = U256::ZERO.concat(&MODULUS); - -impl_modulus!( - FieldModulus, - U256, - // 2 ** 255 - 19 - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" -); -type ResidueType = Residue; - -/// A constant-time implementation of the Ed25519 field. -#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)] -#[repr(transparent)] -pub struct FieldElement(ResidueType); - -// Square root of -1. -// Formula from RFC-8032 (modp_sqrt_m1/sqrt8k5 z) -// 2 ** ((MODULUS - 1) // 4) % MODULUS -const SQRT_M1: FieldElement = FieldElement( - ResidueType::new(&U256::from_u8(2)) - .pow(&MODULUS.saturating_sub(&U256::ONE).wrapping_div(&U256::from_u8(4))), -); - -// Constant useful in calculating square roots (RFC-8032 sqrt8k5's exponent used to calculate y) -const MOD_3_8: FieldElement = FieldElement(ResidueType::new( - &MODULUS.saturating_add(&U256::from_u8(3)).wrapping_div(&U256::from_u8(8)), -)); - -// Constant useful in sqrt_ratio_i (sqrt(u / v)) -const MOD_5_8: FieldElement = FieldElement(ResidueType::sub(&MOD_3_8.0, &ResidueType::ONE)); - -fn reduce(x: U512) -> ResidueType { - ResidueType::new(&U256::from_le_slice( - &x.rem(&NonZero::new(WIDE_MODULUS).unwrap()).to_le_bytes()[.. 32], - )) -} - -constant_time!(FieldElement, ResidueType); -math!( - FieldElement, - FieldElement, - |x: ResidueType, y: ResidueType| x.add(&y), - |x: ResidueType, y: ResidueType| x.sub(&y), - |x: ResidueType, y: ResidueType| x.mul(&y) -); - -macro_rules! from_wrapper { - ($uint: ident) => { - impl From<$uint> for FieldElement { - fn from(a: $uint) -> FieldElement { - Self(ResidueType::new(&U256::from(a))) - } - } - }; -} - -from_wrapper!(u8); -from_wrapper!(u16); -from_wrapper!(u32); -from_wrapper!(u64); -from_wrapper!(u128); - -impl Neg for FieldElement { - type Output = Self; - fn neg(self) -> Self::Output { - Self(self.0.neg()) - } -} - -impl Neg for &FieldElement { - type Output = FieldElement; - fn neg(self) -> Self::Output { - (*self).neg() - } -} - -impl Field for FieldElement { - const ZERO: Self = Self(ResidueType::ZERO); - const ONE: Self = Self(ResidueType::ONE); - - fn random(mut rng: impl RngCore) -> Self { - let mut bytes = [0; 64]; - rng.fill_bytes(&mut bytes); - FieldElement(reduce(U512::from_le_bytes(bytes))) - } - - fn square(&self) -> Self { - FieldElement(self.0.square()) - } - fn double(&self) -> Self { - FieldElement(self.0.add(&self.0)) - } - - fn invert(&self) -> CtOption { - const NEG_2: FieldElement = - FieldElement(ResidueType::new(&MODULUS.saturating_sub(&U256::from_u8(2)))); - CtOption::new(self.pow(NEG_2), !self.is_zero()) - } - - // RFC-8032 sqrt8k5 - fn sqrt(&self) -> CtOption { - let tv1 = self.pow(MOD_3_8); - let tv2 = tv1 * SQRT_M1; - let candidate = Self::conditional_select(&tv2, &tv1, tv1.square().ct_eq(self)); - CtOption::new(candidate, candidate.square().ct_eq(self)) - } - - fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) { - let i = SQRT_M1; - - let u = *u; - let v = *v; - - let v3 = v.square() * v; - let v7 = v3.square() * v; - let mut r = (u * v3) * (u * v7).pow(MOD_5_8); - - let check = v * r.square(); - let correct_sign = check.ct_eq(&u); - let flipped_sign = check.ct_eq(&(-u)); - let flipped_sign_i = check.ct_eq(&((-u) * i)); - - r.conditional_assign(&(r * i), flipped_sign | flipped_sign_i); - - let r_is_negative = r.is_odd(); - r.conditional_negate(r_is_negative); - - (correct_sign | flipped_sign, r) - } -} - -impl PrimeField for FieldElement { - type Repr = [u8; 32]; - - // Big endian representation of the modulus - const MODULUS: &'static str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"; - - const NUM_BITS: u32 = 255; - const CAPACITY: u32 = 254; - - const TWO_INV: Self = FieldElement(ResidueType::new(&U256::from_u8(2)).invert().0); - - // This was calculated with the method from the ff crate docs - // SageMath GF(modulus).primitive_element() - const MULTIPLICATIVE_GENERATOR: Self = Self(ResidueType::new(&U256::from_u8(2))); - // This was set per the specification in the ff crate docs - // The number of leading zero bits in the little-endian bit representation of (modulus - 1) - const S: u32 = 2; - - // This was calculated via the formula from the ff crate docs - // Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S) - const ROOT_OF_UNITY: Self = FieldElement(ResidueType::new(&U256::from_be_hex( - "2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0", - ))); - // Self::ROOT_OF_UNITY.invert() - const ROOT_OF_UNITY_INV: Self = FieldElement(Self::ROOT_OF_UNITY.0.invert().0); - - // This was calculated via the formula from the ff crate docs - // Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S) - const DELTA: Self = FieldElement(ResidueType::new(&U256::from_be_hex( - "0000000000000000000000000000000000000000000000000000000000000010", - ))); - - fn from_repr(bytes: [u8; 32]) -> CtOption { - let res = U256::from_le_bytes(bytes); - CtOption::new(Self(ResidueType::new(&res)), res.ct_lt(&MODULUS)) - } - fn to_repr(&self) -> [u8; 32] { - self.0.retrieve().to_le_bytes() - } - - fn is_odd(&self) -> Choice { - self.0.retrieve().is_odd() - } - - fn from_u128(num: u128) -> Self { - Self::from(num) - } -} - -impl PrimeFieldBits for FieldElement { - type ReprBits = [u8; 32]; - - fn to_le_bits(&self) -> FieldBits { - self.to_repr().into() - } - - fn char_le_bits() -> FieldBits { - MODULUS.to_le_bytes().into() - } -} - -impl FieldElement { - /// Create a FieldElement from a `crypto_bigint::U256`. - /// - /// This will reduce the `U256` by the modulus, into a member of the field. - pub const fn from_u256(u256: &U256) -> Self { - FieldElement(Residue::new(u256)) - } - - /// Create a `FieldElement` from the reduction of a 512-bit number. - /// - /// The bytes are interpreted in little-endian format. - pub fn wide_reduce(value: [u8; 64]) -> Self { - FieldElement(reduce(U512::from_le_bytes(value))) - } - - /// Perform an exponentiation. - pub fn pow(&self, other: FieldElement) -> FieldElement { - let mut table = [FieldElement::ONE; 16]; - table[1] = *self; - for i in 2 .. 16 { - table[i] = table[i - 1] * self; - } - - let mut res = FieldElement::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 = FieldElement::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 - } - - /// The square root of u/v, as used for Ed25519 point decoding (RFC 8032 5.1.3) and within - /// Ristretto (5.1 Extracting an Inverse Square Root). - /// - /// The result is only a valid square root if the Choice is true. - /// RFC 8032 simply fails if there isn't a square root, leaving any return value undefined. - /// Ristretto explicitly returns 0 or sqrt((SQRT_M1 * u) / v). - pub fn sqrt_ratio_i(u: FieldElement, v: FieldElement) -> (Choice, FieldElement) { - let i = SQRT_M1; - - let v3 = v.square() * v; - let v7 = v3.square() * v; - // Candidate root - let mut r = (u * v3) * (u * v7).pow(MOD_5_8); - - // 8032 3.1 - let check = v * r.square(); - let correct_sign = check.ct_eq(&u); - // 8032 3.2 conditional - let neg_u = -u; - let flipped_sign = check.ct_eq(&neg_u); - // Ristretto Step 5 - let flipped_sign_i = check.ct_eq(&(neg_u * i)); - - // 3.2 set - r.conditional_assign(&(r * i), flipped_sign | flipped_sign_i); - - // Always return the even root, per Ristretto - // This doesn't break Ed25519 point decoding as that doesn't expect these steps to return a - // specific root - // Ed25519 points include a dedicated sign bit to determine which root to use, so at worst - // this is a pointless inefficiency - r.conditional_negate(r.is_odd()); - - (correct_sign | flipped_sign, r) - } -} - -impl FromUniformBytes<64> for FieldElement { - fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { - Self::wide_reduce(*bytes) - } -} - -impl Sum for FieldElement { - fn sum>(iter: I) -> FieldElement { - let mut res = FieldElement::ZERO; - for item in iter { - res += item; - } - res - } -} - -impl<'a> Sum<&'a FieldElement> for FieldElement { - fn sum>(iter: I) -> FieldElement { - iter.copied().sum() - } -} - -impl Product for FieldElement { - fn product>(iter: I) -> FieldElement { - let mut res = FieldElement::ONE; - for item in iter { - res *= item; - } - res - } -} - -impl<'a> Product<&'a FieldElement> for FieldElement { - fn product>(iter: I) -> FieldElement { - iter.copied().product() - } -} - -#[test] -fn test_wide_modulus() { - let mut wide = [0; 64]; - wide[.. 32].copy_from_slice(&MODULUS.to_le_bytes()); - assert_eq!(wide, WIDE_MODULUS.to_le_bytes()); -} - -#[test] -fn test_sqrt_m1() { - // Test equivalence against the known constant value - const SQRT_M1_MAGIC: U256 = - U256::from_be_hex("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0"); - assert_eq!(SQRT_M1.0.retrieve(), SQRT_M1_MAGIC); - - // Also test equivalence against the result of the formula from RFC-8032 (modp_sqrt_m1/sqrt8k5 z) - // 2 ** ((MODULUS - 1) // 4) % MODULUS - assert_eq!( - SQRT_M1, - FieldElement::from(2u8).pow(FieldElement(ResidueType::new( - &(FieldElement::ZERO - FieldElement::ONE).0.retrieve().wrapping_div(&U256::from(4u8)) - ))) - ); -} - -#[test] -fn test_field() { - ff_group_tests::prime_field::test_prime_field_bits::<_, FieldElement>(&mut rand_core::OsRng); -} diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index 87fa0f57..74b38887 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -29,33 +29,16 @@ use dalek::{ }; pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE}; -use group::{ +use ::ciphersuite::group::{ ff::{Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes}, Group, GroupEncoding, prime::PrimeGroup, }; -mod field; -pub use field::FieldElement; - mod ciphersuite; pub use crate::ciphersuite::{Ed25519, Ristretto}; -// Use black_box when possible -#[rustversion::since(1.66)] -mod black_box { - pub(crate) fn black_box(val: T) -> T { - #[allow(clippy::incompatible_msrv)] - core::hint::black_box(val) - } -} -#[rustversion::before(1.66)] -mod black_box { - pub(crate) fn black_box(val: T) -> T { - val - } -} -use black_box::black_box; +use core::hint::black_box; fn u8_from_bool(bit_ref: &mut bool) -> u8 { let bit_ref = black_box(bit_ref); @@ -502,3 +485,30 @@ fn test_ed25519_group() { fn test_ristretto_group() { ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng); } + +type ThirtyTwoArray = [u8; 32]; +prime_field::odd_prime_field_with_specific_repr!( + FieldElement, + "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", + "02", + false, + crate::ThirtyTwoArray +); + +impl FieldElement { + /// Create a FieldElement from a `crypto_bigint::U256`. + /// + /// This will reduce the `U256` by the modulus, into a member of the field. + #[deprecated] + pub const fn from_u256(u256: &crypto_bigint::U256) -> Self { + FieldElement::from(&prime_field::crypto_bigint::U256::from_words(*u256.as_words())) + } + + /// Create a `FieldElement` from the reduction of a 512-bit number. + /// + /// The bytes are interpreted in little-endian format. + #[deprecated] + pub fn wide_reduce(value: [u8; 64]) -> Self { + >::from_uniform_bytes(&value) + } +} diff --git a/crypto/dkg/evrf/Cargo.toml b/crypto/dkg/evrf/Cargo.toml index fd5f94e1..7db35571 100644 --- a/crypto/dkg/evrf/Cargo.toml +++ b/crypto/dkg/evrf/Cargo.toml @@ -31,7 +31,7 @@ ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features multiexp = { path = "../../multiexp", version = "0.4", default-features = false } generic-array = { version = "1", default-features = false, features = ["alloc"] } -blake2 = { version = "0.10", default-features = false } +blake2 = { version = "0.11.0-rc.0", default-features = false } rand_chacha = { version = "0.3", default-features = false } generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false } @@ -70,7 +70,6 @@ std = [ "multiexp/std", "multiexp/batch", - "blake2/std", "rand_chacha/std", "generalized-bulletproofs/std", diff --git a/crypto/dkg/evrf/src/curves.rs b/crypto/dkg/evrf/src/curves.rs index 4ab6165c..04720662 100644 --- a/crypto/dkg/evrf/src/curves.rs +++ b/crypto/dkg/evrf/src/curves.rs @@ -7,7 +7,7 @@ use rand_chacha::ChaCha20Rng; use blake2::{ digest::{ - generic_array::{typenum::U32, GenericArray}, + array::{typenum::U32, Array}, crypto_common::KeySizeUser, KeyInit, Mac, }, @@ -48,7 +48,7 @@ impl Generators { /// This is deterministic to the towering curve's (possibly truncated) ID and generator. pub fn new(max_threshold: u16, max_participants: u16) -> Generators { let entropy = ::new(&{ - let mut key = GenericArray::::KeySize>::default(); + let mut key = Array::::KeySize>::default(); let key_len = key.len().min(::ID.len()); { let key: &mut [u8] = key.as_mut(); diff --git a/crypto/ed448/Cargo.toml b/crypto/ed448/Cargo.toml index 290cb30f..2a698230 100644 --- a/crypto/ed448/Cargo.toml +++ b/crypto/ed448/Cargo.toml @@ -32,6 +32,6 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] } ff-group-tests = { path = "../ff-group-tests" } [features] -alloc = ["zeroize/alloc", "prime-field/alloc", "ciphersuite/alloc"] +alloc = ["zeroize/alloc", "sha3/alloc", "prime-field/alloc", "ciphersuite/alloc"] std = ["alloc", "zeroize/std", "prime-field/std", "ciphersuite/std"] default = ["std"] diff --git a/crypto/embedwards25519/Cargo.toml b/crypto/embedwards25519/Cargo.toml index e2d84d89..02c1235c 100644 --- a/crypto/embedwards25519/Cargo.toml +++ b/crypto/embedwards25519/Cargo.toml @@ -20,7 +20,7 @@ std-shims = { version = "0.1", path = "../../common/std-shims", default-features zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } -generic-array = { version = "1", default-features = false } +typenum = { version = "1", default-features = false } prime-field = { path = "../prime-field", default-features = false } short-weierstrass = { path = "../short-weierstrass", default-features = false } @@ -40,6 +40,6 @@ rand_core = { version = "0.6", features = ["std"] } ff-group-tests = { path = "../ff-group-tests" } [features] -alloc = ["std-shims", "zeroize/alloc", "prime-field/alloc", "short-weierstrass/alloc", "curve25519-dalek/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"] +alloc = ["std-shims", "zeroize/alloc", "prime-field/alloc", "short-weierstrass/alloc", "curve25519-dalek/alloc", "blake2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"] std = ["alloc", "std-shims/std", "zeroize/std", "prime-field/std", "short-weierstrass/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"] default = ["std"] diff --git a/crypto/embedwards25519/src/lib.rs b/crypto/embedwards25519/src/lib.rs index fafac7d7..f83be7cd 100644 --- a/crypto/embedwards25519/src/lib.rs +++ b/crypto/embedwards25519/src/lib.rs @@ -109,7 +109,7 @@ impl ciphersuite::Ciphersuite for Embedwards25519 { #[cfg(feature = "alloc")] impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Embedwards25519 { - type ScalarBits = generic_array::typenum::U<{ ::NUM_BITS as usize }>; + type ScalarBits = typenum::U<{ ::NUM_BITS as usize }>; } #[test] diff --git a/crypto/prime-field/src/lib.rs b/crypto/prime-field/src/lib.rs index a87ad71f..46124194 100644 --- a/crypto/prime-field/src/lib.rs +++ b/crypto/prime-field/src/lib.rs @@ -85,7 +85,7 @@ pub mod __prime_field_private { } } -/// Construct a odd-prime field. +/// Construct a odd-prime field with a specified `Repr`. /// /// The length of the `modulus_as_be_hex` string will effect the size of the underlying /// representation and the encoding. It MAY have a "0x" prefix, if preferred. @@ -94,13 +94,15 @@ pub mod __prime_field_private { /// less than `modulus_as_be_hex`. /// /// `big_endian` controls if the encoded representation will be big-endian or not. +#[doc(hidden)] #[macro_export] -macro_rules! odd_prime_field { +macro_rules! odd_prime_field_with_specific_repr { ( $name: ident, $modulus_as_be_hex: expr, $multiplicative_generator_as_be_hex: expr, - $big_endian: literal + $big_endian: literal, + $repr: path ) => { prime_field::__prime_field_private::paste::paste! { mod [<$name __prime_field_private>] { @@ -413,6 +415,7 @@ macro_rules! odd_prime_field { } } + /// The encoded representation of a `$name`. #[derive(Clone, Copy)] pub struct Repr([u8; MODULUS_BYTES]); impl Default for Repr { @@ -432,7 +435,7 @@ macro_rules! odd_prime_field { } impl PrimeField for $name { - type Repr = Repr; + type Repr = $repr; const MODULUS: &str = $modulus_as_be_hex; const NUM_BITS: u32 = MODULUS.bits(); const CAPACITY: u32 = Self::NUM_BITS - 1; @@ -452,26 +455,30 @@ macro_rules! odd_prime_field { }; fn to_repr(&self) -> Self::Repr { - let mut res = Repr([0; _]); - if $big_endian { - res.0.copy_from_slice( - &self.0.retrieve().to_be_bytes()[(UnderlyingUint::BYTES - MODULUS_BYTES) ..] - ); - } else { - res.0.copy_from_slice(&self.0.retrieve().to_le_bytes()[.. MODULUS_BYTES]); + let mut res = Self::Repr::default(); + { + let res: &mut [u8] = res.as_mut(); + if $big_endian { + res.copy_from_slice( + &self.0.retrieve().to_be_bytes()[(UnderlyingUint::BYTES - MODULUS_BYTES) ..] + ); + } else { + res.copy_from_slice(&self.0.retrieve().to_le_bytes()[.. MODULUS_BYTES]); + } } res } fn from_repr(repr: Self::Repr) -> CtOption { let mut expanded_repr = [0; UnderlyingUint::BYTES]; + let repr: &[u8] = repr.as_ref(); let result = Self(if $big_endian { - expanded_repr[(UnderlyingUint::BYTES - MODULUS_BYTES) .. ].copy_from_slice(&repr.0); + expanded_repr[(UnderlyingUint::BYTES - MODULUS_BYTES) .. ].copy_from_slice(repr); Underlying::new(&UnderlyingUint::from_be_bytes(expanded_repr)) } else { - expanded_repr[.. MODULUS_BYTES].copy_from_slice(&repr.0); + expanded_repr[.. MODULUS_BYTES].copy_from_slice(repr); Underlying::new(&UnderlyingUint::from_le_bytes(expanded_repr)) }); - CtOption::new(result, result.to_repr().0.ct_eq(&repr.0)) + CtOption::new(result, repr.ct_eq(&result.to_repr().as_ref())) } fn is_odd(&self) -> Choice { self.0.retrieve().is_odd() @@ -535,3 +542,30 @@ macro_rules! odd_prime_field { } }; } + +/// Construct a odd-prime field. +/// +/// The length of the `modulus_as_be_hex` string will effect the size of the underlying +/// representation and the encoding. It MAY have a "0x" prefix, if preferred. +/// +/// `multiplicative_generator_as_be_hex` MAY have a "0x" prefix. It MAY be short and of a length +/// less than `modulus_as_be_hex`. +/// +/// `big_endian` controls if the encoded representation will be big-endian or not. +#[macro_export] +macro_rules! odd_prime_field { + ( + $name: ident, + $modulus_as_be_hex: expr, + $multiplicative_generator_as_be_hex: expr, + $big_endian: literal + ) => { + prime_field::odd_prime_field_with_specific_repr!( + $name, + $modulus_as_be_hex, + $multiplicative_generator_as_be_hex, + $big_endian, + Repr + ); + }; +} diff --git a/crypto/secq256k1/Cargo.toml b/crypto/secq256k1/Cargo.toml index f49cb671..db4bd1b0 100644 --- a/crypto/secq256k1/Cargo.toml +++ b/crypto/secq256k1/Cargo.toml @@ -18,12 +18,11 @@ hex-literal = { version = "0.4", default-features = false } std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true } -generic-array = { version = "1", default-features = false } k256 = { version = "0.13", default-features = false, features = ["arithmetic"] } prime-field = { path = "../prime-field", default-features = false } short-weierstrass = { path = "../short-weierstrass", default-features = false } -blake2 = { version = "0.11.0-rc.0", default-features = false } +sha2 = { version = "0.11.0-rc.0", default-features = false } ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false } generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false, optional = true } @@ -35,6 +34,6 @@ rand_core = { version = "0.6", features = ["std"] } ff-group-tests = { path = "../ff-group-tests" } [features] -alloc = ["std-shims", "generic-array/alloc", "k256/alloc", "prime-field/alloc", "short-weierstrass/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"] +alloc = ["std-shims", "k256/alloc", "prime-field/alloc", "short-weierstrass/alloc", "sha2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"] std = ["alloc", "std-shims/std", "k256/std", "prime-field/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"] default = ["std"] diff --git a/crypto/secq256k1/src/lib.rs b/crypto/secq256k1/src/lib.rs index 07c10395..89a46f77 100644 --- a/crypto/secq256k1/src/lib.rs +++ b/crypto/secq256k1/src/lib.rs @@ -8,8 +8,10 @@ use std_shims::prelude::*; #[cfg(feature = "alloc")] use std_shims::io::{self, Read}; -// Doesn't use the `generic-array 0.14` exported by `k256::elliptic_curve` as we need `1.0` -use generic_array::{typenum::U33, GenericArray}; +use sha2::{ + digest::array::{typenum::U33, Array}, + Sha512, +}; use k256::elliptic_curve::{ subtle::{Choice, ConstantTimeEq, ConditionallySelectable}, zeroize::Zeroize, @@ -66,11 +68,11 @@ impl ShortWeierstrass for Secq256k1 { }); type Scalar = Scalar; - type Repr = GenericArray; + type Repr = Array; /// Use the SEC1-encoded identity point, which happens to be all zeroes - const IDENTITY: Self::Repr = GenericArray::from_array([0; 33]); + const IDENTITY: Self::Repr = Array([0; 33]); fn encode_compressed(x: Self::FieldElement, odd_y: Choice) -> Self::Repr { - let mut res = GenericArray::default(); + let mut res = Array([0; 33]); res[0] = <_>::conditional_select(&(Tag::CompressedEvenY as u8), &(Tag::CompressedOddY as u8), odd_y); { @@ -110,7 +112,7 @@ pub type Point = Projective; impl ciphersuite::Ciphersuite for Secq256k1 { type F = Scalar; type G = Point; - type H = blake2::Blake2b512; + type H = Sha512; const ID: &'static [u8] = b"secq256k1"; @@ -136,7 +138,7 @@ impl ciphersuite::Ciphersuite for Secq256k1 { #[cfg(feature = "alloc")] impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Secq256k1 { - type ScalarBits = generic_array::typenum::U<{ Scalar::NUM_BITS as usize }>; + type ScalarBits = sha2::digest::array::typenum::U<{ Scalar::NUM_BITS as usize }>; } #[test] @@ -149,7 +151,7 @@ fn generator() { use ciphersuite::group::GroupEncoding; assert_eq!( Point::generator(), - Point::from_bytes(GenericArray::from_slice(&hex_literal::hex!( + Point::from_bytes(&Array(hex_literal::hex!( "020000000000000000000000000000000000000000000000000000000000000001" ))) .unwrap() diff --git a/deny.toml b/deny.toml index 2be57ee8..ea6657af 100644 --- a/deny.toml +++ b/deny.toml @@ -132,6 +132,7 @@ unknown-git = "deny" allow-registry = ["https://github.com/rust-lang/crates.io-index"] allow-git = [ "https://github.com/rust-lang-nursery/lazy-static.rs", + "https://github.com/kayabaNerve/hybrid-array", "https://github.com/kayabaNerve/elliptic-curves", "https://github.com/monero-oxide/monero-oxide", "https://github.com/serai-dex/substrate-bip39", diff --git a/processor/key-gen/Cargo.toml b/processor/key-gen/Cargo.toml index eea820a2..c1de1a81 100644 --- a/processor/key-gen/Cargo.toml +++ b/processor/key-gen/Cargo.toml @@ -29,7 +29,7 @@ rand_core = { version = "0.6", default-features = false, features = ["std", "get rand_chacha = { version = "0.3", default-features = false, features = ["std"] } # Cryptography -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std"] } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] } dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ristretto"] } diff --git a/processor/scanner/Cargo.toml b/processor/scanner/Cargo.toml index 87c47a33..7067c330 100644 --- a/processor/scanner/Cargo.toml +++ b/processor/scanner/Cargo.toml @@ -24,7 +24,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } # Cryptography -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } group = { version = "0.13", default-features = false } # Application diff --git a/processor/signers/Cargo.toml b/processor/signers/Cargo.toml index 48499f21..2a81e7bb 100644 --- a/processor/signers/Cargo.toml +++ b/processor/signers/Cargo.toml @@ -24,7 +24,7 @@ workspace = true rand_core = { version = "0.6", default-features = false } zeroize = { version = "1", default-features = false, features = ["std"] } -blake2 = { version = "0.10", default-features = false, features = ["std"] } +blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] } dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, features = ["std"] } frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false } diff --git a/substrate/client/Cargo.toml b/substrate/client/Cargo.toml index 8fc55fb7..2d9e56ef 100644 --- a/substrate/client/Cargo.toml +++ b/substrate/client/Cargo.toml @@ -49,7 +49,7 @@ monero-address = { git = "https://github.com/monero-oxide/monero-oxide", rev = " rand_core = "0.6" hex = "0.4" -blake2 = "0.10" +blake2 = "0.11.0-rc.0" ciphersuite = { path = "../../crypto/ciphersuite" } dalek-ff-group = { path = "../../crypto/dalek-ff-group" } diff --git a/tests/coordinator/Cargo.toml b/tests/coordinator/Cargo.toml index ac7a7b6f..e0940878 100644 --- a/tests/coordinator/Cargo.toml +++ b/tests/coordinator/Cargo.toml @@ -22,7 +22,7 @@ hex = "0.4" zeroize = { version = "1", default-features = false } rand_core = { version = "0.6", default-features = false } -blake2 = "0.10" +blake2 = "0.11.0-rc.0" ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] } ciphersuite-kp256 = { path = "../../crypto/ciphersuite/kp256", default-features = false, features = ["std"] }