{"record":{"id":"2745bc9afbafb9a5","repo":"golang/go","slug":"crypto-rsa-d-does-not-match-dp","errorCode":null,"errorMessage":"crypto/rsa: d does not match dP","messagePattern":"crypto/rsa: d does not match dP","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/crypto/internal/fips140/rsa/rsa.go","lineNumber":280,"sourceCode":"\tif de.IsOne() != 1 {\n\t\treturn errors.New(\"crypto/rsa: invalid CRT exponent\")\n\t}\n\n\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\")","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/rsa/rsa.go#L262-L298","documentation":"Thrown when d mod (p-1) does not equal the stored dP. The library recomputes the expected dP from d and compares; a mismatch means d and the CRT exponent dP disagree, so the key is internally inconsistent (even though dP may satisfy e*dP ≡ 1 mod p-1 on its own).","triggerScenarios":"Key validation's final consistency check: dP1 = d mod (p-1); dP1.Equal(dP) != 1. Reached after all prior CRT checks passed.","commonSituations":"d replaced/edited without recomputing dP. Key assembled from a d belonging to a different key than p. A partially-redacted or migrated key.","solutions":["Recompute dP = d mod (p-1) (and dQ = d mod (q-1)) whenever d changes, or re-import the whole key.","Regenerate the key pair.","Treat d, p, q, dP, dQ, qInv as atomic — never edit one without the others."],"exampleFix":"// before\n// d updated, dP left stale\n\n// after\npMinus1 := new(big.Int).Sub(p, big.NewInt(1))\ndP = new(big.Int).Mod(d, pMinus1)","handlingStrategy":"validation","validationCode":"pMinus1 := new(big.Int).Sub(p, big.NewInt(1))\nexpected := new(big.Int).Mod(d, pMinus1)\nif expected.Cmp(dP) != 0 {\n    return errors.New(\"d mod (p-1) != dP\")\n}","typeGuard":"func dMatchesDP(d, dP, p *big.Int) bool {\n    pMinus1 := new(big.Int).Sub(p, big.NewInt(1))\n    return new(big.Int).Mod(d, pMinus1).Cmp(dP) == 0\n}","tryCatchPattern":"err := validateKey(priv)\nif err != nil && strings.Contains(err.Error(), \"d does not match dP\") {\n    return err // recompute dP from d, or regenerate\n}","preventionTips":["Treat d, p, q, dP, dQ, qInv as one atomic unit.","Recompute dP and dQ whenever d changes.","Re-import the whole key rather than editing d alone."],"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"}