slackhq/nebula · critical
ErrPublicKeyMismatch
ErrPublicKeyMismatch
Error message
public key mismatch between certificate and handshake
What it means
ErrPublicKeyMismatch is returned by validateCert when the static public key recovered from the peer's certificate does not equal the static public key exchanged during the Noise handshake. This is a consistency check ensuring the certificate actually belongs to the peer performing the handshake. The library throws it to prevent accepting a valid certificate from a different identity than the one whose key was used in the cryptographic exchange.
Source
Thrown at handshake/errors.go:10
package handshake
import "errors"
var (
ErrInitiateOnResponder = errors.New("initiate called on responder")
ErrInitiateAlreadyCalled = errors.New("initiate already called")
ErrInitiateNotCalled = errors.New("initiate must be called before ProcessPacket for initiators")
ErrPacketTooShort = errors.New("packet too short")
ErrPublicKeyMismatch = errors.New("public key mismatch between certificate and handshake")
ErrIncompleteHandshake = errors.New("handshake completed without receiving required content")
ErrMachineFailed = errors.New("handshake machine has failed")
ErrUnknownSubtype = errors.New("unknown handshake subtype")
ErrMissingContent = errors.New("expected handshake content but message was empty")
ErrUnexpectedContent = errors.New("received unexpected handshake content")
ErrInvalidRemoteIndex = errors.New("peer sent an invalid index in handshake payload")
ErrIndexAllocation = errors.New("failed to allocate local index")
ErrNoCredential = errors.New("no handshake credential available for cert version")
ErrAsymmetricCipherKeys = errors.New("noise produced only one cipher key")
ErrMultiMessageUnsupported = errors.New("multi-message handshake patterns are not yet supported by the manager")
ErrSubtypeMismatch = errors.New("packet subtype does not match handshake machine subtype")
)
View on GitHub (pinned to dd8f660c0a)
Solutions
- Regenerate or redistribute the peer's certificate so it is derived from the same static private key used in the handshake
- Verify both peers load credentials from the same, current key directory and that no stale cert cache is in use
- Confirm no proxy or routing change is connecting you to a different peer than intended
Example fix
// before: peer regenerated static key but kept old cert $ ls creds/ key.prv old-cert.pem <- cert signed with previous key // after: reissue cert from the current static key $ meshctl cert issue --key creds/key.prv --out creds/cert.pem
Defensive patterns
Strategy: try-catch
Validate before calling
if cert == nil || peerStatic == nil {
return fmt.Errorf("refusing handshake: missing cert or peer static key")
} Type guard
func certMatchesPeer(cert *x509.Certificate, peerStatic []byte) bool {
rc, err := certpkg.Recombine(cert)
return err == nil && bytes.Equal(rc.PublicKey(), peerStatic)
} Try / catch
res, err := m.ProcessPacket(out, pkt)
if errors.Is(err, handshake.ErrPublicKeyMismatch) {
// identity of peer does not match its certificate: abort, do not retry
conn.Close()
log.Printf("cert/key mismatch from %s", conn.RemoteAddr())
return
} Prevention
- Always issue certificates from the exact static private key used in the handshake
- Atomically rotate key and certificate together
- Monitor for this error: repeated occurrences may indicate MITM or stale credentials
When it happens
Trigger: Machine.validateCert fails at handshake/machine.go:361 because bytes.Equal(rc.PublicKey(), m.hs.PeerStatic()) is false: the peer's ProcessPacket message carried a certificate whose recombined public key differs from the handshake's peer static key.
Common situations: Peer restarted with a regenerated static key but is still presenting a cached/old certificate; certificate files and handshake keys provisioned from different sources; misconfigured credential directories on one node; a MITM or misrouted connection presenting another node's cert.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/92f8ea0e53ab4222.
Report an issue: GitHub.