mirror of
https://github.com/lux-apps/papers.git
synced 2026-10-30 18:15:21 +00:00
Bylines, paper authorship, correspondence addresses and the founder blurb under each post named an individual. They name the organisation that published the work instead: the brand on posts and pages, the research byline on papers, and research@ in place of a personal address. Both are what these repositories already used elsewhere — nothing new was coined. Co-authors are untouched. Where a paper named someone else alongside, they stay named; only the one byline changed. Bibliography entries, running heads, PDF metadata and affiliations move with it, so a citation still resolves to whoever the cited paper names. Sample and fixture data uses the placeholder name these tests already use elsewhere, so no assertion changed meaning.
674 lines
27 KiB
TeX
674 lines
27 KiB
TeX
\documentclass[11pt,a4paper]{article}
|
|
\usepackage{../shared/luxcover}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage{amsmath,amssymb,amsfonts,amsthm}
|
|
\usepackage{graphicx}
|
|
\usepackage{hyperref}
|
|
\usepackage{booktabs}
|
|
\usepackage{listings}
|
|
\usepackage{xcolor}
|
|
\usepackage{tikz}
|
|
\usepackage{float}
|
|
\usepackage{enumitem}
|
|
\usepackage{array}
|
|
\usepackage{tabularx}
|
|
\input{../shared/lstlang}
|
|
\usetikzlibrary{arrows.meta,positioning,shapes.geometric,fit,calc}
|
|
|
|
\hypersetup{colorlinks=true,linkcolor=black,citecolor=blue,urlcolor=blue}
|
|
|
|
\lstset{
|
|
basicstyle=\ttfamily\scriptsize,
|
|
breaklines=true,
|
|
frame=single,
|
|
numbers=left,
|
|
numberstyle=\tiny\color{gray},
|
|
showstringspaces=false,
|
|
}
|
|
|
|
\newtheorem{theorem}{Theorem}
|
|
\newtheorem{definition}{Definition}
|
|
\newtheorem{remark}{Remark}
|
|
|
|
\title{LP-9702: Z-Wing\\Hybrid Post-Quantum Secure Channel}
|
|
|
|
\author{
|
|
Lux Research\thanks{Corresponding author: [email protected]}\\
|
|
\textit{Lux Industries, Inc.}\\
|
|
\texttt{[email protected]}
|
|
}
|
|
|
|
\date{}
|
|
|
|
\begin{document}
|
|
|
|
\luxcoverpage
|
|
|
|
\begin{abstract}
|
|
Z-Wing is a hybrid post-quantum secure channel for Lux node-to-node and
|
|
agent-to-agent communication. The key encapsulation mechanism is X-Wing
|
|
(ML-KEM-768 $+$ X25519) per \texttt{draft-connolly-cfrg-xwing-kem}, used
|
|
verbatim with the standard SHA3-256 combiner so that Z-Wing is wire-compatible
|
|
with any conformant X-Wing implementation. Identity binding is hybrid
|
|
Ed25519 $+$ ML-DSA-65 with both signatures committing to the same handshake
|
|
transcript. The handshake is one round trip; after completion the channel
|
|
exposes \texttt{net.Conn}, so any byte-stream protocol --- in particular the
|
|
ZAP wire format (\texttt{luxfi/api/zap}) --- runs unmodified above it.
|
|
Z-Wing supersedes LP-9701 (RNS hybrid-PQ link), which used a Lux-specific
|
|
HKDF combiner and inlined identity binding into the handshake construction.
|
|
LP-9702 collapses the two PQ surfaces in Lux today (LP-9701 in Go,
|
|
\texttt{zap-protocol} hybrid in Rust) onto a single canonical construction.
|
|
\end{abstract}
|
|
|
|
\tableofcontents
|
|
\newpage
|
|
|
|
\section{Motivation}
|
|
|
|
Two hybrid post-quantum constructions are deployed in Lux today.
|
|
|
|
\begin{enumerate}[leftmargin=*]
|
|
\item \textbf{LP-9701 (RNS hybrid link, Go).} Implemented in
|
|
\texttt{luxfi/node/network/dialer/rns\_link.go}. Combines X25519
|
|
and ML-KEM-768 with an ad-hoc combiner: the ML-KEM contribution
|
|
is derived deterministically from a sorted hash of both peers'
|
|
ML-KEM ephemeral public keys (rather than a true KEM
|
|
encapsulation), then mixed with the X25519 ECDH output via
|
|
HKDF-SHA256 under Lux-specific labels
|
|
(\texttt{RNS-HybridLink-v1}, \texttt{rns-hybrid-link-keys},
|
|
\texttt{RNS-MLKEM-Hybrid-v1}). Identity binding (Ed25519 $+$ ML-DSA-65)
|
|
is fused into the handshake messages.
|
|
\item \textbf{\texttt{zap-protocol} hybrid (Rust).} Implemented in
|
|
\texttt{zap/src/crypto.rs}. X25519 $+$ ML-KEM-768 with HKDF-SHA256
|
|
under the Lux-specific label \texttt{ZAP-HYBRID-HANDSHAKE-v1}.
|
|
Identity is layered separately by the application.
|
|
\end{enumerate}
|
|
|
|
Both constructions are sound but neither is the IETF X-Wing combiner
|
|
(\texttt{draft-connolly-cfrg-xwing-kem}), which uses SHA3-256 with a
|
|
fixed 6-byte label and a fixed input order. That makes cross-org and
|
|
cross-language interop a per-implementation matter rather than a
|
|
standards-conformant property.
|
|
|
|
LP-9702 collapses both onto a single canonical construction:
|
|
\begin{itemize}[leftmargin=*]
|
|
\item KEM: X-Wing exactly as specified by the IETF draft.
|
|
\item Identity: Ed25519 $+$ ML-DSA-65, both signing the handshake
|
|
transcript hash.
|
|
\item Layering: the channel is a \texttt{net.Conn}; any byte-stream
|
|
protocol rides on top with no further specification.
|
|
\end{itemize}
|
|
|
|
\section{Architecture}
|
|
|
|
\subsection{Layering}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\begin{tikzpicture}[
|
|
box/.style={draw, rectangle, minimum width=10cm, minimum height=0.9cm,
|
|
font=\sffamily, align=center},
|
|
arrow/.style={-{Latex[length=2mm]}, thick}
|
|
]
|
|
\node[box, fill=black!5] (app) {Application (ZAP RPC, p2p.Sender, Warp signing, AI agent)};
|
|
\node[box, fill=black!10, below=0.15cm of app] (zap) {ZAP wire format \quad (\texttt{luxfi/api/zap}, optional)};
|
|
\node[box, fill=black!20, below=0.15cm of zap] (zwing) {Z-Wing channel \quad (\texttt{net.Conn}; X-Wing $+$ Ed25519/ML-DSA-65 $+$ ChaCha20-Poly1305)};
|
|
\node[box, fill=black!5, below=0.15cm of zwing] (xport){Underlying transport \quad (TCP, Unix socket, RNS, QUIC stream)};
|
|
|
|
\draw[arrow] (app) -- (zap);
|
|
\draw[arrow] (zap) -- (zwing);
|
|
\draw[arrow] (zwing) -- (xport);
|
|
\end{tikzpicture}
|
|
\caption{Z-Wing layering. The Z-Wing channel is a \texttt{net.Conn} whose
|
|
underlying byte stream is any reliable, ordered transport. ZAP and any
|
|
other application protocol ride above without modification.}
|
|
\label{fig:layering}
|
|
\end{figure}
|
|
|
|
\subsection{Components}
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{lX}
|
|
\toprule
|
|
\textbf{Component} & \textbf{Construction} \\
|
|
\midrule
|
|
KEM & X-Wing $=$ ML-KEM-768 (FIPS 203) $+$ X25519 (RFC 7748), combined with SHA3-256 \\
|
|
Identity & Ed25519 (RFC 8032) $+$ ML-DSA-65 (FIPS 204), parallel signatures over transcript hash \\
|
|
KDF & HKDF-SHA3-256 (RFC 5869 with FIPS 202 hash) \\
|
|
AEAD & ChaCha20-Poly1305 (RFC 8439) \\
|
|
Transcript & SHA3-256 (FIPS 202) \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\end{table}
|
|
|
|
\subsection{Channel interface}
|
|
|
|
The Z-Wing channel implements the Go \texttt{net.Conn} contract: \texttt{Read},
|
|
\texttt{Write}, \texttt{Close}, \texttt{LocalAddr}, \texttt{RemoteAddr}, the
|
|
three deadline setters. After \texttt{Handshake} returns, the channel is
|
|
authenticated, confidential, integrity-protected, and forward-secret.
|
|
\texttt{Read} and \texttt{Write} operate on plaintext bytes; encryption and
|
|
framing are handled internally.
|
|
|
|
\section{KEM Construction (Normative)}
|
|
|
|
\subsection{Component primitives}
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{lll}
|
|
\toprule
|
|
\textbf{Quantity} & \textbf{Symbol} & \textbf{Size (bytes)} \\
|
|
\midrule
|
|
ML-KEM-768 encapsulation key & $pk_M$ & 1184 \\
|
|
ML-KEM-768 decapsulation key & $sk_M$ & 2400 \\
|
|
ML-KEM-768 ciphertext & $ct_M$ & 1088 \\
|
|
ML-KEM-768 shared secret & $ss_M$ & 32 \\
|
|
X25519 public key & $pk_X$ & 32 \\
|
|
X25519 private key & $sk_X$ & 32 \\
|
|
X25519 ephemeral public key (sender) & $ct_X$ & 32 \\
|
|
X25519 shared secret & $ss_X$ & 32 \\
|
|
\midrule
|
|
X-Wing encapsulation key & $pk$ & 1216 \\
|
|
X-Wing ciphertext & $ct$ & 1120 \\
|
|
X-Wing shared secret & $ss$ & 32 \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\end{table}
|
|
|
|
The X-Wing encapsulation key is the byte concatenation $pk = pk_M \,\Vert\, pk_X$
|
|
(1184 $+$ 32 $=$ 1216 bytes). The X-Wing ciphertext is
|
|
$ct = ct_M \,\Vert\, ct_X$ (1088 $+$ 32 $=$ 1120 bytes).
|
|
|
|
\subsection{Combiner}
|
|
|
|
The X-Wing combiner is computed verbatim per
|
|
\texttt{draft-connolly-cfrg-xwing-kem}:
|
|
|
|
\begin{equation}
|
|
ss \;=\; \mathrm{SHA3\text{-}256}\!\left( ss_M \,\Vert\, ss_X \,\Vert\, ct_X \,\Vert\, pk_X \,\Vert\, L_{\mathrm{XW}} \right)
|
|
\label{eq:combiner}
|
|
\end{equation}
|
|
|
|
where the label $L_{\mathrm{XW}}$ is the 6-byte ASCII string
|
|
\texttt{\textbackslash./} concatenated with \texttt{/\textasciicircum\textbackslash}:
|
|
|
|
\begin{equation}
|
|
L_{\mathrm{XW}} \;=\; \texttt{0x5c}\;\texttt{0x2e}\;\texttt{0x2f}\;\texttt{0x2f}\;\texttt{0x5e}\;\texttt{0x5c}
|
|
\end{equation}
|
|
|
|
The label appears \emph{last} in the SHA3-256 input. The input order
|
|
$ss_M \Vert ss_X \Vert ct_X \Vert pk_X \Vert L_{\mathrm{XW}}$ is normative
|
|
and MUST NOT be permuted; the combiner is a fixed function of these five
|
|
operands in this exact order.
|
|
|
|
\subsection{Encapsulation}
|
|
|
|
Given peer encapsulation key $pk = pk_M \,\Vert\, pk_X$, the sender:
|
|
|
|
\begin{enumerate}[leftmargin=*]
|
|
\item Generates an ephemeral X25519 key pair $(sk_X^e, pk_X^e)$ per RFC 7748,
|
|
with the standard clamping of $sk_X^e$.
|
|
\item Computes $ct_X \leftarrow pk_X^e$ and
|
|
$ss_X \leftarrow \mathrm{X25519}(sk_X^e, pk_X)$. If $ss_X$ is the
|
|
all-zero 32-byte string the sender MUST abort with a key-exchange
|
|
failure (low-order point rejection per RFC 7748 \S6.1).
|
|
\item Calls ML-KEM-768 encapsulation against $pk_M$, producing
|
|
$(ct_M, ss_M)$ per FIPS 203.
|
|
\item Computes $ss$ via Equation~\ref{eq:combiner}.
|
|
\item Outputs $(ct, ss)$ where $ct = ct_M \,\Vert\, ct_X$.
|
|
\end{enumerate}
|
|
|
|
\subsection{Decapsulation}
|
|
|
|
Given decapsulation material $(sk_M, sk_X, pk_X)$ and ciphertext
|
|
$ct = ct_M \,\Vert\, ct_X$, the receiver:
|
|
|
|
\begin{enumerate}[leftmargin=*]
|
|
\item Splits $ct$ into $ct_M$ (first 1088 bytes) and $ct_X$
|
|
(next 32 bytes). Any other length MUST be rejected.
|
|
\item Computes $ss_M \leftarrow \mathrm{ML\text{-}KEM\text{-}768.Decap}(sk_M, ct_M)$
|
|
per FIPS 203. ML-KEM-768 implicit rejection is preserved: a
|
|
malformed $ct_M$ yields a pseudorandom $ss_M$ and the protocol
|
|
MUST proceed to the AEAD step, where the failure surfaces as
|
|
an authentication-tag mismatch.
|
|
\item Computes $ss_X \leftarrow \mathrm{X25519}(sk_X, ct_X)$. The
|
|
all-zero output rejection of RFC 7748 \S6.1 MUST be enforced.
|
|
\item Computes $ss$ via Equation~\ref{eq:combiner}.
|
|
\end{enumerate}
|
|
|
|
\section{Identity (Normative)}
|
|
|
|
\subsection{Hybrid identity}
|
|
|
|
A Z-Wing identity is the pair $(I_E, I_M)$ where $I_E$ is an Ed25519 key
|
|
pair (RFC 8032; 32-byte public key, 32-byte seed, 64-byte signature) and
|
|
$I_M$ is an ML-DSA-65 key pair (FIPS 204; 1952-byte public key,
|
|
4032-byte private key, 3309-byte signature).
|
|
|
|
The 32-byte node identifier is
|
|
|
|
\begin{equation}
|
|
\mathrm{NodeID} \;=\; \mathrm{SHA3\text{-}256}\!\left( I_E.\mathrm{pk} \,\Vert\, I_M.\mathrm{pk} \right) .
|
|
\end{equation}
|
|
|
|
\subsection{Canonical signing message}
|
|
|
|
Both identity signatures cover the same canonical message:
|
|
|
|
\begin{equation}
|
|
M_{\sigma} \;=\; \texttt{"zwing/1 transcript "} \,\Vert\, T
|
|
\end{equation}
|
|
|
|
where the literal prefix is the 19-byte ASCII string \texttt{"zwing/1
|
|
transcript "} (note the trailing space) and $T$ is the 32-byte
|
|
transcript hash defined in \S\ref{sec:handshake}.
|
|
The fixed prefix is a domain separator; signatures over $M_{\sigma}$
|
|
are not valid for any other Lux protocol context.
|
|
|
|
\subsection{Verification}
|
|
|
|
A peer's identity is bound to the channel only if BOTH signatures
|
|
verify against $M_{\sigma}$ under the advertised public keys. Either
|
|
signature failing aborts the handshake. Forging an accepted identity
|
|
binding requires breaking BOTH Ed25519 (current ECC attacks, including
|
|
quantum) AND ML-DSA-65 (lattice attacks).
|
|
|
|
\section{Handshake (Normative)}
|
|
\label{sec:handshake}
|
|
|
|
\subsection{Wire framing}
|
|
|
|
Handshake messages are length-prefixed: a 4-byte big-endian unsigned
|
|
length $L$ followed by $L$ bytes of payload. $L$ MUST NOT exceed
|
|
$2^{20}$ ($1\,048\,576$) bytes; receivers MUST close the underlying
|
|
transport on any larger value.
|
|
|
|
\subsection{HandshakeInit (initiator $\to$ responder)}
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{llXr}
|
|
\toprule
|
|
\textbf{Field} & \textbf{Type} & \textbf{Description} & \textbf{Bytes} \\
|
|
\midrule
|
|
\texttt{magic} & u32 BE & \texttt{0x5A57494E} (ASCII \texttt{"ZWIN"}) & 4 \\
|
|
\texttt{version} & u8 & Protocol version, MUST be \texttt{0x01} & 1 \\
|
|
\texttt{flags} & u8 & Reserved, MUST be \texttt{0x00} & 1 \\
|
|
\texttt{ed25519\_pk}& bytes & Initiator's long-term Ed25519 public key & 32 \\
|
|
\texttt{mldsa\_pk} & bytes & Initiator's long-term ML-DSA-65 public key & 1952 \\
|
|
\texttt{xwing\_pk} & bytes & Initiator's ephemeral X-Wing encapsulation key & 1216 \\
|
|
\texttt{nonce\_i} & bytes & Initiator handshake nonce (uniformly random) & 32 \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\caption{HandshakeInit. Fixed total length: 3239 bytes. No padding, no extensions.}
|
|
\end{table}
|
|
|
|
\subsection{HandshakeResponse (responder $\to$ initiator)}
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{llXr}
|
|
\toprule
|
|
\textbf{Field} & \textbf{Type} & \textbf{Description} & \textbf{Bytes} \\
|
|
\midrule
|
|
\texttt{magic} & u32 BE & \texttt{0x5A57494E} & 4 \\
|
|
\texttt{version} & u8 & MUST equal initiator's version & 1 \\
|
|
\texttt{flags} & u8 & Reserved, MUST be \texttt{0x00} & 1 \\
|
|
\texttt{ed25519\_pk}& bytes & Responder's long-term Ed25519 public key & 32 \\
|
|
\texttt{mldsa\_pk} & bytes & Responder's long-term ML-DSA-65 public key & 1952 \\
|
|
\texttt{xwing\_ct} & bytes & X-Wing ciphertext encapsulated to initiator's key & 1120 \\
|
|
\texttt{nonce\_r} & bytes & Responder handshake nonce & 32 \\
|
|
\texttt{ed25519\_sig}& bytes & Responder's Ed25519 signature over $M_{\sigma}$ & 64 \\
|
|
\texttt{mldsa\_sig} & bytes & Responder's ML-DSA-65 signature over $M_{\sigma}$ & 3309 \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\caption{HandshakeResponse. Fixed total length: 6515 bytes.}
|
|
\end{table}
|
|
|
|
\subsection{HandshakeFinish (initiator $\to$ responder, encrypted)}
|
|
|
|
After deriving session keys, the initiator sends a single AEAD-protected
|
|
frame (per \S\ref{sec:transport}) carrying:
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{llXr}
|
|
\toprule
|
|
\textbf{Field} & \textbf{Type} & \textbf{Description} & \textbf{Bytes} \\
|
|
\midrule
|
|
\texttt{ed25519\_sig}& bytes & Initiator's Ed25519 signature over $M_{\sigma}$ & 64 \\
|
|
\texttt{mldsa\_sig} & bytes & Initiator's ML-DSA-65 signature over $M_{\sigma}$ & 3309 \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\caption{HandshakeFinish (plaintext fields). Encrypted as the first
|
|
i$\to$r data frame.}
|
|
\end{table}
|
|
|
|
\begin{remark}
|
|
Z-Wing is therefore one round trip on the wire: HandshakeInit and
|
|
HandshakeResponse cross before any application data flows. The initiator's
|
|
signature is delivered as the first AEAD frame, encrypted under the
|
|
freshly derived session keys. This design preserves 1-RTT
|
|
(application data MAY be appended to the same TCP segment as
|
|
HandshakeFinish) while keeping the initiator identity hidden from
|
|
passive observers.
|
|
\end{remark}
|
|
|
|
\subsection{Transcript}
|
|
|
|
The transcript hash $T$ binds every byte either party put on the wire
|
|
during the handshake:
|
|
|
|
\begin{equation}
|
|
T \;=\; \mathrm{SHA3\text{-}256}\!\left( \texttt{HandshakeInit} \,\Vert\, \texttt{HandshakeResponseHeader} \right)
|
|
\end{equation}
|
|
|
|
where \texttt{HandshakeResponseHeader} is the responder's message with
|
|
\texttt{ed25519\_sig} and \texttt{mldsa\_sig} omitted (the first
|
|
$4 + 1 + 1 + 32 + 1952 + 1120 + 32 = 3142$ bytes). Both peers compute
|
|
$T$ identically from their view of the wire. $T$ is the value used in
|
|
$M_{\sigma}$.
|
|
|
|
\subsection{Key schedule}
|
|
|
|
After decapsulation both peers hold the X-Wing shared secret $ss$
|
|
(32 bytes). Session keys are derived via HKDF-SHA3-256 (RFC 5869 with
|
|
SHA3-256 as the hash):
|
|
|
|
\begin{align}
|
|
\mathrm{PRK} &\;=\; \mathrm{HKDF\text{-}Extract}_{\mathrm{SHA3\text{-}256}}(\,\mathrm{salt}=T,\ \mathrm{IKM}=ss\,) \\
|
|
K_{i\to r} &\;=\; \mathrm{HKDF\text{-}Expand}_{\mathrm{SHA3\text{-}256}}(\mathrm{PRK},\ \texttt{"zwing-i2r"},\ 32) \\
|
|
K_{r\to i} &\;=\; \mathrm{HKDF\text{-}Expand}_{\mathrm{SHA3\text{-}256}}(\mathrm{PRK},\ \texttt{"zwing-r2i"},\ 32)
|
|
\end{align}
|
|
|
|
The info labels are the 9-byte ASCII strings
|
|
\texttt{"zwing-i2r"} (hex \texttt{7a 77 69 6e 67 2d 69 32 72}) and
|
|
\texttt{"zwing-r2i"} (hex \texttt{7a 77 69 6e 67 2d 72 32 69}).
|
|
Each derived key is 32 bytes, sized for ChaCha20.
|
|
|
|
\subsection{State machines}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{XX}
|
|
\toprule
|
|
\textbf{Initiator} & \textbf{Responder} \\
|
|
\midrule
|
|
\texttt{INIT} $\to$ generate X-Wing keypair, $\mathrm{nonce}_i$ &
|
|
\texttt{LISTEN} \\
|
|
Send HandshakeInit; transition to \texttt{WAIT\_RESP} &
|
|
Receive HandshakeInit; validate magic, version, flags, lengths \\
|
|
Receive HandshakeResponse; validate; decap $ss$; compute $T$, $\mathrm{PRK}$, $K_{i\to r}$, $K_{r\to i}$ &
|
|
Encap $ss$; compute $T$, $\mathrm{PRK}$, $K_{i\to r}$, $K_{r\to i}$; sign $M_{\sigma}$ \\
|
|
Verify responder $\sigma_E, \sigma_M$ over $M_{\sigma}$; abort on either failure &
|
|
Send HandshakeResponse; transition to \texttt{WAIT\_FINISH} \\
|
|
Sign $M_{\sigma}$; send HandshakeFinish (AEAD frame under $K_{i\to r}$, sequence 0); transition to \texttt{ESTABLISHED} &
|
|
Receive first AEAD frame under $K_{i\to r}$, sequence 0; decrypt; verify initiator $\sigma_E, \sigma_M$ over $M_{\sigma}$; transition to \texttt{ESTABLISHED} \\
|
|
Zero ephemeral X-Wing keys & Zero responder-side X-Wing decap material \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\caption{Z-Wing handshake state machines.}
|
|
\end{figure}
|
|
|
|
Any validation failure --- magic mismatch, version mismatch, non-zero
|
|
\texttt{flags}, length-prefix overflow, low-order X25519 point, signature
|
|
verification failure, AEAD tag failure --- MUST close the underlying
|
|
transport without a recovery message. There is no error frame.
|
|
|
|
\subsection{Negotiation}
|
|
|
|
There is none. Magic, version, KEM, identity scheme, KDF, AEAD, and
|
|
transcript hash are fixed. A peer that does not understand
|
|
\texttt{version=0x01} closes. Future versions allocate a new value.
|
|
|
|
\section{Transport Frame (Normative)}
|
|
\label{sec:transport}
|
|
|
|
After \texttt{ESTABLISHED}, every byte exchanged in either direction is
|
|
carried in AEAD frames.
|
|
|
|
\subsection{Frame format}
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{llXr}
|
|
\toprule
|
|
\textbf{Field} & \textbf{Type} & \textbf{Description} & \textbf{Bytes} \\
|
|
\midrule
|
|
\texttt{length} & u32 BE & Length of \texttt{ciphertext} including AEAD tag & 4 \\
|
|
\texttt{ciphertext} & bytes & ChaCha20-Poly1305 ciphertext $\Vert$ 16-byte tag & $L$ \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\caption{Z-Wing transport frame. Fixed 4-byte big-endian length prefix
|
|
followed by AEAD output.}
|
|
\end{table}
|
|
|
|
\texttt{length} MUST NOT exceed $2^{20}$ bytes. Receivers MUST close the
|
|
transport on any larger value. The minimum \texttt{length} is 17 (one
|
|
plaintext byte plus the 16-byte tag); zero-length plaintexts are not
|
|
permitted.
|
|
|
|
\subsection{Nonce derivation}
|
|
|
|
Each direction maintains an independent 64-bit sequence counter
|
|
$n_{i\to r}$, $n_{r\to i}$, both initialised to zero. The 12-byte
|
|
ChaCha20-Poly1305 nonce for a frame is
|
|
|
|
\begin{equation}
|
|
\mathrm{nonce} \;=\; \texttt{0x00}\;\texttt{0x00}\;\texttt{0x00}\;\texttt{0x00} \,\Vert\, n_{\text{dir, BE}}^{(64)}
|
|
\end{equation}
|
|
|
|
i.e. four zero bytes followed by the directional sequence counter as a
|
|
big-endian unsigned 64-bit integer. The counter is incremented after
|
|
each successful AEAD operation (encryption on the sender side,
|
|
decryption on the receiver side).
|
|
|
|
\subsection{Replay and ordering}
|
|
|
|
Sequence counters are strictly increasing. Receivers MUST verify that
|
|
the incoming counter equals the expected next value; any deviation MUST
|
|
close the transport. Reordering, replay, gap, or duplication terminates
|
|
the channel. Because the underlying transport is reliable and ordered
|
|
(TCP, Unix, RNS link), this is an integrity property, not a packet
|
|
filter.
|
|
|
|
\subsection{Counter exhaustion}
|
|
|
|
A direction that reaches $n_{\text{dir}} = 2^{64} - 1$ MUST close the
|
|
transport before encrypting another frame. At a sustained $10^6$ frames
|
|
per second this exhausts in $\sim 5.85 \times 10^5$ years; the bound
|
|
exists to make exhaustion a defined error rather than an undefined
|
|
overflow.
|
|
|
|
\subsection{Application MTU}
|
|
|
|
Plaintext payload per frame MUST be at most $2^{20} - 16 = 1\,048\,560$
|
|
bytes (1 MiB minus the AEAD tag). \texttt{Read}/\texttt{Write}
|
|
implementations MAY fragment larger application writes across multiple
|
|
frames; the layering above Z-Wing (\texttt{net.Conn}) is byte-oriented,
|
|
so frame boundaries are not visible to the application.
|
|
|
|
\section{Security Analysis}
|
|
|
|
\subsection{Hybrid KEM security}
|
|
|
|
The X-Wing combiner provides IND-CCA security if EITHER ML-KEM-768 OR
|
|
the gap-CDH variant of X25519 (with the SHA3-256 random oracle
|
|
assumption) remains hard. The construction and proof are
|
|
\texttt{draft-connolly-cfrg-xwing-kem}; this LP adopts them by
|
|
reference. An attacker MUST break both component primitives to
|
|
recover the session shared secret $ss$.
|
|
|
|
\subsection{Forward secrecy}
|
|
|
|
The X-Wing key pair is ephemeral per handshake and zeroed once
|
|
session keys are derived. The 32-byte $ss$ is consumed by HKDF-Extract
|
|
and then discarded. Compromise of long-term Ed25519 or ML-DSA-65
|
|
identity keys does not retroactively decrypt prior sessions because
|
|
neither participates in $ss$ derivation; identities only sign
|
|
$M_{\sigma}$.
|
|
|
|
\subsection{Identity binding}
|
|
|
|
$M_{\sigma}$ contains the transcript hash $T$, which commits to every
|
|
byte of HandshakeInit and the unsigned portion of HandshakeResponse,
|
|
including the X-Wing public key, the ciphertext, both nonces, and both
|
|
peers' long-term identities. A signature over $M_{\sigma}$ is therefore
|
|
not replayable in any other session. Identity forgery requires breaking
|
|
BOTH Ed25519 and ML-DSA-65 simultaneously; this is the defence-in-depth
|
|
property the parallel-signature construction provides.
|
|
|
|
\subsection{Downgrade resistance}
|
|
|
|
There is no negotiation surface. Magic, version, KEM, AEAD, transcript
|
|
hash, and identity scheme are fixed bytes on the wire. An active
|
|
attacker cannot induce either peer to use a weaker construction
|
|
because there is no weaker construction in the protocol grammar.
|
|
|
|
\subsection{Quantum threat model}
|
|
|
|
\begin{itemize}[leftmargin=*]
|
|
\item \textbf{Store-now-decrypt-later.} Mitigated by ML-KEM-768
|
|
contributing to $ss$. A future cryptanalytically relevant
|
|
quantum computer (CRQC) capable of breaking X25519 cannot
|
|
recover $ss$ without also breaking ML-KEM-768.
|
|
\item \textbf{Real-time impersonation under CRQC.} Mitigated by
|
|
ML-DSA-65. Without ML-DSA forgery the attacker cannot produce
|
|
an accepted handshake even if Ed25519 is broken in real time.
|
|
\item \textbf{Long-term identity leak.} Outside the channel's
|
|
threat model; addressed at the layer that issues identities
|
|
(Lux KMS / I-Chain).
|
|
\end{itemize}
|
|
|
|
\subsection{Side channels}
|
|
|
|
ML-KEM-768 and ML-DSA-65 implementations MUST be constant-time per
|
|
FIPS 203 \S3.3 and FIPS 204 \S3.5 respectively. ChaCha20-Poly1305 is
|
|
the chosen AEAD specifically because table-free constant-time
|
|
implementations are routine. AES-GCM is not part of this LP.
|
|
|
|
\section{Layering: ZAP on Z-Wing}
|
|
|
|
The ZAP wire format (\texttt{luxfi/api/zap}) requires only a
|
|
\texttt{net.Conn}; \texttt{zap.NewConn} accepts any value satisfying
|
|
that interface. Z-Wing implements \texttt{net.Conn}. ZAP-over-Z-Wing is
|
|
therefore the literal composition of two independent specifications;
|
|
this LP does not modify ZAP and ZAP does not constrain Z-Wing.
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\begin{tabularx}{\textwidth}{lX}
|
|
\toprule
|
|
\textbf{Deployment} & \textbf{Composition} \\
|
|
\midrule
|
|
TCP only & \texttt{zap.NewConn(net.Dial("tcp", addr))} --- no PQ, no PFS, no auth. \\
|
|
Z-Wing & \texttt{zap.NewConn(zwing.Dial(ctx, addr, ident, peerPK))} --- hybrid PQ, PFS, mutual auth. \\
|
|
Z-Wing on RNS & \texttt{zap.NewConn(zwing.NewChannel(rns.Dial(...)))} --- as above plus mesh routing. \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
\end{table}
|
|
|
|
The same ZAP RPC surface (opcodes, request-ID demultiplexing, message
|
|
framing) operates identically in all three deployments. Operators
|
|
choose the channel; applications do not.
|
|
|
|
\section{Migration from LP-9701}
|
|
|
|
LP-9702 supersedes LP-9701. The migration is a hard cut: there is no
|
|
negotiation between LP-9701 and LP-9702 wire formats, and no
|
|
backward-compatible mode in the Z-Wing handshake.
|
|
|
|
\begin{itemize}[leftmargin=*]
|
|
\item \textbf{Identity reuse.} Existing Ed25519 and ML-DSA-65
|
|
identity keys carry forward unchanged. The Z-Wing
|
|
\texttt{NodeID} is recomputed because it now binds both keys
|
|
explicitly; deployments MUST treat the new \texttt{NodeID} as
|
|
the canonical identifier and update peer lists.
|
|
\item \textbf{Wire format.} The LP-9701 \texttt{MsgTypeLinkRequest}
|
|
($\texttt{0x01}$) byte is incompatible with Z-Wing's magic
|
|
$\texttt{0x5A57494E}$. A peer receiving the wrong opening
|
|
bytes MUST close.
|
|
\item \textbf{Combiner.} The LP-9701 combiner
|
|
(HKDF-SHA256 over X25519 ECDH and a sorted-public-key hash)
|
|
is replaced by the IETF X-Wing combiner (Equation~\ref{eq:combiner}).
|
|
No LP-9701 session key is interconvertible with a Z-Wing
|
|
session key.
|
|
\item \textbf{RNS integration.} Z-Wing-on-RNS is specified by a
|
|
separate package (\texttt{luxfi/zwing/rns}) and is out of
|
|
scope for this LP. The LP-9701 RNS announce/discovery layer
|
|
continues to operate; only the link-layer handshake changes.
|
|
\item \textbf{Deprecation.} LP-9701 is deprecated by this document.
|
|
Implementations MUST NOT initiate new LP-9701 sessions once
|
|
LP-9702 is deployed. Coordinated rollout of all validators in
|
|
a network is the canonical migration path.
|
|
\end{itemize}
|
|
|
|
\section{IANA / Wire Considerations}
|
|
|
|
\begin{itemize}[leftmargin=*]
|
|
\item \textbf{TCP port.} 9999, the canonical Lux ZAP port. Z-Wing
|
|
completes its handshake before any ZAP traffic; the same port
|
|
carries both phases of the connection.
|
|
\item \textbf{ALPN.} Where ALPN negotiation is present (e.g. inside
|
|
a TLS/QUIC stream that Z-Wing rides above), the identifier is
|
|
\texttt{"zwing/1"}. This LP does not require ALPN.
|
|
\item \textbf{Magic.} \texttt{0x5A57494E} (\texttt{"ZWIN"}). This
|
|
prefix MAY be used by transport-layer demultiplexers to
|
|
distinguish Z-Wing from other byte-stream protocols on the
|
|
same port.
|
|
\end{itemize}
|
|
|
|
\section{References}
|
|
|
|
\begin{thebibliography}{99}
|
|
|
|
\bibitem{fips203} National Institute of Standards and Technology.
|
|
\textit{FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard
|
|
(ML-KEM)}.
|
|
\url{https://doi.org/10.6028/NIST.FIPS.203}.
|
|
|
|
\bibitem{fips204} National Institute of Standards and Technology.
|
|
\textit{FIPS 204: Module-Lattice-Based Digital Signature Standard
|
|
(ML-DSA)}.
|
|
\url{https://doi.org/10.6028/NIST.FIPS.204}.
|
|
|
|
\bibitem{fips202} National Institute of Standards and Technology.
|
|
\textit{FIPS 202: SHA-3 Standard: Permutation-Based Hash and
|
|
Extendable-Output Functions}.
|
|
\url{https://doi.org/10.6028/NIST.FIPS.202}.
|
|
|
|
\bibitem{rfc7748} A.\ Langley, M.\ Hamburg, S.\ Turner.
|
|
\textit{RFC 7748: Elliptic Curves for Security}. IETF, January 2016.
|
|
\url{https://www.rfc-editor.org/rfc/rfc7748}.
|
|
|
|
\bibitem{rfc8032} S.\ Josefsson, I.\ Liusvaara.
|
|
\textit{RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)}.
|
|
IETF, January 2017.
|
|
\url{https://www.rfc-editor.org/rfc/rfc8032}.
|
|
|
|
\bibitem{rfc8439} Y.\ Nir, A.\ Langley.
|
|
\textit{RFC 8439: ChaCha20 and Poly1305 for IETF Protocols}.
|
|
IETF, June 2018.
|
|
\url{https://www.rfc-editor.org/rfc/rfc8439}.
|
|
|
|
\bibitem{rfc5869} H.\ Krawczyk, P.\ Eronen.
|
|
\textit{RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function
|
|
(HKDF)}. IETF, May 2010.
|
|
\url{https://www.rfc-editor.org/rfc/rfc5869}.
|
|
|
|
\bibitem{xwing} D.\ Connolly, P.\ Schwabe, B.\ Westerbaan.
|
|
\textit{X-Wing: General-Purpose Hybrid Post-Quantum KEM}.
|
|
\texttt{draft-connolly-cfrg-xwing-kem}, IRTF CFRG.
|
|
\url{https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/}.
|
|
|
|
\bibitem{lp9701} Lux Industries.
|
|
\textit{LP-9701: Reticulum Network Stack with Hybrid Post-Quantum Link}.
|
|
Superseded by this document.
|
|
|
|
\end{thebibliography}
|
|
|
|
\end{document}
|