slackhq/nebula · error
invalid curve: %s
Error message
invalid curve: %s
What it means
TBSCertificate.Sign (and therefore certificate/CA creation) received a Curve value that is neither Curve_CURVE25519 nor Curve_P256, so no signing algorithm can be selected. This happens when a TBSCertificate is built with a zero-value or out-of-range Curve (e.g. constructing the struct manually or decoding a cert with an unknown curve byte).
Source
Thrown at cert/sign.go:71
sp := func(certBytes []byte) ([]byte, error) {
sig := ed25519.Sign(pk, certBytes)
return sig, nil
}
return t.SignWith(signer, curve, sp)
case Curve_P256:
pk, err := ecdsa.ParseRawPrivateKey(elliptic.P256(), key)
if err != nil {
return nil, err
}
sp := func(certBytes []byte) ([]byte, error) {
// We need to hash first for ECDSA
// - https://pkg.go.dev/crypto/ecdsa#SignASN1
hashed := sha256.Sum256(certBytes)
return ecdsa.SignASN1(rand.Reader, pk, hashed[:])
}
return t.SignWith(signer, curve, sp)
default:
return nil, fmt.Errorf("invalid curve: %s", t.Curve)
}
}
// SignWith does the same thing as sign, but uses the function in `sp` to calculate the signature.
// You should only use SignWith if you do not have direct access to your private key.
func (t *TBSCertificate) SignWith(signer Certificate, curve Curve, sp SignerLambda) (Certificate, error) {
if curve != t.Curve {
return nil, fmt.Errorf("curve in cert and private key supplied don't match")
}
if signer != nil {
if t.IsCA {
return nil, fmt.Errorf("can not sign a CA certificate with another")
}
err := checkCAConstraints(signer, t.NotBefore, t.NotAfter, t.Groups, t.Networks, t.UnsafeNetworks)
if err != nil {
return nil, errView on GitHub (pinned to dd8f660c0a)
Solutions
- Set t.Curve to nebula.Curve_CURVE25519 or nebula.Curve_P256 before calling Sign
- If constructing TBSCertificate manually, initialize Curve alongside Name/PublicIp/etc.
- If curve comes from decoded input, validate it against the two supported constants before signing
Example fix
// before
tbs := &nebula.TBSCertificate{Name: "host"}
cert, err := tbs.Sign(key, sp) // Curve unset => invalid curve: 0
// after
tbs := &nebula.TBSCertificate{Name: "host", Curve: nebula.Curve_CURVE25519}
cert, err := tbs.Sign(key, sp) Defensive patterns
Strategy: validation
Validate before calling
if tbs.Curve != nebula.Curve_CURVE25519 && tbs.Curve != nebula.Curve_P256 {
return fmt.Errorf("TBSCertificate.Curve must be set to Curve_CURVE25519 or Curve_P256 before Sign (got %d)", tbs.Curve)
} Type guard
func hasSupportedCurve(c nebula.Curve) bool {
return c == nebula.Curve_CURVE25519 || c == nebula.Curve_P256
} Try / catch
cert, err := tbs.Sign(key, sp)
if err != nil {
return nil, fmt.Errorf("failed to sign certificate: %w (is TBS Curve field set?)", err)
} Prevention
- Never construct TBSCertificate with struct literal only — always set Curve
- Use library constructors (NewCaCert/NewCert) instead of manual assembly
- Reject unknown curve bytes when deserializing external cert data
When it happens
Trigger: Call Sign on a TBSCertificate whose t.Curve is 0 (unset) or any value other than Curve_CURVE25519/Curve_P256 — e.g. building TBSCertificate{} by hand without setting Curve, or NewTestCert-style helpers passing an invalid curve.
Common situations: Hand-rolled certificate generation code forgetting the Curve field, custom tooling writing an unsupported curve constant into cert details, or a corrupted/deserialized TBS block with an unknown curve byte.
Related errors
- ErrEmptySignature
- certificate curve %s does not match expected %s
- curve in cert and private key supplied don't match
- invalid curve: %s
- invalid curve: %s
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/ac59f5f6de9bcaa2.
Report an issue: GitHub.