{"record":{"id":"ee8d4bce7b31d329","repo":"golang/go","slug":"mlkem-invalid-encoding-length","errorCode":null,"errorMessage":"mlkem: invalid encoding length","messagePattern":"mlkem: invalid encoding length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/mlkem/field.go","lineNumber":166,"sourceCode":"\tout, B := sliceForAppend(b, encodingSize12)\n\tfor i := 0; i < n; i += 2 {\n\t\tx := uint32(f[i]) | uint32(f[i+1])<<12\n\t\tB[0] = uint8(x)\n\t\tB[1] = uint8(x >> 8)\n\t\tB[2] = uint8(x >> 16)\n\t\tB = B[3:]\n\t}\n\treturn out\n}\n\n// polyByteDecode decodes the 384-byte encoding of a polynomial, checking that\n// all the coefficients are properly reduced. This fulfills the \"Modulus check\"\n// step of ML-KEM Encapsulation.\n//\n// It implements ByteDecode₁₂, according to FIPS 203, Algorithm 6.\nfunc polyByteDecode[T ~[n]fieldElement](b []byte) (T, error) {\n\tif len(b) != encodingSize12 {\n\t\treturn T{}, errors.New(\"mlkem: invalid encoding length\")\n\t}\n\tvar f T\n\tfor i := 0; i < n; i += 2 {\n\t\td := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16\n\t\tconst mask12 = 0b1111_1111_1111\n\t\tvar err error\n\t\tif f[i], err = fieldCheckReduced(uint16(d & mask12)); err != nil {\n\t\t\treturn T{}, errors.New(\"mlkem: invalid polynomial encoding\")\n\t\t}\n\t\tif f[i+1], err = fieldCheckReduced(uint16(d >> 12)); err != nil {\n\t\t\treturn T{}, errors.New(\"mlkem: invalid polynomial encoding\")\n\t\t}\n\t\tb = b[3:]\n\t}\n\treturn f, nil\n}\n\n// sliceForAppend takes a slice and a requested number of bytes. It returns a","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/mlkem/field.go#L148-L184","documentation":"ML-KEM (FIPS 203) encodes each polynomial as 384 bytes via ByteDecode/ByteEncode₁₂ (twelve bits per coefficient, q=3329, 256 coefficients). polyByteDecode checks that the input slice is exactly encodingSize12 bytes before reading; otherwise it returns 'invalid encoding length'. This is the first validation a public key or ciphertext polynomial undergoes during decapsulation.","triggerScenarios":"polyByteDecode is called (transitively, via NewDecapsulationKey*, Encapsulate, Decapsulate, or the testing-only NIST parser) with a slice whose length is not 384.","commonSituations":"Truncating a key/ciphertext blob; base64/PEM envelope not stripped; slicing the wrong number of polynomials out of an outer structure; using an ML-KEM-768/512 blob where ML-KEM-1024 expects more polynomials.","solutions":["Decode base64/PEM and assert the total length equals the variant's expected public-key/ciphertext size before parsing.","Slice outer blobs by the variant's polynomial counts (k=2/3/4) so each 384-byte block is whole.","Round-trip test serialization to catch off-by-one slicing in the parser."],"exampleFix":"// before\ndk, err := mlkem.NewDecapsulationKey1024(shortSeed)  // wrong API / wrong size\n\n// after\nb, _ := base64.StdEncoding.DecodeString(b64)\nif len(b)%mlkem.EncodingSize12 != 0 { return ErrBadKey }\ndk, err := mlkem.NewDecapsulationKey1024(b)","handlingStrategy":"validation","validationCode":"if len(b)%mlkem.EncodingSize12 != 0 || len(b) == 0 {\n    return ErrBadPolynomialEncoding\n}","typeGuard":"func isWholePolynomialBlock(b []byte) bool { return len(b)%384 == 0 && len(b) > 0 }","tryCatchPattern":null,"preventionTips":["Decode base64/PEM and assert total length before parsing ML-KEM blobs.","Slice outer structures by the variant's polynomial count (k=2/3/4).","Round-trip test serialization to catch off-by-one slicing."],"tags":["crypto","mlkem","fips","validation","input-length"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}