zwing

package module
v0.5.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: BSD-3-Clause Imports: 19 Imported by: 0

Documentation

Overview

Package zwing implements Z-Wing, the canonical Lux post-quantum secure channel. It provides an authenticated, forward-secret, hybrid PQ tunnel that exposes a net.Conn so application protocols (e.g. luxfi/api/zap) run unchanged on top.

Layering:

Application: ZAP RPC (luxfi/api/zap)            <- unchanged
Channel:     Z-Wing (this package)
  KEM:       X-Wing  (X25519 + ML-KEM-768, IETF draft-connolly-cfrg-xwing-kem)
  Identity:  Ed25519 + ML-DSA-65 hybrid signatures
  AEAD:      ChaCha20-Poly1305, sequence-numbered nonces
Routing:     Plain TCP (this version) or RNS (follow-up package)

Z-Wing is one construction. There are no alternates, no build tags, no cipher negotiation. The point of standardising is that two endpoints either speak Z-Wing v1 or they don't talk.

Public API:

id, _ := zwing.GenerateIdentity()
ln, _ := zwing.Listen(":9999", &zwing.Config{LocalIdentity: id})
conn, _ := zwing.Dial(ctx, "host:9999", &zwing.Config{LocalIdentity: clientID})

`conn` and the result of `ln.Accept()` both implement net.Conn and can be passed to zap.NewConn, http.Serve, or anything else that wants a stream.

Index

Constants

View Source
const (
	XWingPublicKeySize  = mlkem.MLKEM768PublicKeySize + curve25519.PointSize  // 1184 + 32
	XWingCiphertextSize = mlkem.MLKEM768CiphertextSize + curve25519.PointSize // 1088 + 32
	XWingSharedSize     = 32
)

X-Wing wire sizes.

View Source
const DefaultPort = 9999

DefaultPort is the canonical Z-Wing TCP port.

IdentityPublicSize is the fixed-size wire encoding of an IdentityPublic.

View Source
const MaxFrameSize = 1 << 20 // 1 MiB

MaxFrameSize is the largest single transport frame Z-Wing will read or write. Application bytes larger than this are split by the channel.

Variables

View Source
var (
	ErrHandshakeFailed     = errors.New("zwing: handshake failed")
	ErrIdentityMismatch    = errors.New("zwing: remote identity does not match expected")
	ErrSignatureInvalid    = errors.New("zwing: signature verification failed")
	ErrMessageTooLarge     = errors.New("zwing: message exceeds maximum size")
	ErrShortRead           = errors.New("zwing: short read")
	ErrInvalidWireFormat   = errors.New("zwing: invalid wire format")
	ErrChannelClosed       = errors.New("zwing: channel closed")
	ErrSequenceExhausted   = errors.New("zwing: AEAD sequence number exhausted")
	ErrConfigMissingID     = errors.New("zwing: config missing LocalIdentity")
	ErrCiphertextCorrupted = errors.New("zwing: AEAD authentication failed")
)

Functions

func Client

func Client(raw net.Conn, cfg *Config) (net.Conn, error)

Client wraps an existing net.Conn (e.g. a TLS conn, a Unix socket, a pipe) and runs the Z-Wing handshake on top as the initiator.

func Dial

func Dial(ctx context.Context, addr string, cfg *Config) (net.Conn, error)

Dial connects to a Z-Wing listener at addr and runs the handshake. The returned net.Conn is the post-handshake encrypted channel.

func DialZAP added in v0.2.1

func DialZAP(ctx context.Context, addr string, cfg *Config) (*zap.Conn, error)

DialZAP opens a Z-Wing channel to addr and wraps it as a ZAP client connection. This is the canonical way to speak ZAP RPC over a PQ transport: one call, one composition, no chance of misuse.

conn, err := zwing.DialZAP(ctx, "host:9999", &zwing.Config{
    LocalIdentity:  myID,
    ExpectedRemote: pinnedServerPub, // optional
})
defer conn.Close()
resp, payload, err := conn.Call(ctx, zap.MsgFoo, body)

func ListenZAP added in v0.2.1

func ListenZAP(addr string, cfg *Config) (*zap.Listener, error)

ListenZAP binds a Z-Wing listener at addr and returns a ZAP listener that accepts post-handshake encrypted ZAP connections. Pair with zap.NewServer to host an RPC service.

ln, _ := zwing.ListenZAP(":9999", &zwing.Config{LocalIdentity: id})
srv  := zap.NewServer(ln, zap.HandlerFunc(handle))
srv.Serve(ctx)

func Server

func Server(raw net.Conn, cfg *Config) (net.Conn, error)

Server wraps an existing net.Conn and runs the handshake as the responder.

func XWingDecapsulate

func XWingDecapsulate(sk *XWingPrivateKey, ciphertext []byte) ([XWingSharedSize]byte, error)

XWingDecapsulate runs the recipient side.

func XWingEncapsulate

func XWingEncapsulate(reader io.Reader, recipient *XWingPublicKey) (ciphertext []byte, shared [XWingSharedSize]byte, err error)

XWingEncapsulate runs the encapsulator side. Output is (ciphertext, shared). The ciphertext is ML-KEM-768 ct || X25519 ephemeral pk.

Types

type Config

type Config struct {
	// LocalIdentity is this endpoint's long-term identity. Required.
	LocalIdentity *Identity

	// ExpectedRemote pins the peer's expected public identity. If set, the
	// handshake fails with ErrIdentityMismatch when the remote presents a
	// different identity. Leave nil for "any peer with a valid identity".
	ExpectedRemote *IdentityPublic

	// HandshakeTimeout bounds the handshake itself. Zero means no timeout.
	HandshakeTimeout time.Duration
}

Config configures a Z-Wing dialer or listener.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is a Z-Wing secure channel implementing net.Conn. After the handshake, every Read/Write goes through ChaCha20-Poly1305 with per-direction sequence-numbered nonces.

func (*Conn) Close

func (c *Conn) Close() error

Close closes the underlying connection.

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr returns the underlying local address.

func (*Conn) Read

func (c *Conn) Read(b []byte) (int, error)

Read reads decrypted plaintext into b. A Z-Wing record may be larger than b, in which case the remainder is buffered for the next call.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr returns the underlying remote address.

func (*Conn) RemoteIdentity

func (c *Conn) RemoteIdentity() *IdentityPublic

RemoteIdentity returns the verified remote IdentityPublic. Callers may pin specific peers via Config.ExpectedRemote, but this getter lets servers see who connected once the handshake succeeded.

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline sets read+write deadline on the underlying conn.

func (*Conn) SetReadDeadline

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline sets the read deadline on the underlying conn.

func (*Conn) SetWriteDeadline

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the write deadline on the underlying conn.

func (*Conn) Write

func (c *Conn) Write(b []byte) (int, error)

Write encrypts b as a single Z-Wing record and writes it to the underlying conn. Application payloads larger than MaxFrameSize-tag are split.

type HandshakeInit

type HandshakeInit struct {
	IdentityPub []byte // serialised IdentityPublic
	Signature   []byte // identity signature over label || IdentityPub
}

HandshakeInit is the wire form of the initiator's first message.

func (*HandshakeInit) MarshalBinary

func (h *HandshakeInit) MarshalBinary() []byte

MarshalBinary encodes a HandshakeInit as

[2 bytes BE: idLen][id bytes][2 bytes BE: sigLen][sig bytes]

type HandshakeResponse

type HandshakeResponse struct {
	XWingCiphertext []byte // X-Wing ct (1088+32 bytes)
	EncryptedID     []byte // AEAD-sealed responder identity + signature
}

HandshakeResponse is the wire form of the responder's reply. The identity payload is encrypted under a key derived from the X-Wing shared secret so the responder identity is not revealed to passive observers.

func (*HandshakeResponse) MarshalBinary

func (h *HandshakeResponse) MarshalBinary() []byte

MarshalBinary encodes a HandshakeResponse as

[XWingCiphertextSize bytes][2 bytes BE: encLen][enc bytes]

type Identity

type Identity struct {
	EdPub  ed25519.PublicKey
	EdPriv ed25519.PrivateKey

	DSAPriv *mldsa.PrivateKey

	XWing *XWingPrivateKey
}

Identity binds a peer to long-term Ed25519 + ML-DSA-65 keys plus an X-Wing static keypair used as the KEM recipient material.

func GenerateIdentity

func GenerateIdentity() (*Identity, error)

GenerateIdentity creates a fresh Z-Wing identity using crypto/rand.

func GenerateIdentityFrom

func GenerateIdentityFrom(r io.Reader) (*Identity, error)

GenerateIdentityFrom creates an identity from a custom entropy source. Useful for deterministic key generation in tests.

func (*Identity) Public

func (id *Identity) Public() *IdentityPublic

Public returns the public half of the identity.

func (*Identity) Sign

func (id *Identity) Sign(ctx, message []byte) []byte

Sign produces the Z-Wing identity signature: an Ed25519 signature over SHA-256(ctx || message) concatenated with an ML-DSA-65 signature over the same digest. ctx is a hard-coded protocol label so signatures are not portable across protocols.

ML-DSA-65 SignCtx only errors on a malformed key. Identities reaching this method came from GenerateIdentity / GenerateIdentityFrom, which never yield an unfit key — the impossible error branch is therefore dropped.

type IdentityPublic

type IdentityPublic struct {
	EdPub    ed25519.PublicKey
	DSAPub   *mldsa.PublicKey
	XWingPub *XWingPublicKey
}

IdentityPublic is the wire-serialisable, secrets-stripped half of an Identity. It is what peers exchange and verify against.

func ParseIdentityPublic

func ParseIdentityPublic(data []byte) (*IdentityPublic, error)

ParseIdentityPublic decodes the wire format produced by MarshalBinary.

func (*IdentityPublic) Equal

func (pub *IdentityPublic) Equal(other *IdentityPublic) bool

Equal reports whether two public identities have byte-identical key material.

func (*IdentityPublic) MarshalBinary

func (pub *IdentityPublic) MarshalBinary() []byte

MarshalBinary serialises the public identity:

[Ed25519 pk: 32][ML-DSA-65 pk: 1952][X-Wing pk: 1216]

func (*IdentityPublic) Verify

func (pub *IdentityPublic) Verify(ctx, message, signature []byte) error

Verify checks an identity signature produced by Sign.

type Listener

type Listener struct {
	// contains filtered or unexported fields
}

Listener is a Z-Wing listener. Accepted connections are post-handshake net.Conns ready for application traffic.

func Listen

func Listen(addr string, cfg *Config) (*Listener, error)

Listen binds a TCP listener at addr that performs Z-Wing handshakes before yielding connections.

func (*Listener) Accept

func (l *Listener) Accept() (net.Conn, error)

Accept returns the next handshaken connection. If a handshake fails the underlying conn is closed and Accept returns the error; callers that want to keep listening should call Accept again.

func (*Listener) Addr

func (l *Listener) Addr() net.Addr

Addr returns the listener's TCP address.

func (*Listener) Close

func (l *Listener) Close() error

Close closes the underlying TCP listener.

type XWingPrivateKey

type XWingPrivateKey struct {
	MLKEMPriv  *mlkem.PrivateKey
	X25519Priv [32]byte
	X25519Pub  [32]byte
}

XWingPrivateKey is the recipient's X-Wing static secret key plus the matching public material needed for the combiner.

func GenerateXWingKey

func GenerateXWingKey(reader io.Reader) (*XWingPrivateKey, error)

GenerateXWingKey produces a fresh X-Wing static keypair.

curve25519.X25519 with the Basepoint never errors (it would only error on a wrong-size scalar — we always pass 32 bytes). The error branch is therefore dead and elided.

func (*XWingPrivateKey) Public

func (sk *XWingPrivateKey) Public() *XWingPublicKey

Public returns the X-Wing public key.

type XWingPublicKey

type XWingPublicKey struct {
	MLKEM  *mlkem.PublicKey
	X25519 [32]byte
}

XWingPublicKey is the recipient's X-Wing static public key.

func ParseXWingPublicKey

func ParseXWingPublicKey(data []byte) (*XWingPublicKey, error)

ParseXWingPublicKey decodes the wire format produced by MarshalBinary.

func (*XWingPublicKey) MarshalBinary

func (pk *XWingPublicKey) MarshalBinary() []byte

MarshalBinary serialises the X-Wing public key as ML-KEM-768 || X25519.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL