golang/go · critical

zero parameter

Error message

zero parameter

What it means

errZeroParam ('zero parameter') is thrown in signLegacy (ecdsa_legacy.go:110-111) when the curve order N.Sign() == 0, i.e. the curve's Params().N is zero. A curve with a zero order is degenerate/invalid; signing math (modular inverse, reduction mod N) would divide by zero. This is a guard against malformed custom curves.

Source

Thrown at src/crypto/ecdsa/ecdsa_legacy.go:57

// hashToInt converts a hash value to an integer. Per FIPS 186-4, Section 6.4,
// we use the left-most bits of the hash to match the bit-length of the order of
// the curve. This also performs Step 5 of SEC 1, Version 2.0, Section 4.1.3.
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
	orderBits := c.Params().N.BitLen()
	orderBytes := (orderBits + 7) / 8
	if len(hash) > orderBytes {
		hash = hash[:orderBytes]
	}

	ret := new(big.Int).SetBytes(hash)
	excess := len(hash)*8 - orderBits
	if excess > 0 {
		ret.Rsh(ret, uint(excess))
	}
	return ret
}

var errZeroParam = errors.New("zero parameter")

// Sign signs a hash (which should be the result of hashing a larger message)
// using the private key, priv. If the hash is longer than the bit-length of the
// private key's curve order, the hash will be truncated to that length. It
// returns the signature as a pair of integers. Most applications should use
// [SignASN1] instead of dealing directly with r, s.
//
// The signature is randomized. Since Go 1.26, a secure source of random bytes
// is always used, and the Reader is ignored unless GODEBUG=cryptocustomrand=1
// is set. This setting will be removed in a future Go release. Instead, use
// [testing/cryptotest.SetGlobalRandom].
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
	sig, err := SignASN1(rand, priv, hash)
	if err != nil {
		return nil, nil, err
	}

	r, s = new(big.Int), new(big.Int)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Use a well-known curve (elliptic.P256 etc.) instead of a custom one; verify Params().N is a positive prime before signing.
  2. If a custom curve is required, populate Params().N correctly (the curve order) and validate it is non-zero.
  3. Validate priv.Curve.Params().N.Sign() > 0 before invoking Sign.

Example fix

// before
params := &elliptic.CurveParams{Name: "bad", N: big.NewInt(0)} // N zero
priv := &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: params}}
_, err := ecdsa.Sign(rand.Reader, priv, hash) // -> error 245

// after
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // N correctly set
Defensive patterns

Strategy: validation

Validate before calling

if priv.Curve == nil || priv.Curve.Params().N == nil || priv.Curve.Params().N.Sign() == 0 {
    return errors.New("curve order N must be a positive prime")
}

Type guard

func curveOrderValid(c elliptic.Curve) bool {
    return c != nil && c.Params().N != nil && c.Params().N.Sign() > 0
}

Prevention

When it happens

Trigger: Calling ecdsa.Sign / SignASN1 on a PrivateKey whose Curve.Params().N == big.Int(0). Reached only via the legacy (non-FIPS) signing path for custom curves. Happens with a hand-rolled elliptic.Curve implementation that returns a zero N, or a corrupted curve parameter.

Common situations: Implementing a custom elliptic.Curve and forgetting to populate N; loading a curve from a misconfigured registry; porting curve params where N was dropped.

Related errors


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