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

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Set t.Curve to nebula.Curve_CURVE25519 or nebula.Curve_P256 before calling Sign
  2. If constructing TBSCertificate manually, initialize Curve alongside Name/PublicIp/etc.
  3. 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

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


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