golang/go · error

invalid P256 compressed point encoding

Error message

invalid P256 compressed point encoding

What it means

Concrete P-256 instance of the generated compressed-point error (generate.go:223 expanded into p256.go:96). Thrown when decompressing a SEC1 compressed P-256 point (type byte 0x02/0x03, length 1+32) whose x-coordinate yields no valid y (x^3-3x+b not a quadratic residue mod the P-256 prime).

Source

Thrown at src/crypto/internal/fips140/nistec/p256.go:96

		if err := p256CheckOnCurve(x, y); err != nil {
			return nil, err
		}
		p.x.Set(x)
		p.y.Set(y)
		p.z.One()
		return p, nil

	// Compressed form.
	case len(b) == p256CompressedLength && (b[0] == 2 || b[0] == 3):
		x, err := new(fiat.P256Element).SetBytes(b[1:])
		if err != nil {
			return nil, err
		}

		// y² = x³ - 3x + b
		y := p256Polynomial(new(fiat.P256Element), x)
		if !p256Sqrt(y, y) {
			return nil, errors.New("invalid P256 compressed point encoding")
		}

		// Select the positive or negative root, as indicated by the least
		// significant bit, based on the encoding type byte.
		otherRoot := new(fiat.P256Element)
		otherRoot.Sub(otherRoot, y)
		cond := y.Bytes()[p256ElementLength-1]&1 ^ b[0]&1
		y.Select(otherRoot, y, int(cond))

		p.x.Set(x)
		p.y.Set(y)
		p.z.One()
		return p, nil

	default:
		return nil, errors.New("invalid P256 point encoding")
	}
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Reject malformed compressed P-256 points from untrusted sources with explicit error handling.
  2. Verify len(b) == 33 and b[0] in {2,3} before calling SetBytes.
  3. Re-serialize the point from a known-good P-256 point via BytesCompressed and compare.
  4. If decompressing from raw x, prefer the curve's own SetBytes to apply the constant-time validation.

Example fix

// before
p, err := nistec.NewP256Point().SetBytes(b) // b corrupted
// after
if len(b) != 33 || (b[0] != 2 && b[0] != 3) {
    return errors.New("not a compressed P-256 point")
}
p, err := nistec.NewP256Point().SetBytes(b)
if err != nil { return fmt.Errorf("off-curve: %w", err) }
Defensive patterns

Strategy: validation

Validate before calling

// Validate compressed P-256 shape before SetBytes.
if len(b) != 33 || (b[0] != 2 && b[0] != 3) {
    return errors.New("not a compressed P-256 point")
}

Type guard

func isCompressedP256(b []byte) bool {
    return len(b) == 33 && (b[0] == 2 || b[0] == 3)
}

Try / catch

p, err := nistec.NewP256Point().SetBytes(b)
if err != nil {
    return fmt.Errorf("P-256 decompression failed (len=%d): %w", len(b), err)
}

Prevention

When it happens

Trigger: Compressed P-256 point whose x is off-curve, an x byte-swapped or end-flipped, or a compressed encoding sized for a different curve passed to the P-256 parser.

Common situations: Fuzzed/adversarial public keys, cross-curve mix-ups, transit corruption, or a copy-paste of a P-384 compressed point.

Related errors


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