Files
zeekayandHanzo Dev 974073844f kms client: the ZAP dial carries a secret only over a hybrid session
Everything this client moves is a secret, so the dial now requires the
X25519 + ML-KEM-768 handshake to have produced an AEAD session, and
requires that session to have run ML-KEM rather than X25519 alone. A peer
that agrees neither gets no client and no request.

Hybrid rather than post-quantum alone because both halves have to fall
before the channel does: a break in the elliptic curve and a quantum
computer are two different futures, and a key read today is stored today.

It is a constant rather than a setting. There is no caller here for whom a
plaintext channel is the right answer, and a setting is a thing an
adversary gets to influence by making the handshake fail.

luxfi/kms v1.12.15 for the RequireSession dial, luxfi/keys v1.4.2 for a
service identity that keeps its signing key past the first signature.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-14 23:49:51 -07:00

56 lines
1.8 KiB
Go

package kmsclient
import (
"net"
"strconv"
"testing"
"github.com/luxfi/zap"
)
// TestZAP_APeerThatAgreesNoSessionGetsNothing pins the one property this
// client exists to keep: everything it carries is a secret, so it does not
// speak to a peer it has not agreed an X25519 + ML-KEM-768 session with.
//
// The peer here accepts the connection and answers nothing on the handshake
// opcode. That is what a peer too old to speak it looks like from this side,
// and it is also what an on-path adversary looks like — dropping one message
// is the whole downgrade, and the reward is every secret afterwards in the
// clear, in both directions.
//
// The other half of the property — that a session which agreed X25519 alone
// is refused too — is what makes the pair hybrid rather than merely modern,
// and it is enforced on the same dial. TestZAP_DialAndGetPut is its evidence:
// that round trip only completes because the peer agreed ML-KEM-768.
func TestZAP_APeerThatAgreesNoSessionGetsNothing(t *testing.T) {
port := pickEphemeralPort(t)
peer := zap.NewNode(zap.NodeConfig{
NodeID: "peer-that-will-not-agree",
ServiceType: "_kms._tcp",
Port: port,
NoDiscovery: true,
})
if err := peer.Start(); err != nil {
t.Fatalf("start peer: %v", err)
}
defer peer.Stop()
ident, err := NewIdentity(testZAPMnemonic, "hanzo/test-client")
if err != nil {
t.Fatalf("NewIdentity: %v", err)
}
defer ident.Wipe()
c, err := New(Config{
Endpoint: "zap://" + net.JoinHostPort("127.0.0.1", strconv.Itoa(port)),
Org: "hanzo",
Env: "test",
Identity: ident,
TransportNodeID: "test-client-transport",
})
if err == nil {
_ = c.Close()
t.Fatal("a usable client came back for a peer that agreed no session — every secret after this crosses in the clear")
}
}