golang/go · critical

crypto/rsa: d does not match dQ

Error message

crypto/rsa: d does not match dQ

What it means

Mirror of the dP check for q: d mod (q-1) must equal the stored dQ. The library recomputes dQ1 = d mod (q-1) and compares; a mismatch means d and the CRT exponent dQ disagree, so the key is internally inconsistent.

Source

Thrown at src/crypto/internal/fips140/rsa/rsa.go:284

	// Check that qInv * q ≡ 1 mod p.
	qP, err := bigmod.NewNat().SetOverflowingBytes(q.Nat().Bytes(q), p)
	if err != nil {
		// q >= 2^⌈log2(p)⌉
		qP = bigmod.NewNat().Mod(q.Nat(), p)
	}
	if qP.Mul(priv.qInv, p).IsOne() != 1 {
		return errors.New("crypto/rsa: invalid CRT coefficient")
	}

	// Check d against dP and dQ, even though we never actually use d,
	// to make sure the key is consistent.
	dP1 := bigmod.NewNat().Mod(priv.d, pMinus1)
	if dP1.Equal(dP) != 1 {
		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 {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Recompute dQ = d mod (q-1) whenever d changes, or re-import the key atomically.
  2. Regenerate the key pair.
  3. Keep d, p, q, dP, dQ, qInv consistent as a unit.

Example fix

// before
// d updated, dQ left stale

// after
qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
dQ = new(big.Int).Mod(d, qMinus1)
Defensive patterns

Strategy: validation

Validate before calling

qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
expected := new(big.Int).Mod(d, qMinus1)
if expected.Cmp(dQ) != 0 {
    return errors.New("d mod (q-1) != dQ")
}

Type guard

func dMatchesDQ(d, dQ, q *big.Int) bool {
    qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
    return new(big.Int).Mod(d, qMinus1).Cmp(dQ) == 0
}

Try / catch

err := validateKey(priv)
if err != nil && strings.Contains(err.Error(), "d does not match dQ") {
    return err // recompute dQ from d, or regenerate
}

Prevention

When it happens

Trigger: Key validation's d/dQ consistency check: dQ1 = d mod (q-1); dQ1.Equal(dQ) != 1. Reached after the d/dP check.

Common situations: d changed without recomputing dQ. d sourced from a different key than q. Partial key migration/merge.

Related errors


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