diff --git a/whitepaper/zssp.tex b/whitepaper/zssp.tex index cdd35fd..df80997 100644 --- a/whitepaper/zssp.tex +++ b/whitepaper/zssp.tex @@ -154,7 +154,7 @@ If we enable all security features, we get \emph{Persistent Mode} ZSSP. This mod If we disable all security features, we get \emph{Opportunistic Mode} ZSSP. This mode of operation will warn the user if it detects a peer may have been compromised, but will not enforce a zero-communication policy. This makes it inherently vulnerable to post-compromise attacks. We still consider this a novel security property for real-time protocols, since it makes undetectable MitM extremely difficult to achieve, even with a full compromise of both communicating peers. In this mode ZSSP can gracefully coordinate resetting two peers' ratchet keys to zero in the event one peer has corrupted their keys, without any additional round trips. This allows a peer that does not have writeable or reliable persistent storage to still be able to use ZSSP. A persistent mode peer can communicate directly with an opportunistic mode peer, without performing any kind of negotiation to do so. -\begin{table}[!ht] +\begin{table}[H] \renewcommand{\arraystretch}{1.3} \caption{The security properties of ZSSP in its two primary modes of operation}\label{table:security_prop} \centering @@ -226,7 +226,7 @@ If Alice has corrupted their storage, Alice can either send the empty string or ZSSP is a stack of three closely related, but independent protocols. These are the \emph{Zeta Key Exchange} (ZKE), the \emph{ZeroTier Challenge Protocol} (ZCP) and the \emph{ZeroTier Fragmentation Protocol} (ZFP), described in \figureref{fig:protocol_stack}. The Zeta Key Exchange is the most complex and most essential from this stack: It is the part of ZSSP that implements Noise XK and Noise KK and directly interfaces with the upper protocol. For that reason we are going to describe it first, in isolation from the lower protocols. -\begin{figure} +\begin{figure}[H] \caption{The protocol stack of ZSSP. When the upper protocol sends a packet, it must pass down the protocol stack from the top to the bottom, where each layer may modify or adds to the packet in some way. When the datagram protocol receives a packet, it passes is up towards the top of the protocol stack. Any layer may consume the incoming packet and not pass it upwards. ZKE performs key exchanges to provide authenticated encryption to packets. ZCP protects any key exchange protocol above it from CPU exhaustion DOS attacks. And ZFP fragments packets to fit within the MTU of the protocol below it, using an algorithm that mitigates common fragmentation DOS attacks.}\label{fig:protocol_stack} \centering \begin{tikzpicture}[stack/.style={rectangle split, rectangle split parts=#1,draw, anchor=center}] @@ -242,7 +242,7 @@ ZSSP is a stack of three closely related, but independent protocols. These are t The next section will be dedicated to describing ZKE in explicit detail. We will be making heavy use of common mathematical notions and notation surrounding Deterministic Finite Automata, State Machines and AKE security. The goal with this section is to describe ZKE at a level of detail necessary both to allow someone to implement ZKE, and to aide future mathematical analysis of ZKE. -\begin{definition}[ZKE packet types] +\begin{definition}[H][ZKE packet types] We define 8 different packet types that are essential for the functioning of ZKE. \begin{itemize} \item $X_1$: This packet type contains the first message of Noise XK. It is identified internally with packet type number `0'. @@ -260,7 +260,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{itemize} \end{definition} -\begin{figure}[!ht] +\begin{figure}[H] \caption{Handshake Hello $X_1$ -- This is the packet structure of the first packet Alice sends to Bob. It conforms to the message structure required by the Noise protocol.}\label{packet:handshake_hello} \centering \begin{bytefield}[bitwidth=5.5em]{4} @@ -278,7 +278,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{bytefield} \end{figure} -\begin{figure}[!ht] +\begin{figure}[H] \caption{Handshake Response $X_2$ -- Bob sends this packet in response to Alice's Hello packet. It also conforms to the message structure of the Noise protocol. All packets in ZKE do.}\label{packet:handshake_response} \centering \begin{bytefield}[bitwidth=5.5em]{4} @@ -292,7 +292,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{bytefield} \end{figure} -\begin{figure}[!ht] +\begin{figure}[H] \caption{Handshake Completion $X_3$ -- This is the packet structure of the final message of Noise XK, which Alice sends to Bob.}\label{packet:handshake_complete} \centering \begin{bytefield}[bitwidth=6em]{4} @@ -305,7 +305,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{bytefield} \end{figure} -\begin{figure}[!ht] +\begin{figure}[H] \caption{Key Confirmation $C_1$, Acknowledgement $C_2$, and Session Rejection $D$ -- These three packet types have the same packet structure. They just contain a MAC to confirm both peers have completed the handshake.}\label{packet:key_conf} \centering \begin{bytefield}[bitwidth=5em]{4} @@ -313,7 +313,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{bytefield} \end{figure} -\begin{figure}[!ht] +\begin{figure}[H] \caption{Rekey Initiation $K_1$, and Rekey Completion $K_2$ -- Both types of Noise KK rekeying packets have the same structure.}\label{packet:rekey} \centering \begin{bytefield}[bitwidth=5.5em]{4} @@ -326,7 +326,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{bytefield} \end{figure} -\begin{figure}[!ht] +\begin{figure}[H] \caption{Data Transport Packet $P$ -- This is the structure of packets containing payloads of data from the upper protocol.}\label{packet:transport} \centering \begin{bytefield}[bitwidth=5.5em]{4} @@ -337,7 +337,7 @@ The next section will be dedicated to describing ZKE in explicit detail. We will \end{bytefield} \end{figure} -\begin{figure}[!ht] +\begin{figure}[H] \caption{The Zeta Key Exchange in ideal conditions.} \centering \begin{sequencediagram} @@ -366,7 +366,7 @@ We model ZKE as two state machines in communication with each other. Each machin At the end of this section we will have defined $\zeta$, which is the high-level state machine that represents both Alice and Bob's side of ZKE. -\begin{definition}[ZKE Automata]\label{def:automata} +\begin{definition}[H][ZKE Automata]\label{def:automata} We will first start by defining Deterministic Finite Automata (DFA) $\beta$, which will be the foundation upon which we construct the ZKE state machine. $\beta$ is depicted in \figureref{fig:automata}. Let $Q = \{\bot, A_1, B_2, A_3, S_1, S_2, R_1, R_2\}$ be the set of possible states within $\beta$. @@ -408,7 +408,7 @@ At the end of this section we will have defined $\zeta$, which is the high-level Let $\beta=(Q, \Sigma, \delta, \bot, \{S_2\})$ be the ZKE Automata. \end{definition} -\begin{figure} +\begin{figure}[H] \caption{The ZKE Automata, $\beta$ -- Both sides of a ZSSP session contain a copy of this machine. $A_1$, $B_2$ and $A_3$ are Noise XK states, $S_1$ and $S_2$ are session established states, and $R_1$ and $R_2$ are Noise KK states. Each symbol represents a received packet type, except for $\tau$ which represents a timeout transition. Omitted are the identity and $\chi$ transitions.}\label{fig:automata} \centering \begin{tikzpicture}[shorten >=1pt,node distance=2.5cm,auto,>=latex] @@ -445,7 +445,7 @@ At the end of this section we will have defined $\zeta$, which is the high-level \end{figure} -\begin{definition}[The Zeta State Machine, $\zeta$]\label{def:state_machine} +\begin{definition}[H][The Zeta State Machine, $\zeta$]\label{def:state_machine} We are going to extend $\beta$ with behaviors and rules that cannot be modelled adequately by DFAs. This new, extended machine will be referred to as $\zeta$, the Zeta state machine. $\zeta$ will retain the core states, alphabet, transition function and initial state as $\beta$, but we are going to add the ability for $\zeta$ to establish a session, send a remote peer encrypted packets, and to process incoming packets. $\bot$ is treated as a unique, nondeterministic state within $\zeta$. Internally, $\zeta$ is always in the $\bot$ state. Whenever an input is received that would cause a transition out of the $\bot$ state, $\zeta$ will stay in the $\bot$ state, but spawn a copy of itself that is initialized to the correct transition state, either $A_1$ or $B_2$. If this new instance of $\zeta$ enters the $\bot$ state, it will be deleted from memory. The original $\zeta$ will still exist in the $\bot$ state, ready to spawn more copies. This is directly analogous to a nondeterministic transition within an NFA. This is how ZKE handles multiple session to a single peer, each session can be considered its own independent instance of the $\zeta$ state machine, running concurrently with each other. If we are only interested in considering one session to one peer, we can safely ignore this rule. @@ -514,11 +514,11 @@ The following are the 4 cryptographic primitives ZKE is based upon. We will be a \newcommand{\COUNTER}{\algn{Count}} \newcommand{\KID}{\algn{KID-Gen}} -\begin{definition}[SHA-512 \cite{fips_sha2}] +\begin{definition}[H][SHA-512 \cite{fips_sha2}] Let $\HASH(M)$ be the SHA-512 hashing algorithm, where $M$ is the string input to be hashed. It has a 512-bit output. \end{definition} -\begin{definition}[KBKDF \cite{fips_kbkdf}] +\begin{definition}[H][KBKDF \cite{fips_kbkdf}] Let $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)$ be the KBKDF key derivation algorithm, instantiated in HMAC-Counter mode using SHA-512. Variable $\textit{K}_{\textit{IN}}$ is the input key material, $\textit{Label}$ is some static label, $\textit{Context}$ is additional input key material, and $N$ is the number of 512-bit outputs to be produced. $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)$ produces as output $(x_1,\ldots,x_N)$, a tuple of $N$ 512-bit outputs. Given $i\in \{1,\ldots,N\}$, we will use the notation $\KDF(\textit{K}_{\textit{IN}}, \textit{Label}, \textit{Context}, N)_i$ to refer to the $i$th output of $\KDF$. When assigning a single 512-bit output to a variable or field that is 256-bits in size, it is assumed that the output is being truncated to just the first 256-bits. @@ -526,17 +526,17 @@ The following are the 4 cryptographic primitives ZKE is based upon. We will be a So $k\gets \KDF(x, l, y, 3)_2$ represents computing KBKDF on inputs $\textit{K}_{\textit{IN}}= x,\, \textit{Label}=l,\, \textit{Context}=y$ and $L=3\cdot 512$, and setting variable $k$ equal to bits $512$ to $1023$ of the output. If $k$ is a variable of size 256-bits, then $k$ is set equal to bits $512$ to $767$ of the output. \end{definition} -\begin{definition}[AES-GCM \cite{fips_aesgcm}] +\begin{definition}[H][AES-GCM \cite{fips_aesgcm}] Let $\AEAD(K, N, H, M)$ be the AES-GCM Authenticated Encryption with Additional Data algorithm, where $K$ is the encryption key, $N$ is the nonce or IV, $H$ is the additional authentication data, and $M$ is the plaintext message to be encrypted. $\AEAD(K, N, H, M)$ produces as output $c||t$, a ciphertext, $c$, concatenated with its 128-bit (16 byte) authentication tag, $t$. \end{definition} -\begin{definition}[P384 \cite{fips_p384}] +\begin{definition}[H][P384 \cite{fips_p384}] Let $\DHGEN()$ be the P384 elliptic curve Diffie-Helman key generation algorithm. $\DHGEN()$ outputs a random keypair, $(x, g^x)$, where $x$ is the private key and $g^x$ is the public key. $g$ is the generator of the P384 elliptic curve group. \end{definition} Given $(x, g^x)$ and $(y, g^y)$, two output keypairs from $\DHGEN()$, a \emph{key agreement} between these keys can be performed by computing $(g^x)^y$ or $(g^y)^x$. It is the case that $(g^x)^y=(g^y)^x=g^{xy}$. This value, $g^{xy}$, is considered the output key material of Diffie-Helman key agreement. -\begin{definition}[Kyber1024 \cite{kyber}] +\begin{definition}[H][Kyber1024 \cite{kyber}] Let $(\KEMGEN, \KEMENC, \KEMDEC)$ be the Kyber1024 key encapsulation mechanism. $\KEMGEN()$ randomly generates a keypair, $(e_{priv}, e_{pub})$, where $e_{priv}$ is the private key and $e_{pub}$ is the public key. $\KEMENC(e_{pub})$ takes as input a Kyber1024 public key, and outputs the pair $(e_{key}, e_{kem})$, where $e_{key}$ is the symmetric key and $e_{kem}$ is the encapsulated ciphertext of $e_{key}$. $\KEMDEC(e_{priv}, e_{kem})$ takes as input a private key and an encapsulated ciphertext, and outputs $e_{key}$, the decryption of $e_{kem}$. Both \KEMENC and \KEMDEC can output $\bot$, the null value, which implies authentication failure due to invalid input keys. \end{definition} @@ -566,7 +566,7 @@ All of these variables are independent \emph{per instance of} $\zeta$. So each i What constitutes a ``single, unique remote peer'' is up to the upper protocol to decide. Usually remote peers are identified and differentiated by their static public keys ($g^u$ or $g^v$). However ZKE does not strictly enforce a one-to-one relationship between static public keys and identity specifically so the upper protocol is able to rotate the static public keys of peers. -\begin{definition}[Session Counter] +\begin{definition}[H][Session Counter] Every instance of $\zeta$ contains a single monotonically increasing counter, that is initialized to 0. We will use the notation $\COUNTER()$ to represent incrementing this counter by 1 and returning the previous counter value. So the very first call to $\COUNTER()$ within $\zeta$ will return 0, the second call will return 1, and so on. @@ -576,7 +576,7 @@ What constitutes a ``single, unique remote peer'' is up to the upper protocol to ZKE uses a 64-bit (8 byte) counter, but AES-GCM uses a 96-bit (12 byte) nonce/IV. The Noise protocol does specify that, in order to use a counter value as an AES nonce, it should be encoded as a big-endian integer with 4 bytes of zero padding to the left of it. However, for good reason, we have decided to utilize the rightmost byte of that 4 byte padding to store the packet type number, as depicted in \figureref{fig:nonce}. This accomplishes two goals. First it explicitly authenticates the packet type of a packet, which makes it infeasible for an attacker to change the packet type field of a header without triggering an authentication failure. We could have used the additional authentication data parameter of AES-GCM to accomplish this, but the Noise protocol already makes extensive use of this parameter. Second, this nonce construction reduces the risk of catastrophic nonce reuse due to implementation error, specifically during key exchanges. While a correctly implemented counter would be sufficient for security, this adds an extra layer of defence. Given a packet type number $p$, and a counter value $c$, we will use the notation $p||c$ to represent this nonce construction. -\begin{figure} +\begin{figure}[H] \caption{Construction of the AES-GCM 96-bit nonce/IV.}\label{fig:nonce} \centering \begin{bytefield}[bitwidth=3.5em]{10} @@ -586,12 +586,12 @@ ZKE uses a 64-bit (8 byte) counter, but AES-GCM uses a 96-bit (12 byte) nonce/IV \end{bytefield} \end{figure} -\begin{definition}[Key Id Generation] +\begin{definition}[H][Key Id Generation] \algn{KID-Gen} is a stateful algorithm that outputs a locally unique, uniform random 32-bit string. The output of this algorithm will be used as the \emph{key id} for a session key, allowing the local peer to multiplex many keys for many remote peers simultaneously. The output of $\algn{KID-Gen}()$ must be locally unique from all other key ids currently in use. $\algn{KID-Gen}()$ may only repeat an output if that output is currently not being used as a key id. \end{definition} -\begin{definition}[Persistent State] +\begin{definition}[H][Persistent State] The variables $\texttt{rk}$ and $\texttt{rf}$ are considered the \emph{persistent state} of ZKE. The values stored in these variables are not lost when a session ends, and can be \emph{restored} at any time to initiate a new session. $\texttt{rk}$ is a tuple of up to two 256-bit \emph{ratchet keys}. Similarly, $\texttt{rf}$ is a tuple of up to two 256-bit \emph{ratchet fingerprints}. $\texttt{rk}$ is initialized to $(0^{256}, \bot)$ and $\texttt{rf}$ is initialized to $(\varepsilon, \bot)$. @@ -603,7 +603,7 @@ $\bot$ represents the ``null'' value. When a variable is set to $\bot$, it means It is assumed that prior to any usage of ZKE, Alice and Bob will generate their static keypairs with $\DHGEN()$. It is also assumed that Alice already knows Bob's public key, $g^v$, prior to any attempt to connect to Bob. Bob does not necessarily know Alice's public key. -\begin{table} +\begin{table}[H] \caption{Intermediate Values of the Noise XKhfs+psk2 and the Noise KKpsk0 Handshakes \cite{noise_protocol} \cite{noise_hfs} -- $(u, g^u)$ and $(v, g^v)$ are respectively the static keypairs of Alice and Bob within \emph{the current Noise handshake}, and the label $L=\texttt{"ZSSP"}$. The ciphertext of packets is hashed and not the plaintext. The following transition algorithms implicitly compute and have access to the values described here. This syntax for defining cryptographic variables comes from Benjamin Dowling et.~al.~\cite{wireguard_analysis}} \centering \renewcommand{\arraystretch}{1.05} @@ -655,7 +655,7 @@ If packet decryption fails because the AEAD MAC is inauthentic, this is consider Prior to the execution of any of the following algorithms, $\zeta$ will check if it is in the correct state, if it is allowed to perform the related state transition, that the received key id matches the expected key id, and that the packet has the correct type number. If any of these checks fail execution is aborted and the packet is ignored. -\begin{algorithm}\label{alg:x1} +\begin{algorithm}[H]\label{alg:x1} \caption{Transition $\delta(\bot, \tau)=A_1$ -- Alice creates and sends a ``Hello'' packet to initialize a session with Bob, \figureref{packet:handshake_hello}. Alice knows Bob's static public key, $g^v$, prior to execution. By sending a Hello packet to Bob, Alice is implicitly trusting Bob.} \begin{algorithmic} \Require $g^v$ @@ -677,7 +677,7 @@ The ratchet fingerprint, \texttt{rf}, is a sacrificial ASK that does nothing mor Including the ratchet fingerprint in the Hello packet does not compromise identity hiding, because the ratchet fingerprint is replaced with every single handshake. In the event that Bob's private keys are compromised, and all ratchet fingerprints used in Hello packets to Bob can be decrypted, all an attacker would see are several indistinguishable 256-bit strings. The one exception is if a peer reuses the same ratchet fingerprint. This would allow an attacker who has compromised Bob's static private key to learn that two Hello packets come from the same peer, but this does not given any information about that peer's static keys or identity. Ratchet fingerprints in normal operation are never reused, this can only happen if the initial Noise XK handshake is aborted before completion, preventing one or more of the participating peers from confirming a new ratchet fingerprint. -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(\bot, X_1)=B_2$ -- Bob has received Alice's Hello packet and replies with the second message of Noise XK, \figureref{packet:handshake_response}. Input $\pi_1$ is a security flag, a bit set by the upper protocol. Bob will only allow Alice to connect with zero persistent state if $\pi_1=0$. If Alice sent an unrecognized ratchet fingerprint and $\pi_1=0$, instead of rejecting Alice's session, Bob will ask Alice if they would like to connect with zero persistent state. ZKE has superior security properties when $\pi_1=1$, but usability suffers.}\label{alg:recv_x1} \begin{algorithmic} \Require $(0,\ 0||c,\ X_1),\, \pi_1,$ @@ -712,7 +712,7 @@ Including the ratchet fingerprint in the Hello packet does not compromise identi \end{algorithmic} \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(A_1, X_2)=A_3$ -- Alice has received Bob's reply and can now send a Handshake Completion packet, \figureref{packet:handshake_complete}. Input $\pi_2$ is a security flag, a bit set by the upper protocol. Bob may ask Alice if they would like to connect with zero persistent state instead of the current persistent state. If $\pi_2=0$ then Alice will accept this request, and reset their ratchet keys. Input \texttt{identity} is an arbitrary string provided by the upper protocol. It is expected to contain some form of cryptographic identifier or certificate for this peer, but it could also be the empty string.}\label{alg:recv_x2} \begin{algorithmic} \Require $(\texttt{kid}^1_1,\ 1||c,\ X_2),\, \pi_2,\, \texttt{identity}$ @@ -750,7 +750,7 @@ Packet types $X_2$ and $K_1$ represent the second to last message of Noise XK an Similarly, the new ratchet key and fingerprint will not have been derived by the remote peer yet. So this peer will have to save these keys to persistent storage, but not yet delete the previous ratchet key and fingerprint until the remote peer sends a key confirmation. This means that for a brief period of time Alice has two sets of ratchet keys and fingerprints saved, instead of just one. We want to make sure Alice and Bob never have more than two of these keys saved at a time. So during the Noise XK handshake if Alice has two sets of keys and fingerprints, they must send both fingerprints to Bob, and delete the key and fingerprint that Bob chooses not to use as the PSK. -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(B_2, X_3)=S_1$ -- Bob receives the final packet of Noise XK. Input $\algn{Accept}$ is a function provided by the upper protocol that takes as input Alice's identity and outputs security flags $(\pi_3, \pi_4)$, $\pi_3$ can be $\bot$. If $\pi_3=\bot$, Bob will reject Alice's session. If $\pi_3=1$, and Bob is aware of a ratchet key Alice should have, but Alice is not connecting with it, Bob will reject Alice's session (similar to when $\pi_2=1$). If Bob rejects Alice's session and $\pi_4 = 0$, Bob will send a packet to Alice explicitly rejecting their session.}\label{alg:recv_x3} \begin{algorithmic} \Require $(\texttt{kid}_2,\ 2||0,\ X_3),\,\algn{Accept}$ @@ -792,7 +792,7 @@ When packet $X_3$ is received, Bob might refuse to start a session with Alice ba Packet types $X_3$ and $K_2$ represent the final messages of Noise XK and Noise KK, respectively. The peer who receives these packets can be sure that the remote peer has derived an identical Noise key already, and so the new key can be reliably used immediately. -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(A_3, C_1)=S_2$ and $\delta(R_2, C_1)=S_2$ -- A key confirmation for either the Noise XK initial handshake or Noise KK rekeying has been received. This peer must send an Acknowledgement, \figureref{packet:key_conf}, to let the remote peer know that it has been received.}\label{alg:key_conf} \begin{algorithmic} \Require $(\texttt{kid}^{\texttt{i} + 1}_{\texttt{r}},\ 3||c,\ C_1)$ @@ -807,7 +807,7 @@ Packet types $X_3$ and $K_2$ represent the final messages of Noise XK and Noise Packet types $C_2$ and $D$ have trivial transition algorithms. They merely decrypt the received packet with the most recent key exchange key, $\texttt{kek}^\texttt{i}_\texttt{r}$. If authentication succeeds then $\zeta$ transitions state. -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(S_2, \tau)=R_1$ -- This peer, whom will be henceforth be referred to as Alice, has decided to initiate rekeying. They create and send a Rekey Initiation packet, \figureref{packet:rekey}, to the remote peer, Bob. The entire Noise KK message is encrypted under the key exchange key for additional security.} \begin{algorithmic} \State $\texttt{kid}^{\texttt{i} + 1}_\texttt{r} \gets \KID()$ @@ -821,7 +821,7 @@ Packet types $C_2$ and $D$ have trivial transition algorithms. They merely decry \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(S_2, K_1)=R_2$ and $\delta(R_1, K_1)=R_2$ -- Bob has received Alice's request to rekey and sends a Rekey Completion packet, \figureref{packet:rekey}, in reply. Bob has now finished Noise KK and has obtained a new Noise key. A new pair of key ids are generated so Bob can tell apart packets encrypted with the new key from those encrypted with the previous key.} \begin{algorithmic} \Require $(\texttt{kid}^\texttt{i}_\texttt{r},\ 5||c,\ K_1)$ @@ -844,7 +844,7 @@ Packet types $C_2$ and $D$ have trivial transition algorithms. They merely decry \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{Transition $\delta(R_1, K_2)=S_1$ -- Alice has received Bob's reply and so can also finish Noise KK. They send a Key Confirmation, \figureref{packet:key_conf}, to signal the completion of the handshake. The second to last Noise key is deleted for forward secrecy.} \begin{algorithmic} \Require $(\texttt{kid}^\texttt{i}_\texttt{r},\ 6||c,\ K_2)$ @@ -864,7 +864,7 @@ Packet types $C_2$ and $D$ have trivial transition algorithms. They merely decry We want to minimize the number of Noise keys alive in memory for the sake of perfect forward secrecy, in case the content of a peer's memory is stolen by an attacker. However we have to keep at least the last 2 keys around for reliability. Otherwise a packet that was sent before rekeying could arrive out of order, after the rekey is completed, and not be decryptable. -\begin{algorithm} +\begin{algorithm}[H] \caption{Data transport send -- The upper protocol has decided to send the remote peer an encrypted payload. Input \texttt{payload} is an arbitrary string provided by the upper protocol.} \begin{algorithmic} \Require $\texttt{payload}$ @@ -874,7 +874,7 @@ We want to minimize the number of Noise keys alive in memory for the sake of per \end{algorithmic} \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{Data transport received -- A data transport packet has been received. It is decrypted and its payload is given to the upper protocol to handle.} \begin{algorithmic} \Require $(\texttt{kid}^i_r,\ 8||c,\ P)$ @@ -914,7 +914,7 @@ Without some kind of mitigation, an attacker consume a small amount of their ban The protocol is initialized with a random 256-bit string \texttt{salt}. As the name implies, this will be used as a salt for SHA-512. When Alice sends their first Hello packet, they execute \algorithmref{alg:challenge_empty}, and append the output to their Hello packet. Under normal conditions Bob will simply ignore this part of Alice's Hello packet and allow the key exchange protocol to proceed. However if Bob is under load, Bob may choose to execute \algorithmref{alg:challenge_create}. This algorithm is overwhelmingly likely to output a \texttt{challenge}, which is then sent to Alice. Alice then computes \algorithmref{alg:challenge_response} upon Bob's \texttt{challenge}, which outputs a \texttt{response}. \texttt{response} is re-appended to Alice's Hello packet, and it is resent to Bob. Both \texttt{response} and \texttt{challenge} are a sequence of bytes with identical internal structure, documented in \figureref{packet:challenge_internal}. -\begin{figure} +\begin{figure}[H] \caption{Bob's Challenge $\texttt{challenge}$, and Alice's Response $\texttt{response}$ -- This is the internal structure of both the challenge and response strings Alice and Bob send each other. They are included as payloads within a larger packet.}\label{packet:challenge_internal} \centering \begin{bytefield}[bitwidth=6.5em]{4} @@ -923,7 +923,7 @@ The protocol is initialized with a random 256-bit string \texttt{salt}. As the n \end{bytefield} \end{figure} -\begin{figure} +\begin{figure}[H] \caption{Challenge Packet -- Whereas Alice's response is sent appended to their Hello packet, Bob's challenge is sent on its own. This usually means it requires a small amount of framing to be considered a well-formed packet that Alice can receive. The ``Peer Identifier'' is defined by the upper protocol to allow for multiplexing between multiple peers. In ZSSP, the Peer Identifier is set to the key id included in Alice's Hello packet, $X_1\texttt{.key\_id}$. In ZSSP challenge packets are identified with packet type number `9'.}\label{packet:challenge} \centering \begin{bytefield}[bitwidth=6em]{4} @@ -942,7 +942,7 @@ To prevent a legitimate Alice from unintentionally straining Bob while Bob is un Implementations may choose to rotate the \texttt{salt} variable periodically. While the protocol as we have described it so far has strong replay protection, it does not automatically expire sent challenges after a set amount of time. This means challenges might never expire, and could be saved up to be used later. We do not consider this a practical DOS attack vector, but for applications that do, rotating \texttt{salt} on a periodic schedule is one of the best solutions. -\begin{algorithm} +\begin{algorithm}[H] \caption{Alice generates a null response -- The returned value is appended to Alice's initial Hello packet to Bob.}\label{alg:challenge_empty} \begin{algorithmic} \State $\texttt{response.counter} \gets 0^{64}$ @@ -952,7 +952,7 @@ Implementations may choose to rotate the \texttt{salt} variable periodically. Wh \end{algorithmic} \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{Bob decides whether to issue a challenge to a Hello packet -- Input \texttt{response} is the last 32 bytes of Alice's Hello packet. Input $a$ contains the ``address'' of the lower datagram protocol from which the packet was received. So when the datagram protocol is UDP, $a$ contains the IP address and port of Alice. This algorithm is stateful, so it has a stateful counter $d$, and can remember counter values $c$ it has seen before, similar to counter-based AEAD decryption. If $\bot$ is returned, this means Bob will not be sent a challenge packet, but instead Alice's Hello packet will be allowed to be processed by the upper protocol.}\label{alg:challenge_create} \begin{algorithmic} \Require $\texttt{response},\, a$ @@ -971,7 +971,7 @@ Implementations may choose to rotate the \texttt{salt} variable periodically. Wh \end{algorithmic} \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{Alice responds to a challenge from Bob -- Input \texttt{previous\_response} is the contents of the \texttt{response} that was appended to Alice's last sent Hello packet. The returned value replaces it in the next Hello packet.}\label{alg:challenge_response} \begin{algorithmic} \Require $\texttt{challenge},\, \texttt{previous\_response}$ @@ -1005,7 +1005,7 @@ The upper protocol must identify every packet with a \emph{packet nonce}. For ea Implementations must support a fragment count of at least 48. Implementations may choose and enforce a maximum length of the final reassembled packet. Since fragment numbers and counts are stored as 1 byte integers, the maximum possible fragment count that can be encoded is 256. If the fragment count for a packet is 256, the fragment count field of the packet header should be set to 0. Implementations that choose to support fragment counts of 256 must interpret a 0 in the fragment count field as a fragment count of 256. -\begin{figure} +\begin{figure}[H] \caption{Header of the ZeroTier Fragmentation Protocol -- The ``Peer Identifier'' is given by the upper protocol to allow for multiplexing between multiple peers. In ZSSP, the Peer Identifier is set to the most recent key id of the remote peer. The ``Packet Nonce'' is also given by the upper protocol, it is expected to contain metadata on behalf of the upper protocol, and as the name implies it should be unique per packet. \figureref{fig:header_nonce} describes how this Nonce is constructed in ZSSP.}\label{packet:header} \centering \begin{bytefield}[bitwidth=6.5em]{4} @@ -1015,7 +1015,7 @@ Implementations must support a fragment count of at least 48. Implementations ma \end{bytefield} \end{figure} -\begin{figure} +\begin{figure}[H] \caption{Construction of the packet nonce within ZSSP -- Notice that this construction is identical with the ZSSP AES-GCM Nonce, but with two less bytes of padding.}\label{fig:header_nonce} \centering \begin{bytefield}[bitwidth=3.5em]{9} @@ -1057,14 +1057,14 @@ The Noise XK handshake packets, as specified by Noise, do not use a unique count We are going to prove that the ZSSP header authentication algorithm is existentially unforgeable under an adaptive chosen message attack. However our algorithm does not fit the standard syntax of message authentication codes. This means we must prove security under a different security experiment than the standard message authentication code experiment. We choose to use the syntax of a message transmission scheme \cite{modern_crypto}, and prove authenticated communication under the secure message transmission experiment, \algorithmref{alg:header_auth}. -\begin{definition}[Message Transmission Scheme \cite{modern_crypto}] +\begin{definition}[H][Message Transmission Scheme \cite{modern_crypto}] A message transmission scheme is a tuple of algorithms $\Pi = (\algn{Gen}, \algn{EncMac}, \algn{Dec})$. \algn{Gen} is the key generation algorithm, \algn{EncMac} is the authenticated encryption algorithm, and \algn{Dec} is the decryption and verification algorithm. A message transmission scheme is \emph{correct} if for all keys $k\gets \algn{Gen}(1^n)$ and messages $m$, $$\algn{Dec}_k(\algn{EncMac}_k(m)) = m.$$ \end{definition} -\begin{algorithm} +\begin{algorithm}[H] \caption{The secure message transmission experiment, $\algn{Auth}_{\mathcal{A},\, \Pi}(n)$ \cite{modern_crypto}}\label{alg:header_auth} \begin{algorithmic} \Require $n$ @@ -1076,12 +1076,12 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{algorithmic} \end{algorithm} -\begin{definition}[Authenticated Communication \cite{modern_crypto}] +\begin{definition}[H][Authenticated Communication \cite{modern_crypto}] Message transmission scheme, $\Pi = (\algn{Gen}, \algn{EncMac}, \algn{Dec})$ achieves authenticated communication if for all probabilistic poly-time adversaries $\mathcal{A}$, there exists a negligible function $\algn{negl}$ such that: $$\prob[\algn{Auth}_{\mathcal{A},\, \Pi}(n) = 1] \leq \algn{negl}(n).$$ \end{definition} -\begin{definition}[Pseudorandom Permutation \cite{modern_crypto}] +\begin{definition}[H][Pseudorandom Permutation \cite{modern_crypto}] Given the security parameter $n$, a keyed permutation, $F$, and a probabilistic poly-time distinguishers $D$, define the advantage of $D$ to be: $$\mathbf{Adv}^\text{ind-prp}_{D,\,F}(n) = |\prob[D^{F_k(\cdot), F_k^{-1}(\cdot)}(1^n) = 1] - \prob[D^{f(\cdot), f^{-1}(\cdot)}(1^n) = 1]|,$$ where $k\gets\$\,\{0,1\}^n$ and $f$ is a truly random permutation. @@ -1091,7 +1091,7 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{definition} -\begin{algorithm} +\begin{algorithm}[H] \caption{$\algn{Gen}(1^n)$ of the ZSSP header authentication algorithm -- We are implicitly assuming here that the header key is truly random, or at least computationally indistinguishable from random.}\label{alg:header_gen} \begin{algorithmic} \Require $1^n$ @@ -1100,7 +1100,7 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{algorithmic} \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{$\algn{EncMac}_k(m)$ of the ZSSP header authentication algorithm -- Input $m$ is bytes 4 to 20 of a fragment, which is the portion of the fragment that ZFP encrypts. Function $F$ is AES-256. For the sake of the security proof we will assume that the input block size of AES-256 is not constant, but instead is equal to $n$. For this reason we also assume $m$ is able to grow to be of size $n$.}\label{alg:header_encmac} \begin{algorithmic} \Require $m$ @@ -1108,7 +1108,7 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{algorithmic} \end{algorithm} -\begin{algorithm} +\begin{algorithm}[H] \caption{$\algn{Dec}_k(c)$ of the ZSSP header authentication algorithm -- Function $F^{-1}$ is inverse of AES-256. Function $\algn{Vrfy}$ is some algorithm provided by the upper protocol for verifying that the packet nonce is valid. For the sake of the security proof we assume that the size of the packet nonce $N$ is able to grow proportionally to $n$. The version of $\algn{Vrfy}$ used by ZSSP is described by \algorithmref{alg:header_vrfy}.}\label{alg:header_dec} \begin{algorithmic} \Require $c$ @@ -1122,14 +1122,14 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{algorithm} -\begin{theorem}\label{theorem:frag_proof} +\begin{theorem}[H]\label{theorem:frag_proof} If $F:\{0,1\}^n\to\{0,1\}^n$ is a strong psuedorandom permutation, and $\prob[\algn{Vrfy}(N) = 1]$ is negligible in $|N|$ for uniform random $N$, then $\Pi=(\text{\algorithmref{alg:header_gen}, \algorithmref{alg:header_encmac}, \algorithmref{alg:header_dec}})$ achieves authenticated communication, with advantage bound: $$\prob[\algn{Auth}_{\mathcal{A},\, \Pi}(n) = 1] \leq \mathbf{Adv}^\text{ind-prp}_{D,\,F}(n) + \frac{2^n}{2^n-q(n)}\prob[\algn{Vrfy}(N) = 1],$$ where $N$ is uniform random, and $q(n)$ is the upper bound on the total number of queries $\mathcal{A}$ asks its oracle. \end{theorem} -\begin{proof} +\begin{proof}[H] Given $\mathcal{A}$ an adversary for $\Pi = (\algn{Gen}, \algn{EncMac}, \algn{Dec})$, we construct $D$, \algorithmref{alg:header_d}, a distinguisher for $F$. Assume without loss of generality $\mathcal{A}$ will never output a string which it has previously received from its oracle. @@ -1168,7 +1168,7 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{flalign*} \end{proof} -\begin{algorithm} +\begin{algorithm}[H] \caption{The implementation of $\algn{Vrfy}(N)$ for ZSSP -- We are assuming that the input $N$ is being interpreted as $p||c$, the packet nonce construction of \figureref{fig:header_nonce}. $D$ is a stateful, finite array of integers, initialized to -1, that stores the value of previously authenticated counters. $D$ is updated after ZSSP decrypts a received packet. ZSSP explicitly does not verify that the padding is zero, for the sake of possible future revisions.}\label{alg:header_vrfy} \begin{algorithmic} \Require $p||c$ @@ -1192,7 +1192,7 @@ We are going to prove that the ZSSP header authentication algorithm is existenti \end{algorithm} \algorithmref{alg:header_vrfy} shows how ZSSP validates packet nonces. Notice that $p$ must be in range 1 through 8 to be considered valid. Also notice that, regardless of the value of $p$, $c$ must fall into some range of integers that is at most $2^{24}$ in size. If we assume the adversary makes no oracle queries, this implies that the advantage against ZSSP header authentication is bound above by -\begin{flalign*} +\begin{flalign*}[H] \prob[\algn{Auth}_{\mathcal{A},\, \Pi}(n) = 1] &\leq \mathbf{Adv}^\text{ind-prp}_{D,\,F}(n) + \prob[\algn{Vrfy}(N) = 1] \\ &\leq \algn{negl}(n) + \frac{8}{2^8}\cdot\frac{2^{24}}{2^{64}} \\ &= \algn{negl}(n) + 2^{-5}\cdot 2^{-40} \\