🖊️

Digital Signatures

ECDSA · EdDSA · Schnorr · Threshold · Post-Quantum

From elliptic-curve mathematics through NIST FIPS 186-5 standards
to SystemVerilog hardware accelerators and Python implementations

Modern Cryptography Series — Presentation 06  |  ← / → to navigate  |  Esc overview

Roadmap

03–05 Foundations
Why sign? · Properties · Hash-then-sign paradigm · DSA family tree
06–12 Classical Schemes
ECDSA deep dive · RFC 6979 · Schnorr signatures · EdDSA / Ed25519 · Comparison
13 Advanced Protocols
Threshold & multi-sig (MuSig2, FROST) · Blind & ring signatures
14–15 Standards & Real-World
FIPS 186-5 · RFC 8032 · BIP 340 · TLS 1.3 · Blockchain
16–17 Hardware
ECDSA/EdDSA FPGA cores · ASIC accelerators · Side-channel countermeasures
18–29 Post-Quantum, Code & Practice
ML-DSA (FIPS 204) · SLH-DSA (FIPS 205) · Demo · Python · SystemVerilog · Performance · Failures

Why Digital Signatures?

A digital signature binds a message to a signer's identity with three guarantees:

🔒

Authentication

Only the holder of the private key can produce a valid signature for the associated public key.

🛡️

Integrity

Any modification to the signed message — even a single bit flip — invalidates the signature.

📜

Non-Repudiation

The signer cannot credibly deny having signed, since only their private key could have produced the signature.

Contrast with MACs: A Message Authentication Code (HMAC) provides authentication and integrity, but not non-repudiation — both parties share the same secret key, so either could have produced the tag.

Anatomy: Hash-then-Sign

All modern signature schemes follow the hash-then-sign paradigm:

Message m Hash SHA-256 / SHA-512 h = H(m) Sign(h, privKey) EC scalar mul + mod arithmetic Private Key d Signature (r, s) or (R, s)
Why hash first?
① Reduces arbitrary-length message to fixed-size digest
② Prevents existential forgery attacks on raw signing
③ Hides message structure from the EC arithmetic
Verification mirror:
Verifier recomputes h = H(m) and checks the signature against the public key. No private key needed — only the corresponding public key Q = d·G.

The DSA Family Tree

ElGamal Signature (1985) DSA — FIPS 186 (1994) Schnorr (1989/1991) ECDSA — ANSI X9.62 (1999) Deprecated for signing in FIPS 186-5 (2023) EdDSA — RFC 8032 (2017) BIP 340 Schnorr (2021) FIPS 186-5 (2023) + deterministic ECDSA Ed25519 Ed448
Key insight: All these schemes derive from ElGamal's 1985 framework. The core idea is the same — prove knowledge of a private scalar d by constructing a relationship between a random nonce point, the message hash, and the public key — but they differ in how the relationship is computed and what security proofs are available.

ECDSA: Key Generation

Given agreed-upon domain parameters (CURVE, G, n):

Private key:   d  ←  random integer in [1, n − 1]
Public key:   Q = d · G    (EC point multiplication — the trapdoor)

NIST P-256 (secp256r1)

Field: 𝔽p where p = 2256 − 2224 + 2192 + 296 − 1
Order: n ≈ 2256 (256-bit private keys)
Security: 128-bit (vs. 3072-bit RSA)
Usage: TLS 1.3, PIV cards, AWS KMS, FIDO2

secp256k1 (Bitcoin)

Field: 𝔽p where p = 2256 − 232 − 977
Order: n ≈ 2256
Cofactor: h = 1 (prime order, no subgroup issues)
Usage: Bitcoin, Ethereum, many blockchains

The ECDLP trapdoor: Computing Q = d·G is fast (tens of microseconds in software). Recovering d from Q requires solving the Elliptic Curve Discrete Logarithm Problem — infeasible for properly chosen curves (~2128 group operations with Pollard's rho).

ECDSA: Signing Algorithm

To sign message m with private key d:

Step 1.   e = H(m)    // e.g. SHA-256(m), truncated to bit-length of n
Step 2.   k ← random in [1, n−1]    // CRITICAL: must be fresh & secret per signature
Step 3.   (x₁, y₁) = k · G    // random curve point
Step 4.   r = x₁ mod n    // if r = 0, go back to Step 2
Step 5.   s = k⁻¹ · (e + r · d) mod n    // if s = 0, go back to Step 2

Signature = (r, s)    // two integers, each ≤ 256 bits for P-256
Nonce reuse = catastrophic key leak. If the same k is used for two different messages m₁, m₂ yielding signatures (r, s₁) and (r, s₂), an attacker can recover the private key:
k = (e₁ − e₂) · (s₁ − s₂)⁻¹ mod n  →  d = (s₁ · k − e₁) · r⁻¹ mod n
This exact flaw was exploited in the 2010 Sony PS3 signing key extraction, where k was static.

ECDSA: Verification Algorithm

Given message m, signature (r, s), and public key Q:

Step 1.   Check r, s ∈ [1, n−1]
Step 2.   e = H(m)
Step 3.   w = s⁻¹ mod n
Step 4.   u₁ = e · w mod n   ;    u₂ = r · w mod n
Step 5.   (x₁, y₁) = u₁ · G + u₂ · Q    // Shamir's trick for dual scalar mul
Step 6.   Accept if x₁ mod n = r

Why it works — the correctness proof

Substitute Q = d·G and s = k⁻¹(e + r·d) into the verification equation:

u₁·G + u₂·Q = (e·w)·G + (r·w)·(d·G) = w·(e + r·d)·G = (s⁻¹)·(s·k)·G = k·G

The x-coordinate of k·G is exactly r, so the check passes. ✓

Deterministic ECDSA — RFC 6979

Eliminates the random nonce k entirely, making signatures reproducible and safe from RNG failures.

k = HMAC-DRBG(private_key ‖ H(message))

How it works

① Compute message hash h₁ = H(m)
② Initialize HMAC-DRBG with seed = privKey ‖ h₁
③ Generate candidate k via HMAC-SHA256 iterations
④ If k ∈ [1, n−1] and r ≠ 0 and s ≠ 0, accept
⑤ Otherwise, reseed and retry

Properties

✓ Same (key, message) → same signature
✓ No RNG required at signing time
✓ k is unique per (key, message) pair
⚠ Approved in FIPS 186-5 § 6.3.2
⚠ Determinism can ease fault injection attacks

FIPS 186-5 status: Both randomized and deterministic ECDSA are approved. Deterministic is preferred for constrained devices without quality RNG; randomized is preferred when fault-injection resistance is critical.

Schnorr Signatures

Proposed by Claus-Peter Schnorr in 1989. Simpler, provably secure under the Random Oracle Model, and crucially — linear.

Sign(m, d):
k ← random in [1, n−1]
R = k · G
e = H(R ‖ P ‖ m)   // key-prefixed
s = k + e · d  mod n
σ = (R, s)   // or (rx, s) — 64 bytes
Verify(m, σ, P):
e = H(R ‖ P ‖ m)
Check: s · G = R + e · P

Advantages over ECDSA

✓ Provable security — reduces to ECDLP in ROM
✓ Linearity: s₁ + s₂ is valid for P₁ + P₂ (with nonce R₁ + R₂ and a shared challenge e)
→ enables native multi-signatures (MuSig2)
✓ Non-malleable — unique (R, s) per (m, d, k)
✓ Batch verification — verify n sigs faster than n × single
✓ Fixed 64-byte encoding (vs. DER 70-72 bytes for ECDSA)

BIP 340 (Bitcoin Taproot): Adopted Schnorr over secp256k1 with x-only 32-byte public keys, tagged hashes for domain separation, and implicit even Y coordinates.

EdDSA — Ed25519 & Ed448

A Schnorr variant on twisted Edwards curves, designed by Bernstein et al. (2011). Deterministic nonce, constant-time, misuse-resistant.

Key generation:
seed ← 32 random bytes
(a, prefix) = SHA-512(seed)
  a: clamp bits (clear 0,1,2,255; set 254)
A = a · B   // public key (32 bytes)
Sign(m, seed):
r = SHA-512(prefix ‖ m) mod ℓ   // deterministic nonce!
R = r · B
h = SHA-512(R ‖ A ‖ m) mod ℓ
S = (r + h · a) mod ℓ
σ = (R, S)   // 64 bytes
Verify:
h = SHA-512(R ‖ A ‖ m) mod ℓ
Check: S · B = R + h · A

Ed25519 Parameters

Curveedwards25519: −x² + y² = 1 + dx²y²
Field𝔽p, p = 2255 − 19
Order ℓ2252 + 27742…336 (≈252 bits)
Cofactorh = 8
HashSHA-512
Key size32 bytes private, 32 bytes public
Sig size64 bytes
Security~128-bit
Deterministic nonce: The nonce r is derived from hashing the private key prefix with the message. No RNG needed at signing time — immune to the PS3/Android Bitcoin nonce-reuse class of attacks.

Scheme Comparison

Property ECDSA EdDSA (Ed25519) Schnorr (BIP 340)
Curve familyWeierstrass (P-256, secp256k1)Twisted Edwards (ed25519, ed448)Weierstrass (secp256k1)
NonceRandom (or RFC 6979)Deterministic (always)Aux-randomized
Signature size70–72 bytes (DER) / 64 (raw)64 bytes64 bytes
Public key size33 bytes (compressed)32 bytes32 bytes (x-only)
Provable securityNot in standard modelROM (Schnorr-based)ROM
Signature malleabilityYes (s → n − s)NoNo
Batch verificationNot nativelyYesYes
Multi-signatureComplexPossibleNative (MuSig2, FROST)
Public key recoveryYes (save bandwidth)NoNo
FIPS approvedFIPS 186-5FIPS 186-5Not yet
Speed (sign, ed25519 ≈ 1.0×)~1.2×1.0×~1.1×

Multi-Signatures & Threshold Signing

Schnorr's linearity enables n-of-n and t-of-n signing protocols that produce a single standard signature.

MuSig2 (n-of-n)

① Each signer i has keypair (dᵢ, Pᵢ)
② Aggregate key: Pagg = Σ aᵢ · Pᵢ  (with key aggregation coefficients aᵢ to prevent rogue-key attacks)
③ Each signer provides two nonces per round (only 2 rounds!)
④ Partial signatures: sᵢ = kᵢ + e · aᵢ · dᵢ
⑤ Final: s = Σ sᵢ   → standard Schnorr sig (R, s)

Verifier sees single 64-byte signature, indistinguishable from solo Schnorr sig.

FROST (t-of-n)

Flexible Round-Optimized Schnorr Threshold signatures allow any t of n participants to produce a valid signature.

① Distributed key generation (DKG) — each party gets a share
② Signing requires only t signers (2-round protocol)
③ Result: standard Schnorr signature — verifier is unaware of threshold

USE CASE Custody wallets, institutional signing, key escrow, MPC-TSS

Blind & Ring Signatures

Blind signatures: Signer signs without seeing the message — used in anonymous credential schemes and e-cash (Chaum, 1983).
Ring signatures: Prove membership in a set without revealing which member signed — used in Monero (CryptoNote) for transaction privacy.

Standards Landscape

StandardAlgorithmsStatusKey Usage
FIPS 186-5 (2023) RSA (PKCS #1 v1.5 & PSS), ECDSA, EdDSA Current US federal, TLS, code signing, PKI
RFC 6979 Deterministic DSA/ECDSA Active Constrained devices, test reproducibility
RFC 8032 Ed25519, Ed25519ph, Ed448 Active OpenSSH, TLS 1.3, GnuPG, DNSSEC, Tor
SP 800-186 Curve parameters (P-256, P-384, ed25519, ed448) Current Companion to FIPS 186-5
BIP 340 Schnorr over secp256k1 Bitcoin Bitcoin Taproot (activated Nov 2021)
FIPS 204 (2024) ML-DSA (CRYSTALS-Dilithium) PQC Post-quantum replacement for ECDSA
FIPS 205 (2024) SLH-DSA (SPHINCS+) PQC Hash-based PQ signatures (conservative)
FIPS 186-5 key change: DSA is deprecated for new signatures (retained only for verification of legacy data). EdDSA (Ed25519, Ed448) added as FIPS-approved for the first time. Deterministic ECDSA explicitly approved per RFC 6979.

Where Signatures Live

🌐

TLS 1.3

Server certificates are signed with RSA or ECDSA (P-256/P-384); Ed25519 is allowed by TLS 1.3 but public CAs do not issue it. Certificate Transparency logs record every publicly trusted cert.

₿

Blockchain

Bitcoin: ECDSA (secp256k1) + Schnorr (Taproot). Ethereum: ECDSA with public key recovery (transactions carry no public key). Solana and Cosmos validators: Ed25519.

🔑

SSH / GPG

OpenSSH has supported Ed25519 since 6.5 (2014); ssh-keygen defaults to it since 9.5 (2023). GnuPG supports Ed25519 since 2.1. FIDO2/WebAuthn uses ECDSA P-256 for hardware security keys.

📱

Mobile / IoT

Apple Secure Enclave: ECDSA P-256. Android Keystore: ECDSA. ARM TrustZone: ECDSA/EdDSA via hardware crypto accelerators.

📦

Code Signing

Windows Authenticode, macOS codesign, Android APK signing, Linux kernel module signing — all use digital signatures to prevent supply-chain attacks.

🏛️

PKI / Certificates

X.509 certificate chains: Root CA → Intermediate CA → Leaf. Each link is a digital signature (RSA or ECDSA) binding an entity to its public key.

Hardware: ECDSA/EdDSA Accelerators

Digital signature operations are bottlenecks in TLS handshakes, blockchain validation, and IoT authentication. Hardware acceleration is essential.

ECDSA Accelerator Block Diagram Host / Bus Controller FSM SHA-256 Engine TRNG / DRBG Nonce gen Modular Arithmetic Unit Add / Sub Multiply Inversion Montgomery EC Point Arithmetic Point Add Point Double Scalar Mul (Montgomery Ladder) Key Storage

FPGA Results (Literature)

PlatformCurveSignVerifySlices
Virtex-5K-1630.27 ms0.54 ms18.5k
Virtex-6B-1630.8 ms0.4 ms~13k LUT
Alveo U250P-256—0.76 ms~50k LUT

ASIC Results

ProcessAreaFreqPower
45nm CMOS0.257 mm²532 MHz63 mW
45nm (ECC only)0.121 mm²990 MHz39 mW

Key bottleneck: EC scalar multiplication dominates ~90% of signing time. Montgomery ladder preferred for constant-time (SPA-resistant) operation.

Side-Channel Attacks & Countermeasures

Signature implementations leak secrets through timing, power, and electromagnetic emanations.

Attack Vectors

Timing attacks: Non-constant-time modular inversion or conditional branches during scalar multiplication reveal bits of k or d.

Simple Power Analysis (SPA): Single power trace distinguishes point-add (P+Q) from point-double (2P) in the double-and-add ladder.

Differential Power Analysis (DPA): Statistical correlation across many traces recovers key bits.

Electromagnetic (EM) attacks: Near-field probes capture leakage from on-chip wires.

Fault injection: Voltage glitches or laser pulses skip instructions or corrupt intermediate values, enabling Bellcore-style attacks on deterministic ECDSA.

Countermeasures

Constant-time code: No data-dependent branches or memory lookups. Ed25519 is designed for this — all operations are uniform.

Montgomery ladder: Always performs both add and double, eliminating SPA distinguishability.

Scalar blinding: Replace d with d + r·n (same result mod n, but randomized trace).

Point blinding: Add random point before scalar mul, subtract after.

Coordinate randomization: Multiply projective coordinates by random λ — changes power profile without affecting result.

Redundant computation: Dual-rail logic, error detection codes for fault resistance. ~20% area overhead.

Post-Quantum: ML-DSA & SLH-DSA

Shor's algorithm breaks ECDSA/EdDSA/Schnorr on a cryptographically relevant quantum computer. NIST has standardized replacements.

ML-DSA — FIPS 204 (2024)

Based on Module Learning With Errors (MLWE) lattice problem.
Derived from CRYSTALS-Dilithium submission.

Parameter SetSecurityPKSigSK
ML-DSA-44Cat. 2 (~128)1,312 B2,420 B2,560 B
ML-DSA-65Cat. 3 (~192)1,952 B3,309 B4,032 B
ML-DSA-87Cat. 5 (~256)2,592 B4,627 B4,896 B

Sign: ~100 µs (software). Verify: ~50 µs. Competitive with ECDSA for speed, but much larger keys and signatures.

SLH-DSA — FIPS 205 (2024)

Based on hash functions only (SPHINCS+).
Conservative assumption: only needs hash collision/preimage resistance.

Parameter SetSecurityPKSig
SLH-DSA-128s128-bit32 B7,856 B
SLH-DSA-128f128-bit32 B17,088 B
SLH-DSA-256s256-bit64 B29,792 B

Very slow signing (~100 ms) but tiny public keys. s = small sigs, f = fast signing.

Size penalty: An Ed25519 signature is 64 bytes. ML-DSA-44 is 2,420 bytes (38×). This is a major concern for bandwidth-constrained protocols (blockchains, IoT). Hybrid schemes combining classical + PQ signatures are being deployed during the transition.

Interactive: ECDSA Signing Simulator

Simplified ECDSA over a tiny curve for educational purposes. Uses a toy prime field.





Press "Sign" to generate an ECDSA signature...

Toy curve: y² = x³ + 5 mod 97, which has 79 points (a prime, so cofactor 1); generator G = (1, 43), order n = 79. Real ECDSA uses 256-bit primes.

Python: Ed25519 Signing


# Ed25519 via the pyca 'cryptography' package (pip install cryptography;
# not the standard library). public_bytes_raw() needs version 40 or later.
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

# Key generation
private_key = Ed25519PrivateKey.generate()
public_key  = private_key.public_key()

# Sign
message   = b"Digital signatures are provably secure"
signature = private_key.sign(message)  # 64 bytes, deterministic

print(f"Public key : {public_key.public_bytes_raw().hex()}")
print(f"Signature  : {signature.hex()}")
print(f"Sig length : {len(signature)} bytes")

# Verify
try:
    public_key.verify(signature, message)
    print("✓ Signature valid")
except Exception:
    print("✗ Signature INVALID")

# Tamper test
try:
    public_key.verify(signature, b"Tampered message!")
    print("✓ Signature valid")       # Should NOT reach here
except Exception:
    print("✗ Tampered message detected — signature invalid")
  

Python: ECDSA (P-256) Low-Level


from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes

# Key generation on NIST P-256
private_key = ec.generate_private_key(ec.SECP256R1())
public_key  = private_key.public_key()

# Extract private scalar d
d = private_key.private_numbers().private_value
print(f"Private key d: {d:#066x}")

# Sign: OpenSSL draws a random nonce k. For RFC 6979 use
# ec.ECDSA(hashes.SHA256(), deterministic_signing=True)  (v43+)
message   = b"ECDSA on NIST P-256"
signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))

print(f"Signature (DER): {signature.hex()}")
print(f"DER length: {len(signature)} bytes")  # usually 70-72

# Decode (r, s) from DER
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
r, s = decode_dss_signature(signature)
print(f"r = {r:#066x}")
print(f"s = {s:#066x}")

# Verify
public_key.verify(signature, message, ec.ECDSA(hashes.SHA256()))
print("✓ ECDSA signature verified")
  

SystemVerilog: Modular Inverse (1/3)

The modular inverse k⁻¹ mod n is the most expensive non-EC operation in ECDSA signing. Fermat's little theorem: k⁻¹ = kn−2 mod n.


module mod_inverse #(
    parameter int WIDTH = 256
)(
    input  logic                clk,
    input  logic                rst_n,
    input  logic                start,
    input  logic [WIDTH-1:0]    k,        // value to invert
    input  logic [WIDTH-1:0]    n,        // modulus (curve order)
    output logic [WIDTH-1:0]    k_inv,    // k^{-1} mod n
    output logic                done
);
    // Fermat's little theorem: k^{-1} = k^{n-2} mod n
    // Right-to-left square-and-multiply, one exponent bit per cycle

    logic [WIDTH-1:0] exp;       // exponent = n - 2
    logic [WIDTH-1:0] base_reg;  // current base (squared each cycle)
    logic [WIDTH-1:0] result;    // accumulator
    logic [$clog2(WIDTH)-1:0] bit_idx;
    logic running;

    assign exp = n - 2;

SystemVerilog: Modular Inverse (2/3)

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            done    <= 1'b0;
            running <= 1'b0;
        end else if (start) begin
            base_reg <= k;
            result   <= {{(WIDTH-1){1'b0}}, 1'b1};  // 1
            bit_idx  <= '0;
            running  <= 1'b1;
            done     <= 1'b0;
        end else if (running) begin
            // Square-and-multiply: check bit_idx of exponent
            if (exp[bit_idx])
                result <= mont_mul(result, base_reg, n);
            base_reg <= mont_mul(base_reg, base_reg, n);
            if (bit_idx == WIDTH - 1) begin
                running <= 1'b0;
                done    <= 1'b1;
            end
            bit_idx <= bit_idx + 1;
        end
    end

SystemVerilog: Modular Inverse (3/3)

    assign k_inv = result;

    // Behavioural modular multiply (simulation model). A real core uses a
    // Montgomery multiplier here: see Presentation 01 and deck 05.
    function automatic logic [WIDTH-1:0] mont_mul(
        input logic [WIDTH-1:0] a, b, m
    );
        logic [2*WIDTH-1:0] p;
        p = {{WIDTH{1'b0}}, a} * {{WIDTH{1'b0}}, b};  // full 2W-bit product
        return WIDTH'(p % {{WIDTH{1'b0}}, m});
    endfunction
endmodule
  

SystemVerilog: ECDSA Sign Top-Level (1/2)


module ecdsa_sign_top #(
    parameter int N = 256  // Curve order bit-width
)(
    input  logic             clk, rst_n, start,
    input  logic [N-1:0]     msg_hash,    // e = H(m), pre-computed
    input  logic [N-1:0]     priv_key,    // d
    input  logic [N-1:0]     nonce_k,     // k (from TRNG or RFC 6979)
    // curve_n, mont_mul, mont_add: config register / modular ALU (omitted)
    output logic [N-1:0]     sig_r,
    output logic [N-1:0]     sig_s,
    output logic             valid
);
    typedef enum logic [2:0] {
        IDLE, SCALAR_MUL, CALC_R, MOD_INV, CALC_S, DONE
    } state_t;

    state_t state;
    logic [N-1:0] kG_x;       // x-coord of k·G
    logic [N-1:0] k_inv;      // k^{-1} mod n
    logic ec_done, inv_done;

    // Sub-modules (instantiation ports abbreviated)
    ec_point_mul   #(.N(N)) u_pmul  (.*);  // k·G → (kG_x, kG_y)
    mod_inverse    #(.WIDTH(N)) u_inv(.*);  // k^{-1} mod n

SystemVerilog: ECDSA Sign Top-Level (2/2)

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin state <= IDLE; valid <= 1'b0; end
        else case (state)
            IDLE:       if (start) state <= SCALAR_MUL;
            SCALAR_MUL: if (ec_done)  state <= CALC_R;
            CALC_R: begin
                logic [N-1:0] r_v;
                r_v    = kG_x % curve_n;     // r = x₁ mod n
                sig_r <= r_v;               // test r_v, not the old sig_r
                state <= (r_v == 0) ? IDLE : MOD_INV;  // r = 0: new k
            end
            MOD_INV:    if (inv_done) state <= CALC_S;
            CALC_S: begin
                // s = k⁻¹ · (e + r·d) mod n
                logic [N-1:0] s_v;
                s_v    = mont_mul(k_inv,
                         mont_add(msg_hash, mont_mul(sig_r, priv_key)));
                sig_s <= s_v;
                state <= (s_v == 0) ? IDLE : DONE;     // s = 0: new k
            end
            DONE: begin valid <= 1'b1; state <= IDLE; end
        endcase
    end
endmodule
  

Performance Comparison

ImplementationAlgorithmSignVerifyThroughput
OpenSSL 3.x (x86-64)ECDSA P-256~55 µs~175 µs~18k sign/s
OpenSSL 3.x (x86-64)Ed25519~42 µs~115 µs~24k sign/s
libsodium (x86-64)Ed25519~32 µs~88 µs~31k sign/s
Bitcoin Core (x86-64)Schnorr secp256k1~48 µs~52 µs~19k verify/s
FPGA Virtex-5ECDSA K-163~270 µs~540 µs~1.8k sign/s
FPGA Alveo U250ECDSA P-256—~760 µs~1.3k verify/s
ASIC 45nmECDSA B-163~6 µs—~160k sign/s
Software (ref)ML-DSA-44~100 µs~50 µs~10k sign/s
ASIC PQ (12nm)ML-DSA-87~97 µs~58 µs~10k sign/s
Key takeaway: Software Ed25519 is the fastest classical scheme. ASICs can achieve 160k+ signatures/second for ECDSA. ML-DSA hardware is catching up — post-layout ASIC results show competitive latency with ~10× larger area than ECDSA accelerators.

Notable Attacks & Failures

Sony PS3 (2010)

Sony used a static nonce k for signing PS3 firmware updates with ECDSA. The fail0verflow group recovered the private key instantly using the nonce-reuse equation. Result: full PS3 jailbreak, homebrew code execution.

Android Bitcoin Wallets (2013)

Android's SecureRandom had a flaw producing insufficient entropy for ECDSA nonces. Multiple Bitcoin wallets generated duplicate k values, allowing attackers to extract private keys and steal funds.

ROCA (2017)

Infineon's RSA library (TPMs, smart cards) generated primes with a special structure that made the keys practical to factor. Though not ECDSA-specific, it shows how flawed key generation in hardware undermines any signature scheme. Estonian e-ID cards were vulnerable.

Minerva (2019)

Timing side-channel in a smart card's ECDSA implementation leaked the bit-length of each nonce through variable-time scalar multiplication. A few thousand signatures were enough to recover the private key via lattice attacks (Hidden Number Problem).

Lesson: The mathematics of ECDSA/EdDSA are sound. Nearly all real-world breaks exploit implementation failures: bad RNG, nonce reuse, non-constant-time code, or faulty hardware. EdDSA's deterministic nonce eliminates the largest class of these failures by design.

Key Takeaways

Core Concepts

▸ Digital signatures provide authentication, integrity, and non-repudiation
▸ All modern schemes use hash-then-sign over elliptic curve groups
▸ ECDSA, EdDSA, and Schnorr are all rooted in ElGamal's framework
▸ The ECDLP trapdoor: Q = d·G is easy; recovering d from Q is infeasible
▸ Nonce security is paramount — reuse leaks the private key

Recommendations

Best Ed25519 for new systems (fast, deterministic, misuse-resistant)
Good ECDSA P-256 for FIPS compliance and existing infrastructure
BTC Schnorr (BIP 340) for Bitcoin/Taproot ecosystems
Future ML-DSA for post-quantum readiness (FIPS 204)

Standards

▸ FIPS 186-5 (2023): ECDSA + EdDSA approved; DSA deprecated
▸ RFC 8032: Ed25519 & Ed448 specification
▸ RFC 6979: Deterministic ECDSA nonce generation
▸ FIPS 204/205 (2024): ML-DSA & SLH-DSA for post-quantum

Hardware

▸ EC scalar multiplication dominates ~90% of sign/verify time
▸ Montgomery ladder preferred for constant-time (SPA-resistant) operation
▸ ASIC 45nm achieves ~160k ECDSA signatures/second
▸ Post-quantum ML-DSA accelerators emerging (0.23 mm², 1.2 GHz)

References

Standards:
FIPS 186-5, Digital Signature Standard (2023)
NIST SP 800-186, Elliptic Curve Domain Parameters (2023)
RFC 6979, Deterministic DSA/ECDSA (2013)
RFC 8032, Edwards-Curve Digital Signature Algorithm (2017)
BIP 340, Schnorr Signatures for secp256k1 (2021)
FIPS 204, Module-Lattice-Based Digital Signature Standard (2024)
FIPS 205, Stateless Hash-Based Digital Signature Standard (2024)

Papers:
ElGamal, "A Public Key Cryptosystem…" IEEE ToIT (1985)
Schnorr, "Efficient Signature Generation…" J. Cryptology (1991)
Johnson, Menezes, Vanstone, "The ECDSA," IJIS (2001)
Bernstein et al., "High-speed high-security signatures," CHES (2011)
Nick, Ruffing, Seurin, "MuSig2," CRYPTO (2021)
Komlo, Goldberg, "FROST," SAC (2020); RFC 9591 (2024)
Ducas et al., "CRYSTALS-Dilithium," TCHES (2018)

Implementation:
libsodium — Ed25519 reference library
OpenSSL 3.x — ECDSA/EdDSA provider
Bitcoin Core — secp256k1 + Schnorr library
"Fast Hardware Implementation of ECDSA" — IEEE (2016)
"Lightweight HW Accelerator for PQ Digital Signature" — IACR ePrint (2022)