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

  1. Regenerate the key with a generator that draws p and q independently.
  2. On import, reject keys where p.Cmp(q) == 0 before the fips path.
  3. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/8d04681cd4663164. Report an issue: GitHub.