{"record":{"id":"013aebcf127a3f6c","repo":"slackhq/nebula","slug":"recombine-cert-w","errorCode":null,"errorMessage":"recombine cert: %w","messagePattern":"recombine cert: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"handshake/machine.go","lineNumber":356,"sourceCode":"\n\treturn nil\n}\n\nfunc (m *Machine) validateCert(payload Payload) error {\n\tcred := m.getCred(m.myVersion)\n\tif cred == nil {\n\t\tm.failed = true\n\t\treturn fmt.Errorf(\"%w: %v\", ErrNoCredential, m.myVersion)\n\t}\n\trc, err := cert.Recombine(\n\t\tcert.Version(payload.CertVersion),\n\t\tpayload.Cert,\n\t\tm.hs.PeerStatic(),\n\t\tcred.Cert.Curve(),\n\t)\n\tif err != nil {\n\t\tm.failed = true\n\t\treturn fmt.Errorf(\"recombine cert: %w\", err)\n\t}\n\n\tif !bytes.Equal(rc.PublicKey(), m.hs.PeerStatic()) {\n\t\tm.failed = true\n\t\treturn ErrPublicKeyMismatch\n\t}\n\n\t// Version negotiation, if the peer sent a different version and we have it, switch\n\tif rc.Version() != m.myVersion {\n\t\tif m.getCred(rc.Version()) != nil {\n\t\t\tm.myVersion = rc.Version()\n\t\t}\n\t}\n\n\tverified, err := m.verifier(rc)\n\tif err != nil {\n\t\tm.failed = true\n\t\treturn fmt.Errorf(\"verify cert: %w\", err)","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/handshake/machine.go#L338-L374","documentation":"validateCert wraps errors from cert.Recombine as 'recombine cert: %w'. Recombine merges the peer's certificate bytes with the peer's Noise static key to reconstruct the full certificate and validate it cryptographically. Failure means the cert bytes, peer static key, curve, or network (CA) don't fit together — the presented certificate cannot be recombined for the given curve/version.","triggerScenarios":"ProcessPacket -> processPayload -> validateCert where cert.Recombine(cert.Version(payload.CertVersion), payload.Cert, m.hs.PeerStatic(), cred.Cert.Curve(), ...) errors — cert version mismatch with curve, malformed cert bytes in the payload, or peer static key inconsistent with the certificate's signature.","commonSituations":"Peer certificate issued for a different elliptic curve (P-256 vs curve25519) than the CA, tampered/forged cert payload, cert bytes truncated by an intermediary, incompatible nebula-cert versions between CA and node certs.","solutions":["Re-issue the peer's certificate with nebula-cert matching the CA's curve and version","Confirm all hosts use certs signed by the same CA and curve family","Upgrade peers so certificate encoding versions match","Replace the cert payload source — if this is a middlebox/proxy issue, verify end-to-end packet integrity"],"exampleFix":"// before: CA on curve25519, node cert issued as P-256\nnebula-cert sign -name host -curve P256 ...\n// after: match the CA curve\nnebula-cert sign -ca ca.crt -key ca.key -name host -ip 10.0.0.2/24 -curve CURVE25519","handlingStrategy":"validation","validationCode":"// before deployment, check cert/CA curve compatibility\ncaCert, _ := cert.UnmarshalNebulaCertificate(caPEM)\nhostCert, _ := cert.UnmarshalNebulaCertificate(hostPEM)\nif caCert.Curve() != hostCert.Curve() {\n\treturn fmt.Errorf(\"cert curve %v does not match CA curve %v\", hostCert.Curve(), caCert.Curve())\n}","typeGuard":null,"tryCatchPattern":"_, _, err := machine.ProcessPacket(pkt)\nif err != nil && strings.Contains(err.Error(), \"recombine cert:\") {\n\t// peer cert unusable for this curve/version — reject and reissue peer cert\n}","preventionTips":["Issue all node certs with the same nebula-cert version and curve as the CA","Validate the full chain (CA + leaf) in CI with nebula-cert before rollout","Never accept certs from external tooling without verifying curve and format","Keep CA and node cert versions in lockstep during upgrades"],"tags":["handshake","certificate","curve-mismatch"],"backgroundTag":"cert-recombine-failed","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"}