golang/go · error

ecdsa: private key is zero

Error message

ecdsa: private key is zero

What it means

Thrown by fips140/ecdsa.NewPrivateKey after the scalar was parsed when d.IsZero() == 1. A zero private scalar is invalid because the corresponding public point would be the identity element and signing would be undefined/leak everything. The zero check runs after SetBytes succeeds, so it catches an all-zero D of the correct length.

Source

Thrown at src/crypto/internal/fips140/ecdsa/ecdsa.go:177

// NewPrivateKey creates a new ECDSA private key from the given D and Q byte
// slices. D must be the fixed-length big-endian encoding of the private scalar,
// and Q must be the compressed or uncompressed encoding of the public point.
func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) {
	fips140.RecordApproved()
	pub, err := NewPublicKey(c, Q)
	if err != nil {
		return nil, err
	}
	if len(D) != c.N.Size() {
		return nil, errors.New("ecdsa: invalid private key length")
	}
	d, err := bigmod.NewNat().SetBytes(D, c.N)
	if err != nil {
		return nil, err
	}
	if d.IsZero() == 1 {
		return nil, errors.New("ecdsa: private key is zero")
	}
	priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)}
	return priv, nil
}

// NewPublicKey creates a new ECDSA public key from the given Q byte slice.
// Q must be the compressed or uncompressed encoding of the public point.
func NewPublicKey[P Point[P]](c *Curve[P], Q []byte) (*PublicKey, error) {
	// SetBytes checks that Q is a valid point on the curve, and that its
	// coordinates are reduced modulo p, fulfilling the requirements of SP
	// 800-89, Section 5.3.2.
	if len(Q) < 1 || Q[0] == 0 {
		return nil, errors.New("ecdsa: invalid public key encoding")
	}
	_, err := c.newPoint().SetBytes(Q)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Generate the scalar with the approved GenerateKey rather than supplying bytes.
  2. Check that D is not all zeros before calling NewPrivateKey.
  3. If sourcing from entropy, verify the RNG actually wrote non-zero bytes.

Example fix

// before
priv, err := ecdsa.NewPrivateKey(curve, make([]byte, curve.N.Size())) // all zero

// after: use the generator
priv, err := ecdsa.GenerateKey(curve, rand.Reader)
Defensive patterns

Strategy: validation

Validate before calling

if allZero(D) {
    return errors.New("ecdsa private key is zero")
}
return ecdsa.NewPrivateKey(curve, D, Q)

Type guard

func nonZeroScalar(D []byte) bool {
    for _, b := range D { if b != 0 { return true } }
    return false
}

Prevention

When it happens

Trigger: Calling NewPrivateKey with a D slice of the correct length but all bytes zero.

Common situations: Uninitialized/zeroed key buffers, a freshly-allocated slice never written to, or a derandomized/test fixture that is accidentally zero.

Related errors


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