DSA (Digital Signature Algorithm)¶
A discrete-log signature scheme from the 1990s. Important to understand, but deprecated — new systems should sign with Ed25519 or ECDSA.
What it is¶
DSA is a U.S. federal standard for digital signatures, proposed by NIST in 1991. It signs and verifies but cannot encrypt. Its security rests on the discrete logarithm problem — the same hard problem behind Diffie-Hellman.
Status: FIPS 186-5 (2023) withdrew DSA for signing. It survives in old protocols and keys, so it's worth recognizing, but don't choose it for anything new. ECDSA is the elliptic-curve version of the same idea with much smaller keys, and Ed25519 improves on both.
How it works¶
Everyone shares three domain parameters: a large prime p, a prime q dividing
p − 1, and a generator g of order q.
- Keys: private
xis random in[1, q−1]; publicy = g^x mod p. - Sign message
m: pick a fresh randomk, thenr = (g^k mod p) mod qands = k⁻¹ (H(m) + x·r) mod q. The signature is(r, s). - Verify: with
w = s⁻¹ mod q, check that(g^(H(m)·w) · y^(r·w) mod p) mod q == r.
The k problem¶
The per-signature secret k is DSA's sharp edge. If k is ever reused for
two messages, or even slightly predictable, anyone can solve for the private
key x from the public signatures. This is exactly how the Sony PlayStation 3
signing key leaked. Modern practice derives k deterministically from the
message and key (RFC 6979), and Ed25519 bakes that in by design —
see Why does randomness matter.
When to use it¶
- New systems: don't. Use Ed25519 (preferred) or ECDSA.
- Legacy interop only — verifying old signatures or reading existing keys.
Example (Python)¶
Core sign/verify, assuming the domain parameters and keys already exist. Prime generation lives in the Miller-Rabin page.
from hashlib import sha256
from secrets import randbelow
def sign(msg, p, q, g, x):
h = int.from_bytes(sha256(msg).digest(), "big") % q
while True:
k = 1 + randbelow(q - 1) # MUST be fresh and unpredictable
r = pow(g, k, p) % q
s = pow(k, -1, q) * (h + x * r) % q
if r and s:
return r, s
def verify(msg, sig, p, q, g, y):
r, s = sig
if not (0 < r < q and 0 < s < q):
return False
h = int.from_bytes(sha256(msg).digest(), "big") % q
w = pow(s, -1, q)
u1, u2 = h * w % q, r * w % q
return (pow(g, u1, p) * pow(y, u2, p) % p) % q == r
Security notes¶
- Reusing or biasing
kleaks the private key — the single most important rule. - Parameters should be 2048-bit
pwith 256-bitqif you must use DSA at all; the old 1024/160 sizes are too small now. - DSA signs a hash, so the hash matters too — use SHA-256, not SHA-1.
See also¶
- ECDSA — the same scheme on elliptic curves, smaller keys
- Ed25519 — modern replacement, deterministic
k - Diffie-Hellman — shares the discrete-log foundation
References¶
- FIPS 186-5: Digital Signature Standard (DSS) — withdraws DSA for signing
- RFC 6979: Deterministic Usage of DSA and ECDSA