Deterministic vs randomized ECDSA is about one value: the per-signature nonce k. Its quality decides whether the private key stays private, because one repeated nonce or a slight bias across many signatures is enough to compute the key. FIPS 186-5 allows two ways to produce it, and a third, hedged signing, combines them.
At a glance
| Randomized | Deterministic (RFC 6979) | Hedged | |
|---|---|---|---|
| Nonce source | Fresh CSPRNG output per signature | HMAC-DRBG over private key and message hash | Deterministic derivation mixed with fresh randomness |
| Standard | FIPS 186-5 §6.3.1, Appendix A.3.1 and A.3.2 | RFC 6979 §3.2; FIPS 186-5 §6.3.2 and Appendix A.3.3 | RFC 6979 §3.6 option; expired CFRG draft; BIP-340 aux randomness |
| Same message signed twice | Different signatures | Identical signatures | Different signatures |
| Weak or broken RNG | Key can leak | Not affected | Not affected |
| Fault injection on repeated signing | Hard to exploit | Can leak the key | Hard to exploit |
| Reproducible test vectors | No | Yes (RFC 6979 Appendix A.2) | Only with fixed extra input |
| Verifier changes | None | None | None |
| Default in | OpenSSL, Node.js, Java, Python cryptography | libsecp256k1, @noble/curves | Opt-in: @noble/curves extraEntropy: true, BIP-340 signing |
How each one fails
Randomized nonces depend completely on the random number generator. If it repeats a value (Sony's PlayStation 3 used a constant), the key falls to simple algebra. If it is merely biased, for example because the top bits are always zero or a timing side channel leaks the nonce length, lattice techniques solving the hidden number problem recover the key from a few hundred to a few thousand signatures; Minerva and TPM-Fail (2019) did exactly that against smart cards and TPMs. Embedded devices that sign shortly after boot, cloned virtual machines and forked processes are the classic risk environments.
Deterministic nonces remove the generator from the picture: k is derived with HMAC from the private key and the message hash, so the same input always yields the same nonce, and different messages yield unrelated nonces. The risk moves to physical attacks. If an attacker can glitch the device while it signs the same message twice, one correct and one faulty signature with the same nonce can reveal the key; Poddebniak and colleagues demonstrated such fault attacks against deterministic ECDSA and EdDSA in 2018. Deterministic signing also makes signatures repeatable, which some protocols want and others must not leak.
Hedged nonces feed both the deterministic inputs and fresh randomness into the derivation. A broken generator degrades the scheme to deterministic signing, and a fault attack faces a different nonce each time. RFC 6979 §3.6 already allows additional input, BIP-340 recommends 32 bytes of auxiliary randomness, ML-DSA (FIPS 204 §3.4) is hedged by default, and an IRTF draft to standardize hedged ECDSA and EdDSA expired in 2025 without becoming an RFC.
All three produce ordinary signatures. A verifier cannot distinguish them, which also means you cannot audit a signer's nonce strategy from its output, except by noticing that identical messages produce identical signatures.
What libraries do
- OpenSSL signs with random nonces by default. OpenSSL 3.2 (November 2023) added RFC 6979 through the signature parameter
nonce-typeset to 1, which requires an explicit digest, and OpenSSL 3.6 brought deterministic ECDSA to its FIPS provider. On the command line:openssl pkeyutl -sign -pkeyopt nonce-type:1 -pkeyopt digest:sha256. - Python
cryptographyis randomized by default and has offeredec.ECDSA(hashes.SHA256(), deterministic_signing=True)since version 43.0 (July 2024). - Node.js
crypto.sign()and WebCrypto use random nonces with no option to change it. - @noble/curves derives
kwith RFC 6979 by default and hedges when called withextraEntropy: trueor extra bytes. - libsecp256k1, used by Bitcoin Core, uses RFC 6979 with optional extra data.
- EdDSA is deterministic by definition (RFC 8032); some platform implementations add randomness anyway.
The difference is visible in a few lines of Python with cryptography 46.0.3:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
key = ec.generate_private_key(ec.SECP256R1())
message = b"invoice 2026-10-20 #4411"
randomized = [key.sign(message, ec.ECDSA(hashes.SHA256())) for _ in range(2)]
deterministic = [
key.sign(message, ec.ECDSA(hashes.SHA256(), deterministic_signing=True)) for _ in range(2)
]
print("randomized signatures equal: ", randomized[0] == randomized[1])
print("deterministic signatures equal:", deterministic[0] == deterministic[1])
for sig in randomized + deterministic:
key.public_key().verify(sig, message, ec.ECDSA(hashes.SHA256()))
print("all four verify with the same verifier")
randomized signatures equal: False
deterministic signatures equal: True
all four verify with the same verifier
Testing and reproducibility
Deterministic signing makes known-answer tests possible: RFC 6979 Appendix A.2 lists expected signatures for P-192 through P-521 and other curves, and the test vectors tool and Project Wycheproof build on such vectors. With randomized or hedged signing you can only test that signatures verify and that malformed ones do not. For audits, deterministic mode is also how you check a suspicious signer: sign the same message twice and see whether the output repeats.
To detect actual nonce reuse in signatures you already have, compare r values across signatures from one key; a repeated r means a repeated nonce, and the nonce reuse checker does that for a batch.
When to choose which
- Servers and CI with a healthy OS generator: any of the three is acceptable; prefer deterministic or hedged to remove a dependency.
- Embedded devices, early-boot code, cloned VMs: deterministic or hedged, never purely random.
- Hardware exposed to attackers (hardware wallets, smart cards, payment terminals): hedged, together with fault countermeasures such as verifying each signature before releasing it.
- Test suites and interoperability work: deterministic, so vectors are reproducible.
- Protocols where repeated identical signatures would leak information (for example that the same message was signed again): randomized or hedged.
Migration notes
Switching nonce strategy changes only the signer. Keys, signature formats and verifiers stay the same.
- Check library support and versions: OpenSSL 3.2 or later,
cryptography43 or later, or a library with RFC 6979 built in. - Make sure the digest is set explicitly when enabling RFC 6979, since the derivation depends on it.
- Add a regression test that signs one message twice and asserts the expected behavior (equal for deterministic, different for hedged).
- Keep randomness healthy anyway: hedged signing still benefits from a good generator, and key generation always needs one.
- Review any place where signatures are compared or cached, because deterministic signatures make "same input, same output" true for the first time.
References
- RFC 6979, Deterministic Usage of DSA and ECDSA (§3.2, §3.6, Appendix A.2)
- FIPS 186-5, §6.3 per-message secret number
- FIPS 204, §3.4 hedged and deterministic signing
- draft-irtf-cfrg-det-sigs-with-noise (expired Internet-Draft)
- BIP-340, default signing with auxiliary randomness
- Minerva: lattice attacks on ECDSA nonce leakage
- OpenSSL CHANGES (3.2.0: RFC 6979 support)
- pyca/cryptography changelog (43.0.0: deterministic ECDSA)