2026-03-31 11:46:24 +00:00
|
|
|
|
# curve25519 (`solana-ed25519`)
|
2026-03-24 11:42:07 +00:00
|
|
|
|
|
|
|
|
|
|
**A pure-Rust implementation of group operations on Ristretto and Curve25519, forked from
|
|
|
|
|
|
[curve25519-dalek] with HEEA scalar decomposition and a reduced backend set.**
|
|
|
|
|
|
|
|
|
|
|
|
> For the original curve25519-dalek documentation see [README_dalek.md](README_dalek.md).
|
|
|
|
|
|
|
2026-03-31 11:46:24 +00:00
|
|
|
|
This crate is part of the [cryptography](https://github.com/anza-xyz/cryptography/) workspace.
|
2026-03-24 11:42:07 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Changes from curve25519-dalek
|
|
|
|
|
|
|
|
|
|
|
|
### HEEA Scalar Decomposition
|
|
|
|
|
|
|
|
|
|
|
|
A new `HEEADecomposition` trait and implementation have been added in:
|
|
|
|
|
|
|
|
|
|
|
|
- [`src/scalar/heea.rs`](src/scalar/heea.rs) – `curve25519_heea_vartime`, the core
|
|
|
|
|
|
half-extended Euclidean algorithm
|
|
|
|
|
|
- [`src/traits.rs`](src/traits.rs) – `HEEADecomposition` trait (`heea_decompose`)
|
|
|
|
|
|
- [`src/backend/serial/scalar_mul/vartime_triple_base.rs`](src/backend/serial/scalar_mul/vartime_triple_base.rs) –
|
|
|
|
|
|
`mul_128_128_256`, a four-variable MSM optimised for two 128-bit and one 256-bit scalar
|
|
|
|
|
|
|
|
|
|
|
|
Given a 256-bit hash scalar `h`, `heea_decompose` returns `(ρ, τ, flip_h)` such that:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-06-12 02:10:13 +00:00
|
|
|
|
flip_h = false: ρ ≡ τ·h (mod ℓ)
|
|
|
|
|
|
flip_h = true: ρ ≡ -τ·h (mod ℓ)
|
2026-03-24 11:42:07 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
This allows verification of `sB = R + hA` to be rewritten as a 4-point MSM over ~128-bit
|
|
|
|
|
|
scalars, reducing the number of point doublings required and yielding roughly **~15% faster**
|
|
|
|
|
|
verification in practice.
|
|
|
|
|
|
|
|
|
|
|
|
See the [TCHES 2025 paper] for the full algorithm description.
|
|
|
|
|
|
|
|
|
|
|
|
### Reduced Backends
|
|
|
|
|
|
|
|
|
|
|
|
Only the following backends are maintained in this fork:
|
|
|
|
|
|
|
|
|
|
|
|
| Backend | Selection | Notes |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `serial` | Automatic fallback | Pure Rust, 64-bit word size on 64-bit targets |
|
|
|
|
|
|
| `simd` / AVX2 | Runtime on x86-64 | Vectorised 4-wide field arithmetic |
|
|
|
|
|
|
| CUDA | Opt-in (`curve25519-cuda` crate) | GPU MSM via SPPARK/BLST |
|
|
|
|
|
|
|
|
|
|
|
|
The `fiat` (formally-verified fiat-crypto) and `unstable_avx512` backends present in upstream
|
|
|
|
|
|
have been removed.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-26 12:09:10 +00:00
|
|
|
|
## Ed25519 Signatures (`ed_sigs`)
|
|
|
|
|
|
|
|
|
|
|
|
This crate includes a **ZIP-215-compliant Ed25519 signature implementation** in the
|
|
|
|
|
|
`ed_sigs` module, forked from [ed25519-zebra] and extended with HEEA-accelerated
|
|
|
|
|
|
verification.
|
|
|
|
|
|
|
|
|
|
|
|
> For the original ed25519-zebra documentation see [README_zebra.md](README_zebra.md).
|
|
|
|
|
|
|
2026-03-31 11:46:24 +00:00
|
|
|
|
### `verify_zebra`: fast-path signature verification
|
2026-03-26 12:09:10 +00:00
|
|
|
|
|
2026-06-12 02:10:13 +00:00
|
|
|
|
`VerificationKey::verify_zebra` is the HEEA implementation used by the default
|
|
|
|
|
|
`VerificationKey::verify` method. Both accept the same arguments and produce identical
|
|
|
|
|
|
ZIP-215 results.
|
2026-03-26 12:09:10 +00:00
|
|
|
|
|
|
|
|
|
|
The HEEA method (TCHES 2025) transforms the standard 2-point MSM:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
[8][s]B = [8]R + [8][h]A (standard)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
into a 4-point MSM over half-size (~128-bit) scalars:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-06-12 02:10:13 +00:00
|
|
|
|
flip_h = false: τs_lo·B + τs_hi·(2¹²⁸·B) = τ·R + ρ·A
|
|
|
|
|
|
flip_h = true: τs_lo·B + τs_hi·(2¹²⁸·B) = τ·R - ρ·A
|
2026-03-26 12:09:10 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 02:10:13 +00:00
|
|
|
|
where `ρ ≡ τ·h (mod ℓ)` when `flip_h` is false, `ρ ≡ -τ·h (mod ℓ)` when
|
|
|
|
|
|
`flip_h` is true, and `τs = τs_hi·2¹²⁸ + τs_lo`. All four scalars are ≤128 bits
|
2026-03-26 12:09:10 +00:00
|
|
|
|
and the two basepoints (`B` and `2¹²⁸B`) use precomputed lookup tables, giving approximately
|
|
|
|
|
|
**~15% faster** verification compared to the standard path.
|
|
|
|
|
|
|
|
|
|
|
|
### ZIP 215
|
|
|
|
|
|
|
|
|
|
|
|
ZIP-215-compliant Ed25519 validation rules are fully preserved from ed25519-zebra:
|
|
|
|
|
|
|
|
|
|
|
|
- Non-canonical point encodings are accepted for `A` and `R`.
|
|
|
|
|
|
- `s` must be a canonical integer less than the group order `ℓ`.
|
|
|
|
|
|
- The cofactor-cleared equation `[8][s]B = [8]R + [8][h]A` is used (not the RFC 8032 variant).
|
|
|
|
|
|
|
|
|
|
|
|
See [ZIP 215] for full details.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-24 11:42:07 +00:00
|
|
|
|
## Use
|
|
|
|
|
|
|
|
|
|
|
|
```toml
|
2026-03-31 11:46:24 +00:00
|
|
|
|
curve25519 = { package = "solana-ed25519", git = "https://github.com/anza-xyz/cryptography" }
|
2026-03-24 11:42:07 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-26 12:09:10 +00:00
|
|
|
|
### Ed25519 signing and verification
|
|
|
|
|
|
|
|
|
|
|
|
```rust,no_run
|
|
|
|
|
|
use core::convert::TryFrom;
|
|
|
|
|
|
use curve25519::ed_sigs::{SigningKey, VerificationKey};
|
|
|
|
|
|
|
2026-03-31 11:46:24 +00:00
|
|
|
|
let msg = b"curve25519-sol";
|
|
|
|
|
|
|
|
|
|
|
|
// Generate key and sign
|
2026-06-10 10:44:17 +00:00
|
|
|
|
let sk = SigningKey::from_bytes(&[1u8; 32]);
|
2026-03-31 11:46:24 +00:00
|
|
|
|
let sig = sk.sign(msg);
|
|
|
|
|
|
let vk = VerificationKey::from(&sk);
|
|
|
|
|
|
|
|
|
|
|
|
// Standard ZIP-215 verification with heea acceleration
|
|
|
|
|
|
vk.verify(&sig, msg).expect("valid signature");
|
2026-03-26 12:09:10 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Batch verification
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
|
use curve25519::ed_sigs::batch;
|
|
|
|
|
|
|
|
|
|
|
|
let mut verifier = batch::Verifier::new();
|
|
|
|
|
|
for (vk_bytes, sig, msg) in items {
|
|
|
|
|
|
verifier.queue((vk_bytes, sig, msg));
|
|
|
|
|
|
}
|
2026-05-19 01:50:26 +00:00
|
|
|
|
verifier.verify(rand::thread_rng()).expect("all valid");
|
2026-03-26 12:09:10 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-03-24 11:42:07 +00:00
|
|
|
|
### HEEA decomposition example
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
|
use curve25519::traits::HEEADecomposition;
|
|
|
|
|
|
use curve25519::scalar::Scalar;
|
|
|
|
|
|
use sha2::{Sha512, Digest};
|
|
|
|
|
|
|
|
|
|
|
|
// h is a typical 256-bit hash scalar
|
|
|
|
|
|
let h = Scalar::from_hash(Sha512::new().chain_update(b"some message"));
|
|
|
|
|
|
|
|
|
|
|
|
// Decompose into two ~128-bit scalars
|
|
|
|
|
|
let (rho, tau, flip_h) = h.heea_decompose();
|
2026-06-12 02:10:13 +00:00
|
|
|
|
// flip_h == false: rho ≡ tau·h (mod ℓ)
|
|
|
|
|
|
// flip_h == true: rho ≡ -tau·h (mod ℓ)
|
2026-03-24 11:42:07 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Feature Flags
|
|
|
|
|
|
|
|
|
|
|
|
| Feature | Default? | Description |
|
|
|
|
|
|
|---|:---:|---|
|
2026-06-12 02:10:13 +00:00
|
|
|
|
| `alloc` | ✓ | Multiscalar multiplication, batch inversion, batch compress, and the Ed25519 batch module. |
|
2026-03-24 11:42:07 +00:00
|
|
|
|
| `zeroize` | ✓ | `Zeroize` for all scalar and point types. |
|
|
|
|
|
|
| `precomputed-tables` | ✓ | Precomputed basepoint tables (~400 KB, ~4× faster basepoint mul). |
|
2026-06-12 02:10:13 +00:00
|
|
|
|
| `rand_core` | ✓ | `Scalar::random`, `RistrettoPoint::random`, `SigningKey::new`, and randomized batch verification. |
|
2026-03-26 12:09:10 +00:00
|
|
|
|
| `digest` | ✓ | Hash-to-curve, `Scalar::from_hash`, and Ed25519 hashing. |
|
|
|
|
|
|
| `std` | | Enables `std::error::Error` impl on `ed_sigs::Error`. |
|
|
|
|
|
|
| `serde` | | Serialization for all point, scalar, and key types. |
|
|
|
|
|
|
| `pkcs8` | | PKCS#8 DER encoding/decoding for Ed25519 keys. |
|
|
|
|
|
|
| `pem` | | PEM encoding/decoding for Ed25519 keys (requires `pkcs8`). |
|
2026-03-24 11:42:07 +00:00
|
|
|
|
| `legacy_compatibility` | | `Scalar::from_bits` (broken arithmetic, use only if required). |
|
|
|
|
|
|
| `group` | | `group` and `ff` crate trait impls. |
|
|
|
|
|
|
| `group-bits` | | `ff::PrimeFieldBits` for `Scalar`. |
|
|
|
|
|
|
| `lizard` | | Bytestring-to-Ristretto-point injection. |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Backends
|
|
|
|
|
|
|
|
|
|
|
|
### Serial (default)
|
|
|
|
|
|
|
|
|
|
|
|
Pure-Rust, available on all targets. 64-bit arithmetic on 64-bit platforms.
|
|
|
|
|
|
|
|
|
|
|
|
### AVX2 (automatic on x86-64)
|
|
|
|
|
|
|
|
|
|
|
|
Runtime CPU-feature detection via `cpufeatures`. 4-wide vectorised field elements in
|
|
|
|
|
|
radix-25.5 representation. Automatically selected when the CPU supports AVX2; falls through to
|
|
|
|
|
|
`serial` otherwise.
|
|
|
|
|
|
|
|
|
|
|
|
To hard-code AVX2 at compile time:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
RUSTFLAGS='-C target-feature=+avx2' cargo build --release
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### CUDA (opt-in)
|
|
|
|
|
|
|
|
|
|
|
|
See the [`curve25519-cuda`](../curve25519-cuda) crate. Provides GPU-accelerated
|
|
|
|
|
|
multi-scalar multiplication using the [SPPARK] library.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Safety
|
|
|
|
|
|
|
|
|
|
|
|
All point types enforce validity invariants at the type level (no invalid `EdwardsPoint` can be
|
|
|
|
|
|
constructed). All secret-operand operations use constant-time logic via the [`subtle`] crate.
|
|
|
|
|
|
Variable-time functions are explicitly marked `vartime`.
|
|
|
|
|
|
|
|
|
|
|
|
The SIMD backend uses `unsafe` internally for SIMD intrinsics, guarded by runtime CPU-feature
|
|
|
|
|
|
checks.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## MSRV
|
|
|
|
|
|
|
|
|
|
|
|
Rust **1.85.0** (Edition 2024).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
|
|
- [TCHES 2025 paper] – _Accelerating EdDSA Signature Verification with Faster Scalar Size Halving_
|
2026-03-26 12:09:10 +00:00
|
|
|
|
- [curve25519-dalek] – upstream curve25519 library (isis lovecruft, Henry de Valence)
|
|
|
|
|
|
- [ed25519-zebra] – upstream Ed25519 library (Zcash Foundation)
|
|
|
|
|
|
- [ZIP 215] – Ed25519 validation rules for Zcash
|
2026-03-24 11:42:07 +00:00
|
|
|
|
- [Original curve25519-dalek README](README_dalek.md)
|
2026-03-26 12:09:10 +00:00
|
|
|
|
- [Original ed25519-zebra README](README_zebra.md)
|
2026-03-24 11:42:07 +00:00
|
|
|
|
|
|
|
|
|
|
[TCHES 2025 paper]: https://tches.iacr.org/index.php/TCHES/article/view/11971
|
|
|
|
|
|
[curve25519-dalek]: https://github.com/dalek-cryptography/curve25519-dalek
|
2026-03-26 12:09:10 +00:00
|
|
|
|
[ed25519-zebra]: https://github.com/ZcashFoundation/ed25519-zebra
|
|
|
|
|
|
[ZIP 215]: https://zips.z.cash/zip-0215
|
2026-03-24 11:42:07 +00:00
|
|
|
|
[SPPARK]: https://github.com/supranational/sppark
|
|
|
|
|
|
[subtle]: https://docs.rs/subtle
|