diff --git a/Cargo.lock b/Cargo.lock index 3824db8b..61a26133 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9306,6 +9306,7 @@ dependencies = [ "schnorr-signatures", "secq256k1", "short-weierstrass", + "std-shims", ] [[package]] diff --git a/common/std-shims/Cargo.toml b/common/std-shims/Cargo.toml index 84f22555..efaf1ab4 100644 --- a/common/std-shims/Cargo.toml +++ b/common/std-shims/Cargo.toml @@ -18,9 +18,10 @@ workspace = true [dependencies] rustversion = { version = "1", default-features = false } -spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "once", "lazy"] } -hashbrown = { version = "0.16", default-features = false, features = ["default-hasher", "inline-more"] } +spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "fair_mutex", "once", "lazy"] } +hashbrown = { version = "0.16", default-features = false, features = ["default-hasher", "inline-more"], optional = true } [features] -std = [] +alloc = ["hashbrown"] +std = ["alloc", "spin/std"] default = ["std"] diff --git a/common/std-shims/README.md b/common/std-shims/README.md index b5bc121d..cf552579 100644 --- a/common/std-shims/README.md +++ b/common/std-shims/README.md @@ -1,11 +1,28 @@ -# std shims +# `std` shims -A crate which passes through to std when the default `std` feature is enabled, -yet provides a series of shims when it isn't. +`std-shims` is a Rust crate with two purposes: +- Expand the functionality of `core` and `alloc` +- Polyfill functionality only available on newer version of Rust -No guarantee of one-to-one parity is provided. The shims provided aim to be sufficient for the -average case. +The goal is to make supporting no-`std` environments, and older versions of +Rust, as simple as possible. For most use cases, replacing `std::` with +`std_shims::` and adding `use std_shims::prelude::*` is sufficient to take full +advantage of `std-shims`. -`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 +# API Surface + +`std-shims` only aims to have items _mutually available_ between `alloc` (with +extra dependencies) and `std` publicly exposed. Items exclusive to `std`, with +no shims available, will not be exported by `std-shims`. + +# Dependencies + +`HashSet` and `HashMap` are provided via `hashbrown`. Synchronization +primitives are provided via `spin` (avoiding a requirement on +`critical-section`). Sections of `std::io` are independently matched as +possible. `rustversion` is used to detect when to provide polyfills. + +# Disclaimer + +No guarantee of one-to-one parity is provided. The shims provided aim to be +sufficient for the average case. Pull requests are _welcome_. diff --git a/common/std-shims/src/collections.rs b/common/std-shims/src/collections.rs index d3c74bad..5d0f0a56 100644 --- a/common/std-shims/src/collections.rs +++ b/common/std-shims/src/collections.rs @@ -1,7 +1,7 @@ +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub use extern_alloc::collections::*; +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub use hashbrown::{HashSet, HashMap}; + #[cfg(feature = "std")] pub use std::collections::*; - -#[cfg(not(feature = "std"))] -pub use alloc::collections::*; -#[cfg(not(feature = "std"))] -pub use hashbrown::{HashSet, HashMap}; diff --git a/common/std-shims/src/io.rs b/common/std-shims/src/io.rs index 6ab8436b..6d3019fd 100644 --- a/common/std-shims/src/io.rs +++ b/common/std-shims/src/io.rs @@ -1,42 +1,74 @@ -#[cfg(feature = "std")] -pub use std::io::*; - #[cfg(not(feature = "std"))] mod shims { - use core::fmt::{Debug, Formatter}; - use alloc::{boxed::Box, vec::Vec}; + use core::fmt::{self, Debug, Display, Formatter}; + #[cfg(feature = "alloc")] + use extern_alloc::{boxed::Box, vec::Vec}; + use crate::error::Error as CoreError; + /// The kind of error. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum ErrorKind { UnexpectedEof, Other, } + /// An error. + #[derive(Debug)] pub struct Error { kind: ErrorKind, - error: Box, + #[cfg(feature = "alloc")] + error: Box, } - impl Debug for Error { - fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { - fmt.debug_struct("Error").field("kind", &self.kind).finish_non_exhaustive() + impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + ::fmt(self, f) } } + impl CoreError for Error {} + + #[cfg(not(feature = "alloc"))] + pub trait IntoBoxSendSyncError {} + #[cfg(not(feature = "alloc"))] + impl IntoBoxSendSyncError for I {} + #[cfg(feature = "alloc")] + pub trait IntoBoxSendSyncError: Into> {} + #[cfg(feature = "alloc")] + impl>> IntoBoxSendSyncError for I {} impl Error { - pub fn new(kind: ErrorKind, error: E) -> Error { - Error { kind, error: Box::new(error) } + /// Create a new error. + /// + /// The error object itself is silently dropped when `alloc` is not enabled. + #[allow(unused)] + pub fn new(kind: ErrorKind, error: E) -> Error { + #[cfg(not(feature = "alloc"))] + let res = Error { kind }; + #[cfg(feature = "alloc")] + let res = Error { kind, error: error.into() }; + res } - pub fn other(error: E) -> Error { - Error { kind: ErrorKind::Other, error: Box::new(error) } + /// Create a new error with `io::ErrorKind::Other` as its kind. + /// + /// The error object itself is silently dropped when `alloc` is not enabled. + #[allow(unused)] + pub fn other(error: E) -> Error { + #[cfg(not(feature = "alloc"))] + let res = Error { kind: ErrorKind::Other }; + #[cfg(feature = "alloc")] + let res = Error { kind: ErrorKind::Other, error: error.into() }; + res } + /// The kind of error. pub fn kind(&self) -> ErrorKind { self.kind } - pub fn into_inner(self) -> Option> { + /// Retrieve the inner error. + #[cfg(feature = "alloc")] + pub fn into_inner(self) -> Option> { Some(self.error) } } @@ -94,6 +126,7 @@ mod shims { } } + #[cfg(feature = "alloc")] impl Write for Vec { fn write(&mut self, buf: &[u8]) -> Result { self.extend(buf); @@ -101,6 +134,8 @@ mod shims { } } } - #[cfg(not(feature = "std"))] pub use shims::*; + +#[cfg(feature = "std")] +pub use std::io::{ErrorKind, Error, Result, Read, BufRead, Write}; diff --git a/common/std-shims/src/lib.rs b/common/std-shims/src/lib.rs index b627c63d..0ea8da07 100644 --- a/common/std-shims/src/lib.rs +++ b/common/std-shims/src/lib.rs @@ -2,25 +2,44 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] -pub extern crate alloc; +#[cfg(not(feature = "alloc"))] +pub use core::*; +#[cfg(not(feature = "alloc"))] +pub use core::{alloc, borrow, ffi, fmt, slice, str, task}; + +#[cfg(not(feature = "std"))] +#[rustversion::before(1.81)] +pub mod error { + use core::fmt::Debug::Display; + pub trait Error: Debug + Display {} +} +#[cfg(not(feature = "std"))] +#[rustversion::since(1.81)] +pub use core::error; + +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub extern crate alloc as extern_alloc; +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub use extern_alloc::{alloc, borrow, boxed, ffi, fmt, rc, slice, str, string, task, vec, format}; +#[cfg(feature = "std")] +pub use std::{alloc, borrow, boxed, error, ffi, fmt, rc, slice, str, string, task, vec, format}; -pub mod sync; pub mod collections; pub mod io; - -pub use alloc::vec; -pub use alloc::str; -pub use alloc::string; +pub mod sync; pub mod prelude { - pub use alloc::{ + // Shim the `std` prelude + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use extern_alloc::{ format, vec, - boxed::Box, borrow::ToOwned, + boxed::Box, vec::Vec, string::{String, ToString}, }; + // Shim `div_ceil` #[rustversion::before(1.73)] #[doc(hidden)] pub trait StdShimsDivCeil { @@ -61,6 +80,7 @@ pub mod prelude { } } + // Shim `io::Error::other` #[cfg(feature = "std")] #[rustversion::before(1.74)] #[doc(hidden)] diff --git a/common/std-shims/src/sync.rs b/common/std-shims/src/sync.rs index b25bfc61..7857c50d 100644 --- a/common/std-shims/src/sync.rs +++ b/common/std-shims/src/sync.rs @@ -1,19 +1,26 @@ -pub use core::sync::*; -pub use alloc::sync::*; +pub use core::sync::atomic; +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub use extern_alloc::sync::{Arc, Weak}; mod mutex_shim { - #[cfg(feature = "std")] - pub use std::sync::*; #[cfg(not(feature = "std"))] - pub use spin::*; + pub use spin::{Mutex, MutexGuard}; + #[cfg(feature = "std")] + pub use std::sync::{Mutex, MutexGuard}; + /// A shimmed `Mutex` with an API mutual to `spin` and `std`. #[derive(Default, Debug)] pub struct ShimMutex(Mutex); impl ShimMutex { + /// Construct a new `Mutex`. pub const fn new(value: T) -> Self { Self(Mutex::new(value)) } + /// Acquire a lock on the contents of the `Mutex`. + /// + /// On no-`std` environments, this may spin until the lock is acquired. On `std` environments, + /// this may panic if the `Mutex` was poisoned. pub fn lock(&self) -> MutexGuard<'_, T> { #[cfg(feature = "std")] let res = self.0.lock().unwrap(); @@ -25,10 +32,8 @@ mod mutex_shim { } pub use mutex_shim::{ShimMutex as Mutex, MutexGuard}; -#[cfg(not(feature = "std"))] -pub use spin::Lazy as LazyLock; #[rustversion::before(1.80)] -#[cfg(feature = "std")] +#[cfg(not(feature = "std"))] pub use spin::Lazy as LazyLock; #[rustversion::since(1.80)] #[cfg(feature = "std")] diff --git a/crypto/ciphersuite/Cargo.toml b/crypto/ciphersuite/Cargo.toml index d1911369..80f29e0e 100644 --- a/crypto/ciphersuite/Cargo.toml +++ b/crypto/ciphersuite/Cargo.toml @@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] -std-shims = { path = "../../common/std-shims", version = "0.1.4", default-features = false, optional = true } +std-shims = { path = "../../common/std-shims", version = "0.1.4", default-features = false } zeroize = { version = "^1.5", default-features = false, features = ["derive"] } subtle = { version = "^2.4", default-features = false } @@ -33,7 +33,7 @@ hex = { version = "0.4", default-features = false, features = ["std"] } ff-group-tests = { version = "0.13", path = "../ff-group-tests" } [features] -alloc = ["std-shims", "zeroize/alloc", "digest/alloc", "ff/alloc"] +alloc = ["zeroize/alloc", "digest/alloc", "ff/alloc"] std = [ "alloc", diff --git a/crypto/ciphersuite/src/lib.rs b/crypto/ciphersuite/src/lib.rs index 4816565b..f09ad71a 100644 --- a/crypto/ciphersuite/src/lib.rs +++ b/crypto/ciphersuite/src/lib.rs @@ -3,10 +3,8 @@ #![cfg_attr(not(feature = "std"), no_std)] use core::fmt::Debug; -#[cfg(feature = "alloc")] #[allow(unused_imports)] use std_shims::prelude::*; -#[cfg(feature = "alloc")] use std_shims::io::{self, Read}; use subtle::{CtOption, ConstantTimeEq, ConditionallySelectable}; @@ -112,7 +110,6 @@ pub trait GroupCanonicalEncoding: WrappedGroup { } /// `std::io` extensions for `GroupCanonicalEncoding.` -#[cfg(feature = "alloc")] #[allow(non_snake_case)] pub trait GroupIo: GroupCanonicalEncoding { /// Read a canonical field element from something implementing `std::io::Read`. @@ -129,8 +126,6 @@ pub trait GroupIo: GroupCanonicalEncoding { } /// Read a canonical point from something implementing `std::io::Read`. - #[cfg(feature = "alloc")] - #[allow(non_snake_case)] fn read_G(reader: &mut R) -> io::Result { let mut bytes = ::Repr::default(); reader.read_exact(bytes.as_mut())?; diff --git a/crypto/embedwards25519/Cargo.toml b/crypto/embedwards25519/Cargo.toml index 6d2efed8..70696472 100644 --- a/crypto/embedwards25519/Cargo.toml +++ b/crypto/embedwards25519/Cargo.toml @@ -16,7 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] hex-literal = { version = "1", default-features = false } -std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true } +std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false } zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } @@ -39,6 +39,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", "blake2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"] +alloc = ["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 71e13dc9..03305090 100644 --- a/crypto/embedwards25519/src/lib.rs +++ b/crypto/embedwards25519/src/lib.rs @@ -2,7 +2,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "alloc")] #[allow(unused_imports)] use std_shims::prelude::*; diff --git a/crypto/schnorr/Cargo.toml b/crypto/schnorr/Cargo.toml index ef2e6103..006275a1 100644 --- a/crypto/schnorr/Cargo.toml +++ b/crypto/schnorr/Cargo.toml @@ -26,8 +26,8 @@ digest = { version = "0.11.0-rc.1", default-features = false, features = ["block transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, optional = true } -ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] } -multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["batch"] } +ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false } +multiexp = { path = "../multiexp", version = "0.4", default-features = false, features = ["batch"], optional = true } [dev-dependencies] hex = "0.4" @@ -40,6 +40,7 @@ dalek-ff-group = { path = "../dalek-ff-group" } ciphersuite = { path = "../ciphersuite" } [features] -aggregate = ["transcript"] -std = ["std-shims/std", "rand_core/std", "zeroize/std", "transcript?/std", "ciphersuite/std", "multiexp/std"] +alloc = ["zeroize/alloc", "digest/alloc", "ciphersuite/alloc", "multiexp"] +aggregate = ["alloc", "transcript"] +std = ["alloc", "std-shims/std", "rand_core/std", "zeroize/std", "transcript?/std", "ciphersuite/std", "multiexp/std"] default = ["std"] diff --git a/crypto/schnorr/src/lib.rs b/crypto/schnorr/src/lib.rs index 42aca4d1..071ef8e6 100644 --- a/crypto/schnorr/src/lib.rs +++ b/crypto/schnorr/src/lib.rs @@ -3,14 +3,15 @@ #![cfg_attr(not(feature = "std"), no_std)] use core::ops::Deref; -#[cfg(not(feature = "std"))] -#[macro_use] +#[cfg(all(feature = "alloc", not(feature = "std")))] extern crate alloc; -use std_shims::{ - vec::Vec, - io::{self, Read, Write}, -}; +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::vec::Vec; +#[allow(unused_imports)] +use std_shims::prelude::*; +use std_shims::io::{self, Read, Write}; +#[cfg(feature = "alloc")] use rand_core::{RngCore, CryptoRng}; use zeroize::{Zeroize, Zeroizing}; @@ -22,6 +23,7 @@ use ciphersuite::{ }, GroupIo, }; +#[cfg(feature = "alloc")] use multiexp::{multiexp_vartime, BatchVerifier}; /// Half-aggregation from . @@ -59,6 +61,7 @@ impl SchnorrSignature { } /// Serialize a SchnorrSignature, returning a `Vec`. + #[cfg(feature = "alloc")] pub fn serialize(&self) -> Vec { let mut buf = vec![]; self.write(&mut buf).unwrap(); @@ -106,7 +109,12 @@ impl SchnorrSignature { /// different keys/messages. #[must_use] pub fn verify(&self, public_key: C::G, challenge: C::F) -> bool { - multiexp_vartime(&self.batch_statements(public_key, challenge)).is_identity().into() + let statements = self.batch_statements(public_key, challenge); + #[cfg(feature = "alloc")] + let res = multiexp_vartime(&statements); + #[cfg(not(feature = "alloc"))] + let res = statements.into_iter().map(|(scalar, point)| point * scalar).sum::(); + res.is_identity().into() } /// Queue a signature for batch verification. @@ -114,6 +122,7 @@ impl SchnorrSignature { /// This challenge must be properly crafted, which means being binding to the public key, nonce, /// and any message. Failure to do so will let a malicious adversary to forge signatures for /// different keys/messages. + #[cfg(feature = "alloc")] pub fn batch_verify( &self, rng: &mut R, diff --git a/crypto/secq256k1/Cargo.toml b/crypto/secq256k1/Cargo.toml index e0c4f3f4..6d752545 100644 --- a/crypto/secq256k1/Cargo.toml +++ b/crypto/secq256k1/Cargo.toml @@ -16,7 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] hex-literal = { version = "1", default-features = false } -std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true } +std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false } sha2 = { version = "0.11.0-rc.0", default-features = false } k256 = { version = "0.13", default-features = false, features = ["arithmetic", "expose-field"] } @@ -34,6 +34,6 @@ rand_core = { version = "0.6", features = ["std"] } ff-group-tests = { path = "../ff-group-tests" } [features] -alloc = ["std-shims", "k256/alloc", "prime-field/alloc", "short-weierstrass/alloc", "sha2/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"] +alloc = ["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 ed92dce1..a77760c7 100644 --- a/crypto/secq256k1/src/lib.rs +++ b/crypto/secq256k1/src/lib.rs @@ -2,7 +2,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "alloc")] #[allow(unused_imports)] use std_shims::prelude::*; diff --git a/tests/no-std/Cargo.toml b/tests/no-std/Cargo.toml index 3777d8ec..a828fe9c 100644 --- a/tests/no-std/Cargo.toml +++ b/tests/no-std/Cargo.toml @@ -17,6 +17,8 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] +std-shims = { path = "../../common/std-shims", default-features = false } + flexible-transcript = { path = "../../crypto/transcript", default-features = false, features = ["recommended", "merlin"] } multiexp = { path = "../../crypto/multiexp", default-features = false, features = ["batch"], optional = true } @@ -26,7 +28,7 @@ minimal-ed448 = { path = "../../crypto/ed448", default-features = false } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false } -schnorr-signatures = { path = "../../crypto/schnorr", default-features = false, optional = true } +schnorr-signatures = { path = "../../crypto/schnorr", default-features = false } prime-field = { path = "../../crypto/prime-field", default-features = false } short-weierstrass = { path = "../../crypto/short-weierstrass", default-features = false } @@ -42,6 +44,8 @@ bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, fea [features] alloc = [ + "std-shims/alloc", + "multiexp", "dalek-ff-group/alloc", @@ -49,7 +53,7 @@ alloc = [ "ciphersuite/alloc", - "schnorr-signatures", + "schnorr-signatures/alloc", "prime-field/alloc", "short-weierstrass/alloc", diff --git a/tests/no-std/src/lib.rs b/tests/no-std/src/lib.rs index 805f52a4..57f41a0e 100644 --- a/tests/no-std/src/lib.rs +++ b/tests/no-std/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] +pub use std_shims; + pub use flexible_transcript; pub use dalek_ff_group; @@ -11,18 +13,20 @@ pub use prime_field; pub use short_weierstrass; pub use secq256k1; pub use embedwards25519; -/* -pub use modular_frost; -pub use frost_schnorrkel; -*/ + +pub use schnorr_signatures; #[cfg(feature = "alloc")] pub mod alloc { pub use multiexp; - pub use schnorr_signatures; pub use dkg; pub use dkg_evrf; pub use bitcoin_serai; + + /* + pub use modular_frost; + pub use frost_schnorrkel; + */ }