slackhq/nebula · error

certificate curve %s does not match expected %s

Error message

certificate curve %s does not match expected %s

What it means

Recombine unmarshals a raw certificate against an expected curve and then checks that the parsed certificate's curve matches the caller-supplied curve. This error means the certificate was issued on a different elliptic curve than the one the caller declared (e.g. passing Curve_CURVE25519 while the cert is P256).

Source

Thrown at cert/cert.go:155

	var c Certificate
	var err error

	switch v {
	// Implementations must ensure the result is a valid cert!
	case VersionPre1, Version1:
		c, err = unmarshalCertificateV1(rawCertBytes, publicKey)
	case Version2:
		c, err = unmarshalCertificateV2(rawCertBytes, publicKey, curve)
	default:
		return nil, ErrUnknownVersion
	}

	if err != nil {
		return nil, err
	}

	if c.Curve() != curve {
		return nil, fmt.Errorf("certificate curve %s does not match expected %s", c.Curve().String(), curve.String())
	}

	return c, nil
}

// CalculateAlternateFingerprint calculates a 2nd fingerprint representation for P256 certificates
// CAPool blocklist testing through `VerifyCertificate` and `VerifyCachedCertificate` automatically performs this step.
func CalculateAlternateFingerprint(c Certificate) (string, error) {
	if c.Curve() != Curve_P256 {
		return "", nil
	}

	nc := c.Copy()
	b, err := p256.Swap(nc.Signature())
	if err != nil {
		return "", err
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Pass the curve the certificate was actually issued with (check the cert's curve before calling Recombine)
  2. Re-issue the certificate on the expected curve
  3. Ensure keypair generation and cert signing use the same curve

Example fix

// before
c, err := cert.Recombine(pubKey, raw, vpe, cert.Curve_CURVE25519) // cert is P256
// after
expected := detectCurve(raw) // e.g. from cert metadata
c, err := cert.Recombine(pubKey, raw, vpe, expected)
Defensive patterns

Strategy: validation

Validate before calling

if c.Curve() != expectedCurve {
    return fmt.Errorf("refusing Recombine: cert curve %s != expected %s", c.Curve(), expectedCurve)
}
cert, err := cert.Recombine(pubKey, raw, vpe, expectedCurve)

Prevention

When it happens

Trigger: Calling Recombine(publicKey, rawCertBytes, vpe, curve) where c.Curve() != curve — typically a Version2 cert (or an expected curve passed to v1 unmarshal path) whose embedded curve differs from the expected parameter.

Common situations: Mixing P256 and Curve25519 artifacts during migration between cert versions/curves; hardcoding the wrong curve constant; loading a cert bundle where v2 certs use a different curve than the handshake's key curve.

Understand the failure class

Related errors


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