# Security Reference
This document maps every cnfy algorithm to its timing behavior, catalogs known
attack vectors against ECC implementations, and identifies the constant-time
alternative for each operation. Users can choose the right tool for the right
job: **default** when operating on public data (speed), **constant-time**
(`--features ct`) when operating on secret data (security).
---
## Table of Contents
- [Algorithm Timing Properties](#algorithm-timing-properties)
- [cnfy-uint](#cnfy-uint)
- [cnfy-secp256k1](#cnfy-secp256k1)
- [Feature Flags](#feature-flags)
- [Known Attacks on ECC Implementations](#known-attacks-on-ecc-implementations)
- [Timing Attacks](#timing-attacks)
- [Cache-Timing Attacks](#cache-timing-attacks)
- [EM Emanation Attacks](#em-emanation-attacks)
- [Power Analysis Attacks](#power-analysis-attacks)
- [Fault Injection Attacks](#fault-injection-attacks)
- [Lattice Attacks on Biased Nonces](#lattice-attacks-on-biased-nonces)
- [Supply Chain Attacks](#supply-chain-attacks)
- [Implementation Bugs](#implementation-bugs)
- [Modular Inversion Algorithm Comparison](#modular-inversion-algorithm-comparison)
- [Recommendations by Use Case](#recommendations-by-use-case)
- [Library Comparison](#library-comparison)
- [References](#references)
---
## Algorithm Timing Properties
### cnfy-uint
Operations with a **Feature** column entry have both default and constant-time variants.
Granular feature flags switch individual dispatchers to the constant-time path at compile
time. Default (no feature flags) provides maximum performance.
| `add_mod` | conditional subtract | **default/CT** | `ct-field` | `add_mod_ct`: branchless `ct_select` (3.75 ns) | 3 branches on carry/borrow/comparison (1.93 ns) | Which of 3 reduction paths; field element magnitude | Medium |
| `sub_mod` | conditional add | **default/CT** | `ct-field` | `sub_mod_ct`: branchless `ct_select` (2.92 ns) | single `match` on borrow (1.85 ns) | Whether self < other (if compiler branches) | Low |
| `mul_mod` dispatch | sparse vs KnuthD | CT (secp256k1) | — | N/A (branch on public modulus structure) | N/A | Nothing for secp256k1 (branch outcome is fixed) | None |
| `mul_mod_sparse` | fused multiply+reduce | **default/CT** | `ct-field` | `mul_mod_sparse_ct`: always executes round 2, branchless final reduction (15.42 ns) | `if overflow == 0` early return; final comparison (11.89 ns) | Product magnitude (2-3 code paths) | Medium |
| `square_mod_sparse` | fused square+reduce | **default/CT** | `ct-field` | `square_mod_sparse_ct`: same pattern as mul_mod_sparse_ct (12.25 ns) | same as mul_mod_sparse (8.21 ns) | Same as mul_mod_sparse | Medium |
| `sparse_reduce` (U512) | folding reduction | **default/CT** | `ct-field` | `sparse_reduce_ct`: always executes round 2, branchless ct_select (7.39 ns) | `if overflow == 0` early return (3.66 ns) | Product magnitude | Medium |
| `modulo` | subtraction | **default/CT** | `ct-field` | `modulo_ct`: 3 branchless conditional subs | variable iteration count (0-2) | How far above modulus the residue lands | Medium |
| `mod_inv` | extended GCD / divsteps | **default/CT** | `ct-inv` | `mod_inv_ct`: SafeGCD, 12x62 Bernstein-Yang divsteps (8.69 µs) | Lehmer GCD, variable iterations (802 ns) | Full GCD trajectory; structural info about input | **High** |
| `pow_mod` | square-and-multiply | **default/CT** | `ct-pow` | `pow_mod_ct`: always 256 iters, unconditional mul + ct_select (11.04 µs) | loop count = `bit_len(exp)`; per-bit conditional multiply (10.25 µs) | Exponent bits (safe when exp is public, e.g. P-2) | Context-dependent |
| `div_u256` (KnuthD) | multi-precision division | **default only** | — | — | variable iteration count | Quotient structure | Medium |
### cnfy-secp256k1
| `add_affine` / `add` | madd-2008-g formula | default/CT (inherited) | `ct-field` | inherits CT field ops | no own branches; inherits field op timing | Intermediate coordinate magnitudes | Medium |
| `double` | dbl-2009-l formula | default/CT (inherited) | `ct-field` | inherits CT field ops | no own branches; inherits field op timing | Intermediate coordinate magnitudes | Medium |
| `to_public_key` | Jacobian to affine | **default/CT** | `ct-inv` | inherits CT `mod_inv` (SafeGCD) | `mod_inv` on Z coordinate | Z-coordinate structure (derived from scalar) | **High** |
| Scalar mul (comb) | `into_jacobian_point` | **default/CT** | `ct-scalar` | CT: 4-bit signed comb with Booth recoding, 8-entry CT scan per column (15.9 µs) | `if v != 0` skips zero columns; secret-indexed table lookup (5.50 µs) | 4-bit scalar nybbles; which columns are zero; cache-timing on table index | **Critical** |
| `first_nonzero_point` | comb startup | **default only** | — | — (only used in default accumulation path) | loop until first nonzero column | Position of MSB byte of scalar | **High** |
### Feature Flags
Granular features allow enabling only the constant-time operations you need, minimizing
performance overhead. Each feature activates its constant-time dispatcher at compile time
with zero overhead to the default path when disabled.
| `ct-field` | cnfy-uint | `add_mod`, `sub_mod`, `mul_mod_sparse`, `square_mod_sparse`, `sparse_reduce`, `modulo` | — | 30-95% per op |
| `ct-inv` | cnfy-uint | `mod_inv` (SafeGCD, Bernstein-Yang divsteps) | `ct-field` | 10.8x vs Lehmer |
| `ct-pow` | cnfy-uint | `pow_mod` (always 256 iters, ct_select) | `ct-field` | 8% |
| `ct-scalar` | cnfy-secp256k1 | `into_jacobian_point` (4-bit signed comb, 8-entry CT scan, 65-col loop) | `ct-field` | 2.9x |
| `ct` | both | All of the above (backward compatible umbrella) | all | varies |
**Usage examples:**
```bash
# Full CT (ECDSA signing ready)
cargo build --features ct
# Only CT field arithmetic (cheapest, still protects intermediate values)
cargo build --features ct-field
# CT scalar multiplication + field (for key generation)
cargo build --features ct-scalar
# CT modular inversion only (e.g. for Jacobian→affine conversion)
cargo build --features ct-inv
```
---
## Known Attacks on ECC Implementations
This section catalogs real-world attacks that have broken production
cryptographic systems. Each demonstrates why specific defensive measures
matter when choosing or building an ECC library.
### Timing Attacks
Measure execution time differences to infer secret-dependent code paths.
| CVE-2024-13176 | OpenSSL P-521 ECDSA | ~300ns timing difference when top word of inverted nonce is zero | Private key recovery via lattice reduction | 2025 |
| CVE-2020-13162 | OpenSSL P-256 ECDSA | Missing `BN_FLG_CONSTTIME` on nonces; non-CT `BN_mod_inverse` path | Cache-timing key recovery | 2020 |
| Bug 1623116 | Mozilla NSS P-384/P-521 | Side-channel vulnerable modular inversion | Key recovery | 2020 |
| Minerva (CVE-2024-23342) | libgcrypt, wolfSSL, MatrixSSL, SunEC, python-ecdsa | Nonce bit-length leak during `k*G` | Key recovery from 500-2100 signatures | 2020 |
| CVE-2019-15809 | Athena/Atmel FIPS smart cards | Nonce bit-length timing leak | Key recovery from hundreds of signatures | 2019 |
**Defense**: Constant-time field arithmetic (branchless conditional subtract
via bitmask), constant-time modular inversion (SafeGCD or Fermat), constant-time
scalar multiplication (fixed-window with dummy ops).
### Cache-Timing Attacks
Monitor CPU cache behavior (FLUSH+RELOAD, PRIME+PROBE, port contention) to
observe which code paths and memory addresses a victim accesses during signing.
| "Ooh Aah" (Benger et al.) | OpenSSL secp256k1 | FLUSH+RELOAD on scalar mul | Full key from ~200 signatures | 2014 |
| CVE-2014-0076 | OpenSSL ECDSA | FLUSH+RELOAD on Montgomery ladder swap | Full key from ~2400 traces | 2014 |
| CVE-2018-5407 (PortSmash) | OpenSSL P-384 | Port contention on SMT/Hyper-Threading | Full P-384 key from TLS server | 2018 |
| LadderLeak | OpenSSL P-192/P-256/secp256k1 | FLUSH+RELOAD on Montgomery ladder | Key recovery from < 1 bit/signature | 2020 |
**Defense**: No secret-dependent memory accesses. Fixed-window scalar
multiplication with constant-time table lookups (touch all entries, select
via bitmask). Avoid wNAF representations that leak Hamming weight. Disable
SMT where cryptographic operations occur.
### EM Emanation Attacks
Measure EM radiation emitted during computation. Can work at distance (across
a wall) and breaks implementations that are "constant-time" in software.
| Genkin et al. | OpenSSL/CoreBitcoin on iOS | $2 magnetic probe near phone | Full ECDSA key from mobile device | 2016 |
| Genkin et al. | OpenSSL on PC | EM emanation from adjacent room | ECDH key extraction through a wall | 2016 |
| Nonce@Once | OpenSSL, Libgcrypt, HACL* | Single EM trace on cswap | 100% success, full nonce from 1 trace | 2021 |
**Key insight**: The Nonce@Once attack broke multiple supposedly constant-time
implementations (OpenSSL, Libgcrypt, HACL*, curve25519-donna) via
operand-dependent EM leakage in conditional swap operations. Software-only
"constant-time" is insufficient against EM probes.
**Defense**: Scalar and coordinate blinding (randomize intermediate values so
EM traces are uncorrelated with the secret). Hardware-level EM shielding and
noise injection. Randomized projective coordinates.
### Power Analysis Attacks
Measure a device's power consumption during cryptographic operations. Simple
Power Analysis (SPA) reads key bits from a single trace; Differential Power
Analysis (DPA) uses statistics over many traces.
| Coron | General double-and-add | SPA on power trace | Entire scalar from 1 trace | 1999 |
| Ark of the ECC | CrypTech FPGA P-256 | Open-source DPA via ChipWhisperer | Full key recovery | 2021 |
| Trezor RDP attack | Trezor One/Model T | Voltage glitching on STM32 | Full seed extraction ($75, 15 min) | 2020 |
**Defense**: Montgomery ladder or always-double-and-add (constant operation
sequence). Randomized projective coordinates. Scalar blinding. Secure elements
with built-in DPA countermeasures.
### Fault Injection Attacks
Deliberately induce computational errors (voltage glitching, clock
manipulation, laser pulses, Rowhammer) during signing. The faulty output
reveals information about the secret key.
| Biehl-Meyer-Muller | General ECC | Bit errors during scalar mul | Key recovery via weak-curve DLP | 2000 |
| Blomer-Otto-Seifert | General ECC | Sign change (negate y-coordinate) | Key recovery; point stays on curve | 2006 |
| Degenerate fault | OpenSSL secp256k1 | Instruction skip during point decompression | Single-fault full key recovery | 2019 |
| DFA on RFC 6979 | Deterministic ECDSA | Rowhammer during signing | Lattice-based key recovery | 2017 |
**secp256k1 note**: The degenerate fault attack is *especially devastating*
for secp256k1 (j-invariant = 0) — key recovery reduces to a single field
division after one faulty signature.
**Defense**: Hedged signatures ([draft-irtf-cfrg-det-sigs-with-noise](https://cfrg.github.io/draft-irtf-cfrg-det-sigs-with-noise/draft-irtf-cfrg-det-sigs-with-noise.html)) —
mix fresh randomness into RFC 6979 nonce generation. Verify output point is on
curve after scalar multiplication. Re-verify each signature before releasing it.
### Lattice Attacks on Biased Nonces
If ECDSA nonces have any statistical bias — even a few bits — an attacker
observing enough signatures formulates a Hidden Number Problem (HNP) and
solves it via lattice reduction (LLL/BKZ) to recover the private key.
| Biased Nonce Sense | Bitcoin blockchain (secp256k1) | Weak RNGs producing short nonces | Hundreds of real Bitcoin keys recovered | 2019 |
| Polynonce | Bitcoin/Ethereum (secp256k1) | LCG-based nonce generation | Key recovery in ~70ms on laptop | 2023 |
| Sony PS3 | Sony firmware signing | Same nonce `k` reused for every signature | Full firmware signing key published | 2010 |
**Quantitative thresholds** (Breitner & Heninger, 2019): For 256-bit ECDSA,
4 bits of nonce bias = completely broken. Two signatures with 128-bit nonces =
75% key recovery probability. Nonce reuse = instant key recovery from 2
signatures via `d = (s1*z2 - s2*z1) / (s2*r1 - s1*r2) mod n`.
**Defense**: RFC 6979 deterministic nonces (eliminates RNG dependency). Hedged
nonces for fault resistance. Never use LCGs or other weak PRNGs. Validate
nonces are in full range [1, n-1] with uniform distribution.
### Supply Chain Attacks
Compromise the software supply chain — backdoor a dependency, an RNG standard,
or a build process — so downstream users unknowingly ship compromised code.
| Dual_EC_DRBG | NIST SP 800-90A / RSA BSAFE | NSA-backdoored RNG standard | All crypto operations compromised | 2004-2014 |
| event-stream | npm / Copay Bitcoin wallet | Social-engineered maintainership | Targeted Bitcoin wallet theft | 2018 |
| CVE-2024-3094 (xz) | xz/liblzma / OpenSSH | 2-year trust-building, backdoor in build | RCE via SSH (caught pre-release) | 2024 |
| SolarWinds SUNBURST | SolarWinds Orion | Build process injection, signed with genuine cert | 18,000+ organizations compromised | 2020 |
**Defense**: Zero third-party dependencies (the cnfy approach). Reproducible
builds. Vendored and audited dependencies if external code is necessary.
CSPRNG from the OS (`getrandom()`) rather than userspace PRNGs.
### Implementation Bugs
Logical errors — missing validations, incorrect algorithms, arithmetic bugs —
that allow attacks without any physical or timing observation.
| CVE-2020-0601 (CurveBall) | Windows CryptoAPI | Generator point not validated in certs | Forge any code-signing or TLS cert | 2020 |
| CVE-2022-21449 (Psychic Signatures) | Java JDK 15-18 | `r=0, s=0` signature not rejected | Universal signature forgery | 2022 |
| CVE-2015-7940 | Bouncy Castle / SunEC | Missing point-on-curve check in ECDH | Full TLS key via invalid curve attack | 2015 |
| CVE-2024-48930 | secp256k1-node (npm) | Missing point validation in compressed key loading | Full key from 11 ECDH sessions | 2024 |
**Defense**: Validate every received point is on the curve (`Y^2 == X^3 + 7`
for secp256k1). Validate signature components `r, s` are in `[1, n-1]`.
Validate generator points match the expected standard. Test against
[Project Wycheproof](https://github.com/google/wycheproof) vectors.
---
## Modular Inversion Algorithm Comparison
| **Constant-time** | No | **Yes** | Yes (if exp is public) |
| **GPU-portable** | No (deeply branchy) | No (data-dependent branching) | **Yes** (branchless) |
| **Speed (256-bit field inv)** | **~802 ns** (cnfy benchmark) | ~8,690 ns (cnfy `--features ct-inv`) | ~10,250 ns (cnfy `pow_mod`) |
| **Operations** | Variable (Euclidean steps) | 724 divsteps, batched in groups of 62 = 12 outer iterations | 255 squarings + 14 multiplications = 269 field ops |
| **Why fast/slow** | Fewest total ops; early termination | More iterations but each divstep is cheap (shift+add); batching amortizes big-int work | Each op is a full field multiply; most total work |
| **ECDSA signing impact** | N/A (not constant-time, unsafe for signing) | **25-30% faster signing** vs Fermat (libsecp256k1 data) | Baseline for CT signing |
| **Batch-friendly** | Yes (Montgomery's trick) | Yes (Montgomery's trick) | Yes (Montgomery's trick) |
| **Rust implementations** | cnfy-uint `mod_inv` (default) | cnfy-uint `mod_inv` (`--features ct-inv`), crypto-bigint `SafeGcdInverter`, k256 crate | cnfy-uint `pow_mod` |
### SafeGCD (Bernstein-Yang divsteps) details
The algorithm transforms `(delta, f, g)` per step:
- 1 parity check (LSB of g), 1 sign check (delta), 1 conditional swap, 1 subtraction, 1 right-shift
- All constant-time via masked operations (no branches)
- **Batching**: 62 divsteps computed on low 62 bits (fitting in u64), producing a 2x2 transition matrix applied once to full-width 256-bit values
- **Iteration bound**: 724 divsteps proven sufficient for 256-bit (formally verified in Coq)
- **Outer loop**: ceil(724/62) = 12 iterations, each doing 4 full-width multiply-accumulates
---
## Recommendations by Use Case
### Operations on public data only
When all inputs are public (e.g., adding known generator points, batch
conversions of public keys), there is no side-channel risk. Default
operations are safe and preferred for speed.
| Point addition (public + public) | Use default implementation | No secret data; speed matters |
| Batch inversion (Montgomery) | Use default `mod_inv` | Amortized to ~46 ns/element; no secret |
### ECDSA signing
**All operations touching the private key or nonce must be constant-time.**
A single bit of nonce leakage per signature enables key recovery.
| Scalar multiplication (`k * G`) | **CT available**: 4-bit signed comb, 8-entry CT scan, 65-col loop (15.9 µs) | CT: fixed-window with dummy ops + CT table scan | `ct-scalar` |
| Modular inversion (`k^-1 mod N`) | **CT available**: SafeGCD, Bernstein-Yang divsteps (8.69 µs) | CT: SafeGCD or Fermat | `ct-inv` |
| Field arithmetic (`mul_mod`, `add_mod`) | **CT available**: branchless via `ct_select` | Already implemented; enable `ct-field` feature | `ct-field` |
| Nonce generation | Not yet implemented | Must use RFC 6979 deterministic nonces + hedging | — |
**cnfy's position**: With `--features ct`, cnfy now provides constant-time
paths for all arithmetic operations required for ECDSA signing: field ops,
modular inversion (SafeGCD), and scalar multiplication (4-bit signed comb
with Booth recoding and 8-entry CT table lookups). The remaining gap is nonce
generation (RFC 6979), which requires SHA-256 (planned in `cnfy-hash`).
### Key generation
| Scalar multiplication (`secret * G`) | **CT available** (15.9 µs) | CT scalar mul | `ct-scalar` |
| Random scalar sampling | Not yet implemented | CSPRNG + rejection sampling in CT | — |
### Input validation (all use cases)
| Point on curve (`Y^2 == X^3 + 7 mod P`) | Every received point | Invalid curve attacks (CVE-2015-7940, CVE-2024-48930) |
| Point not at infinity | Every received point | Degenerate inputs |
| Signature `r, s` in `[1, n-1]` | Every signature verification | Psychic Signatures (CVE-2022-21449) |
| Generator matches standard | Certificate/key file parsing | CurveBall (CVE-2020-0601) |
---
## Library Comparison
This table compares cnfy against production ECC libraries across every attack
category documented above. The goal is transparency: users should pick the
library that matches their threat model, even if that means choosing a
competitor.
**Legend**: Yes = protected, **No** = vulnerable, Partial = some protection,
N/A = feature not implemented / not applicable.
**Note**: The `secp256k1` Rust FFI crate (wrapping the C library) is **no longer
maintained**. Its developers recommend migrating to **k256**. It is included here
for reference since existing projects may still depend on it, but should not be
chosen for new projects.
### Side-channel protection
| CT scalar multiplication | **Yes** (`ct-scalar`: 4-bit signed comb, Booth recoding, 65-col branchless loop) | Yes (inherits C library) | Yes (fixed-window, CT table scan, unified add/double) | Yes (projective formulas, `subtle` crate CT select) | Yes (BoringSSL nistz256, CT) | Yes (post-2018 fixes, Montgomery ladder with CT swap) |
| CT table lookups | **Yes** (`ct-scalar`: 8-entry linear scan with ct_select per column) | Yes (inherits C library) | Yes (linear scan with CT select) | Yes (`subtle::ConditionallySelectable`) | Yes (CT table access) | Yes (post CVE-2014-0076 fix) |
| CT modular inversion | **Yes** (`ct-inv`: SafeGCD, Bernstein-Yang divsteps) | Yes (inherits C library) | Yes (SafeGCD, Bernstein-Yang divsteps) | Yes (SafeGCD via `crypto-bigint`) | Yes (Fermat inversion) | Partial (CT since CVE-2024-13176 fix; historically vulnerable) |
| CT field arithmetic | **Yes** (`ct-field`: branchless `add_mod`, `sub_mod`, `mul_mod_sparse`, `square_mod_sparse`, `sparse_reduce`, `modulo` via `ct_select`) | Yes (inherits C library) | Yes (branchless, constant memory access) | Yes (branchless via `subtle` crate) | Yes (BoringSSL fiat-crypto, formally verified) | Partial (improved over time; historical CVEs) |
| Scalar blinding | No | No | No | No | No | Yes (optional) |
| Coordinate randomization | No | No | No | No | No | Yes (optional) |
**cnfy's position**: With `--features ct`, cnfy provides constant-time paths
for all core signing operations: field arithmetic (`ct-field`), modular
inversion via SafeGCD (`ct-inv`), and scalar multiplication with 4-bit signed
comb and 8-entry CT table lookups (`ct-scalar`). The default remains the fastest option for
public-data operations. For ECDSA signing, enable `--features ct` (or the
specific granular features needed). The remaining gap for production ECDSA
is nonce generation (RFC 6979), planned for `cnfy-hash`.
### Input validation
| Point on curve (Y^2 = X^3 + 7) | Yes (`AffinePoint::is_on_curve`, `new_verified`, `CompressedPoint::y`) | Yes (inherits C library) | Yes (on all public API inputs) | Yes (on deserialization) | Yes | Yes |
| Point at infinity rejected | Yes (`Error::PointAtInfinity`, Z=0 check) | Yes (inherits C library) | Yes | Yes | Yes | Yes |
| Secret key range [1, n-1] | Yes (`SecretKey::is_valid_scalar`, CT comparison) | Yes (inherits C library) | Yes | Yes | Yes | Yes |
| Signature r,s in [1, n-1] | N/A (no ECDSA) | Yes (inherits C library) | Yes | Yes | Yes | Yes |
| Reject (r=0, s=0) signature | N/A (no ECDSA) | Yes (inherits C library) | Yes | Yes | Yes | Yes |
**cnfy's position**: Input validation is thorough. Deserialization paths
(`from_sec1_bytes`, `from_be_bytes`) always validate. Internal constructors
(`new`) skip validation by design — arithmetic on valid inputs produces valid
outputs. This matches the libsecp256k1 and k256 approach.
### Nonce security (ECDSA signing)
| RFC 6979 deterministic nonces | N/A (no ECDSA) | Yes (inherits C library) | Yes (default) | Yes (via `ecdsa` crate) | **No** (random nonces via SystemRandom) | Yes (opt-in since OpenSSL 3.2) |
| Hedged nonces (RFC 6979 + randomness) | N/A | Yes (inherits C library) | Yes (extra entropy parameter) | Yes (RFC 6979 + RNG) | Partial (random-only resists fault injection, but depends on RNG quality) | No |
**ring's position**: ring deliberately uses random nonces instead of RFC 6979.
This protects against fault injection on deterministic signing but creates
dependency on RNG quality. ring mitigates this by requiring `SystemRandom`
(OS CSPRNG), which is the strongest available RNG — but it cannot protect
against a compromised OS entropy source.
### Supply chain and code quality
| Third-party dependencies | **0** | ~3 (secp256k1-sys vendors C, cc, libc) | **0** (C library) | ~15 (crypto-bigint, subtle, elliptic-curve, etc.) | ~5 (minimal, mostly internal) | System library (C) |
| `unsafe` code | **None** | Yes (FFI layer to C) | N/A (C) | Minimal (via `subtle` crate) | Yes (assembly, FFI) | N/A (C) |
| Language | Pure Rust | Rust FFI wrapping C | C | Pure Rust | Rust + C/asm | C |
| Formal verification | No | No (inherits C library properties) | No (but exhaustive testing, Coq proof for SafeGCD bounds) | Partial (fiat-crypto for some field ops) | Partial (fiat-crypto, BoringSSL heritage) | No |
| Known ECC CVEs | 0 (new library) | 0 (wrapper layer) | ~1 (timing leak, fixed 2015) | 0 | 0 | 118 total; ~6 ECC-specific |
| Security audit | No | Inherits C library review | Extensively reviewed by Bitcoin Core contributors | Audited as part of RustCrypto ecosystem | Google security team review | Extensively audited; frequent CVEs still found |
| Actively maintained | Yes | **No** (deprecated, migrate to k256) | Yes (Bitcoin Core) | Yes (RustCrypto) | Yes (Google) | Yes (OpenSSL project) |
### Attack-by-attack vulnerability matrix
| **Timing (scalar mul)** | **Protected** (with `ct-scalar`) | Protected | Protected | Protected | Protected | Protected (post-2018) |
| **Timing (mod_inv)** | **Protected** (with `ct-inv`) | Protected (SafeGCD) | Protected (SafeGCD) | Protected (SafeGCD) | Protected (Fermat) | Mostly protected (post-2025) |
| **Timing (field ops)** | **Protected** (with `ct-field`) | Protected | Protected | Protected | Protected | Mostly protected |
| **Cache (table lookup)** | **Protected** (with `ct-scalar`) | Protected (CT scan) | Protected (CT scan) | Protected (`subtle`) | Protected | Protected (post-2014) |
| **Cache (PortSmash/SMT)** | **Protected** (with `ct-scalar`) | Protected | Protected | Protected | Protected | Protected (post-2018) |
| **EM emanation** | Vulnerable | Partially (no blinding) | Partially (no blinding) | Partially (no blinding) | Partially (no blinding) | Partially (optional blinding) |
| **Power analysis (SPA)** | Vulnerable | Partially (no blinding) | Partially (no blinding) | Partially (no blinding) | Partially (no blinding) | Partially (optional blinding) |
| **Fault injection** | N/A (no ECDSA) | Protected (hedged nonces) | Protected (hedged nonces) | Protected (hedged nonces) | Protected (random nonces) | Vulnerable (no hedged nonces) |
| **Biased nonces** | N/A (no ECDSA) | Protected (RFC 6979) | Protected (RFC 6979) | Protected (RFC 6979) | Protected (OS CSPRNG) | Protected (RFC 6979) |
| **Nonce reuse** | N/A (no ECDSA) | Protected (RFC 6979) | Protected (RFC 6979) | Protected (RFC 6979) | Protected (random) | Protected (RFC 6979) |
| **Supply chain** | **Protected** (0 deps) | Low risk (~3 deps, vendors C) | **Protected** (0 deps) | Exposed (~15 deps) | Low risk (~5 deps) | System-level risk |
| **Invalid curve (ECDH)** | Protected (point validation) | Protected | Protected | Protected | Protected | Protected |
| **Signature forgery (r=0,s=0)** | N/A (no ECDSA) | Protected | Protected | Protected | Protected | Protected |
### When to use each library
| **ECDSA signing (production, Rust)** | k256 or cnfy (`--features ct`) | k256: mature, audited, RFC 6979. cnfy: zero deps, CT (needs SHA-256 for nonces) |
| **ECDSA signing (Rust, existing secp256k1 FFI users)** | k256 (migrate) | secp256k1 Rust FFI is deprecated; k256 is the recommended replacement |
| **ECDSA signing (C/C++)** | libsecp256k1 | Gold standard, formally verified SafeGCD, hedged nonces |
| **High-throughput public-data operations** | cnfy | Fastest field arithmetic, batch operations, zero deps |
| **TLS / general-purpose crypto** | ring or OpenSSL | Broad algorithm support, well-tested |
| **Minimal supply chain risk** | cnfy or libsecp256k1 | Zero third-party dependencies |
| **Embedded / no-std** | cnfy or k256 | Pure Rust, no heap, no-std compatible |
---
## References
**Algorithms**:
- [Bernstein-Yang: "Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/safegcd-20190413.pdf)
- [libsecp256k1 safegcd implementation](https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md)
- [libsecp256k1 PR #831: SafeGCD inverses](https://github.com/bitcoin-core/secp256k1/pull/831)
- [Formal proof of safegcd bounds (Coq)](https://medium.com/blockstream/a-formal-proof-of-safegcd-bounds-695e1735a348)
- [Addition chains for ECC inversion](https://briansmith.org/ecc-inversion-addition-chains-01)
- [Hedged signatures (draft-irtf-cfrg-det-sigs-with-noise)](https://cfrg.github.io/draft-irtf-cfrg-det-sigs-with-noise/draft-irtf-cfrg-det-sigs-with-noise.html)
**Timing and cache attacks**:
- [CVE-2024-13176: OpenSSL P-521 timing leak](https://nvd.nist.gov/vuln/detail/CVE-2024-13176)
- [CVE-2019-15809: Athena smart card ECDSA timing](https://nvd.nist.gov/vuln/detail/CVE-2019-15809)
- [Minerva: timing attack on ECDSA nonces](https://minerva.crocs.fi.muni.cz/)
- ["Ooh Aah... Just a Little Bit" — FLUSH+RELOAD on OpenSSL ECDSA](https://eprint.iacr.org/2014/161.pdf)
- [CVE-2018-5407: PortSmash](https://github.com/bbbrumley/portsmash)
- [LadderLeak: Breaking ECDSA With Less Than One Bit Of Nonce Leakage](https://eprint.iacr.org/2020/615.pdf)
**EM and power analysis**:
- [ECDSA Key Extraction from Mobile Devices via Nonintrusive Physical Side Channels](https://cs-people.bu.edu/tromer/mobilesc/)
- [Nonce@Once: Single-Trace EM Attack on Constant-Time ECC](https://faculty.cc.gatech.edu/~genkin/papers/nonceatonce.pdf)
- [Ark of the ECC: Open-Source ECDSA Power Analysis](https://eprint.iacr.org/2021/1520)
**Fault injection**:
- [Biehl-Meyer-Muller: Differential Fault Attacks on ECC (CRYPTO 2000)](https://link.springer.com/chapter/10.1007/3-540-44598-6_8)
- [Blomer-Otto-Seifert: Sign Change Fault Attacks](https://eprint.iacr.org/2004/227)
- [Degenerate Fault Attacks on Elliptic Curve Parameters in OpenSSL](https://eprint.iacr.org/2019/400.pdf)
- [Attacking Deterministic Signature Schemes using Fault Attacks](https://eprint.iacr.org/2017/1014.pdf)
**Lattice attacks and biased nonces**:
- [Biased Nonce Sense: Lattice Attacks on Weak ECDSA in Cryptocurrencies](https://eprint.iacr.org/2019/023.pdf)
- [Polynonce: A Novel ECDSA Attack (DEF CON 31)](https://kudelskisecurity.com/research/polynonce-a-tale-of-a-novel-ecdsa-attack-and-bitcoin-tears)
**Implementation bugs**:
- [CVE-2020-0601: CurveBall / Windows CryptoAPI](https://unit42.paloaltonetworks.com/threat-brief-windows-cryptoapi-spoofing-vulnerability-cve-2020-0601/)
- [CVE-2022-21449: Psychic Signatures in Java](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/)
- [CVE-2015-7940: Invalid Curve Attacks on TLS-ECDH](https://link.springer.com/chapter/10.1007/978-3-319-24174-6_21)
- [CVE-2024-48930: secp256k1-node invalid curve attack](https://github.com/advisories/GHSA-584q-6j8j-r5pm)
- [Project Wycheproof test vectors](https://github.com/google/wycheproof)
**Supply chain**:
- [Dual_EC_DRBG backdoor](https://en.wikipedia.org/wiki/Dual_EC_DRBG)
- [event-stream npm attack post-mortem](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/)
- [CVE-2024-3094: xz Utils backdoor](https://www.crowdstrike.com/en-us/blog/cve-2024-3094-xz-upstream-supply-chain-attack/)
**Implementations**:
- [crypto-bigint SafeGcdInverter (Rust)](https://github.com/RustCrypto/crypto-bigint)
- [k256: pure Rust secp256k1 with CT inversion](https://docs.rs/k256)