{"record":{"id":"4184438e0a2ee7e2","repo":"golang/go","slug":"crypto-rsa-d-does-not-match-dq","errorCode":null,"errorMessage":"crypto/rsa: d does not match dQ","messagePattern":"crypto/rsa: d does not match dQ","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/crypto/internal/fips140/rsa/rsa.go","lineNumber":284,"sourceCode":"\t// Check that qInv * q ≡ 1 mod p.\n\tqP, err := bigmod.NewNat().SetOverflowingBytes(q.Nat().Bytes(q), p)\n\tif err != nil {\n\t\t// q >= 2^⌈log2(p)⌉\n\t\tqP = bigmod.NewNat().Mod(q.Nat(), p)\n\t}\n\tif qP.Mul(priv.qInv, p).IsOne() != 1 {\n\t\treturn errors.New(\"crypto/rsa: invalid CRT coefficient\")\n\t}\n\n\t// Check d against dP and dQ, even though we never actually use d,\n\t// to make sure the key is consistent.\n\tdP1 := bigmod.NewNat().Mod(priv.d, pMinus1)\n\tif dP1.Equal(dP) != 1 {\n\t\treturn errors.New(\"crypto/rsa: d does not match dP\")\n\t}\n\tdQ1 := bigmod.NewNat().Mod(priv.d, qMinus1)\n\tif dQ1.Equal(dQ) != 1 {\n\t\treturn errors.New(\"crypto/rsa: d does not match dQ\")\n\t}\n\n\t// Check that |p - q| > 2^(nlen/2 - 100).\n\t//\n\t// If p and q are very close to each other, then N=pq can be trivially\n\t// factored using Fermat's factorization method. Broken RSA implementations\n\t// do generate such keys. See Hanno Böck, Fermat Factorization in the Wild,\n\t// https://eprint.iacr.org/2023/026.pdf.\n\tdiff := bigmod.NewNat()\n\tif qP, err := bigmod.NewNat().SetBytes(q.Nat().Bytes(q), p); err != nil {\n\t\t// q > p\n\t\tpQ, err := bigmod.NewNat().SetBytes(p.Nat().Bytes(p), q)\n\t\tif err != nil {\n\t\t\treturn errors.New(\"crypto/rsa: p == q\")\n\t\t}\n\t\t// diff = 0 - p mod q = q - p\n\t\tdiff.ExpandFor(q).Sub(pQ, q)\n\t} else {","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/rsa/rsa.go#L266-L302","documentation":"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.","triggerScenarios":"Key validation's d/dQ consistency check: dQ1 = d mod (q-1); dQ1.Equal(dQ) != 1. Reached after the d/dP check.","commonSituations":"d changed without recomputing dQ. d sourced from a different key than q. Partial key migration/merge.","solutions":["Recompute dQ = d mod (q-1) whenever d changes, or re-import the key atomically.","Regenerate the key pair.","Keep d, p, q, dP, dQ, qInv consistent as a unit."],"exampleFix":"// before\n// d updated, dQ left stale\n\n// after\nqMinus1 := new(big.Int).Sub(q, big.NewInt(1))\ndQ = new(big.Int).Mod(d, qMinus1)","handlingStrategy":"validation","validationCode":"qMinus1 := new(big.Int).Sub(q, big.NewInt(1))\nexpected := new(big.Int).Mod(d, qMinus1)\nif expected.Cmp(dQ) != 0 {\n    return errors.New(\"d mod (q-1) != dQ\")\n}","typeGuard":"func dMatchesDQ(d, dQ, q *big.Int) bool {\n    qMinus1 := new(big.Int).Sub(q, big.NewInt(1))\n    return new(big.Int).Mod(d, qMinus1).Cmp(dQ) == 0\n}","tryCatchPattern":"err := validateKey(priv)\nif err != nil && strings.Contains(err.Error(), \"d does not match dQ\") {\n    return err // recompute dQ from d, or regenerate\n}","preventionTips":["Recompute dQ whenever d changes.","Never migrate d between keys without recomputing CRT params.","Round-trip the whole key through a standard parser."],"tags":["crypto","rsa","key-validation","crt","go"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}