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
- Variables
- func Client(raw net.Conn, cfg *Config) (net.Conn, error)
- func Dial(ctx context.Context, addr string, cfg *Config) (net.Conn, error)
- func DialZAP(ctx context.Context, addr string, cfg *Config) (*zap.Conn, error)
- func ListenZAP(addr string, cfg *Config) (*zap.Listener, error)
- func Server(raw net.Conn, cfg *Config) (net.Conn, error)
- func XWingDecapsulate(sk *XWingPrivateKey, ciphertext []byte) ([XWingSharedSize]byte, error)
- func XWingEncapsulate(reader io.Reader, recipient *XWingPublicKey) (ciphertext []byte, shared [XWingSharedSize]byte, err error)
- type Config
- type Conn
- func (c *Conn) Close() error
- func (c *Conn) LocalAddr() net.Addr
- func (c *Conn) Read(b []byte) (int, error)
- func (c *Conn) RemoteAddr() net.Addr
- func (c *Conn) RemoteIdentity() *IdentityPublic
- func (c *Conn) SetDeadline(t time.Time) error
- func (c *Conn) SetReadDeadline(t time.Time) error
- func (c *Conn) SetWriteDeadline(t time.Time) error
- func (c *Conn) Write(b []byte) (int, error)
- type HandshakeInit
- type HandshakeResponse
- type Identity
- type IdentityPublic
- type Listener
- type XWingPrivateKey
- type XWingPublicKey
Constants ¶
const ( XWingPublicKeySize = mlkem.MLKEM768PublicKeySize + curve25519.PointSize // 1184 + 32 XWingCiphertextSize = mlkem.MLKEM768CiphertextSize + curve25519.PointSize // 1088 + 32 )
X-Wing wire sizes.
const DefaultPort = 9999
DefaultPort is the canonical Z-Wing TCP port.
const IdentityPublicSize = ed25519.PublicKeySize + mldsa.MLDSA65PublicKeySize + XWingPublicKeySize
IdentityPublicSize is the fixed-size wire encoding of an IdentityPublic.
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 ¶
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 ¶
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 ¶
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
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
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 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) Read ¶
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 ¶
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 ¶
SetDeadline sets read+write deadline on the underlying conn.
func (*Conn) SetReadDeadline ¶
SetReadDeadline sets the read deadline on the underlying conn.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline sets the write deadline on the underlying conn.
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 ¶
GenerateIdentity creates a fresh Z-Wing identity using crypto/rand.
func GenerateIdentityFrom ¶
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 ¶
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 ¶
Listen binds a TCP listener at addr that performs Z-Wing handshakes before yielding connections.
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 ¶
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.