{"record":{"id":"4955051dee621dc5","repo":"golang/go","slug":"sha3-invalid-hash-state","errorCode":null,"errorMessage":"sha3: invalid hash state","messagePattern":"sha3: invalid hash state","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/sha3/sha3.go","lineNumber":199,"sourceCode":"\tcase dsbyteShake:\n\t\tb = append(b, magicShake...)\n\tcase dsbyteCShake:\n\t\tb = append(b, magicCShake...)\n\tcase dsbyteKeccak:\n\t\tb = append(b, magicKeccak...)\n\tdefault:\n\t\tpanic(\"unknown dsbyte\")\n\t}\n\t// rate is at most 168, and n is at most rate.\n\tb = append(b, byte(d.rate))\n\tb = append(b, d.a[:]...)\n\tb = append(b, byte(d.n), byte(d.state))\n\treturn b, nil\n}\n\nfunc (d *Digest) UnmarshalBinary(b []byte) error {\n\tif len(b) != marshaledSize {\n\t\treturn errors.New(\"sha3: invalid hash state\")\n\t}\n\n\tmagic := string(b[:len(magicSHA3)])\n\tb = b[len(magicSHA3):]\n\tswitch {\n\tcase magic == magicSHA3 && d.dsbyte == dsbyteSHA3:\n\tcase magic == magicShake && d.dsbyte == dsbyteShake:\n\tcase magic == magicCShake && d.dsbyte == dsbyteCShake:\n\tcase magic == magicKeccak && d.dsbyte == dsbyteKeccak:\n\tdefault:\n\t\treturn errors.New(\"sha3: invalid hash state identifier\")\n\t}\n\n\trate := int(b[0])\n\tb = b[1:]\n\tif rate != d.rate {\n\t\treturn errors.New(\"sha3: invalid hash state function\")\n\t}","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/sha3/sha3.go#L181-L217","documentation":"First line of defense in sha3 Digest.UnmarshalBinary: if the input length is not exactly marshaledSize, the state cannot be parsed. The sha3 marshaled layout is fixed-length (magic + rate byte + 200-byte Keccak state + n + state byte), so any size mismatch is rejected up front before the magic is even inspected.","triggerScenarios":"Calling UnmarshalBinary with truncated, padded, or over-length SHA-3 state bytes; passing SHAKE-128 state to a plain SHA-3 digest (the SHAKE wrapper carries an extra initBlock suffix beyond marshaledSize and is handled by shake.go, not here); passing bytes from a non-SHA-3 source.","commonSituations":"Storing sha3 state and SHAKE state in the same column without tagging; a transport that fragments/reassembles the buffer incorrectly; reading the buffer with io.ReadFull that hit EOF early without the caller noticing.","solutions":["Verify len(b) == marshaledSize before calling; if not, discard and re-hash from the source.","Tag persisted hash state with its algorithm family (sha3-256 vs shake128 vs cshake) so the right UnmarshalBinary is invoked.","For SHAKE state, call UnmarshalBinary on the *SHAKE type (which expects the trailing initBlock), not on the inner Digest.","Use io.ReadFull and check the returned error when loading the buffer from disk/network."],"exampleFix":"// before\nb, _ := os.ReadFile(\"state.bin\")\ndigest.UnmarshalBinary(b) // size mismatch -> error\n// after\nb, err := os.ReadFile(\"state.bin\")\nif err != nil || len(b) != marshaledSize { rehash() }\ndigest.UnmarshalBinary(b)","handlingStrategy":"validation","validationCode":"if len(b) != marshaledSize {\n    return fmt.Errorf(\"sha3 state size mismatch: got %d want %d\", len(b), marshaledSize)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Tag persisted SHA-3 state with the variant (sha3-256 vs shake128 vs cshake).","For SHAKE state use the SHAKE wrapper's UnmarshalBinary, not the inner Digest's.","Load state with io.ReadFull and surface EOF as a re-hash trigger."],"tags":["crypto","hash","sha3","serialization","go-stdlib"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}