slackhq/nebula · error

invalid curve: %s

Error message

invalid curve: %s

What it means

VerifyPrivateKey validates the curve identifier before processing the key. If curve is neither Curve_Ed25519 nor Curve_P256, neither switch branch runs and the function returns 'invalid curve'. This guards against calling the verifier with an unsupported or misspelled curve value.

Source

Thrown at cert/cert_v1.go:160

			// the call to PublicKey below will panic slice bounds out of range otherwise
			if len(key) != ed25519.PrivateKeySize {
				return fmt.Errorf("key was not 64 bytes, is invalid ed25519 private key")
			}

			if !ed25519.PublicKey(c.details.publicKey).Equal(ed25519.PrivateKey(key).Public()) {
				return fmt.Errorf("public key in cert and private key supplied don't match")
			}
		case Curve_P256:
			privkey, err := ecdh.P256().NewPrivateKey(key)
			if err != nil {
				return fmt.Errorf("cannot parse private key as P256: %w", err)
			}
			pub := privkey.PublicKey().Bytes()
			if !bytes.Equal(pub, c.details.publicKey) {
				return fmt.Errorf("public key in cert and private key supplied don't match")
			}
		default:
			return fmt.Errorf("invalid curve: %s", curve)
		}
		return nil
	}

	var pub []byte
	switch curve {
	case Curve_CURVE25519:
		var err error
		pub, err = curve25519.X25519(key, curve25519.Basepoint)
		if err != nil {
			return err
		}
	case Curve_P256:
		privkey, err := ecdh.P256().NewPrivateKey(key)
		if err != nil {
			return err
		}
		pub = privkey.PublicKey().Bytes()

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Pass cert.Curve_Ed25519 or cert.Curve_P256 explicitly as the curve argument
  2. Read the intended curve from the certificate details and default sensibly if unset
  3. Check whether you are mixing curve constants from a different certificate API/version
  4. Log/print the curve value being passed to confirm what is reaching the function

Example fix

// before
err := cert.VerifyPrivateKey(key, 0 /* unset curve */, keyBytes)
// after
err := cert.VerifyPrivateKey(key, cert.Curve_P256, keyBytes)
Defensive patterns

Strategy: validation

Validate before calling

func isSupportedCurve(curve cert.Curve) bool {
    return curve == cert.Curve_Ed25519 || curve == cert.Curve_P256
}
if !isSupportedCurve(curveArg) {
    return fmt.Errorf("caller passed unsupported curve %v", curveArg)
}
err := c.VerifyPrivateKey(key, curveArg, keyBytes)

Type guard

func hasCurveSet(curve cert.Curve) bool {
    return curve == cert.Curve_Ed25519 || curve == cert.Curve_P256
}

Try / catch

if err := c.VerifyPrivateKey(key, curveArg, keyBytes); err != nil {
    var badCurve = fmt.Errorf("invalid curve %v: must be Curve_Ed25519 or Curve_P256", curveArg)
    if strings.Contains(err.Error(), "invalid curve") {
        return badCurve
    }
    return err
}

Prevention

When it happens

Trigger: Calling (*certificateV1).VerifyPrivateKey with curve set to anything other than Curve_Ed25519 or Curve_P256 (e.g. Curve_UNSET, zero value, or a curve value from a different cert version).

Common situations: Passing the certificate's raw protobuf curve enum when it is unset; copying code written for cert v2 curve values; forgetting to set the curve field on a synthesized certificate.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/b197e74ab4923355. Report an issue: GitHub.