{"record":{"id":"4314143beaa74a62","repo":"slackhq/nebula","slug":"key-was-not-64-bytes-is-invalid-ed25519-private-k","errorCode":null,"errorMessage":"key was not 64 bytes, is invalid ed25519 private key","messagePattern":"key was not 64 bytes, is invalid ed25519 private key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/cert_v1.go","lineNumber":144,"sourceCode":"\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (c *certificateV1) Expired(t time.Time) bool {\n\treturn c.details.notBefore.After(t) || c.details.notAfter.Before(t)\n}\n\nfunc (c *certificateV1) VerifyPrivateKey(curve Curve, key []byte) error {\n\tif curve != c.details.curve {\n\t\treturn fmt.Errorf(\"curve in cert and private key supplied don't match\")\n\t}\n\tif c.details.isCA {\n\t\tswitch curve {\n\t\tcase Curve_CURVE25519:\n\t\t\t// the call to PublicKey below will panic slice bounds out of range otherwise\n\t\t\tif len(key) != ed25519.PrivateKeySize {\n\t\t\t\treturn fmt.Errorf(\"key was not 64 bytes, is invalid ed25519 private key\")\n\t\t\t}\n\n\t\t\tif !ed25519.PublicKey(c.details.publicKey).Equal(ed25519.PrivateKey(key).Public()) {\n\t\t\t\treturn fmt.Errorf(\"public key in cert and private key supplied don't match\")\n\t\t\t}\n\t\tcase Curve_P256:\n\t\t\tprivkey, err := ecdh.P256().NewPrivateKey(key)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"cannot parse private key as P256: %w\", err)\n\t\t\t}\n\t\t\tpub := privkey.PublicKey().Bytes()\n\t\t\tif !bytes.Equal(pub, c.details.publicKey) {\n\t\t\t\treturn fmt.Errorf(\"public key in cert and private key supplied don't match\")\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid curve: %s\", curve)\n\t\t}\n\t\treturn nil","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/cert_v1.go#L126-L162","documentation":"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.","triggerScenarios":"Calling VerifyPrivateKey(Curve_CURVE25519, key) on a certificateV1 where c.details.isCA is true and len(key) != ed25519.PrivateKeySize (64).","commonSituations":"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.","solutions":["Supply the full 64-byte ed25519 private key (seed + public half); if you only have a 32-byte seed, expand it with ed25519.NewKeyFromSeed","Verify you are not passing a P256/private key of the wrong algorithm","Check the key file was loaded completely (no truncation, correct decoding)"],"exampleFix":"// before\nseed := make([]byte, 32)\nrand.Read(seed)\nerr := caCert.VerifyPrivateKey(cert.Curve_CURVE25519, seed) // 32 bytes: error\n// after\nkey := ed25519.NewKeyFromSeed(seed) // full 64-byte private key\nerr := caCert.VerifyPrivateKey(cert.Curve_CURVE25519, key)","handlingStrategy":"validation","validationCode":"if cert.Curve() == cert.Curve_CURVE25519 && len(key) != ed25519.PrivateKeySize {\n    return fmt.Errorf(\"need full 64-byte ed25519 private key, got %d bytes\", len(key))\n}\nerr := cert.VerifyPrivateKey(cert.Curve(), key)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store and load the full 64-byte ed25519 private key, not the 32-byte seed","Expand seeds with ed25519.NewKeyFromSeed before use","Check key file lengths after decode to catch truncation","Never reuse the same key blob across curve types"],"tags":["certificate","private-key","ed25519","key-length"],"backgroundTag":"invalid-private-key-length","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}