{"record":{"id":"b7beab25b9e1f370","repo":"golang/go","slug":"mldsa-semi-expanded-private-key-inconsistent-with","errorCode":null,"errorMessage":"mldsa: semi-expanded private key inconsistent with t0","messagePattern":"mldsa: semi-expanded private key inconsistent with t0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/mldsa/semiexpanded.go","lineNumber":104,"sourceCode":"\t//  > return values that are not in the correct range. Hence, skDecode\n\t//  > should only be run on inputs that come from trusted sources.\n\t//\n\t// so it sounds like it doesn't even want us to check the coefficients are\n\t// within bounds, but especially if using this format for key exchange, that\n\t// sounds like a bad idea. So we check everything.\n\n\tt1 := make([][n]uint16, k, maxK)\n\tfor i := range k {\n\t\ttHat := priv.s2[i]\n\t\tfor j := range l {\n\t\t\ttHat = polyAdd(tHat, nttMul(A[i*l+j], priv.s1[j]))\n\t\t}\n\t\tt := inverseNTT(tHat)\n\t\tfor j := range n {\n\t\t\tr1, r0 := power2Round(t[j])\n\t\t\tt1[i][j] = r1\n\t\t\tif r0 != t0[i][j] {\n\t\t\t\treturn nil, errors.New(\"mldsa: semi-expanded private key inconsistent with t0\")\n\t\t\t}\n\t\t}\n\t}\n\n\tpk := pkEncode(priv.pub.raw[:0], ρ[:], t1, p)\n\tif computePublicKeyHash(pk) != tr {\n\t\treturn nil, errors.New(\"mldsa: semi-expanded private key inconsistent with public key hash\")\n\t}\n\tcomputeT1Hat(priv.t1[:k], t1) // NTT(t₁ ⋅ 2ᵈ)\n\n\treturn priv, nil\n}\n\nfunc TestingOnlyPrivateKeySemiExpandedBytes(priv *PrivateKey) []byte {\n\tk, l, η := priv.pub.p.k, priv.pub.p.l, priv.pub.p.η\n\tsk := make([]byte, 0, semiExpandedPrivKeySize(priv.pub.p))\n\tsk = append(sk, priv.pub.raw[:32]...) // ρ\n\tsk = append(sk, priv.k[:]...)         // K","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/mldsa/semiexpanded.go#L86-L122","documentation":"When reconstructing a private key from the semi-expanded form, the library recomputes the public polynomial t1 from s1/s2 and uses power2Round to derive t0; if the derived t0 does not match the t0 supplied in the byte blob, the key is internally inconsistent and errInvalidSignature-style 'inconsistent with t0' is returned. It is a cross-check that guards against a corrupted or hand-edited semi-expanded key.","triggerScenarios":"TestingOnlyNewPrivateKeyFromSemiExpanded with bytes where the embedded s1/s2 and t0 regions do not correspond (one was edited without the other).","commonSituations":"Test vectors assembled by hand instead of from a conformant key; bytes mutated between semi-expanded encoding and decoding; mixing regions from two different keys.","solutions":["Regenerate the semi-expanded bytes from a key produced by NewPrivateKey* via TestingOnlyPrivateKeySemiExpandedBytes.","Use official NIST/ACVP test vectors verbatim rather than hand-built ones.","If mutating test data intentionally, re-encode through the round-trip helper to keep regions consistent."],"exampleFix":"// before\npriv, err := mldsa.TestingOnlyNewPrivateKeyFromSemiExpanded(editedBytes)\n\n// after\nref, _ := mldsa.NewPrivateKey44(seed)\npriv, err := mldsa.TestingOnlyNewPrivateKeyFromSemiExpanded(\n    mldsa.TestingOnlyPrivateKeySemiExpandedBytes(ref))","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := mldsa.TestingOnlyNewPrivateKeyFromSemiExpanded(sk); err != nil {\n    // regenerate the blob from a known-good key instead of patching it\n    ref, _ := mldsa.NewPrivateKey44(seed)\n    sk = mldsa.TestingOnlyPrivateKeySemiExpandedBytes(ref)\n}","preventionTips":["Never hand-edit individual regions of a semi-expanded key.","Regenerate blobs from NewPrivateKey* output through the round-trip helper.","Use untouched NIST/ACVP vectors for conformance tests."],"tags":["crypto","mldsa","fips","testing","consistency"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}