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
- Pass cert.Curve_Ed25519 or cert.Curve_P256 explicitly as the curve argument
- Read the intended curve from the certificate details and default sensibly if unset
- Check whether you are mixing curve constants from a different certificate API/version
- 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
- Always pass named constants (Curve_Ed25519/Curve_P256), never raw ints
- Initialize curve fields explicitly when constructing certificates
- Watch for unset (zero) enum values when reading from protobuf
- Add a caller-side curve allowlist check
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
- certificate curve %s does not match expected %s
- curve in cert and private key supplied don't match
- invalid curve: %s
- invalid curve: %s
- pki: use of %s is not allowed in FIPS 140-only mode
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/b197e74ab4923355.
Report an issue: GitHub.