golang/go · error

invalid {{ .Element }} encoding

Error message

invalid {{ .Element }} encoding

What it means

Generated into each fiat-curve element type (P224/P256/P384/P521) from generate.go:219. Thrown by (*Element).SetBytes when the input is not exactly the curve's element length (ElementLen). This is the field-element parser rejecting wrong-size big-endian scalars/coordinates before any range check.

Source

Thrown at src/crypto/internal/fips140/nistec/fiat/generate.go:219

	// rather than happen on the heap.
	var out [{{ .Prefix }}ElementLen]byte
	return e.bytes(&out)
}

func (e *{{ .Element }}) bytes(out *[{{ .Prefix }}ElementLen]byte) []byte {
	var tmp {{ .Prefix }}NonMontgomeryDomainFieldElement
	{{ .Prefix }}FromMontgomery(&tmp, &e.x)
	{{ .Prefix }}ToBytes(out, (*{{ .Prefix }}UntypedFieldElement)(&tmp))
	{{ .Prefix }}InvertEndianness(out[:])
	return out[:]
}

// SetBytes sets e = v, where v is a big-endian {{ .BytesLen }}-byte encoding, and returns e.
// If v is not {{ .BytesLen }} bytes or it encodes a value higher than {{ .Prime }},
// SetBytes returns nil and an error, and e is unchanged.
func (e *{{ .Element }}) SetBytes(v []byte) (*{{ .Element }}, error) {
	if len(v) != {{ .Prefix }}ElementLen {
		return nil, errors.New("invalid {{ .Element }} encoding")
	}

	// Check for non-canonical encodings (p + k, 2p + k, etc.) by comparing to
	// the encoding of -1 mod p, so p - 1, the highest canonical encoding.
	var minusOneEncoding = new({{ .Element }}).Sub(
		new({{ .Element }}), new({{ .Element }}).One()).Bytes()
	if subtle.ConstantTimeLessOrEqBytes(v, minusOneEncoding) == 0 {
		return nil, errors.New("invalid {{ .Element }} encoding")
	}

	var in [{{ .Prefix }}ElementLen]byte
	copy(in[:], v)
	{{ .Prefix }}InvertEndianness(in[:])
	var tmp {{ .Prefix }}NonMontgomeryDomainFieldElement
	{{ .Prefix }}FromBytes((*{{ .Prefix }}UntypedFieldElement)(&tmp), &in)
	{{ .Prefix }}ToMontgomery(&e.x, &tmp)
	return e, nil
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Right-pad/truncate the value to exactly ElementLen bytes for the target curve before calling SetBytes.
  2. When converting from big.Int, use a fixed-size FillBytes call and check its returned length.
  3. Confirm the byte slice is raw big-endian, not hex/base64.
  4. Dispatch on curve ID and use the matching ElementLen constant.

Example fix

// before
x, err := new(fiat.P256Element).SetBytes(v) // v from big.Int.Bytes(), short by 1
// after
buf := make([]byte, fiat.P256ElementLen)
if !bi.FillBytes(buf) { return errors.New("value too large for P-256") }
x, err := new(fiat.P256Element).SetBytes(buf)
Defensive patterns

Strategy: validation

Validate before calling

// Pad to the curve's ElementLen before SetBytes.
buf := make([]byte, fiat.P256ElementLen)
if !bi.FillBytes(buf) {
    return errors.New("value too large for field")
}
e, err := new(fiat.P256Element).SetBytes(buf)

Type guard

func isCurveElementBytes(b []byte, elLen int) bool { return len(b) == elLen }

Try / catch

e, err := new(fiat.P256Element).SetBytes(v)
if err != nil {
    return fmt.Errorf("field element rejected (len=%d, want=%d): %w", len(v), fiat.P256ElementLen, err)
}

Prevention

When it happens

Trigger: Passing a 32-byte value to a P-521 element (66 bytes), a 31-byte value to P-256 (32 bytes), a hex string instead of raw bytes, or a value with a leading zero stripped.

Common situations: Cross-curve mix-ups (P-256 value fed to P-384 routine), big.Int.Bytes() that dropped a leading zero, un-decoded hex/base64, or assuming one curve's ElementLen for all curves.

Related errors


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