LIONESS-PAYLOAD-ENCRYPTION-FOR-MIX

FieldValue
NameLIONESS encryption scheme for LIBP2P-MIX payload encryption
Slug183
Statusraw
CategoryStandards Track
EditorMohammed Alghazwi [email protected]
ContributorsBalázs Kőműves [email protected]

Timeline

  • 2026-05-25f1017b9 — Add LIONESS specification for Mix payload encryption (#329)

Abstract

This specification defines the LIONESS wide-block encryption scheme for the Sphinx payload encryption. The purpose of this is to perform layered-encryption and preserve payload integrity in the Sphinx construction used by the Mix Protocol while keeping the payload fixed-size. The routing header integrity mechanism defined in the Mix Protocol remains unchanged. Only the payload field is affected by this specification.

Scope

This specification defines:

  • the LIONESS construction to use for payload encryption and decryption,
  • the concrete primitives used to instantiate LIONESS for the Mix Protocol.
  • the required Sphinx payload format and construction.
  • the hop payload processing and integrity verification.

1. Introduction

The Mix Protocol uses a Sphinx packet format with four fields (). The header fields () provide per-hop layered-encrypted routing information which includes header integrity. The payload field carries only the application message in layered-encrypted form.

In the current Mix Protocol, AES-CTR is used to encrypt the routing header . This is sufficient for because header integrity is separately protected by the per-hop MAC field . However, the integrity of is not covered by , since the Sphinx design intentionally separates header integrity from payload integrity.

This separation is necessary because SURB replies require the sender of the SURB to construct the return header before the reply payload is known. As a result, payload integrity MUST be provided independently of header integrity.

A malleable encryption scheme such as AES-CTR does not satisfy this requirement. Bit modifications to the ciphertext result in modifications to the decrypted plaintext. This violates the integrity of the Sphinx payload. Therefore, the payload-encryption scheme MUST satisfy the following:

  1. It MUST preserve the fixed payload size .
  2. It MUST support layered encryption and per-hop layer removal.
  3. It MUST be compatible with SURBs and therefore MUST NOT require payload-dependent header authentication.
  4. It MUST allow the final hop to detect payload tampering.
  5. It MUST avoid adding external authentication material/tags that change the packet or payload size.

To achieve this, this specification uses LIONESS as described in anderson et al. LIONESS is a wide-block cipher built from a stream cipher and a keyed hash function. LIONESS acts as a pseudo-random permutation (PRP) over the entire payload block, allowing us to add the payload integrity prefix into the plaintext and verify it after decryption.

2. Terminology

The following terms are used throughout this specification. Other terms are as defined in the Mix Protocol.

  • Sphinx packet: The packet format as defined in the Mix Protocol, consisting of ().
  • Sphinx payload () The fixed-size encrypted field of a Sphinx packet. It carries the padded application message and any payload extensions such as SURBs.
  • Payload integrity prefix A fixed all-zero byte string prepended to the plaintext payload before applying the payload encryption. The final hop verifies this prefix after payload decryption to detect payload tampering.
  • Payload encryption key () A per-hop key used to encrypt or decrypt one layer of the Sphinx payload field . For LIONESS payload encryption, this key is used as the seed for deriving the internal LIONESS round keys.
  • Round keys: The four keys used by the LIONESS Feistel network.
  • Wide-block cipher: A block cipher with a large block size compared to conventional fixed-size block ciphers such as AES. LIONESS is a wide-block cipher that supports variable-length input blocks above a lower bound.
  • Pseudo-random permutation (PRP): A keyed permutation over a fixed-size message block that is computationally indistinguishable from a uniformly random permutation. Changing even one bit of the input is expected to produce unpredictable changes to a large number of output bits.

3. Cryptographic Primitives

This section defines the primitives used by this specification. In this specification, we will assume the following constants:

  • The security parameter bytes (-bits) as defined in the Mix Protocol.
  • The LIONESS internal parameter bytes, which defines the size of multiple LIONESS components.

3.1 Stream Cipher

The stream cipher used in LIONESS can be abstracted as the following keyed function that produces an arbitrary-length keystream:

where:

  • is a -byte key
  • is the output arbitrary-length keystream.

Encryption and decryption can then be done by first generating a key stream and then XORing the key stream with the message/ciphertext. Encryption and decryption work in the same way:

where is the plaintext and is the ciphertext.

  • is the arbitrary-length message
  • is the arbitrary-length ciphertext of the same size as , i.e.,

3.2 Keyed Hash Function

In this specification, the keyed hash function used in LIONESS is denoted as:

where:

  • is a -byte key
  • is an arbitrary-length input message.
  • is the -byte digest output

3.3 Key Derivation Function (KDF)

The key derivation function is used to derive fixed-size keys from a domain-separation string and a seed:

where

  • : is an arbitrary-length domain-separation string.
  • : is an arbitrary-length seed at least bytes in size.
  • is a -byte output key.

4. Concrete Primitive Instantiation

This section defines the concrete primitive choices used by this specification.

4.1 Stream Cipher Instantiation

The LIONESS stream cipher is instantiated using AES-CTR.

Each LIONESS stream-cipher round key is bytes. For AES-CTR, this -byte value is split as follows:

where:

  • is the first bytes of .
  • is the last bytes of .

4.2 Keyed Hash Instantiation

The LIONESS keyed hash function is instantiated as SHA-256 with the key prepended to the input message:

where:

  • is a -byte key.
  • is the input message.
  • the output is a -byte digest.

This construction is used only as the keyed hash function inside the LIONESS Feistel construction. It is not (and must not be used as) a general-purpose MAC.

4.3 KDF Instantiation

The KDF is instantiated using SHA-256 with domain-separation:

where:

  • is a domain-separation string.
  • is an arbitrary-length seed at least bytes in size.
  • the output is a -byte key.

The following domain-separation strings are used by this specification:

PurposeDomain-separator
Payload encryption key payload_enc_key
LIONESS round key lioness_key1
LIONESS round key lioness_key2
LIONESS round key lioness_key3
LIONESS round key lioness_key4

5. LIONESS Construction

5.1 High-level API

In general, the LIONESS wide-block cipher provides the following:

where:

  • is the arbitrary-length LIONESS seed from which the internal round keys are derived, with size at least bytes
  • is the plaintext message with size bytes.
  • is the corresponding ciphertext with size

5.2 Block Structure

LIONESS is a wide-block cipher, meaning that its block size is large compared to conventional fixed-size block ciphers such as AES. In this specification, LIONESS is applied once to the entire Sphinx payload . The whole payload is treated as a single message block, and LIONESS acts as a PRP over that full payload block. For both LIONESS encryption and decryption, the input block is split into two chunks:

where:

  • is the plaintext message block of any size
  • is the left chunk with size bytes
  • is the right chunk with size bytes.

In this specification, we require the size of the message to be at least , i.e., . This requirement ensures that and . The Mix protocol payload size satisfies this requirement since the expected payload is much larger than .

In summary, we set bytes. Therefore:

  • bytes
  • bytes,
  • bytes.

The choice bytes must match:

  • the stream cipher () key size, and
  • the keyed hash () key size and digest () size.

As a result, we can observe that for large messages, the right chunk is expected to be much larger than the left chunk.

+----------------+----------------------------------+
|       L        |          R                       |
|   mu bytes     |    (|B| - mu) bytes              |
+----------------+----------------------------------+

5.3 Key Derivation

LIONESS requires four internal round keys, one for each round: :

  • and are the keys used for the stream cipher.
  • and are the keys used for the keyed hash function.

All internal round keys are bytes in size. Given a LIONESS seed , we require four calls to the , each with a different domain-separation string:

The LIONESS seed is arbitrary-length and MUST be at least bytes. The derivation of the seed depends on whether the packet is a forward packet or a reply packet. Further details are specified in Section 6 and section 7.

5.4 Encryption

Let:

  • plaintext message
  • round keys
  • is the stream cipher as defined in section 3.1
  • is the keyed hash function as defined in section 3.2

LIONESS encryption proceeds with applying a small Feistel network of four rounds:

round 1:  R1 = R0 ^ S(L0 ^ K1)                        
                                                      
+-----------+                           +-----------+ 
|    L0     |                           |    R0     | 
+-----------+                           +-----------+ 
      |                 K1                    |       
      |                 │                     | xor   
      |                 │                     v       
      |                 v              +-------------+
      +----------------xor------------>|      S      |
                                       +-------------+
                                              |       
                                              v       
+-----------+                           +-----------+ 
|    L0     |                           |    R1     | 
+-----------+                           +-----------+ 
                                                      
                                                      
round 2:  L1 = L0 ^ H_K2(R1)                          
                                                      
+-----------+                           +-----------+ 
|    L0     |                           |    R1     | 
+-----------+                           +-----------+ 
      |                                       |       
      | xor                                   |       
      v                                       |       
+-------------+<------------------------------+       
|      H      |<----- K2                              
+-------------+                                       
      |                                               
      v                                               
+-----------+                           +-----------+ 
|    L1     |                           |    R1     | 
+-----------+                           +-----------+ 
                                                      
                                                      
round 3:  R2 = R1 ^ S(L1 ^ K3)                        
                                                      
+-----------+                           +-----------+ 
|    L1     |                           |    R1     | 
+-----------+                           +-----------+ 
      |                K3                     |       
      |                │                      | xor   
      |                │                      v       
      |                v               +-------------+
      +---------------xor------------->|      S      |
                                       +-------------+
                                              |       
                                              v       
+-----------+                           +-----------+ 
|    L1     |                           |    R2     | 
+-----------+                           +-----------+ 
                                                      
                                                      
round 4:  L2 = L1 ^ H_K4(R2)                          
                                                      
+-----------+                           +-----------+ 
|    L1     |                           |    R2     | 
+-----------+                           +-----------+ 
      |                                       |       
      | xor                                   |       
      v                                       |       
+-------------+<------------------------------+       
|      H      |<----- K4                              
+-------------+                                       
      |                                               
      v                                               
+-----------+                           +-----------+ 
|    L2     |                           |    R2     | 
+-----------+                           +-----------+ 

5.5 Decryption

LIONESS decryption is the inverse of the four internal rounds defined in the previous section:

6. Payload Construction

This section specifies how the Sphinx payload is constructed using the LIONESS wide-block encryption. Some parts of this section restates the Mix specification for clarity. For full specification of how the Sphinx packet is constructed, refer to the mix protocol specification.

6.1 Payload Plaintext Format

Before layered encryption, the sender MUST construct the payload plaintext as the following concatenation:

where:

  • is the payload integrity prefix, consisting of zero bytes.
  • is the application message padded to fill the remaining payload space.

Thus:

  • bytes

The size of the payload is specified in the Mix protocol, and if the application message is small payload padding is added as specified in the mix protocol.

6.2 Sphinx Payload Construction

6.2.1 Forward Payload

Once the plaintext is formatted as specified in section 6.1, it needs to be encrypted in layers such that each hop in the mix path removes exactly one layer using the per-hop session key. This ensures that only the final hop (i.e., the exit node) can fully recover the plaintext message , validate its integrity, and forward it to the destination. To compute the encrypted payload, perform the following steps for each hop down to , recursively:

  1. Derive the payload encryption key:

    where is the per-hop shared secret for hop i as defined in the Mix protocol specification.

  2. Using , compute the encrypted payload :

  • If (i.e., exit node):

  • Otherwise (i.e., intermediary node):

The resulting is placed into the final Sphinx packet.

6.2.2 Reply payload (SURB payload)

For a SURB reply, the reply sender (i.e., the SURB user not the SURB creator) does not know the return-path shared secrets . Instead, it only has the first node in the return path (), a pre-computed Sphinx header (), and a reply key ().

Therefore, the reply sender encrypts the reply payload only once using as the LIONESS seed:

Note that the reply key is bytes in size as defined in the Mix Protocol, so it satisfies the LIONESS seed requirement. The resulting is placed into the SURB reply packet. Then each hop on the return path subsequently applies the normal payload-processing rule, namely one LIONESS decryption under its per-hop payload encryption key, resulting in one layer of LIONESS encryption and layers of LIONESS decryptions. These return path layers are later removed during reply recovery as described in section 7.3.

7. Sphinx Payload Processing

Once the Sphinx packet is deserialized into () and the header is preprocessed as specified in the Mix protocol, the mix node performs the following steps depending on its role (as defined in the Mix Protocol, Section 8.6.2):

7.1 Intermediary Processing

If the node is an intermediary, it MUST:

  1. Derive the payload encryption key using the shared secret :

  2. Decrypt one layer of the payload using the payload encryption key :

  3. use as the outgoing payload,

  4. forward the updated packet as defined by the Mix Protocol.

7.2 Exit Processing - Forward packet

If the node is the exit, and the packet is not a reply (using SURBs), it MUST:

  1. Derive the payload encryption key using the shared secret in the same way as defined in section 7.1 step 1.
  2. Decrypt one layer of the payload using the payload encryption key in the same way as defined in section 7.1 step 2.
  3. perform the payload integrity prefix check as follows:
  • parse the decrypted payload as , where bytes.
  • verify that
  • discard the packet if this payload integrity prefix check fails.
  • otherwise remove (i.e., the bytes of payload integrity prefix) and return .
  1. pass to the Mix Exit Layer.

7.3 Exit Processing - Reply packet

If the node is the exit, and the packet is a reply, it MUST:

  1. reverse the return-path transformations, i.e., since the hops apply LIONESS decryption, the exit must apply LIONESS encryption. Therefore, the node must perform the same LIONESS layered encryption pattern as defined in Section 6.2.1, with the reply payload used as the initial input instead of the plaintext payload block .

  2. Decrypt the final layer i.e., reversing the effect of the initial encryption in section 6.2.2:

  3. perform the payload integrity prefix check as defined in section 7.2 step 3 and then pass to the Mix Exit Layer.

Note: We assume here that the exit is the SURB creator, if not then the exit will simply forward to the destination which will process the reply payload as specified above.

8. Security Considerations

8.1 LIONESS Blocks/Messages

This specification requires the LIONESS input block to be at least bytes, i.e., 64 bytes since we assume .

LIONESS splits the input block into two parts

where:

  • bytes,
  • bytes.

Therefore, the 64-byte minimum ensures that both and contain at least bytes. The Mix Protocol payload size is expected to be much larger than this minimum, so this requirement is satisfied by normal Mix payloads.

8.2 LIONESS Integrity

This specification does not use an explicit payload authentication tag. Instead, integrity is obtained by:

  • embedding a fixed payload integrity prefix, specified as bytes of zeros, into the plaintext,
  • encrypting the whole payload as a single block with LIONESS.

Because the payload is encrypted as a single block, LIONESS acts as a pseudo-random permutation (PRP) over the entire payload, a modification to the ciphertext will, except with negligible probability, produce a decrypted plaintext whose first bytes are not all zero. Replacing LIONESS with a malleable stream construction invalidates this property.

8.3 Primitive Choices

The security of LIONESS depends on the security of the primitives used to instantiate it:

  • the stream cipher ,
  • the keyed hash function ,
  • the key derivation function .

For detailed analysis on the security of LIONESS, refer to the paper.

9. Reference Implementations

These reference implementations are generic and can support any compatible , , and .

10. Future Work

The following are under research/consideration:

References