golang/go · error
ecdsa: private key scalar is zero or negative
Error message
ecdsa: private key scalar is zero or negative
What it means
Thrown by privateKeyToFIPS when converting a legacy crypto/ecdsa PrivateKey to its FIPS nistec form. The guard checks priv.D.Sign() <= 0, rejecting a private scalar that is zero or negative. A valid ECDSA private key must be a positive integer in [1, N-1]; zero/negative is either corrupted input, a mis-encoded key, or an uninitialized (*big.Int)(nil)-adjacent value.
Source
Thrown at src/crypto/ecdsa/ecdsa.go:602
return nil, err
}
return ecdsa.NewPublicKey(c, Q)
}
var privateKeyCache fips140cache.Cache[PrivateKey, ecdsa.PrivateKey]
func privateKeyToFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey) (*ecdsa.PrivateKey, error) {
Q, err := pointFromAffine(priv.Curve, priv.X, priv.Y)
if err != nil {
return nil, err
}
// Reject values that would not get correctly encoded.
if priv.D.BitLen() > priv.Curve.Params().N.BitLen() {
return nil, errors.New("ecdsa: private key scalar too large")
}
if priv.D.Sign() <= 0 {
return nil, errors.New("ecdsa: private key scalar is zero or negative")
}
size := (priv.Curve.Params().N.BitLen() + 7) / 8
const maxScalarSize = 66 // enough for a P-521 private key
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.View on GitHub (pinned to b6b368adc5)
Solutions
- Verify priv.D is set and in the range [1, N-1] before calling Sign/SignASN1; load keys only via x509.ParseECPrivateKey / ParsePKCS8PrivateKey.
- Regenerate the key with ecdsa.GenerateKey if the source scalar is corrupt.
- If loading from raw bytes, build the key with D.SetBytes and validate it is non-zero and below the curve order N.
Example fix
// before
priv := new(ecdsa.PrivateKey)
priv.D = big.NewInt(0) // corrupt/empty
sig, err := ecdsa.SignASN1(rand.Reader, priv, hash) // -> error 240
// after
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil { return err }
sig, err := ecdsa.SignASN1(rand.Reader, priv, hash) Defensive patterns
Strategy: validation
Validate before calling
if priv.D == nil || priv.D.Sign() <= 0 {
return errors.New("private key scalar must be positive")
}
N := priv.Curve.Params().N
if priv.D.Cmp(N) >= 0 {
return errors.New("private key scalar must be < curve order")
} Type guard
func validPrivateKeyScalar(priv *ecdsa.PrivateKey) bool {
return priv != nil && priv.D != nil && priv.D.Sign() > 0 && priv.D.Cmp(priv.Curve.Params().N) < 0
} Try / catch
sig, err := ecdsa.SignASN1(rand.Reader, priv, hash)
if err != nil {
if strings.Contains(err.Error(), "private key scalar is zero or negative") {
// key is corrupt; regenerate or reject
}
return err
} Prevention
- Always generate keys with ecdsa.GenerateKey; load stored keys via x509.ParseECPrivateKey/ParsePKCS8PrivateKey.
- Never hand-construct PrivateKey.D; if you must, validate it is in [1, N-1].
- Treat externally-supplied keys as untrusted and validate D before use.
When it happens
Trigger: Called from ecdsa.Sign / SignASN1 / SignReader (ecdsa.go:301,425,459) when priv.D is zero or has a negative sign. Happens when a PrivateKey is constructed by hand with priv.D = big.NewInt(0), when a key is parsed from malformed encoding that yields a zero/invalid scalar, or when unmarshaling logic leaves D unset and a zero default is passed.
Common situations: Importing a private key from a corrupt PEM/DER file, deserializing a key from an untrusted source, copy-paste errors that zero out D, or test fixtures that forgot to populate the scalar.
Related errors
- negative coordinate
- overflowing coordinate
- ecdsa: public key point is the infinity
- crypto/ecdsa: use of custom curves is not allowed in FIPS 14
- zero parameter
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/3e304c58009ef7d0.
Report an issue: GitHub.