13 Commits

Author SHA1 Message Date
Luke Parker
ddbc32de4d Update ciphersuite/dkg MSRVs 2025-08-19 18:20:19 -04:00
Luke Parker
e5ccfac19e Replace bespoke LazyLock/OnceLock with spin re-exports
Presumably notably slower on platforms with std, yet only when compiled with old
versions of Rust for which the option is this or no support anyways.
2025-08-19 18:10:33 -04:00
Luke Parker
432daae1d1 Polyfill extension traits for div_ceil and io::Error::other 2025-08-19 18:04:29 -04:00
Luke Parker
da3a85efe5 Only drop OnceLock value if initialized 2025-08-19 17:50:04 -04:00
Luke Parker
1e0240123d Shim LazyLock when before 1.70 2025-08-19 17:40:19 -04:00
Luke Parker
f6d4d1b084 Remove unused import, fix dirty Cargo.lock 2025-08-19 16:24:19 -04:00
Luke Parker
1b37dd2951 Shim std::sync::LazyLock for Rust < 1.80
Allows downgrading some crypto crates' MSRV to 1.79 as well.
2025-08-19 16:15:44 -04:00
Luke Parker
f32e0609f1 Add warning to dalek-ff-group 2025-08-19 15:25:40 -04:00
Luke Parker
ca85f9ba0c Remove the poorly-designed reduce_512 API
Unused and unpublished. This was only added in the FCMP++ branch as a quick fix
for performance reasons. Finding a better API is still a tricky question, but
this API is _bad_.
2025-08-19 15:24:49 -04:00
Luke Parker
cfd1cb3a37 Add FieldElement::wide_reduce to dalek-ff-group 2025-08-19 13:48:54 -04:00
Luke Parker
f2c13a0040 Expose Once within std-shims, bump spin to 0.9
This is technically a semver break due to bumping spin to 0.10, with the types
from spin being directly exposed. Long-term, we should not directly expose spin
but instead have our own types which are thin wrappers around spin (clearly
defining our API and allowing upgrading internals without breaking semver).
2025-08-19 13:36:01 -04:00
Luke Parker
961f46bc04 Add const fn to create a dalek-ff-group FieldElement 2025-08-19 13:17:39 -04:00
Luke Parker
2c4de3bab4 Bump version of ff-group-tests 2025-08-19 12:51:16 -04:00
22 changed files with 132 additions and 58 deletions

13
Cargo.lock generated
View File

@@ -2634,7 +2634,7 @@ dependencies = [
[[package]]
name = "ff-group-tests"
version = "0.13.1"
version = "0.13.2"
dependencies = [
"bls12_381",
"ff",
@@ -9613,6 +9613,12 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spin"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "spki"
version = "0.7.3"
@@ -9686,10 +9692,11 @@ dependencies = [
[[package]]
name = "std-shims"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"hashbrown 0.14.5",
"spin 0.9.8",
"rustversion",
"spin 0.10.0",
]
[[package]]

View File

@@ -160,6 +160,7 @@ directories-next = { path = "patches/directories-next" }
[workspace.lints.clippy]
unwrap_or_default = "allow"
manual_is_multiple_of = "allow"
incompatible_msrv = "allow" # Manually verified with a GitHub workflow
borrow_as_ptr = "deny"
cast_lossless = "deny"
cast_possible_truncation = "deny"

View File

@@ -1,13 +1,13 @@
[package]
name = "std-shims"
version = "0.1.2"
version = "0.1.3"
description = "A series of std shims to make alloc more feasible"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/common/std-shims"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["nostd", "no_std", "alloc", "io"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.64"
[package.metadata.docs.rs]
all-features = true
@@ -17,7 +17,8 @@ rustdoc-args = ["--cfg", "docsrs"]
workspace = true
[dependencies]
spin = { version = "0.9", default-features = false, features = ["use_ticket_mutex", "lazy"] }
rustversion = { version = "1", default-features = false }
spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "once", "lazy"] }
hashbrown = { version = "0.14", default-features = false, features = ["ahash", "inline-more"] }
[features]

View File

@@ -3,4 +3,9 @@
A crate which passes through to std when the default `std` feature is enabled,
yet provides a series of shims when it isn't.
`HashSet` and `HashMap` are provided via `hashbrown`.
No guarantee of one-to-one parity is provided. The shims provided aim to be sufficient for the
average case.
`HashSet` and `HashMap` are provided via `hashbrown`. Synchronization primitives are provided via
`spin` (avoiding a requirement on `critical-section`).
types are not guaranteed to be

View File

@@ -11,3 +11,64 @@ pub mod io;
pub use alloc::vec;
pub use alloc::str;
pub use alloc::string;
pub mod prelude {
#[rustversion::before(1.73)]
#[doc(hidden)]
pub trait StdShimsDivCeil {
fn div_ceil(self, rhs: Self) -> Self;
}
#[rustversion::before(1.73)]
mod impl_divceil {
use super::StdShimsDivCeil;
impl StdShimsDivCeil for u8 {
fn div_ceil(self, rhs: Self) -> Self {
(self + (rhs - 1)) / rhs
}
}
impl StdShimsDivCeil for u16 {
fn div_ceil(self, rhs: Self) -> Self {
(self + (rhs - 1)) / rhs
}
}
impl StdShimsDivCeil for u32 {
fn div_ceil(self, rhs: Self) -> Self {
(self + (rhs - 1)) / rhs
}
}
impl StdShimsDivCeil for u64 {
fn div_ceil(self, rhs: Self) -> Self {
(self + (rhs - 1)) / rhs
}
}
impl StdShimsDivCeil for u128 {
fn div_ceil(self, rhs: Self) -> Self {
(self + (rhs - 1)) / rhs
}
}
impl StdShimsDivCeil for usize {
fn div_ceil(self, rhs: Self) -> Self {
(self + (rhs - 1)) / rhs
}
}
}
#[cfg(feature = "std")]
#[rustversion::before(1.74)]
#[doc(hidden)]
pub trait StdShimsIoErrorOther {
fn other<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>;
}
#[cfg(feature = "std")]
#[rustversion::before(1.74)]
impl StdShimsIoErrorOther for std::io::Error {
fn other<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
std::io::Error::new(std::io::ErrorKind::Other, error)
}
}
}

View File

@@ -25,7 +25,20 @@ mod mutex_shim {
}
pub use mutex_shim::{ShimMutex as Mutex, MutexGuard};
#[cfg(not(feature = "std"))]
pub use spin::Once as OnceLock;
#[rustversion::before(1.70)]
#[cfg(feature = "std")]
pub use std::sync::LazyLock;
pub use spin::Once as OnceLock;
#[rustversion::since(1.70)]
#[cfg(feature = "std")]
pub use std::sync::OnceLock;
#[cfg(not(feature = "std"))]
pub use spin::Lazy as LazyLock;
#[rustversion::before(1.80)]
#[cfg(feature = "std")]
pub use spin::Lazy as LazyLock;
#[rustversion::since(1.80)]
#[cfg(feature = "std")]
pub use std::sync::LazyLock;

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ciphersuite
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["ciphersuite", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.73"
[package.metadata.docs.rs]
all-features = true

View File

@@ -28,12 +28,6 @@ macro_rules! dalek_curve {
$Point::generator()
}
fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
let res = Scalar::from_bytes_mod_order_wide(&scalar);
scalar.zeroize();
res
}
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
Scalar::from_hash(Sha512::new_with_prefix(&[dst, data].concat()))
}

View File

@@ -66,12 +66,6 @@ impl Ciphersuite for Ed448 {
Point::generator()
}
fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
let res = Self::hash_to_F(b"Ciphersuite-reduce_512", &scalar);
scalar.zeroize();
res
}
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_ref().try_into().unwrap())
}

View File

@@ -6,7 +6,7 @@ use group::ff::PrimeField;
use elliptic_curve::{
generic_array::GenericArray,
bigint::{NonZero, CheckedAdd, Encoding, U384, U512},
bigint::{NonZero, CheckedAdd, Encoding, U384},
hash2curve::{Expander, ExpandMsg, ExpandMsgXmd},
};
@@ -31,22 +31,6 @@ macro_rules! kp_curve {
$lib::ProjectivePoint::GENERATOR
}
fn reduce_512(scalar: [u8; 64]) -> Self::F {
let mut modulus = [0; 64];
modulus[32 ..].copy_from_slice(&(Self::F::ZERO - Self::F::ONE).to_bytes());
let modulus = U512::from_be_slice(&modulus).checked_add(&U512::ONE).unwrap();
let mut wide =
U512::from_be_bytes(scalar).rem(&NonZero::new(modulus).unwrap()).to_be_bytes();
let mut array = *GenericArray::from_slice(&wide[32 ..]);
let res = $lib::Scalar::from_repr(array).unwrap();
wide.zeroize();
array.zeroize();
res
}
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
// While one of these two libraries does support directly hashing to the Scalar field, the
// other doesn't. While that's probably an oversight, this is a universally working method

View File

@@ -3,8 +3,11 @@
#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt::Debug;
#[cfg(any(feature = "alloc", feature = "std"))]
use std_shims::io::{self, Read};
#[allow(unused_imports)]
use std_shims::{
prelude::*,
io::{self, Read},
};
use rand_core::{RngCore, CryptoRng};
@@ -62,12 +65,6 @@ pub trait Ciphersuite:
// While group does provide this in its API, privacy coins may want to use a custom basepoint
fn generator() -> Self::G;
/// Reduce 512 bits into a uniform scalar.
///
/// If 512 bits is insufficient to perform a reduction into a uniform scalar, the ciphersuite
/// will perform a hash to sample the necessary bits.
fn reduce_512(scalar: [u8; 64]) -> Self::F;
/// Hash the provided domain-separation tag and message to a scalar. Ciphersuites MAY naively
/// prefix the tag to the message, enabling transpotion between the two. Accordingly, this
/// function should NOT be used in any scheme where one tag is a valid substring of another

View File

@@ -216,6 +216,20 @@ impl PrimeFieldBits for FieldElement {
}
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)))
}
/// Interpret the value as a little-endian integer, square it, and reduce it into a FieldElement.
pub fn from_square(value: [u8; 32]) -> FieldElement {
let value = U256::from_le_bytes(value);

View File

@@ -359,7 +359,12 @@ macro_rules! dalek_group {
$BASEPOINT_POINT: ident,
$BASEPOINT_TABLE: ident
) => {
/// Wrapper around the dalek Point type. For Ed25519, this is restricted to the prime subgroup.
/// Wrapper around the dalek Point type.
///
/// All operations will be restricted to a prime-order subgroup (equivalent to the group itself
/// in the case of Ristretto). The exposure of the internal element does allow bypassing this
/// however, which may lead to undefined/computationally-unsafe behavior, and is entirely at
/// the user's risk.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct $Point(pub $DPoint);
deref_borrow!($Point, $DPoint);

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.73"
[package.metadata.docs.rs]
all-features = true

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg/dealer"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.73"
[package.metadata.docs.rs]
all-features = true

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg/musig"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true

View File

@@ -29,10 +29,6 @@ impl<C: Ciphersuite> Ciphersuite for AltGenerator<C> {
C::G::generator() * <C as Ciphersuite>::hash_to_F(b"DKG Promotion Test", b"generator")
}
fn reduce_512(scalar: [u8; 64]) -> Self::F {
<C as Ciphersuite>::reduce_512(scalar)
}
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
<C as Ciphersuite>::hash_to_F(dst, data)
}

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dkg/recover
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["dkg", "multisig", "threshold", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.73"
[package.metadata.docs.rs]
all-features = true

View File

@@ -6,6 +6,8 @@ use core::{
ops::Deref,
fmt::{self, Debug},
};
#[allow(unused_imports)]
use std_shims::prelude::*;
use std_shims::{sync::Arc, vec, vec::Vec, collections::HashMap, io};
use zeroize::{Zeroize, Zeroizing};

View File

@@ -1,6 +1,6 @@
[package]
name = "ff-group-tests"
version = "0.13.1"
version = "0.13.2"
description = "A collection of sanity tests for implementors of ff/group APIs"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ff-group-tests"

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/multiexp"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["multiexp", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true

View File

@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/schnorr"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["schnorr", "ff", "group"]
edition = "2021"
rust-version = "1.80"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true