golang/go · error

negative coordinate

Error message

negative coordinate

What it means

Thrown by pointFromAffine (ecdsa.go:625) when either the X or Y coordinate of an ECDSA PublicKey has a negative sign. ECDSA point coordinates are field elements represented as non-negative big.Int values; a negative value indicates corruption or misuse of math/big (e.g. a subtraction that went negative, or an externally-constructed key with bogus coordinates).

Source

Thrown at src/crypto/ecdsa/ecdsa.go:625

	if size > maxScalarSize {
		return nil, errors.New("ecdsa: internal error: curve size too large")
	}
	D := priv.D.FillBytes(make([]byte, size, maxScalarSize))

	return privateKeyCache.Get(priv, func() (*ecdsa.PrivateKey, error) {
		return ecdsa.NewPrivateKey(c, D, Q)
	}, func(k *ecdsa.PrivateKey) bool {
		return subtle.ConstantTimeCompare(k.PublicKey().Bytes(), Q) == 1 &&
			subtle.ConstantTimeCompare(k.Bytes(), D) == 1
	})
}

// pointFromAffine is used to convert the PublicKey to a nistec SetBytes input.
func pointFromAffine(curve elliptic.Curve, x, y *big.Int) ([]byte, error) {
	bitSize := curve.Params().BitSize
	// Reject values that would not get correctly encoded.
	if x.Sign() < 0 || y.Sign() < 0 {
		return nil, errors.New("negative coordinate")
	}
	if x.BitLen() > bitSize || y.BitLen() > bitSize {
		return nil, errors.New("overflowing coordinate")
	}
	// Encode the coordinates and let [ecdsa.NewPublicKey] reject invalid points.
	byteLen := (bitSize + 7) / 8
	buf := make([]byte, 1+2*byteLen)
	buf[0] = 4 // uncompressed point
	x.FillBytes(buf[1 : 1+byteLen])
	y.FillBytes(buf[1+byteLen : 1+2*byteLen])
	return buf, nil
}

// pointToAffine is used to convert a nistec Bytes encoding to a PublicKey.
func pointToAffine(curve elliptic.Curve, p []byte) (x, y *big.Int, err error) {
	if len(p) == 1 && p[0] == 0 {
		// This is the encoding of the point at infinity.
		return nil, nil, errors.New("ecdsa: public key point is the infinity")

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Always reduce coordinates with x.Mod(x, P) (where P is the field prime) before building the PublicKey so the sign is non-negative.
  2. Load public keys only through x509.ParsePKIXPublicKey / cryptobyte ASN.1 parsing rather than constructing PublicKey{X,Y} directly.
  3. Validate pub.X.Sign() >= 0 && pub.Y.Sign() >= 0 before invoking ecdsa.Verify*.

Example fix

// before
x := new(big.Int).Sub(a, b)        // may be negative
pub := ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}
ok := ecdsa.Verify(&pub, hash, r, s) // -> error 241 via pointFromAffine

// after
P := elliptic.P256().Params().P
x := new(big.Int).Sub(a, b)
x.Mod(x, P) // now in [0, P)
pub := ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}
Defensive patterns

Strategy: validation

Validate before calling

if pub.X.Sign() < 0 || pub.Y.Sign() < 0 {
    return errors.New("public key coordinates must be non-negative")
}

Type guard

func nonNegativeCoords(pub *ecdsa.PublicKey) bool {
    return pub != nil && pub.X != nil && pub.Y != nil && pub.X.Sign() >= 0 && pub.Y.Sign() >= 0
}

Prevention

When it happens

Trigger: Reached via privateKeyToFIPS (line 592) and parsePublicKey (line 582) — any Sign/Verify path that converts a PublicKey to the FIPS nistec representation. Triggered when pub.X.Sign() < 0 || pub.Y.Sign() < 0, e.g. coordinates produced by ModInverse/Sub without a final Mod into the field, or hand-built keys with negative components.

Common situations: Custom curve arithmetic that uses big.Int subtraction without taking a positive modulus; importing coordinates from JSON/ASN.1 that allowed a leading sign bit; porting code from a library that permits signed coordinates.

Related errors


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