mirror of
https://github.com/luxfi/zwing.git
synced 2026-09-24 00:24:38 +00:00
Z-Wing is the canonical Lux post-quantum secure channel. One construction,
one wire format, no negotiation. Layered design exposes a net.Conn so
luxfi/api/zap (and any other stream protocol) runs unchanged on top.
KEM: X-Wing (X25519 + ML-KEM-768, IETF draft-connolly-cfrg-xwing-kem
SHA3-256 combiner: "\./" || "X-Wing" || ssM || ssX || ctX || pkX)
Identity: Ed25519 + ML-DSA-65 hybrid signatures
AEAD: ChaCha20-Poly1305 with sequence-numbered nonces (per direction)
Transport: TCP (RNS routing is a follow-up package)
Public API: GenerateIdentity / Dial / Listen / Client / Server.
Six tests cover the spec end-to-end: handshake round-trip, TCP dial+listen,
ExpectedRemote pinning, key-zero-after-handshake forward secrecy, sequenced
nonces, and ZAP-over-Z-Wing application round-trip.
62 lines
1.6 KiB
Go
62 lines
1.6 KiB
Go
// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package zwing
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
// Dial connects to a Z-Wing listener at addr and runs the handshake.
|
|
// The returned net.Conn is the post-handshake encrypted channel.
|
|
func Dial(ctx context.Context, addr string, cfg *Config) (net.Conn, error) {
|
|
if cfg == nil || cfg.LocalIdentity == nil {
|
|
return nil, ErrConfigMissingID
|
|
}
|
|
d := net.Dialer{}
|
|
raw, err := d.DialContext(ctx, "tcp", addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return clientHandshake(raw, cfg)
|
|
}
|
|
|
|
// 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 Client(raw net.Conn, cfg *Config) (net.Conn, error) {
|
|
return clientHandshake(raw, cfg)
|
|
}
|
|
|
|
// Server wraps an existing net.Conn and runs the handshake as the responder.
|
|
func Server(raw net.Conn, cfg *Config) (net.Conn, error) {
|
|
return serverHandshake(raw, cfg)
|
|
}
|
|
|
|
func clientHandshake(raw net.Conn, cfg *Config) (net.Conn, error) {
|
|
if cfg.HandshakeTimeout > 0 {
|
|
_ = raw.SetDeadline(time.Now().Add(cfg.HandshakeTimeout))
|
|
defer raw.SetDeadline(time.Time{})
|
|
}
|
|
res, err := runInitiator(raw, cfg)
|
|
if err != nil {
|
|
raw.Close()
|
|
return nil, err
|
|
}
|
|
return newConn(raw, res, true)
|
|
}
|
|
|
|
func serverHandshake(raw net.Conn, cfg *Config) (net.Conn, error) {
|
|
if cfg.HandshakeTimeout > 0 {
|
|
_ = raw.SetDeadline(time.Now().Add(cfg.HandshakeTimeout))
|
|
defer raw.SetDeadline(time.Time{})
|
|
}
|
|
res, err := runResponder(raw, cfg)
|
|
if err != nil {
|
|
raw.Close()
|
|
return nil, err
|
|
}
|
|
return newConn(raw, res, false)
|
|
}
|