golang/go · error
crypto/ecdh: mismatched curves
Error message
crypto/ecdh: mismatched curves
What it means
Thrown by fips140/ecdh.ecdh when the curve passed as the operation context c does not match the curve of the supplied PrivateKey (c.curve != k.pub.curve). The context curve, the private key's curve, and the peer's public key curve must all agree before any scalar multiplication.
Source
Thrown at src/crypto/internal/fips140/ecdh/ecdh.go:241
// the point is on the curve. Along with the rejection of the point at
// infinity (the identity element) above, this fulfills the requirements
// of NIST SP 800-56A Rev. 3, Section 5.6.2.3.4.
if _, err := c.newPoint().SetBytes(key); err != nil {
return nil, err
}
return &PublicKey{curve: c.curve, q: bytes.Clone(key)}, nil
}
func ECDH[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
return ecdh(c, k, peer)
}
func ecdh[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
if c.curve != k.pub.curve {
return nil, errors.New("crypto/ecdh: mismatched curves")
}
if k.pub.curve != peer.curve {
return nil, errors.New("crypto/ecdh: mismatched curves")
}
// This applies the Shared Secret Computation of the Ephemeral Unified Model
// scheme specified in NIST SP 800-56A Rev. 3, Section 6.1.2.2.
// Per Section 5.6.2.3.4, Step 1, reject the identity element (0x00).
if len(k.pub.q) == 1 {
return nil, errors.New("crypto/ecdh: public key is the identity element")
}
// SetBytes checks that (x, y) are reduced modulo p, and that they are on
// the curve, performing Steps 2-3 of Section 5.6.2.3.4.
p, err := c.newPoint().SetBytes(peer.q)
if err != nil {
return nil, errView on GitHub (pinned to b6b368adc5)
Solutions
- Derive c from the same curve constant used to generate priv (e.g. both ecdh.P256()).
- Add a guard comparing the context curve to the key's curve before calling ECDH.
- Centralize curve selection so a single constant flows to all key ops.
Example fix
// before secret, err := ecdh.ECDH(ecdh.P256(), p384Priv, peer) // mismatch // after: use the curve the key belongs to secret, err := ecdh.ECDH(ecdh.P384(), p384Priv, peer)
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the context curve matches the key's curve before ECDH.
if c.Curve() != priv.Curve() {
return errors.New("context curve does not match private key curve")
}
return ecdh.ECDH(c, priv, peer) Type guard
func sameCurveContext(c *ecdh.Curve, priv *ecdh.PrivateKey) bool {
return c.Curve() == priv.Curve()
} Prevention
- Derive the context curve from the key's own Curve() rather than a separate constant.
- Centralize curve selection in one place.
- Add assertions in multi-curve code paths.
When it happens
Trigger: Calling ECDH(c, priv, peer) where c was obtained for a different curve than the one priv was generated on.
Common situations: Mixing a P-256 context with a P-384 private key, or refactoring that passes the wrong curve handle into ECDH.
Related errors
- crypto/ecdh: invalid private key
- crypto/ecdh: invalid public key
- crypto/ecdh: public key is the identity element
- ecdsa: private key does not match curve
- crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/29ebf1a5b005f9e0.
Report an issue: GitHub.