Sphinx packet format ==================== Sphinx is concrete packet format for mixnets, with the following goals: - compact (small overhead over the payload) - hiding the path length and relay position - unlinkability between the legs - indistinguishability of forward and reply packets - provable security The main trick Sphinx achieves compactness is re-using a single public key (via a blinding mechanism) for each leg. In this note we describe the Sphinx packet format as specified in the 2009 paper "Sphinx: A Compact and Provably Secure Mix Format" by George Danezis and Ian Goldberg. In a separate document we will propose some modifications. #### Links - the [Sphinx paper](https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf) - a [nice talk on youtube](https://www.youtube.com/watch?v=34TKXELJa2c) about Sphinx ### Security parameters We denote the intended security level by $\lambda$ as usual (instead of $\kappa$ as in the paper). By default we target $\lambda=128$ bits of security. Hence: - private keys are of size $p = 2\lambda$, that is, 32 bytes - as we use elliptic curve groups, public keys are the same size - symmetric keys are size $s=\lambda$, that is, 16 bytes - MACs are also of size $\lambda$, that is, 16 bytes A further parameter is maximum number of hops (as all packets need to have the same size, you need to pre-agree on this). This is denoted by $r$, and a good recommended default value is $r = 5$. #### Elliptic curve For public key cryptography, we use elliptic curves as they are compact (unlike for example RSA), and also conceptually simple. We denote the elliptic curve group by $\mathbb{G}$, its fixed generator by $\mathbf{g}\in \mathbb{G}$, and the corresponding scalar field by $\mathbb{F}_q \cong \mathbb{Z}_q$. The elliptic curve scalar multiplication is denoted by $*:\mathbb{Z}_q \times \mathbb{G} \to \mathbb{G}$. The standard curve choice is [Curve25519](https://en.wikipedia.org/wiki/Curve25519). #### Symmetric primitives - we will need a MAC; a standard choice is HMAC-SHA256 truncated to $\lambda=128$ bits - we will need a (set of) key derivation function(s) $\mathsf{KDF}$. A standard choice is SHA256 with a domain separation; eg. $\mathsf{KDF}_{\mathsf{MAC}}(x):=H(\texttt{"MAC"}\|x)$, truncated as necessary - we need a pseudo-random stream generator, which will be used to encrypt the routing information. Usually this is AES128-CTR (that is, AES in [counter mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CTR)), but it could be also a XOF like SHAKE128. Below we will denote this by $\mathsf{XOF}(k)$ where $k$ is the key. - we also need a pseudo-random permutation (sometimes called a "large block cipher") to encrypt the payload. In particular, one **SHOULD NOT USE** a standard block cipher in counter mode for this. Note: This is kind of tricky, see more about this below. The default choice for the PRP is Lioness (built on various primitives, for example: the original Lioness paper used SHA1 + SEAL; many implementation use Blake2b + ChaCha20; and of course one could also use SHA256 + AES128). Remark: While this is what the original Sphinx paper needs, there may be tweaks of the design, using more modern symmetric primitives, eg. AEAD to replace the problematic pseudo-random permutation. See below, near the bottom. ## Packet format A Sphinx packet consists of four parts, traditionally denoted by $\alpha$, $\beta$, $\gamma$, and $\delta$: - $\alpha$ is a (blinded) public key (group element; $\alpha\in\mathbb{G})$. - $\beta$ is the (encrypted) routing information - $\gamma$ is a MAC (message authentication code) - $\delta$ is the (encrypted) payload The first three of these, $\mathcal{H}=(\alpha,\beta,\gamma)$ is called the header. Observation: Because we need to allow for the user to create reply paths in advance (SURBs, or single-use reply blocks), where the payload is not yet known, we cannot use traditional "onion" encryption; instead, the header and the payload must be separate. However, the payload will be still encrypted in several layers, and its integrity is protected (if implemented properly). ### Constructing a packet First, the user selects a random path of mix nodes $n_0,\dots n_{\ell-1}$ with $\ell\le r$. We will also need a final destination $\Delta_\mathrm{final}$; and in case of replies, a message identifier $J$. #### Computing the shared secrets We assume all mix nodes have a long-term private-public keypair $(\mathsf{sk}_i,\mathsf{pk}_i)$. The user first generates an ephemeral secret key $x$; in practice this is just a random number $x\in\mathbb{Z}_q^\times$. It can then iteratively derive a sequence of keys (one set per hop) consisting of: - a per-node secret key $x_i\in\mathbb{Z}_q^\times$ - a per-node public key $\alpha_i:=x_i*\mathbf{g}\in \mathbb{G}$ - a per-node shared secret $s_i\in \mathbb{G}$, derived using Diffie-Hellman: $s_i:=x_i*\mathsf{pk}_i = \mathsf{sk}_i*\alpha_i$ - a blinding factor $b_i\in\mathbb{Z}_q^\times$, computed from $\mathsf{KDF}_{\mathsf{blind}}(\alpha_i,s_i)$ Remark: The blinding factor is supposed to be in $\mathbb{Z}_q^\times$ (recall that $q\approx 2^{256}$). We can ensure this either by simply taking it modulo $q$, or more properly by rejection sampling a deterministic sequence. Implementations however usually simply say $b_i:=H(\alpha_i,s_i)$ where $H$ is eg. SHA256. Same care should be taken in an actual implementation to avoid possible corner cases. Remark \#2: With X25519, not all scalar field elements are valid secret keys. However, we cannot simply apply the standard "masking" algorithm to the blinded private key, because the mix nodes doing the processing won't have access to the secret keys... At least the cofactor 8 subgroup is kept invariant by multiplication. I think that the remaining of the mask is to ensure uniformity of random secret keys. So this is probably still OK at the end. The key sequence is defined iteratively: - $x_0:=x$ - $x_{i+1}:=b_i\cdot x_i$ All the rest can be computed from $x_i$: - the public key is $\alpha_i = x_i * \mathbf{g}$ - the shared secret is $s_i=x_i*\mathsf{pk}_i$ - and the blinding factor is $b_i=H(\alpha_i,s_i)$ The idea behind this construction is that each hop will have a unique sender public key, from which a shared secret can be derived, and then further symmetric keys for MAC and encryption via a KDF. When composing the forwarded packet for the next hop, the node can then "tweak" this public key using their own blinding factor: $\alpha_{i+1}=b_i * \alpha_i$. Thus each hop can only decrypt their own header, and if they try to break the protocol, the message will be ruined. #### Size of a mix header The size of the mix header must be constant (otherwise, mix nodes could guess where they are in the path), and its processing uniform (except for the final hop). With the usual parameters, we have $|\alpha|=32$ and $|\gamma|=16$. Thus $N_\beta:=|\beta|$ determines the header size. This must be big enough to fit $(r-1)$ mix node addresses $A_i$ and MACs $\gamma_i$ (for $1\le i