slackhq/nebula · error

invalid curve: %v

Error message

invalid curve: %v

What it means

EncryptAndMarshalSigningPrivateKey only supports Curve_CURVE25519 (Ed25519) and Curve_P256 (ECDSA P-256). Passing any other Curve enum value (e.g. Curve_CURVE448 or an unset/unknown value) has no PEM banner mapping, so the function fails before encoding.

Source

Thrown at cert/crypto.go:189

				Memory:      kdfParams.Memory,
				Parallelism: uint32(kdfParams.Parallelism),
				Iterations:  kdfParams.Iterations,
				Salt:        kdfParams.salt,
			},
		},
		Ciphertext: ciphertext,
	})
	if err != nil {
		return nil, err
	}

	switch curve {
	case Curve_CURVE25519:
		return pem.EncodeToMemory(&pem.Block{Type: EncryptedEd25519PrivateKeyBanner, Bytes: b}), nil
	case Curve_P256:
		return pem.EncodeToMemory(&pem.Block{Type: EncryptedECDSAP256PrivateKeyBanner, Bytes: b}), nil
	default:
		return nil, fmt.Errorf("invalid curve: %v", curve)
	}
}

// UnmarshalNebulaEncryptedData will unmarshal a protobuf byte representation of a nebula cert into its
// protobuf-generated struct.
func UnmarshalNebulaEncryptedData(b []byte) (*NebulaEncryptedData, error) {
	if len(b) == 0 {
		return nil, fmt.Errorf("nil byte array")
	}
	var rned RawNebulaEncryptedData
	err := proto.Unmarshal(b, &rned)
	if err != nil {
		return nil, err
	}

	if rned.EncryptionMetadata == nil {
		return nil, fmt.Errorf("encoded EncryptionMetadata was nil")
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Pass only Curve_CURVE25519 for Ed25519 keys or Curve_P256 for ECDSA P-256 keys; verify which key type you actually hold before calling
  2. Check that the curve value was not lost/zeroed during protobuf unmarshalling - an unset proto3 enum field reads as 0 and will fail
  3. If you need another curve, it is unsupported by this API; re-generate the key as Ed25519 or P-256

Example fix

// before
pem, err := cert.EncryptAndMarshalSigningPrivateKey(key, pass, cert.Curve_CURVE448)
// after
pem, err := cert.EncryptAndMarshalSigningPrivateKey(key, pass, cert.Curve_CURVE25519)
Defensive patterns

Strategy: validation

Validate before calling

if curve != cert.Curve_CURVE25519 && curve != cert.Curve_P256 { return fmt.Errorf("unsupported curve: %v", curve) }

Type guard

func isSupportedCurve(c cert.Curve) bool { return c == cert.Curve_CURVE25519 || c == cert.Curve_P256 }

Try / catch

pemBytes, err := cert.EncryptAndMarshalSigningPrivateKey(key, pass, curve)
if err != nil {
    if strings.Contains(err.Error(), "invalid curve") { /* check curve value source */ }
    return err
}

Prevention

When it happens

Trigger: Calling EncryptAndMarshalSigningPrivateKey with a signing key and a curve argument other than Curve_CURVE25519 or Curve_P256 - typically Curve(0) from an unset field or a value deserialized from an untrusted/unknown source.

Common situations: Protobuf-decoding a cert whose curve field is 0/unknown and passing it straight into encryption; copying code from a P-256 example while holding a Curve25519 key (or vice versa); adding a new curve to the enum without updating this switch.

Related errors


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