This document defines the Sphinx cryptographic packet format for
decryption mix networks, and provides a parameterization based around
generic cryptographic primitives types. This document does not
introduce any new crypto, but is meant to serve as an implementation
guide.

The Sphinx cryptographic packet format is a compact and provably
secure design introduced by George Danezis and Ian Goldberg [SPHINX09].
It supports a full set of security features: indistinguishable
replies, hiding the path length and relay position, detection of
tagging attacks and replay attacks, as well as providing
unlinkability for each leg of the packet’s journey over the
network.

When used with the default payload authentication mechanism,
the SPRP MUST be “fragile” in that any amount of modifications
to M results in a large number of unpredictable changes across
the whole message upon a SPRP_Encrypt() or SPRP_Decrypt()
operation.

EXP(X,Y) - An exponentiation function which takes the
GROUP_ELEMENT_LENGTH byte octet array group elements X and Y,
and returns X^^Y as a GROUP_ELEMENT_LENGTH byte octet array.

Let G denote the generator of the group, and EXP_KEYGEN()
return a GROUP_ELEMENT_LENGTH byte octet array group element
usable as private key.

The group defined by G and EXP(X,Y) MUST satisfy the Decision
Diffie-Hellman problem.

additional_data - Unencrypted per-packet Additional Data (AD)
that is visible to every hop. The AD is authenticated on a
per-hop basis.

As the additional_data is sent in the clear and traverses the
network unaltered, implementations MUST take care to ensure
that the field cannot be used to track individual packets.

group_element - An element of the cyclic group, used to derive
the per-hop key material required to authenticate and process
the rest of the SphinxHeader and decrypt a single layer of the
Sphinx Packet Payload encryption.

routing_information - A vector of per-hop routing information,
encrypted and authenticated in a nested manner. Each element of
the vector consists of a series of routing commands, specifying
all of the information required to process the packet.

The routing_information component of the Sphinx Packet Header
contains a vector of per-hop routing information. When processing a
packet, the per hop processing is set up such that the first element
in the vector contains the routing commands for the current hop.

While the NullCommand’s padding field is specified as opaque,
implementations SHOULD zero fill the padding. The choice of ‘0x00’
as the terminal NullCommand is deliberate to ease implementation,
as ZEROBYTES(N) produces a valid NullCommand RoutingCommand,
resulting in “appending zero filled padding” producing valid output.

Implementations MUST pad the routing_commands vector so that it is
exactly PER_HOP_RI_LENGTH bytes, by appending a terminal NullCommand
if necessary.

Every non-terminal hop’s routing_commands MUST include a
NextNodeHopCommand.

The Sphinx Packet Payload refers to the block of data immediately
following the Sphinx Packet Header in a Sphinx Packet.

For most purposes the structure of the Sphinx Packet Payload can be
treated as a single contiguous byte vector of opaque data.

Upon packet creation, the payload is repeatedly encrypted (unless it
is a SURB Reply, see Section 7) via keys derived from the
Diffie-Hellman key exchange between the packet’s group_element
and the public key of each node in the path.

Authentication of packet integrity is done by prepending a tag set
to a known value to the plaintext prior to the first encrypt
operation. By virtue of the fragile nature of the SPRP function,
any alteration to the encrypted payload as it traverses the network
will result in an irrecoverably corrupted plaintext when the payload
is decrypted by the recipient.

For the sake of brevity, the pseudocode for all of the operations
will take a vector of the following PathHop structure as a
parameter named path[] to specify the path a packet will traverse,
along with the per-hop routing commands and per-hop public keys.

struct{/* There is no need for a node_id here, as routing_commands[0].next_hop specifies that information for all non-terminal hops. */opaquepublic_key[GROUP_ELEMENT_LENGTH];RoutingCommandrouting_commands<1...2^8-1>;}PathHop;

It is assumed that each routing_commands vector except for the
terminal entry contains at least a RoutingCommand consisting of
a partially assembled NextNodeHopCommand with the next_hop element
filled in with the identifier of the next hop.

order, specifying the node id, public
key, and routing commands for each hop.

Outputs:

sphinx_header The resulting Sphinx Packet Header.

payload_keys The vector of SPRP keys used to encrypt

the Sphinx Packet Payload, in hop order.

The Sphinx_Create_Header operation consists of the following steps:

Derive the key material for each hop.

num_hops=route.lenroute_keys=[]route_group_elements=[]priv_key=EXP_KEYGEN()/* Calculate the key material for the 0th hop. */group_element=EXP(G,priv_key)route_group_elements+=group_elementshared_secret=EXP(path[0].public_key,priv_key)route_keys+=Sphinx_KDF(KDF_INFO,shared_secret)blinding_factor=keys[0].blinding_factor/* Calculate the key material for rest of the hops. */fori=1;i<num_hops;++i:shared_secret=EXP(path[i].public_key,priv_key)forj=0;j<i;++j:shared_secret=EXP(shared_secret,keys[j].blinding_factor)route_keys+=Sphinx_KDF(KDF_INFO,shared_secret)group_element=EXP(group_element,keys[i-1].blinding_factor)route_group_elements+=group_elementAttheconclusionofthederivationprocess:route_keys-Avectorofper-hopSphinxKeys.route_group_elements-Avectorofper-hopgroupelements.

Derive the routing_information keystream and encrypted
padding for each hop.

/* Start with the terminal hop, and work backwards. */i=num_hops-1/* Encode the terminal hop's routing commands. As the terminal hop can never have a NextNodeHopCommand, there are no per-hop alterations to be made. */ri_fragment=path[i].routing_commands|ZEROBYTES(PER_HOP_RI_LENGTH-LEN(path[i].routing_commands))/* Encrypt and MAC. */ri_fragment^=ri_keystream[i]mac=MAC(route_keys[i].header_mac,additional_data|route_group_elements[i]|ri_fragment|ri_padding[i-1])routing_info=ri_fragmentifnum_hops<MAX_HOPS:pad_len=(MAX_HOPS-num_hops)*PER_HOP_RI_LENGTHrouting_info=routing_info|ZEROBYTES(pad_len)/* Calculate the routing info for the rest of the hops. */fori=num_hops-2;i>=0;--i:cmds_to_encode=[]/* Find and finalize the NextNodeHopCommand. */forj=0;j<LEN(path[i].routing_commands;j++:cmd=path[i].routing_commands[j]ifcmd.command==next_node_hop:/* Finalize the NextNodeHopCommand. */cmd.MAC=maccmds_to_encode=cmds_to_encode+cmd/* Append *//* Append a terminal NullCommand. */ri_fragment=cmds_to_encode|ZEROBYTES(PER_HOP_RI_LENGTH-LEN(cmds_to_encode))/* Encrypt and MAC */routing_info=ri_fragment|routing_info/* Prepend. */routing_info^=ri_keystream[i]ifi>0:mac=MAC(route_keys[i].header_mac,additional_data|route_group_elements[i]|routing_info|ri_padding[i-1])else:mac=MAC(route_keys[i].header_mac,additional_data|route_group_elements[i]|routing_info)Attheconclusionofthederivationprocess:routing_info-Thecompletedrouting_infoblock.mac-TheMACforthe0thhop.

Mix nodes process incoming packets first by performing the
Sphinx_Unwrap operation to authenticate and decrypt the packet, and
if applicable prepare the packet to be forwarded to the next node.

If Sphinx_Unwrap returns an error for any given packet, the packet
MUST be discarded with no additional processing.

After a packet has been unwrapped successfully, a replay detection
tag is checked to ensure that the packet has not been seen before.
If the packet is a replay, the packet MUST be discarded with no
additional processing.

The routing commands for the current hop are interpreted and
executed, and finally the packet is forwarded to the next mix node
over the network or presented to the application if the current
node is the final recipient.

If the header’s additional_data element contains information
required to complete the unwrap operation, such as specifying
the packet format version or the cryptographic primitives used
examine it now.

Implementations MUST NOT treat the information in the
additional_data element as trusted until after the completion
of Step 3 (“Validate the Sphinx Packet Header”).

derived_mac=MAC(keys.header_mac,hdr.additional_data|hdr.group_element|hdr.routing_information)if!CONSTANT_TIME_CMP(derived_mac,hdr.MAC):/* MUST abort processing if the header is invalid. */returnErrorInvalidHeader

Extract the per-hop routing commands for the current hop.

/* Append padding to preserve length-invariance, as the routing commands for the current hop will be removed. */padding=ZEROBYTES(PER_HOP_RI_LENGTH)B=hdr.routing_information|padding/* Decrypt the entire routing_information block. */B=B^S(keys.header_encryption,keys.header_encryption_iv)

Parse the per-hop routing commands.

cmd_buf=B[:PER_HOP_RI_LENGTH]new_routing_information=B[PER_HOP_RI_LENGTH:]next_mix_command_idx=-1routing_commands=[]foridx=0;idx<PER_HOP_RI_LENGTH{/* WARNING: Bounds checking omitted for brevity. */cmd_type=b[idx]cmd=NULLswitchcmd_type{casenull:gotodone/* No further commands. */casenext_node_hop:cmd=RoutingCommand(B[idx:idx+1+LEN(NextNodeHopCommand)])next_mix_command_idx=i/* Save for step 7. */idx+=1+LEN(NextNodeHopCommand)breakcaserecipient:cmd=RoutingCommand(B[idx:idx+1+LEN(FinalDestinationCommand)])idx+=1+LEN(RecipientCommand)breakcasesurb_reply:cmd=RoutingCommand(B[idx:idx+1+LEN(SURBReplyCommand)])idx+=1+LEN(SURBReplyCommand)breakdefault:/* MUST abort processing on unrecognized commands. */returnErrorInvalidCommand}routing_commands+=cmd/* Append cmd to the tail of the list. */}done:

Upon the completion of the Sphinx_Unwrap operation, implementations
MUST take several additional steps. As the exact behavior is mostly
implementation specific, pseudocode will not be provided for most of
the post processing steps.

Apply replay detection to the packet.

The replay_tag value returned by Sphinx_Unwrap MUST be unique
across all packets processed with a given private_routing_key.

The exact specifics of how to detect replays is left up to the
implementation, however any replays that are detected MUST
be discarded immediately.

Act on the routing commands, if any.

The exact specifics of how implementations chose to apply
routing commands is deliberately left unspecified, however in
general:

Iff there is a NextNodeHopCommand, the packet should be
forwarded to the next node based on the next_hop field
upon completion of the post processing.

The lack of a NextNodeHopCommand indicates that the packet
is destined for the current node.

Iff there is a SURBReplyCommand, the packet should be
treated as a SURBReply destined for the current node,
and decrypted accordingly (See Section 7.2).

If the implementation supports multiple recipients on a
single node, the RecipientCommand command should be used
to determine the correct recipient for the packet, and
the payload delivered as appropriate.

Note: It is possible for both a RecipientCommand and a
NextNodeHopCommand to be present simultaneously in the
routing commands for a given hop. The behavior when
this situation occurs is implementation defined.

Authenticate the packet if required.

Iff the packet is destined for the current node, the integrity
of the payload MUST be authenticated.

The authentication is done as follows:

derived_tag=sphinx_packet.payload[:PAYLOAD_TAG_LENGTH]expected_tag=ZEROBYTES(PAYLOAD_TAG_LENGTH)if!CONSTANT_TIME_CMP(derived_tag,expected_tag):/* Discard the packet with no further processing. */returnErrorInvalidPayload/* Remove the authentication tag before presenting the payload to the application. */sphinx_packet.payload=sphinx_packet.payload[PAYLOAD_TAG_LENGTH:]

A Single Use Reply Block (SURB) is a delivery token with a short
lifetime, that can be used by the recipient to reply to the initial
sender.

SURBs allow for anonymous replies, when the recipient does not know
the sender of the message. Usage of SURBs guarantees anonymity
properties but also makes the reply messages indistinguishable
from forward messages both to external adversaries as well as the
mix nodes.

When a SURB is created, a matching reply block Decryption Token
is created, which is used to decrypt the reply message that is
produced and delivered via the SURB.

The Sphinx SURB wire encoding is implementation defined, but for
the purposes of illustrating creation and use, the following will
be used:

Structurally a SURB consists of three parts, a pre-generated Sphinx
Packet Header, a node identifier for the first hop to use when using
the SURB to reply, and cryptographic keying material by which to
encrypt the reply’s payload. All elements must be securely
transmitted to the recipient, perhaps as part of a forward Sphinx
Packet’s Payload, but the exact specifics on how to accomplish this
is left up to the implementation.

When creating a SURB, the terminal routing_commands vector SHOULD
include a SURBReplyCommand, containing an identifier to ensure
that the payload can be decrypted with the correct set of keys
(Decryption Token). The routing command is left optional, as
it is conceivable that implementations may chose to use trial
decryption, and or limit the number of outstanding SURBs to solve
this problem.

A Sphinx Reply packet that was generated using a SURB is externally
indistinguishable from a forward Sphinx Packet as it traverses the
network. However, the recipient of the reply has an additional
decryption step, the packet starts off unencrypted, and accumulates
layers of Sphinx Packet Payload decryption as it traverses the
network.

Determining which decryption token to use when decrypting the SURB
reply can be done via the SURBReplyCommand’s id field, if one is
included at the time of the SURB’s creation.

Sphinx_Decrypt_SURB_Reply(decryption_token,payload)->message

Inputs:

decryption_token The vector of keys allowing a client to

decrypt the reply ciphertext payload. This
decryption_token is generated when the
SURB is created.

payload The Sphinx Packet ciphertext payload.

Outputs:

error Indicating a unsuccessful unwrap

operation if applicable.

message The plaintext message.

The Sphinx_Decrypt_SURB_Reply operation consists of the following
steps:

Encrypt the message to reverse the decrypt operations the
payload acquired as it traversed the network.

The process for using a SURB to reply anonymously is slightly
different from the standard packet creation process, as the
Sphinx Packet Header is already generated (as part of the SURB),
and there is an additional layer of Sphinx Packet Payload
encryption that must be performed.

Sphinx_Create_SURB_Reply(sphinx_surb,payload)->sphinx_packet

Inputs:

sphinx_surb The SphinxSURB structure, decoded from

the implementation defined wire encoding.

payload The packet payload message plaintext.

The Sphinx_Create_SURB_Reply operation consists of the following
steps:

Depending on the mix topology, there is no hard requirement that the
per-hop routing info is padded to one fixed constant length.

For example, assuming a layered topology (referred to as stratified
topology in the literature) [MIXTOPO10], where the layer of any given
mix node is public information, as long as the following two
invariants are maintained, there is no additional information
available to an adversary:

All packets entering any given mix node in a certain layer are
uniform in length.

All packets leaving any given mix node in a certain layer are
uniform in length.

The only information available to an external or internal observer is
the layer of any given mix node (via the packet length), which is
information they are assumed to have by default in such a design.

The Sphinx Packet Construct is crafted such that any given packet
is bitwise unlinkable after a Sphinx_Unwrap operation, provided
that the optional Additional Data (AD) facility is not used. This
property ensures that external passive adversaries are unable to
track a packet based on content as it traverses the network. As
the on-the-wire AD field is static through the lifetime of a
packet (ie: left unaltered by the Sphinx_Unwrap operation),
implementations and applications that wish to use this facility
MUST NOT transmit AD that can be used to distinctly identify
individual packets.

Each node acting as a mix MUST regenerate their asymmetric key pair
relatively frequently. Upon key rotation the old private key MUST
be securely destroyed. As each layer of a Sphinx Packet is encrypted
via key material derived from the output of an ephemeral/static
Diffie-Hellman key exchange, without the rotation, the construct
does not provide Perfect Forward Secrecy. Implementations SHOULD
implement defense-in-depth mitigations, for example by using
strongly forward-secure link protocols to convey Sphinx Packets
between nodes.

This frequent mix routing key rotation can limit SURB usage by
directly reducing the lifetime of SURBs. In order to have a strong
Forward Secrecy property while maintaining a higher SURB lifetime,
designs such as forward secure mixes [SFMIX03] could be used.

Reply Blocks (SURBs), forward and reply Sphinx packets are all
vulnerable to the compulsion threat, if they are captured by an
adversary. The adversary can request iterative decryptions or keys
from a series of honest mixes in order to perform a deanonymizing
trace of the destination.

While a general solution to this class of attacks is beyond the
scope of this document, applications that seek to mitigate or
resist compulsion threats could implement the defenses proposed
in [COMPULS05] via a series of routing command extensions.

Given a hypothetical scenario where Alice and Bob both wish to keep
their location on the mix network hidden from the other, and Alice
has somehow received a SURB from Bob, Alice MUST not utilize the
SURB directly because in the volunteer operated mix network the
first hop specified by the SURB could be operated by Bob for the
purpose of deanonymizing Alice.

This problem could be solved via the incorporation of a “cross-over
point” such as that described in [MIXMINION], for example by
having Alice delegating the transmission of a SURB Reply to a
randomly selected crossover point in the mix network, so that
if the first hop in the SURB’s return path is a malicious mix,
the only information gained is the identity of the cross-over
point.

The payload encryption’s use of a fragile (non-malleable) SPRP is
deliberate and implementations SHOULD NOT substitute it with a
primitive that does not provide such a property (such as a stream
cipher based PRF). In particular there is a class of correlation
attacks (tagging attacks) targeting anonymity systems that involve
modification to the ciphertext that are mitigated if alterations
to the ciphertext result in unpredictable corruption of the
plaintext (avalanche effect).

Additionally, as the PAYLOAD_TAG_LENGTH based tag-then-encrypt
payload integrity authentication mechanism is predicated on the
use of a non-malleable SPRP, implementations that substitute a
different primitive MUST authenticate the payload using a
different mechanism.

Alternatively, extending the MAC contained in the Sphinx Packet
Header to cover the Sphinx Packet Payload will both defend against
tagging attacks and authenticate payload integrity. However, such an
extension does not work with the SURB construct presented in this
specification, unless the SURB is only used to transmit payload
that is known to the creator of the SURB.