golang/go · error

sha3: invalid hash state

Error message

sha3: invalid hash state

What it means

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.

Source

Thrown at src/crypto/internal/fips140/sha3/sha3.go:199

	case dsbyteShake:
		b = append(b, magicShake...)
	case dsbyteCShake:
		b = append(b, magicCShake...)
	case dsbyteKeccak:
		b = append(b, magicKeccak...)
	default:
		panic("unknown dsbyte")
	}
	// rate is at most 168, and n is at most rate.
	b = append(b, byte(d.rate))
	b = append(b, d.a[:]...)
	b = append(b, byte(d.n), byte(d.state))
	return b, nil
}

func (d *Digest) UnmarshalBinary(b []byte) error {
	if len(b) != marshaledSize {
		return errors.New("sha3: invalid hash state")
	}

	magic := string(b[:len(magicSHA3)])
	b = b[len(magicSHA3):]
	switch {
	case magic == magicSHA3 && d.dsbyte == dsbyteSHA3:
	case magic == magicShake && d.dsbyte == dsbyteShake:
	case magic == magicCShake && d.dsbyte == dsbyteCShake:
	case magic == magicKeccak && d.dsbyte == dsbyteKeccak:
	default:
		return errors.New("sha3: invalid hash state identifier")
	}

	rate := int(b[0])
	b = b[1:]
	if rate != d.rate {
		return errors.New("sha3: invalid hash state function")
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Verify len(b) == marshaledSize before calling; if not, discard and re-hash from the source.
  2. Tag persisted hash state with its algorithm family (sha3-256 vs shake128 vs cshake) so the right UnmarshalBinary is invoked.
  3. For SHAKE state, call UnmarshalBinary on the *SHAKE type (which expects the trailing initBlock), not on the inner Digest.
  4. Use io.ReadFull and check the returned error when loading the buffer from disk/network.

Example fix

// before
b, _ := os.ReadFile("state.bin")
digest.UnmarshalBinary(b) // size mismatch -> error
// after
b, err := os.ReadFile("state.bin")
if err != nil || len(b) != marshaledSize { rehash() }
digest.UnmarshalBinary(b)
Defensive patterns

Strategy: validation

Validate before calling

if len(b) != marshaledSize {
    return fmt.Errorf("sha3 state size mismatch: got %d want %d", len(b), marshaledSize)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/4955051dee621dc5. Report an issue: GitHub.