{"record":{"id":"2303b1c6dd477b00","repo":"slackhq/nebula","slug":"bytes-did-not-contain-a-proper-public-key-banner","errorCode":null,"errorMessage":"bytes did not contain a proper public key banner","messagePattern":"bytes did not contain a proper public key banner","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/pem.go","lineNumber":170,"sourceCode":"// consumed data or an error on failure. Only key-agreement (ECDH) public key banners are accepted.\n// Use UnmarshalSigningPublicKeyFromPEM for Ed25519/ECDSA banners.\nfunc UnmarshalPublicKeyFromPEM(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 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 {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/pem.go#L152-L188","documentation":"The PEM block decoded successfully but its Type banner is not X25519PublicKeyBanner or P256PublicKeyBanner, the only banners UnmarshalPublicKeyFromPEM accepts (ECDH key-agreement keys). The function deliberately rejects signing-key banners and suggests UnmarshalSigningPublicKeyFromPEM for those.","triggerScenarios":"Calling UnmarshalPublicKeyFromPEM with a PEM block typed e.g. 'NEBULA ED25519 PUBLIC KEY', 'CERTIFICATE', or 'NEBULA ED25519 ENCRYPTED PRIVATE KEY' instead of an X25519/P256 public key banner.","commonSituations":"Mixing up signing keys and encryption keys in Nebula configs — trying to load a node's Ed25519 signing public key (or a certificate) where the X25519 ECDH public key is required, often when populating static_host_map or firewall keys.","solutions":["Use UnmarshalSigningPublicKeyFromPEM if the PEM type is an Ed25519/ECDSA signing public key banner","Ensure the input is an ECDH public key: '-----BEGIN NEBULA X25519 PUBLIC KEY-----' or '-----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----'","Extract the correct key type from the certificate via the appropriate API rather than feeding the whole certificate PEM to this function"],"exampleFix":"// before\n_, rest, curve, err := cert.UnmarshalPublicKeyFromPEM(ed25519PubPEM) // signing key banner\n// after\n_, rest, curve, err := cert.UnmarshalSigningPublicKeyFromPEM(ed25519PubPEM)","handlingStrategy":"validation","validationCode":"func isECDHBanner(b []byte) bool {\n\tblk, _ := pem.Decode(b)\n\tif blk == nil {\n\t\treturn false\n\t}\n\treturn blk.Type == cert.X25519PublicKeyBanner || blk.Type == cert.P256PublicKeyBanner\n}","typeGuard":null,"tryCatchPattern":"pub, rest, curve, err := cert.UnmarshalPublicKeyFromPEM(b)\nif err != nil {\n\tif strings.Contains(err.Error(), \"proper public key banner\") {\n\t\tif blk, _ := pem.Decode(b); blk != nil && (blk.Type == cert.Ed25519PublicKeyBanner || blk.Type == cert.ECDSAP256PublicKeyBanner) {\n\t\t\t_, _, _, err = cert.UnmarshalSigningPublicKeyFromPEM(b)\n\t\t\treturn err\n\t\t}\n\t}\n\treturn err\n}","preventionTips":["Track which key type (signing vs encryption) each file/field holds; name files accordingly (host.key vs host.pub)","Route Ed25519/ECDSA signing banners to UnmarshalSigningPublicKeyFromPEM instead","Do not pass certificate PEM blocks to public-key unmarshal functions"],"tags":["pem","banner-mismatch","public-key","key-agreement","go"],"backgroundTag":"pem-banner-mismatch","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"}