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, err

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Derive c from the same curve constant used to generate priv (e.g. both ecdh.P256()).
  2. Add a guard comparing the context curve to the key's curve before calling ECDH.
  3. 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

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


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