v3.60.0 — Audit Campaign Wave II · ZK Layer · Full GPU Parity · Wallet API · Performance
Release date: 2026-04-04
Previous release: v3.50.0 (2026-03-XX)
Commits since v3.50.0: 50+
ABI-compatible with v3.50.x — drop-in upgrade
Security & Correctness
-
ECDSA large-x fix (
cpu/src/ecdsa.cpp) — corrected r_less_than_pmn comparison in both FE52 and 4×64 paths. Wrong PMN constants assumed limb[2]=0; actual p−n has limb[2]=1. Signatures where k·G.x ∈ [n, p−1] (~2⁻¹²⁸ probability per sig) were incorrectly rejected. Equivalent to the Stark Bank CVE-2021-43568..43572 false-negative class. Found and confirmed by Wycheproof tcId 346. ([ea8cfb3c])
-
ECDSA r-overflow test suite (
audit/test_exploit_ecdsa_r_overflow.cpp) — 19 checks: k·G.x ≥ n accept case (tcId 346), r=p−3 strict-parse rejection, r=n zero-reduction reject, r=0 reject, range sanity and sign/verify consistency. Closes Wycheproof PR #206 / Stark Bank CVE assurance gap.
-
Wycheproof ECDSA Bitcoin vectors (
audit/test_wycheproof_ecdsa_bitcoin.cpp) — 53 checks: BIP-62 low-S enforcement, tcId 346/347/348/351, high-S malleability boundary, r=0/s=0 special-value rejection, point-at-infinity rejection during verify.
-
CUDA
jacobian_add_mixed_unchecked infinity flag — missing r->infinity = false in the normal code path caused generator table entries table[3..15] to carry uninitialized infinity flags. Scalars with many consecutive high nibbles (e.g. n−1) hit table[15] and produced wrong public keys. All 52/52 CUDA signing tests now pass.
-
ASan/UBSan clean: 210/210 C++ tests pass under
-fsanitize=address,undefined -fno-sanitize-recover=all after full rebuild.
Exploit PoC / Audit Coverage (Wave II)
| Suite |
Tests |
What it proves |
test_exploit_schnorr_nonce_reuse.cpp |
SNR-1..16 |
Nonce reuse → full privkey recovery via d' = (s1−s2)·(e1−e2)⁻¹ mod n; RFC6979 safety |
test_exploit_bip32_child_key_attack.cpp |
CKA-1..18 |
xpub + child_sk → parent_sk recovery; chained grandchild→child→master; hardened blockage |
test_exploit_frost_identifiable_abort.cpp |
FIA-1..14 |
frost_verify_partial() correctly attributes bad partial sigs; multi-cheater, honest subset |
test_exploit_hash_algo_sig_isolation.cpp |
HAS-1..11 |
Cross-hash confusion rejected; Schnorr↔ECDSA format confusion; domain prefix isolation |
test_exploit_zk_adversarial.cpp |
14 tests |
Malformed/forged ZK proofs: garbage bytes, scalar overflow, identity pubkey, 64-byte-flip |
test_exploit_pedersen_adversarial.cpp |
12 tests |
Switch commitment security, imbalanced verify_sum, double-spend detection |
test_exploit_ethereum_differential.cpp |
10 tests |
go-ethereum / web3.py / ethers.js KAT vectors, ecrecover, EIP-155/EIP-191, keccak256 |
test_fuzz_musig2_frost.cpp |
15 tests |
MuSig2 key_agg/nonce_agg/partial_verify; FROST keygen/sign/verify random inputs (5000+ rounds) |
test_wycheproof_ecdsa_bitcoin.cpp |
53 checks |
Wycheproof BIP-62 + large-x vectors |
test_exploit_ecdsa_r_overflow.cpp |
19 checks |
Wycheproof PR #206 r-overflow class |
| EIP-712 KAT |
12 tests |
Typed structured data, 13 assertions |
Python Dynamic Audit Suite (9 CTest targets)
All powered by --lib path/to/libufsecp.so, integrated in CI and unified_audit_runner:
| CTest target |
Checks |
What it catches |
py_differential_crossimpl |
1000+ |
Wrong low-S, pubkey parity bugs, ECDH mismatches (vs coincurve + python-ecdsa) |
py_nonce_bias |
10,000+ ops |
Chi-squared + KS + per-bit sweep (Minerva/TPM-FAIL-class biases) |
py_rfc6979_spec |
200+ |
Independent RFC 6979 §3.2 HMAC-SHA256 nonce derivation + Appendix A.2.5 KAT |
py_bip32_cka |
— |
Live BIP-32 parent key recovery demo + hardened immunity |
py_glv_exhaustive |
5000+ scalars |
GLV decomposition — adversarial Babai-boundary scalars vs coincurve reference |
py_semantic_props |
1450+ |
Algebraic properties (kG+lG==(k+l)G), roundtrip, determinism, Hypothesis |
py_invalid_input_grammar |
37 |
Structured rejection — bad prefix, x≥p, sk=0/n, r=0/s=0, invalid BIP-32 paths |
py_stateful_sequences |
401+ |
Error-injection recovery, BIP-32 multi-level consistency, 5000-op endurance |
py_dev_bug_scan |
221 files |
15-category static scanner: NULL, CPASTE, SIG, RETVAL, MSET, OB1, ZEROIZE, … |
-
ClusterFuzzLite expanded to 5 targets: added
fuzz_ecdsa.cpp (sign→verify invariant, wrong-msg, compact parse) and fuzz_schnorr.cpp (BIP-340 sign→verify, adversarial from_bytes).
Performance
| Path |
Before |
After |
Delta |
| CUDA ECDSA Sign (w=8 generator table) |
220.9 ns |
198.3 ns |
−10.2% |
| CUDA/OpenCL/Metal MSM (GLV Shamir w=1 scatter) |
baseline |
+18–24% |
+18–24% |
| ARM64 ECDSA Sign (SHA-2 HW accel) |
25.89 µs |
22.22 µs |
−14.2% |
| ARM64 Schnorr Sign (precomputed) |
17.73 µs |
16.67 µs |
−6.0% |
| Bulletproof MSM verifier |
5,079 µs |
2,634 µs |
−48% (1.93×) |
| CPU KPlan zero-alloc (stack wnaf arrays) |
heap |
stack |
alloc eliminated |
| BIP-352 SHA-256 tag midstate |
per-call |
precomputed |
hash call eliminated |
precompute (scalar_mul_generator) |
2 heap allocs |
0 |
zero-alloc hot path |
Zero-Knowledge Proof Layer (new)
-
Knowledge proofs — non-interactive Schnorr PoK, Fiat-Shamir with tagged SHA-256, no trusted setup.
-
DLEQ proofs — discrete log equality, batch-verify capable.
-
Bulletproof range proofs — 64-bit range, MSM-optimized verifier (Pippenger + Montgomery batch inversion).
-
GPU ZK: CUDA CT kernels (
ct_zk.cuh), OpenCL (secp256k1_zk.cl), Metal (kernels 19–22), all batch-capable.
-
5 new GPU C ABI functions:
ufsecp_gpu_zk_knowledge_verify_batch, ufsecp_gpu_zk_dleq_verify_batch, ufsecp_gpu_bulletproof_verify_batch, ufsecp_gpu_bip324_aead_encrypt_batch, ufsecp_gpu_bip324_aead_decrypt_batch.
-
24 tests in
test_zk.cpp; 8.5 benchmarks in bench_unified.
Full GPU Parity (zero Unsupported stubs)
-
Bulletproof on OpenCL + Metal: removed
#if 0 guard in secp256k1_zk.cl; fixed address-space qualifiers; wired bulletproof_verify_batch on both backends. CUDA ↔ OpenCL ↔ Metal parity complete.
-
4 new OpenCL kernels wired:
zk_knowledge_verify_batch, zk_dleq_verify_batch, bip324_aead_encrypt_batch, bip324_aead_decrypt_batch.
-
4 Metal kernels connected: same 4 operations — kernels existed but dispatch was unwired.
-
Metal ZK fix:
zk_knowledge_verify_batch was treating pubkey buffer as a scalar; corrected to lift_x to recover full point.
-
CUDA 13 compatibility: replaced deprecated
cudaDeviceProp::clockRate / ::memoryClockRate with cudaDeviceGetAttribute. Backward-compatible with CUDA 12. (RTX 5080 / CUDA 13 reported by @craigraw)
Wallet API & Address Formats (new)
-
Unified Wallet API (
wallet.hpp/wallet.cpp) — chain-agnostic key management, address generation, message signing, pubkey recovery — Bitcoin, Ethereum, Tron, and all 28 coins from a single wallet:: namespace.
-
BIP-39 Mnemonic (
bip39.hpp/bip39.cpp) — entropy→mnemonic (12–24 words), validation, PBKDF2-HMAC-SHA512 seed derivation. 57 tests.
-
Bitcoin message signing (
message_signing.hpp) — BIP-137/Electrum compatible: sign, verify, recover, Base64.
-
P2SH-P2WPKH (nested SegWit, BIP-49) —
3... addresses.
-
P2SH and P2WSH primitives.
-
CashAddr (BIP-0185) —
bitcoincash:q... addresses.
-
Tron (TRX) coin — coin_type=195,
0x41 prefix + Keccak-256 + Base58Check. Now 28 coins total.
Bindings
-
Stable validation closure across 11 language bindings: C#, Java, Swift, Python, Go, Rust, Node.js, PHP, Ruby, Dart, React Native. Fixed wrapper/API drift, zero-length FFI buffer edge cases, Dart
NativeFinalizer, local Dart smoke-runner.
Audit Coverage Summary
| Surface |
Count |
C ABI functions (ufsecp_*) |
155 |
GPU C ABI functions (ufsecp_gpu_*) |
23 |
| Unified audit runner modules |
70 |
| Python CTest audit targets |
9 |
| ClusterFuzz targets |
5 |
| Exploit PoC test files |
13 |
| Active GPU Unsupported stubs |
0 |
CI/CD
- All jobs green on: Linux (GCC + Clang), macOS (arm64 + amd64), Windows, Android (ARM64), RISC-V, ARM64 cross, ROCm, CUDA.
- Fixed: macOS
externally-managed-environment, Windows Unicode cp1252, Python-order CMake embed, ASan/MSan/TSan py_* symbol issue, SonarCloud coincurve missing.
- 10 CodeQL code-scanning alerts resolved.
Full diff: v3.50.0...v3.60.0
Top comments (0)