golang/go · error

crypto/sha1: invalid hash state identifier

Error message

crypto/sha1: invalid hash state identifier

What it means

Returned by sha1Hash.UnmarshalBinary in the BoringCrypto SHA-1 backend when the marshalled blob does not begin with the magic bytes "sha\x01". This is the Go encoding.BinaryUnmarshalser contract used by hash.Hash; the magic identifies which algorithm/version produced the state so it cannot be loaded into the wrong hasher.

Source

Thrown at src/crypto/internal/boring/sha.go:182

}

func (h *sha1Hash) AppendBinary(b []byte) ([]byte, error) {
	d := (*sha1Ctx)(unsafe.Pointer(&h.ctx))
	b = append(b, sha1Magic...)
	b = byteorder.BEAppendUint32(b, d.h[0])
	b = byteorder.BEAppendUint32(b, d.h[1])
	b = byteorder.BEAppendUint32(b, d.h[2])
	b = byteorder.BEAppendUint32(b, d.h[3])
	b = byteorder.BEAppendUint32(b, d.h[4])
	b = append(b, d.x[:d.nx]...)
	b = append(b, make([]byte, len(d.x)-int(d.nx))...)
	b = byteorder.BEAppendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
	return b, nil
}

func (h *sha1Hash) UnmarshalBinary(b []byte) error {
	if len(b) < len(sha1Magic) || string(b[:len(sha1Magic)]) != sha1Magic {
		return errors.New("crypto/sha1: invalid hash state identifier")
	}
	if len(b) != sha1MarshaledSize {
		return errors.New("crypto/sha1: invalid hash state size")
	}
	d := (*sha1Ctx)(unsafe.Pointer(&h.ctx))
	b = b[len(sha1Magic):]
	b, d.h[0] = consumeUint32(b)
	b, d.h[1] = consumeUint32(b)
	b, d.h[2] = consumeUint32(b)
	b, d.h[3] = consumeUint32(b)
	b, d.h[4] = consumeUint32(b)
	b = b[copy(d.x[:], b):]
	b, n := consumeUint64(b)
	d.nl = uint32(n << 3)
	d.nh = uint32(n >> 29)
	d.nx = uint32(n) % 64
	return nil
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Confirm the blob was produced by MarshalBinary on the same SHA-1 implementation (same BoringCrypto setting) in the same Go version.
  2. Use sha1.New() fresh and re-hash the data instead of round-tripping state across builds/versions.
  3. Validate the magic prefix ("sha\x01") and length before calling UnmarshalBinary.

Example fix

// before
h := sha1.New()
h.(encoding.BinaryUnmarshalser).UnmarshalBinary(stateFromOtherBuild)
// after
h := sha1.New()
h.Write(data) // recompute instead of cross-loading state
Defensive patterns

Strategy: validation

Validate before calling

func isValidSHA1State(state []byte) bool {
    const magic = "sha\x01"
    return len(state) >= len(magic) && string(state[:len(magic)]) == magic
}
// call before h.(encoding.BinaryUnmarshalser).UnmarshalBinary(state)

Type guard

// n/a

Try / catch

if err := h.(encoding.BinaryUnmarshalser).UnmarshalBinary(state); err != nil {
    // treat as unrecoverable; recompute the hash from source data
}

Prevention

When it happens

Trigger: Calling h.(encoding.BinaryUnmarshalser).UnmarshalBinary(state) on a sha1.New() hash with state bytes that do not start with "sha\x01", are empty, or were produced by a different hash (e.g. sha256.MarshalBinary output) or a non-BoringCrypto sha1 implementation.

Common situations: Cross-build portability: state marshalled by the standard (non-Boring) crypto/sha1 uses a different magic and cannot be unmarshalled by the BoringCrypto build, or vice versa. Also triggered by truncation, corruption, or feeding a SHA-256/512 blob into a SHA-1 hasher.

Related errors


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