{"record":{"id":"9614bf901cd9144f","repo":"slackhq/nebula","slug":"key-was-not-d-bytes-is-invalid-s-public-key","errorCode":null,"errorMessage":"key was not %d bytes, is invalid %s public key","messagePattern":"key was not (.+?) bytes, is invalid (.+?) public key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/pem.go","lineNumber":173,"sourceCode":"\tk, r := pem.Decode(b)\n\tif k == nil {\n\t\treturn nil, r, 0, fmt.Errorf(\"input did not contain a valid PEM encoded block\")\n\t}\n\tvar expectedLen int\n\tvar curve Curve\n\tswitch k.Type {\n\tcase X25519PublicKeyBanner:\n\t\texpectedLen = 32\n\t\tcurve = Curve_CURVE25519\n\tcase P256PublicKeyBanner:\n\t\t// Uncompressed\n\t\texpectedLen = 65\n\t\tcurve = Curve_P256\n\tdefault:\n\t\treturn nil, r, 0, fmt.Errorf(\"bytes did not contain a proper public key banner\")\n\t}\n\tif len(k.Bytes) != expectedLen {\n\t\treturn nil, r, 0, fmt.Errorf(\"key was not %d bytes, is invalid %s public key\", expectedLen, curve)\n\t}\n\treturn k.Bytes, r, curve, nil\n}\n\n// UnmarshalSigningPublicKeyFromPEM will try to unmarshal the first pem block in a byte array, returning any non\n// consumed data or an error on failure. Only Ed25519/ECDSA public key banners are accepted.\n// Use UnmarshalPublicKeyFromPEM for X25519/P256 (ECDH) banners.\nfunc UnmarshalSigningPublicKeyFromPEM(b []byte) ([]byte, []byte, Curve, error) {\n\tk, r := pem.Decode(b)\n\tif k == nil {\n\t\treturn nil, r, 0, fmt.Errorf(\"input did not contain a valid PEM encoded block\")\n\t}\n\tvar expectedLen int\n\tvar curve Curve\n\tswitch k.Type {\n\tcase Ed25519PublicKeyBanner:\n\t\texpectedLen = 32\n\t\tcurve = Curve_CURVE25519","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/pem.go#L155-L191","documentation":"The PEM banner was valid but the block body length does not match the expected raw key size: 32 bytes for X25519 (CURVE25519) or 65 bytes for an uncompressed P256 public key. The parsed Curve name is interpolated into the message so you can see which key type was expected.","triggerScenarios":"Calling UnmarshalPublicKeyFromPEM with a correctly-bannered block whose k.Bytes is the wrong length — e.g. compressed (33-byte) P256 point, truncated body, extra padding inside the block, or a point-encoded P256 key from another tool.","commonSituations":"Exporting P256 public keys from OpenSSL in compressed form (33 bytes) instead of uncompressed (65 bytes), copy/paste truncating base64, or hand-assembling PEM blocks with wrong DER/bytes content.","solutions":["For P256 keys, ensure the point is in uncompressed form (0x04 prefix + 32-byte X + 32-byte Y = 65 bytes); re-export with uncompressed encoding","For X25519 keys, ensure exactly 32 raw key bytes are in the PEM body","Re-emit the public key via the library's Marshal path (e.g. certificate PublicKey marshalling) rather than hand-building the PEM"],"exampleFix":"// before\nblk.Bytes = elliptic.MarshalCompressed(curve, x, y) // 33 bytes for P256\n// after\nblk.Bytes = elliptic.Marshal(curve, x, y) // 65 bytes, uncompressed","handlingStrategy":"validation","validationCode":"func checkECDHKeyLen(b []byte) error {\n\tblk, _ := pem.Decode(b)\n\tif blk == nil {\n\t\treturn fmt.Errorf(\"no PEM\")\n\t}\n\tswitch blk.Type {\n\tcase cert.X25519PublicKeyBanner:\n\t\tif len(blk.Bytes) != 32 { return fmt.Errorf(\"X25519 key must be 32 bytes, got %d\", len(blk.Bytes)) }\n\tcase cert.P256PublicKeyBanner:\n\t\tif len(blk.Bytes) != 65 || blk.Bytes[0] != 0x04 { return fmt.Errorf(\"P256 key must be 65-byte uncompressed point\") }\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"pub, rest, curve, err := cert.UnmarshalPublicKeyFromPEM(b)\nif err != nil {\n\tif strings.Contains(err.Error(), \"invalid\") && strings.Contains(err.Error(), \"public key\") {\n\t\treturn fmt.Errorf(\"key body length does not match banner; re-export the key in the expected encoding: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Export P256 public keys uncompressed (0x04-prefixed, 65 bytes), never compressed","X25519 public keys are always exactly 32 bytes; verify before PEM-encoding","Prefer the library's own marshal functions over hand-building PEM blocks"],"tags":["pem","key-length","public-key","go"],"backgroundTag":"invalid-public-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"}