slackhq/nebula · error

key was not 64 bytes, is invalid ed25519 private key

Error message

key was not 64 bytes, is invalid ed25519 private key

What it means

For CA (isCA) certificates on Curve_CURVE25519, the signing key must be a 64-byte ed25519 private key; VerifyPrivateKey rejects any other length before deriving the public key (which would otherwise panic on a slice bounds error). This error means the supplied key bytes have the wrong length for an ed25519 private key.

Source

Thrown at cert/cert_v1.go:144

	default:
		return false
	}
}

func (c *certificateV1) Expired(t time.Time) bool {
	return c.details.notBefore.After(t) || c.details.notAfter.Before(t)
}

func (c *certificateV1) VerifyPrivateKey(curve Curve, key []byte) error {
	if curve != c.details.curve {
		return fmt.Errorf("curve in cert and private key supplied don't match")
	}
	if c.details.isCA {
		switch curve {
		case Curve_CURVE25519:
			// 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

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Supply the full 64-byte ed25519 private key (seed + public half); if you only have a 32-byte seed, expand it with ed25519.NewKeyFromSeed
  2. Verify you are not passing a P256/private key of the wrong algorithm
  3. Check the key file was loaded completely (no truncation, correct decoding)

Example fix

// before
seed := make([]byte, 32)
rand.Read(seed)
err := caCert.VerifyPrivateKey(cert.Curve_CURVE25519, seed) // 32 bytes: error
// after
key := ed25519.NewKeyFromSeed(seed) // full 64-byte private key
err := caCert.VerifyPrivateKey(cert.Curve_CURVE25519, key)
Defensive patterns

Strategy: validation

Validate before calling

if cert.Curve() == cert.Curve_CURVE25519 && len(key) != ed25519.PrivateKeySize {
    return fmt.Errorf("need full 64-byte ed25519 private key, got %d bytes", len(key))
}
err := cert.VerifyPrivateKey(cert.Curve(), key)

Prevention

When it happens

Trigger: Calling VerifyPrivateKey(Curve_CURVE25519, key) on a certificateV1 where c.details.isCA is true and len(key) != ed25519.PrivateKeySize (64).

Common situations: Passing a 32-byte ed25519 seed instead of the full 64-byte private key; passing a P256 key to a Curve25519 CA cert; reading a PEM/der key file that was truncated or in a different format.

Related errors


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