From c90c9a03fdfbe81b2674f2c697db24229cdd62a9 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 12 Jun 2026 17:20:04 -0600 Subject: [PATCH] elliptic-curve: remove `basepoint-table` and `wnaf` features We can move all of this functionality to `primeorder`, which is *much* easier than trying to keep all of it here, while also making it easier to iterate on because it's all in one repo. The real goal was to share this code between `k256` and elsewhere, with much of it originating in `k256`, but now `k256` uses `primeorder` as a hard dependency when its `arithmetic` feature is enabled. --- .github/workflows/elliptic-curve.yml | 4 +- Cargo.lock | 34 ---- elliptic-curve/Cargo.toml | 8 +- elliptic-curve/src/lib.rs | 2 - elliptic-curve/src/point.rs | 11 +- elliptic-curve/src/point/basepoint_table.rs | 193 -------------------- elliptic-curve/src/point/lookup_table.rs | 66 ------- 7 files changed, 4 insertions(+), 314 deletions(-) delete mode 100644 elliptic-curve/src/point/basepoint_table.rs delete mode 100644 elliptic-curve/src/point/lookup_table.rs diff --git a/.github/workflows/elliptic-curve.yml b/.github/workflows/elliptic-curve.yml index ac83091c3..dacf100a5 100644 --- a/.github/workflows/elliptic-curve.yml +++ b/.github/workflows/elliptic-curve.yml @@ -46,7 +46,6 @@ jobs: - run: cargo build --target ${{ matrix.target }} --release --no-default-features - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features arithmetic - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features critical-section - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features dev - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features digest - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features ecdh @@ -54,7 +53,6 @@ jobs: - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features pkcs8 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features sec1 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features serde - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features wnaf - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,arithmetic - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,arithmetic,pkcs8 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,serde @@ -66,7 +64,7 @@ jobs: with: working-directory: ${{ github.workflow }} stable-cmd: | - cargo hack check --feature-powerset --no-dev-deps --skip basepoint-table + cargo hack check --feature-powerset --no-dev-deps cargo check --all-features test: diff --git a/Cargo.lock b/Cargo.lock index d4b284212..e1ccc3c71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,12 +125,6 @@ dependencies = [ "libc", ] -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - [[package]] name = "crypto" version = "0.6.0-pre" @@ -217,14 +211,12 @@ dependencies = [ "hex-literal", "hkdf", "hybrid-array", - "once_cell", "pem-rfc7468", "pkcs8", "rand_core", "sec1", "serdect", "subtle", - "wnaf", "zeroize", ] @@ -410,16 +402,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -dependencies = [ - "critical-section", - "portable-atomic", -] - [[package]] name = "password-hash" version = "0.6.1" @@ -460,12 +442,6 @@ dependencies = [ "spki", ] -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - [[package]] name = "prettyplease" version = "0.2.37" @@ -797,16 +773,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "wnaf" -version = "0.14.0-pre.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b8f9936fa4378fbe26130d702e51a9d723b22a073105500dfd80d9bb508199" -dependencies = [ - "ff", - "group", -] - [[package]] name = "zeroize" version = "1.8.2" diff --git a/elliptic-curve/Cargo.toml b/elliptic-curve/Cargo.toml index ea6a782db..936c57d9e 100644 --- a/elliptic-curve/Cargo.toml +++ b/elliptic-curve/Cargo.toml @@ -30,12 +30,10 @@ ff = { version = "0.14", optional = true, default-features = false } group = { version = "0.14", optional = true, default-features = false } hkdf = { version = "0.13", optional = true, default-features = false } hex-literal = { version = "1", optional = true } -once_cell = { version = "1.21", optional = true, default-features = false } pem-rfc7468 = { version = "1", optional = true, features = ["alloc"] } pkcs8 = { version = "0.11", optional = true, default-features = false } sec1 = { version = "0.8", optional = true, features = ["ctutils", "subtle", "zeroize"] } serdect = { version = "0.4", optional = true, default-features = false, features = ["alloc"] } -wnaf = { version = "0.14.0-pre.0", optional = true } [dev-dependencies] hex-literal = "1" @@ -52,14 +50,11 @@ alloc = [ ] std = [ "alloc", - "once_cell?/std", "pkcs8?/std", "sec1?/std" ] arithmetic = ["group"] -basepoint-table = ["arithmetic"] -critical-section = ["basepoint-table", "once_cell/critical-section"] dev = ["arithmetic", "dep:hex-literal", "pem", "pkcs8"] ecdh = ["arithmetic", "digest", "dep:hkdf"] getrandom = ["arithmetic", "bigint/getrandom", "common/getrandom"] @@ -67,10 +62,9 @@ group = ["dep:group", "ff"] pkcs8 = ["dep:pkcs8", "sec1"] pem = ["dep:pem-rfc7468", "alloc", "arithmetic", "pkcs8/pem", "sec1/pem"] serde = ["dep:serdect", "alloc", "pkcs8", "sec1/serde"] -wnaf = ["alloc", "arithmetic", "dep:wnaf"] [lints] workspace = true [package.metadata.docs.rs] -features = ["basepoint-table", "bits", "ecdh", "pem", "std"] +features = ["ecdh", "pem", "serde", "std"] diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index 5a4c02fda..894645e76 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -106,8 +106,6 @@ pub use zeroize; pub use crate::error::{DecodeError, DecodeResult}; #[cfg(feature = "pkcs8")] pub use pkcs8; -#[cfg(feature = "wnaf")] -pub use wnaf; #[cfg(feature = "arithmetic")] pub use { crate::{ diff --git a/elliptic-curve/src/point.rs b/elliptic-curve/src/point.rs index 6fd143d0e..4ed30af64 100644 --- a/elliptic-curve/src/point.rs +++ b/elliptic-curve/src/point.rs @@ -1,18 +1,11 @@ //! Traits for elliptic curve points. -mod basepoint_table; -mod lookup_table; mod non_identity; -#[cfg(all(feature = "basepoint-table", feature = "wnaf"))] -pub use self::basepoint_table::vartime::{BasepointTableVartime, PointWithBasepointTableVartime}; -#[cfg(feature = "basepoint-table")] -pub use self::basepoint_table::{BasepointTable, PointWithBasepointTable}; - +#[cfg(feature = "arithmetic")] +pub use self::non_identity::NonIdentity; use crate::{Curve, FieldBytes}; use subtle::{Choice, CtOption}; -#[cfg(feature = "arithmetic")] -pub use {self::non_identity::NonIdentity, lookup_table::LookupTable}; #[cfg(feature = "arithmetic")] use crate::CurveArithmetic; diff --git a/elliptic-curve/src/point/basepoint_table.rs b/elliptic-curve/src/point/basepoint_table.rs deleted file mode 100644 index 4004de6e0..000000000 --- a/elliptic-curve/src/point/basepoint_table.rs +++ /dev/null @@ -1,193 +0,0 @@ -//! Precomputed basepoint tables for accelerating fixed-base scalar multiplication. - -#![cfg(feature = "basepoint-table")] -#![allow(clippy::cast_possible_truncation, clippy::needless_range_loop)] - -#[cfg(not(any(feature = "critical-section", feature = "std")))] -compile_error!("`basepoint-table` feature requires either `critical-section` or `std`"); - -use super::LookupTable; -use group::Group; -use subtle::ConditionallySelectable; -use {core::ops::Deref, ff::PrimeField}; - -#[cfg(feature = "critical-section")] -use once_cell::sync::Lazy as LazyLock; -#[cfg(all(feature = "std", not(feature = "critical-section")))] -use std::sync::LazyLock; - -/// Associate a precomputed `BASEPOINT_TABLE` constant with a curve point. -pub trait PointWithBasepointTable: Group { - /// Basepoint table for this curve. - const BASEPOINT_TABLE: &'static BasepointTable; -} - -/// Precomputed lookup table of multiples of a base point, a.k.a. generator. -/// -/// This type leverages lazy computation, and requires one of the following crate features to be -/// enabled in order to work: -/// - `std`: leverages `std::sync::LazyLock` -/// - `critical-section`: leverages `once_cell::sync::Lazy` via the `critical-section` crate, -/// enabling the feature to be used in `no_std` contexts. -#[derive(Debug)] -pub struct BasepointTable { - tables: LazyLock<[LookupTable; WINDOW_SIZE]>, -} - -impl BasepointTable -where - Point: ConditionallySelectable + Default + Group, -{ - /// Create a new [`BasepointTable`] which is lazily initialized on first use and can be bound - /// to a constant. - /// - /// Computed using the `Point`'s [`Group::generator`] as the base point. - pub const fn new() -> Self { - /// Inner function to initialize the table. - fn init_table() -> [LookupTable; N] - where - Point: ConditionallySelectable + Default + Group, - { - // Ensure basepoint table contains the expected number of entries for the scalar's size - const { - assert!( - N as u32 == 1 + Point::Scalar::NUM_BITS / 8, - "incorrectly sized basepoint table" - ); - } - - let mut generator = Point::generator(); - let mut res = [LookupTable::::default(); N]; - - for i in 0..N { - res[i] = LookupTable::new(generator); - // We are storing tables spaced by two radix steps, - // to decrease the size of the precomputed data. - for _ in 0..8 { - generator = generator.double(); - } - } - - res - } - - Self { - tables: LazyLock::new(init_table), - } - } -} - -impl Default for BasepointTable -where - Point: ConditionallySelectable + Default + Group, -{ - fn default() -> Self { - Self::new() - } -} - -impl Deref for BasepointTable { - type Target = [LookupTable; WINDOW_SIZE]; - - #[inline] - fn deref(&self) -> &[LookupTable; WINDOW_SIZE] { - &self.tables - } -} - -#[cfg(feature = "wnaf")] -pub(super) mod vartime { - use super::LazyLock; - use alloc::vec::Vec; - use core::ops::Mul; - use group::Group; - use wnaf::{WnafBase, WnafScalar}; - - /// Associate a precomputed `BASEPOINT_TABLE_VARTIME` constant with a curve point. - pub trait PointWithBasepointTableVartime: Group { - /// Basepoint table for this curve. - const BASEPOINT_TABLE_VARTIME: &'static BasepointTableVartime; - } - - /// Window table for a curve's base point (a.k.a. generator) precomputed to improve the speed of - /// variable-time scalar multiplication. - /// - ///
- /// Security Warning - /// - /// Variable-time scalar multiplication can potentially leak secret values and should NOT be - /// used with them. - ///
- /// - /// This type leverages lazy computation, and requires one of the following crate features to be - /// enabled in order to work: - /// - `std`: leverages `std::sync::LazyLock` - /// - `critical-section`: leverages `once_cell::sync::Lazy` via the `critical-section` crate, - /// enabling the feature to be used in `no_std` contexts. - #[derive(Debug)] - pub struct BasepointTableVartime { - table: LazyLock>, - } - - impl BasepointTableVartime { - /// Create a new [`BasepointTableVartime`] which is lazily initialized on first use and can - /// be bound to a constant. - /// - /// Computed using the `Point`'s [`Group::generator`] as the base point. - pub const fn new() -> Self { - /// Inner function to initialize the wNAF context. - fn init_wnaf() -> WnafBase - where - Point: Group, - { - WnafBase::new(Point::generator()) - } - - Self { - table: LazyLock::new(init_wnaf), - } - } - - /// Multiply `Point::generator` by the given scalar in variable-time, using the precomputed - /// window table to accelerate the scalar multiplication. - pub fn mul(&self, scalar: &Point::Scalar) -> Point { - self.table.mul(&WnafScalar::new(scalar)) - } - - /// Multiply `Point::generator` by the given scalar in variable-time, then compute a linear - /// combination of the remaining points and scalars, i.e. - /// - /// ```text - /// scalar * G + scalars[0] * Points[0] + ... - /// ``` - pub fn lincomb( - &self, - scalar: &Point::Scalar, - points_and_scalars: &[(Point, Point::Scalar)], - ) -> Point { - let mut bases = Vec::with_capacity(points_and_scalars.len() + 1); - bases.push(self.table.clone()); - bases.extend( - points_and_scalars - .iter() - .map(|(point, _)| WnafBase::new(*point)), - ); - - let mut scalars = Vec::with_capacity(points_and_scalars.len() + 1); - scalars.push(WnafScalar::new(scalar)); - scalars.extend( - points_and_scalars - .iter() - .map(|(_, scalar)| WnafScalar::new(scalar)), - ); - - WnafBase::multiscalar_mul(scalars, bases) - } - } - - impl Default for BasepointTableVartime { - fn default() -> Self { - Self::new() - } - } -} diff --git a/elliptic-curve/src/point/lookup_table.rs b/elliptic-curve/src/point/lookup_table.rs deleted file mode 100644 index 9e13daa6d..000000000 --- a/elliptic-curve/src/point/lookup_table.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! Precomputed lookup tables which allow multiples of an elliptic curve point to be selected in -//! constant time. - -#![cfg(feature = "arithmetic")] - -use group::Group; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; - -/// Internal constant for the number of entries in a [`LookupTable`]. -/// -/// This is defined separately from `LookupTable::SIZE` because we can't use an inherent associated -/// constant of a generic type in generic contexts, and this doesn't vary depending on `Point`. -const LUT_SIZE: usize = 8; - -/// Lookup table containing precomputed values `[p, 2p, 3p, ..., 8p]` -#[derive(Clone, Copy, Debug, Default)] -pub struct LookupTable { - points: [Point; LUT_SIZE], -} - -impl LookupTable -where - Point: ConditionallySelectable + Group, -{ - /// Number of entries in the lookup table. - pub const SIZE: usize = LUT_SIZE; - - /// Compute a new lookup table from the given point. - #[inline] - pub fn new(p: Point) -> Self { - let mut points = [p; LUT_SIZE]; - - for j in 0..(LUT_SIZE - 1) { - points[j + 1] = p + points[j]; - } - - Self { points } - } - - /// Given `-8 <= x <= 8`, returns `x * p` in constant time. - #[allow(clippy::cast_sign_loss)] - #[inline] - pub fn select(&self, x: i8) -> Point { - debug_assert!((-8..=8).contains(&x)); - - // Compute xabs = |x| - let xmask = x >> 7; - let xabs = (x + xmask) ^ xmask; - - // Get an array element in constant time - let mut t = Point::identity(); - - #[allow(clippy::cast_possible_truncation)] - for j in 1..(LUT_SIZE + 1) { - let c = (xabs as u8).ct_eq(&(j as u8)); - t.conditional_assign(&self.points[j - 1], c); - } - // Now t == |x| * p. - - let neg_mask = Choice::from((xmask & 1) as u8); - t.conditional_assign(&-t, neg_mask); - // Now t == x * p. - - t - } -}