golang/go · error
ecdsa: private key does not match curve
Error message
ecdsa: private key does not match curve
What it means
Thrown by fips140/ecdsa.Sign when the supplied private key's curve does not match the operation context curve (priv.pub.curve != c.curve). The signing curve handle must match the key's curve before the hedged (draft-irtf-cfrg-det-sigs-with-noise) signing proceeds.
Source
Thrown at src/crypto/internal/fips140/ecdsa/ecdsa.go:284
}
// testingOnlyRejectionSamplingLooped is called when rejection sampling in
// randomPoint rejects a candidate for being higher than the modulus.
var testingOnlyRejectionSamplingLooped func()
// Signature is an ECDSA signature, where r and s are represented as big-endian
// byte slices of the same length as the curve order.
type Signature struct {
R, S []byte
}
// Sign signs a hash (which should be the result of hashing a larger message with
// the hash function H) 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.
func Sign[P Point[P], H hash.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) {
if priv.pub.curve != c.curve {
return nil, errors.New("ecdsa: private key does not match curve")
}
if len(hash) == 0 {
return nil, errors.New("ecdsa: hash cannot be empty")
}
fips140.RecordApproved()
fipsSelfTest()
// Random ECDSA is dangerous, because a failure of the RNG would immediately
// leak the private key. Instead, we use a "hedged" approach, as specified
// in draft-irtf-cfrg-det-sigs-with-noise-04, Section 4. This has also the
// advantage of closely resembling Deterministic ECDSA.
Z := make([]byte, len(priv.d))
if err := drbg.ReadWithReader(rand, Z); err != nil {
return nil, err
}
// See https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6View on GitHub (pinned to b6b368adc5)
Solutions
- Derive both c and priv from the same curve constant.
- Assert priv.pub.curve matches c before calling Sign.
- Store the curve with the key and select c from it.
Example fix
// before sig, err := ecdsa.Sign(ecdsa.P256(), sha256.New, p384Priv, rand, hash) // after: match the key's curve sig, err := ecdsa.Sign(ecdsa.P384(), sha512.New, p384Priv, rand, hash)
Defensive patterns
Strategy: validation
Validate before calling
if priv.PublicKey().Curve() != c.Curve() {
return errors.New("private key curve differs from signing context")
}
return ecdsa.Sign(c, h, priv, rand, hash) Type guard
func keyMatchesSignCurve(c *ecdsa.Curve, priv *ecdsa.PrivateKey) bool {
return priv.PublicKey().Curve() == c.Curve()
} Prevention
- Derive c from the key's Curve().
- Bind the curve to the key object.
- Add a pre-sign assertion in multi-curve services.
When it happens
Trigger: Calling Sign(c, h, priv, rand, hash) where c and priv come from different curves.
Common situations: Refactoring that swaps a curve constant, multi-curve code that passes the wrong handle, or a key deserialized without recording its curve.
Related errors
- crypto/ecdh: mismatched curves
- ecdsa: invalid private key length
- ecdsa: private key is zero
- ecdsa: invalid public key encoding
- ecdsa: public key does not match curve
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/3469581ffe517ada.
Report an issue: GitHub.