golang/go · critical
crypto/rsa: p == q
Error message
crypto/rsa: p == q
What it means
Thrown when computing |p - q| and both directions of SetBytes fail, which happens when p == q (neither fits into the other's modulus because they are equal and each equals the modulus of itself). Distinct primes are mandatory for RSA; identical factors make N = p^2 trivially factorable.
Source
Thrown at src/crypto/internal/fips140/rsa/rsa.go:298
return errors.New("crypto/rsa: d does not match dP")
}
dQ1 := bigmod.NewNat().Mod(priv.d, qMinus1)
if dQ1.Equal(dQ) != 1 {
return errors.New("crypto/rsa: d does not match dQ")
}
// Check that |p - q| > 2^(nlen/2 - 100).
//
// If p and q are very close to each other, then N=pq can be trivially
// factored using Fermat's factorization method. Broken RSA implementations
// do generate such keys. See Hanno Böck, Fermat Factorization in the Wild,
// https://eprint.iacr.org/2023/026.pdf.
diff := bigmod.NewNat()
if qP, err := bigmod.NewNat().SetBytes(q.Nat().Bytes(q), p); err != nil {
// q > p
pQ, err := bigmod.NewNat().SetBytes(p.Nat().Bytes(p), q)
if err != nil {
return errors.New("crypto/rsa: p == q")
}
// diff = 0 - p mod q = q - p
diff.ExpandFor(q).Sub(pQ, q)
} else {
// p > q
// diff = 0 - q mod p = p - q
diff.ExpandFor(p).Sub(qP, p)
}
// A tiny bit of leakage is acceptable because it's not adaptive, an
// attacker only learns the magnitude of p - q.
if diff.BitLenVarTime() <= N.BitLen()/2-100 {
return errors.New("crypto/rsa: |p - q| too small")
}
// Check that d > 2^(nlen/2).
//
// See section 3 of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf
// for more details about attacks on small d values.View on GitHub (pinned to b6b368adc5)
Solutions
- Regenerate the key with a generator that draws p and q independently.
- On import, reject keys where p.Cmp(q) == 0 before the fips path.
- Audit custom prime-generation code to ensure q is a fresh draw, not a copy of p.
Example fix
// before
// q accidentally set equal to p
// after
if p.Cmp(q) == 0 {
return errors.New("p and q must be distinct primes")
} Defensive patterns
Strategy: validation
Validate before calling
if p.Cmp(q) == 0 {
return errors.New("p and q must be distinct")
} Type guard
func primesDistinct(p, q *big.Int) bool { return p.Cmp(q) != 0 } Try / catch
err := validateKey(priv)
if err != nil && strings.Contains(err.Error(), "p == q") {
return err // regenerate with distinct primes
} Prevention
- Generate p and q with independent randomness draws.
- On import, reject p == q immediately.
- Audit custom generators to ensure q is not a copy of p.
When it happens
Trigger: The |p-q| Fermat-protection check: qP = SetBytes(q, p) fails, then pQ = SetBytes(p, q) also fails, indicating p == q. Reached during key validation after all CRT checks passed.
Common situations: A key generator that duplicated p into q. A test fixture that reuses the same prime twice. Manual key assembly that assigned p to both fields.
Related errors
- crypto/rsa: invalid prime
- crypto/rsa: p * q != n
- crypto/rsa: invalid CRT exponent
- crypto/rsa: invalid CRT coefficient
- crypto/rsa: d does not match dP
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/8d04681cd4663164.
Report an issue: GitHub.