RustCrypto: Elliptic Curve Tools
Extra Rust Crypto elliptic-curve adaptors, functions, and macros, built on the
group/ff/elliptic-curve 0.14 traits.
It provides two things for any group/ff type:
serdeadaptors for scalars and group elements (and their array/Vec/Box<[_]>collections).- Multi-scalar multiplication (
sum_of_products), in constant or variable time, with buffer reuse and fixed-base precomputation.
Requires Rust 1.85 or higher (Edition 2024).
Cargo features
| feature | default | description |
|---|---|---|
std |
yes | builds against std |
alloc |
no_std + alloc (the serde adaptors and multiexp live here) |
|
legacy |
bridge curves still on the group/ff 0.13 traits (see below) |
The serde adaptors and sum_of_products require the alloc or std feature.
Serialization
#[serde(with = "...")] adaptors for scalars and group elements, plus their fixed-size
array, Vec, and Box<[_]> variants:
- scalars:
prime_field,prime_field_array,prime_field_vec,prime_field_boxed_slice - groups:
group,group_array,group_vec,group_boxed_slice
use PrimeField;
use prime_field;
use ;
] F);
Sum of products (multi-scalar multiplication)
The SumOfProducts trait is implemented for every Group, computing
scalar[0] * point[0] + ... + scalar[n] * point[n]:
use SumOfProducts;
use ProjectivePoint;
// pairs: &[(Scalar, ProjectivePoint)]
let ct = sum_of_products; // constant-time (secret scalars)
let vt = sum_of_products_vartime; // variable-time (public scalars)
sum_of_productsis constant-time — use it whenever any scalar may be secret.sum_of_products_vartimeis faster but leaks scalars through memory access; use it only when every scalar is public (e.g. verification).
Reusing scratch space
To avoid per-call heap allocation, build a Scratch once and reuse it across calls. One
Scratch::new(capacity) is valid for any call with capacity or fewer pairs, in either
the constant- or variable-time path:
use ;
let mut scratch = new;
let result = sum_of_products_inplace?;
The buffer sizes are checked up front; an undersized Scratch returns
InsufficientScratch without panicking.
Fixed-base precomputation
When the same basis is reused across many calls with different scalars (Pedersen and polynomial commitments, threshold signatures over fixed generators), precompute it once:
use Precomputed;
let basis = new;
let a = basis.sum_of_products?; // constant-time, no per-call table build
let b = basis.sum_of_products_vartime?;
Iterator input
sum_of_products_iter consumes an ExactSizeIterator of (scalar, point) (e.g. a zip)
without first materializing a slice. Precomputed has _iter variants too.
Legacy curves (group/ff 0.13)
Curves that have not migrated to group/ff 0.14 — curve25519-dalek, ed448-goldilocks,
bls12-381 forks, and others — can still use these functions via the optional legacy
feature. Call the bridge trait directly on the native point type:
use SumOfProducts;
// RistrettoPoint, EdwardsPoint, DecafPoint, G1Projective, ...
let result = sum_of_products;
The legacy module also exposes Group013<P> / Scalar013<F> wrappers that present any
0.13 group element / scalar as the 0.14 traits, so the full API (Scratch, Precomputed,
the _inplace/_iter methods) works with 0.13 curves as well.
Notes on the implementation
- The constant-time path uses signed-digit (booth-recoded) Straus; the variable-time path uses Pippenger with benchmark-tuned window thresholds.
- Both big-endian (RustCrypto Weierstrass) and little-endian (curve25519 / ed448 / bls12-381 style) scalar representations are handled.
- Constant-time behavior is checked with dudect (timing) and ctgrind (Valgrind) harnesses
under
ct-tests/; interop with 0.13 curves is checked underinterop-tests/.
Changes in 0.3
- Constant-time
sum_of_productsrewritten around signed-digit Straus; large constant-time inputs are dramatically faster, and the variable-time thresholds were re-tuned. - Fixed little-endian scalar representations producing incorrect results.
- New allocation-free
Scratch/*_inplaceAPI,Precomputedfixed-base API, andsum_of_products_iter. - New optional
legacyfeature bridginggroup/ff0.13 curves. - Dropped the external
multiexpdependency; multi-scalar multiplication is now in-crate.
SemVer Policy
- All on-by-default features of this library are covered by SemVer
- MSRV is considered exempt from SemVer as noted above
License
All crates licensed under either of
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.