Title | Description | Link(s) | |
---|---|---|---|
📖 | Admin Guide | Detailed guide for deploying and managing Katzenpost servers, including setting up a local Docker-based mixnet. | HTML / PDF |
📖 | Design specifications | Documentation of the mixnet design. | HTML |
🔒 | Threat Model | An evolving document defining Katzenpost’s security assumptions, attack scenarios, and mitigation strategies. | HTML / PDF |
📚 | Literature Review | A curated review of academic literature, explaining the theoretical foundations behind Katzenpost’s design decisions. | |
🎧 | Audio Engineering Considerations for a Modern Mixnet | Technical analysis of audio transmission challenges and solutions for modern mixnets, with a focus on usability and scalability. | HTML / PDF |
This is the multi-page printable view of this section. Click here to print.
Katzenpost Documentation
- 1: Administration guide
- 2: Specifications
- 3: Audio Engineering Considerations for a Modern Mixnet
- 4: Threat Model
1 - Administration guide
Use the documentation in this section to explore how the mixnet works and to implement your own.
Title | Description | Link(s) | |
---|---|---|---|
📖 | Admin Guide | Detailed guide for deploying and managing Katzenpost servers, including setting up a local Docker-based mixnet. |
1.1 -
Table of Contents
This topic provides collects basic commands for installed Katzenpost server components in a single convenient place. All system commands require superuser privileges.
The commands in this topic do not apply to the Katzenpost Docker image, which has its own controls. For more information, see Using the Katzenpost Docker test network.
These commands match the suggested systemd setup described in Installing Katzenpost.
Table 1. Systemd control commands for Katzenpost
Task |
Command |
---|---|
Start a mix node. |
systemctl start katzenpost-mixserver |
Stop a mix node. |
systemctl stop katzenpost-mixserver |
Restart a mix node. |
systemctl restart katzenpost-mixserver |
Start a diectory authority node. |
systemctl start katzenpost-authority |
Stop a diectory authority node. |
systemctl stop katzenpost-authority |
Restart a diectory authority node. |
systemctl restart katzenpost-authority |
The primary Katzenpost server binaries are katzenpost-mixserver, which instantiates a mix node, gateway node, or service provider depending on its configuration, and katzenpost-authority, which instantiates a directory authority.
Table 2. Command options for the Katzenpost server binaries
Task |
Command |
---|---|
Control a mix node. |
Run katzenpost-mixserver -h for options.
The The |
Control a directory authority. |
Run katzenpost-authority -h for options.
The The |
Katzenpost provides a management interface that is accessed through a unix domain
socket. The interface supports run-time changes to nodes without requiring a restart.
By
default, the management interface is disabled. To enable it, change the
Management
section of the node's configuration file so that
Enable = true
:
[Management]
Enable = true
Path = "/node_datadir
/management_sock"
For more information about management configuration, see the details for your node type in Components and configuration of the Katzenpost mixnet.
Use the socat command-line utility to connect to the management socket and issue commands, with the following syntax:
#
socat unix:/node-datadir
/management_sock STDOUT
The following commands are supported.
-
QUIT - Exit the management socket session.
-
SHUTDOWN - Shut down the server gracefully.
-
ADD_USER - Add a user and associate it with a public link key provided in either hexadecimal or Base64 format.
ADD_USER
user
key
-
UPDATE_USER - Update a user's link key.
UPDATE_USER
user
key
-
REMOVE_USER - Remove a user.
REMOVE_USER
user
-
SET_USER_IDENTITY - Set a user's identity key.
SET_USER_IDENTITY
user
key
-
REMOVE_USER_IDENTITY - Remove a user's identity key. This command must be followed up with a
REMOVE_USER
command.REMOVE_USER_IDENTITY
user
-
USER_IDENTITY - Retrieve a user's identity key.
USER_IDENTITY
user
-
SEND_RATE - Set the packet rate limit to a per-minute integer value.
SEND_RATE
value
-
SEND_BURST - Set the packet burst-rate limit to a per-minute integer value.
SEND_BURST
value
Katzenpost logging information can be viewed in real time with the following commands:
#
journalctl -u katzenpost-mixserver -f -n 2000
or
#
journalctl -u katzenpost-authority -f -n 2000
Logging levels include ERROR, WARNING, NOTICE, INFO, and DEBUG, with INFO as the default. For information about setting the log level, see the documentation for each node type in Components and configuration of the Katzenpost mixnet.
1.2 -
Table of Contents
The section provides an overview of how to download Katzenpost, set up a development environment, build the code, install the Katzenpost binaries, and configure the components.
An up-to-date Debian or Ubuntu Linux system is assumed as the build and hosting environment for all Katzenpost server-side components. Required packages include the following:
-
git
-
gcc
-
build-essential
-
libc-dev-bin
Complete the following steps to set up a local Katzenpost git repository.
-
Clone Katzenpost.
$
git clone git@github.com:katzenpost/katzenpost.git -
Get the latest tagged commit of the Katzenpostwith the following commands.
$
git fetch --tags$
tag=$(git describe --tags `git rev-list --tags --max-count=1`)$
git checkout $tag
Download the latest version of the Go programming language from https://go.dev/dl and unzip it in a suitable location. As root, set the necessary environment variables:
#
export PATH=$PATH:/<your Go location>
/bin#
export GO111MODULE=on#
export CGO_CFLAGS_ALLOW="-DPARAMS=sphincs-shake-256f"
The go/bin
path must be included in your user $PATH
environment variable.
![]() |
Note |
---|---|
Do not use the Debian/Ubuntu golang packages. They are probably too old. |
To build a Katzenpost server component, navigate to the directory containing its source code and run go build. The paths shown are relative to the Katzenpost repository root.
Table 1. Server component directories
Component | Source code directory | Binary |
---|---|---|
Mix, gateway, or service node | server/cmd/server/ |
server |
Directory authority | authority/cmd/dirauth/ |
dirauth |
The Katzenpost client components are useful for testing an operational mixnet. To build them, navigate to the directory containing each component's source code and run go build. The paths shown are relative to the Katzenpost repository root.
![]() |
Note |
---|---|
The Katzen chat client is under development and not currently functional. For more information about the clients generally, see Clients. |
Table 2. Client directories
Component | Source code directory | Binary or application |
---|---|---|
Ping |
ping/ |
ping |
Fetch |
|
fetch |
Status |
Obtain from GitHub repository katzenpost/status. | status.py |
Worldmap |
Obtain from GitHub repository katzenpost/worldmap | world_map.py |
To install the server binaries, run the following commands from the katzenpost repository root.
#
cp server/cmd/server/server /usr/local/bin/katzenpost-mixserver#
cp authority/cmd/dirauth/dirauth /usr/local/bin/katzenpost-authority
Create a service account account for each of the node types that you deploy.
To create a service user for a directory authority.
#
adduser \
--disabled-login \
--disabled-password \
--system \
--group \
--home /var/lib/katzenpost-authority \
katzenpost-authority
To create a service user for a mix, gateway, or service node.
#
adduser \
--disabled-login \
--disabled-password \
--system \
--group \
--home /var/lib/katzenpost-mixserver \
katzenpost-mixserver
The best way currently to construct a node configuration file is to use one of the samples in Appendix: Configuration files from the Docker test mixnet, and to modify it based on the published component parameters, combined with attention to the latest state of the code tree. Bear in mind that the IP address:port scheme used in the Docker image is specific to that container environment, and is not transferable to a production network without modifcation.
Katzenpost currently has no configuration automation tool that is ready for general use.
If you are running your Katzenpost components under systemd, create and install a systemd service file for each node type that you plan to deploy. The following scripts are examples of how to do this.
To create a systemd service file for a directory authority.
#!/bin/bash -x cat << EOF > /etc/systemd/system/katzenpost-mixserver.service [Unit] Description=Katzenpost Mix Server After=network.target [Service] IPAccounting=yes Type=simple User=katzenpost-mixserver WorkingDirectory=/var/lib/katzenpost-mixserver ExecStart=/usr/local/bin/katzenpost-mixserver -f /etc/katzenpost-mixserver/katzenpost.toml PrivateTmp=yes NoNewPrivileges=yes # RestartSec=5 Restart=on-failure [Install] WantedBy=default.target EOF
To create a systemd service file for a mix, gateway, or service node.
#!/bin/bash -x cat << EOF > /etc/systemd/system/katzenpost-authority.service [Unit] Description=Katzenpost Authority After=network.target [Service] Type=simple IPAccounting=yes User=katzenpost-authority WorkingDirectory=/var/lib/katzenpost-authority ExecStart=/usr/local/bin/katzenpost-authority -f /etc/katzenpost-authority/authority.toml PrivateTmp=yes NoNewPrivileges=yes Restart=on-failure [Install] WantedBy=default.target EOF
The first time that you run a server binary directly or using
systemd, identity and encryption keys are automatically generated and installed if
they
are not already present. The key location is specified by the value of
DataDir
in the [Server]
section of the configuration. For
configuration parameter details, see Components
and configuration of the Katzenpost mixnet. For server binary commandline options,
see the Quickstart guide.
Once the keys are in place, restart the server to begin operations.
1.3 -
Table of Contents
Katzenpost provides a ready-to-deploy Docker image for developers who need a non-production test environment for developing and testing client applications and server side plugins. By running this image on a single computer, you avoid the need to build and manage a complex multi-node mix net. The image can also be run using Podman
The test mix network includes the following components:
Before running the Katzenpost docker image, make sure that the following software is installed.
-
A Debian GNU Linux or Ubuntu system
-
Docker, Docker Compose, and (optionally) Podman
Note If both Docker and Podman are present on your system, Katzenpost uses Podman. Podman is a drop-in daemonless equivalent to Docker that does not require superuser privileges to run.
On Debian, these software requirements can be installed with the following commands (running as superuser). Apt will pull in the needed dependencies.
#
apt update#
apt install git golang make docker docker-compose podman
Complete the following procedure to obtain, build, and deploy the Katzenpost test network.
-
Install the Katzenpost code repository, hosted at
https://github.com/katzenpost
. The main Katzenpost repository contains code for the server components as well as the docker image. Clone the repository with the following command (your directory location may vary):~$
git clone https://github.com/katzenpost/katzenpost.git -
Navigate to the new
katzenpost
subdirectory and ensure that the code is up to date.~$
cd katzenpost~/katzenpost$
git checkout main~/katzenpost$
git pull -
(Optional) Create a development branch and check it out.
~/katzenpost$
git checkout -b devel -
(Optional) If you are using Podman, complete the following steps:
-
Point the DOCKER_HOST environment variable at the Podman process.
$
export DOCKER_HOST=unix:///var/run/user/$(id -u)/podman/podman.sock -
Set up and start the Podman server (as superuser).
$
podman system service -t 0 $DOCKER_HOST &$
systemctl --user enable --now podman.socket
-
Navigate to katzenpost/docker
. The Makefile
contains target operations to create, manage, and test the self-contained Katzenpost
container network. To invoke a target, run a command with the using the following
pattern:
~/katzenpost/docker$
maketarget
Running make with no target specified returns a list of available targets.
Table 1. Table 1: Makefile targets
[none] |
Display this list of targets. |
start |
Run the test network in the background. |
stop |
Stop the test network. |
wait |
Wait for the test network to have consensus. |
watch |
Display live log entries until Ctrl-C. |
status |
Show test network consensus status. |
show-latest-vote |
Show latest consensus vote. |
run-ping |
Send a ping over the test network. |
clean-bin |
Stop all components and delete binaries. |
clean-local |
Stop all components, delete binaries, and delete data. |
clean-local-dryrun |
Show what clean-local would delete. |
clean |
Same as clean-local, but also
deletes |
The first time that you run make start, the Docker image is downloaded, built, installed, and started. This takes several minutes. When the build is complete, the command exits while the network remains running in the background.
~/katzenpost/docker$
make start
Subsequent runs of make start either start or restart the network without building the components from scratch. The exception to this is when you delete any of the Katzenpost binaries (dirauth.alpine, server.alpine, etc.). In that case, make start rebuilds just the parts of the network dependent on the deleted binary. For more information about the files created during the Docker build, see the section called “Network topology and components”.
![]() |
Note |
---|---|
When running make start , be aware of the following considerations:
|
After the make start command exits, the mixnet runs in the background, and you can run make watch to display a live log of the network activity.
~/katzenpost/docker$
make watch
...
<output>
...
When installation is complete, the mix servers vote and reach a consensus. You can use the wait target to wait for the mixnet to get consensus and be ready to use. This can also take several minutes:
~/katzenpost/docker$
make wait
...
<output>
...
You can confirm that installation and configuration are complete by issuing the status command from the same or another terminal. When the network is ready for use, status begins returning consensus information similar to the following:
~/katzenpost/docker$
make status
...
00:15:15.003 NOTI state: Consensus made for epoch 1851128 with 3/3 signatures: &{Epoch: 1851128 GenesisEpoch: 1851118
...
At this point, you should have a locally running mix network. You can test whether it is working correctly by using run-ping, which launches a packet into the network and watches for a successful reply. Run the following command:
~/katzenpost/docker$
make run-ping
If the network is functioning properly, the resulting output contains lines similar to the following:
19:29:53.541 INFO gateway1_client: sending loop decoy !19:29:54.108 INFO gateway1_client: sending loop decoy 19:29:54.632 INFO gateway1_client: sending loop decoy 19:29:55.160 INFO gateway1_client: sending loop decoy !19:29:56.071 INFO gateway1_client: sending loop decoy !19:29:59.173 INFO gateway1_client: sending loop decoy !Success rate is 100.000000 percent 10/10)
lf run-ping fails to receive a reply, it eventually times out with an error message. If this happens, try the command again.
![]() |
Note |
---|---|
If you attempt use run-ping too quickly after starting the mixnet, and consensus has not been reached, the utility may crash with an error message or hang indefinitely. If this happens, issue (if necessary) a Ctrl-C key sequence to abort, check the consensus status with the status command, and then retry run-ping. |
The mix network continues to run in the terminal where you started it until you issue a Ctrl-C key sequence, or until you issue the following command in another terminal:
~/katzenpost/docker$
make stop
When you stop the network, the binaries and data are left in place. This allows for a quick restart.
Several command targets can be used to uninstall the Docker image and restore your system to a clean state. The following examples demonstrate the commands and their output.
-
clean-bin
To stop the network and delete the compiled binaries, run the following command:
~/katzenpost/docker$
make clean-bin [ -e voting_mixnet ] && cd voting_mixnet && DOCKER_HOST=unix:///run/user/1000/podman/podman.sock docker-compose down --remove-orphans; rm -fv running.stamp Stopping voting_mixnet_auth3_1 ... done Stopping voting_mixnet_servicenode1_1 ... done Stopping voting_mixnet_metrics_1 ... done Stopping voting_mixnet_mix3_1 ... done Stopping voting_mixnet_auth2_1 ... done Stopping voting_mixnet_mix2_1 ... done Stopping voting_mixnet_gateway1_1 ... done Stopping voting_mixnet_auth1_1 ... done Stopping voting_mixnet_mix1_1 ... done Removing voting_mixnet_auth3_1 ... done Removing voting_mixnet_servicenode1_1 ... done Removing voting_mixnet_metrics_1 ... done Removing voting_mixnet_mix3_1 ... done Removing voting_mixnet_auth2_1 ... done Removing voting_mixnet_mix2_1 ... done Removing voting_mixnet_gateway1_1 ... done Removing voting_mixnet_auth1_1 ... done Removing voting_mixnet_mix1_1 ... done removed 'running.stamp' rm -vf ./voting_mixnet/*.alpine removed './voting_mixnet/echo_server.alpine' removed './voting_mixnet/fetch.alpine' removed './voting_mixnet/memspool.alpine' removed './voting_mixnet/panda_server.alpine' removed './voting_mixnet/pigeonhole.alpine' removed './voting_mixnet/ping.alpine' removed './voting_mixnet/reunion_katzenpost_server.alpine' removed './voting_mixnet/server.alpine' removed './voting_mixnet/voting.alpine'This command leaves in place the cryptographic keys, the state data, and the logs.
-
clean-local-dryrun
To diplay a preview of what clean-local would remove, without actually deleting anything, run the following command:
~/katzenpost/docker$
make clean-local-dryrun -
clean-local
To delete both compiled binaries and data, run the following command:
~/katzenpost/docker$
make clean-local [ -e voting_mixnet ] && cd voting_mixnet && DOCKER_HOST=unix:///run/user/1000/podman/podman.sock docker-compose down --remove-orphans; rm -fv running.stamp Removing voting_mixnet_mix2_1 ... done Removing voting_mixnet_auth1_1 ... done Removing voting_mixnet_auth2_1 ... done Removing voting_mixnet_gateway1_1 ... done Removing voting_mixnet_mix1_1 ... done Removing voting_mixnet_auth3_1 ... done Removing voting_mixnet_mix3_1 ... done Removing voting_mixnet_servicenode1_1 ... done Removing voting_mixnet_metrics_1 ... done removed 'running.stamp' rm -vf ./voting_mixnet/*.alpine removed './voting_mixnet/echo_server.alpine' removed './voting_mixnet/fetch.alpine' removed './voting_mixnet/memspool.alpine' removed './voting_mixnet/panda_server.alpine' removed './voting_mixnet/pigeonhole.alpine' removed './voting_mixnet/reunion_katzenpost_server.alpine' removed './voting_mixnet/server.alpine' removed './voting_mixnet/voting.alpine' git clean -f -x voting_mixnet Removing voting_mixnet/ git status . On branch main Your branch is up to date with 'origin/main'. -
clean
To stop the the network and delete the binaries, the data, and the go_deps image, run the following command as superuser:
~/katzenpost/docker$
sudo make clean
The Docker image deploys a working mixnet with all components and component groups needed to perform essential mixnet functions:
-
message mixing (including packet reordering, timing randomization, injection of decoy traffic, obfuscation of senders and receivers, and so on)
-
service provisioning
-
internal authentication and integrity monitoring
-
interfacing with external clients
![]() |
Warning |
---|---|
While suited for client development and testing, the test mixnet omits performance and security redundancies. Do not use it in production. |
The following diagram illustrates the components and their network interactions. The gray blocks represent nodes, and the arrows represent information transfer.
On the left, the Client transmits a message (shown by purple arrows) through the Gateway node, across three mix node layers, to the Service node. The Service node processes the request and responds with a reply (shown by the green arrows) that traverses the mix node layers before exiting the mixnet via the Gateway node and arriving at the Client.
On the right, directory authorities Dirauth 1, Dirauth 2, and Dirauth 3 provide PKI services. The directory authorities receive mix descriptors from the other nodes, collate these into a consensus document containing validated network status and authentication materials , and make that available to the other nodes.
The elements in the topology diagram map to the mixnet's component nodes as shown in the following table. Note that all nodes share the same IP address (127.0.0.1, i.e., localhost), but are accessed through different ports. Each node type links to additional information in Components and configuration of the Katzenpost mixnet.
Table 2. Table 2: Test mixnet hosts
Node type | Docker ID | Diagram label | IP address | TCP port |
---|---|---|---|---|
auth1 | Dirauth1 |
127.0.0.1 (localhost) |
30001 |
|
auth2 |
Dirauth 2 |
30002 |
||
auth3 |
Dirauth 3 |
30003 |
||
Gateway node | gateway1 | Gateway node | 30004 | |
servicenode1 |
Service node |
30006 |
||
mix1 |
Layer 1 mix node |
30008 |
||
mix2 |
Layer 2 mix node |
30010 |
||
mix3 |
Layer 3 mix node |
30012 |
The following tree
output shows the location, relative to the katzenpost
repository root, of the files created by the Docker build. During testing and use,
you would normally touch only the TOML configuration file associated with each node,
as highlighted in the listing. For help in understanding these files and a complete
list of configuration options, follow the links in Table 2: Test mixnet
hosts.
katzenpost/docker/voting_mixnet/ |---auth1 | |---authority.toml | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---link.private.pem | |---link.public.pem | |---persistence.db |---auth2 | |---authority.toml | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---link.private.pem | |---link.public.pem | |---persistence.db |---auth3 | |---authority.toml | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---link.private.pem | |---link.public.pem | |---persistence.db |---client | |---client.toml |---client2 | |---client.toml |---dirauth.alpine |---docker-compose.yml |---echo_server.alpine |---fetch.alpine |---gateway1 | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---katzenpost.toml | |---link.private.pem | |---link.public.pem | |---management_sock | |---spool.db | |---users.db |---memspool.alpine |---mix1 | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---katzenpost.toml | |---link.private.pem | |---link.public.pem |---mix2 | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---katzenpost.toml | |---link.private.pem | |---link.public.pem |---mix3 | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---katzenpost.toml | |---link.private.pem | |---link.public.pem |---panda_server.alpine |---pigeonhole.alpine |---ping.alpine |---prometheus.yml |---proxy_client.alpine |---proxy_server.alpine |---running.stamp |---server.alpine |---servicenode1 | |---identity.private.pem | |---identity.public.pem | |---katzenpost.log | |---katzenpost.toml | |---link.private.pem | |---link.public.pem | |---management_sock | |---map.storage | |---memspool.13.log | |---memspool.storage | |---panda.25.log | |---panda.storage | |---pigeonHole.19.log | |---proxy.31.log |---voting_mixnet
Examples of complete TOML configuration files are provided in Appendix: Configuration files from the Docker test mixnet.
1.4 -
Table of Contents
This section of the Katzenpost technical documentation provides an introduction to the software components that make up Katzenpost and guidance on how to configure each component. The intended reader is a system administrator who wants to implement a working, production Katzenpost network.
For a quickly deployable, non-production test network (primarily for use by developers), Using the Katzenpost Docker test network.
The core of Katzenpost consists of two program executables, dirauth and server. Running the dirauth commmand runs a
directory authority node, or dirauth, that
functions as part of the mixnet's public-key infrastructure (PKI). Running the
server runs either a mix node, a
gateway node, or a service node, depending
on the configuration. Configuration settings are provided in an associated
katzenpost-authority.toml
or
katzenpost.toml
file respectively.
In addition to the server components, Katzenpost also supports connections to client applications hosted externally to the mix network and communicating with it through gateway nodes.
A model mix network is shown in Figure 1.
Figure 1. The pictured element types correspond to discrete client and server programs that Katzenpost requires to function.

The mix network contains an n-layer topology of mix-nodes, with three nodes per layer in this example. Sphinx packets traverse the network in one direction only. The gateway nodes allow clients to interact with the mix network. The service nodes provide mix network services that mix network clients can interact with. All messages sent by clients are handed to a connector daemon hosted on the client system, passed across the Internet to a gateway, and then relayed to a service node by way of the nine mix nodes. The service node sends its reply back across the mix-node layers to a gateway, which transmits it across the Internet to be received by the targeted client. The mix, gateway, and service nodes send mix descriptors to the dirauths and retrieve a consensus document from them, described below.
In addition to the server components, Katzenpost supports connections to client applications hosted externally to the mix network and communicating with it through gateway nodes and, in some cases, a client connector.
Dirauths compose the decentralized public key infrastructure (PKI) that serves as the root of security for the entire mix network. Clients, mix nodes, gateways nodes, and service nodes rely on the PKI/dirauth system to maintain and sign an up-to-date consensus document, providing a view of the network including connection information and public cryptographic key materials and signatures.
Every 20 minutes (the current value for an epoch), each mix, gateway, and service node signs a mix descriptor and uploads it to the dirauths. The dirauths then vote on a new consensus document. If consensus is reached, each dirauth signs the document. Clients and nodes download the document as needed and verify the signatures. Consensus fails when 1/2 + 1 nodes fail, which yields greater fault tolerance than, for example, Byzantine Fault Tolerance, which fails when 1/3 + 1 of the nodes fail.
The PKI signature scheme is fully configurable by the dirauths. Our recommendation is to use a hybrid signature scheme consisting of classical Ed25519 and the post-quantum, stateless, hash-based signature scheme known as Sphincs+ (with the parameters: "sphincs-shake-256f"), which is designated in Katzenpost configurations as "Ed25519 Sphincs+". Examples are provided below.
The mix node is the fundamental building block of the mix network.
Katzenpost mix nodes are arranged in a layered topology to achieve the best levels of anonymity and ease of analysis while being flexible enough to scale with traffic demands.
Gateway nodes provide external client access to the mix network. Because gateways are uniquely positioned to identify clients, they are designed to have as little information about client behavior as possible. Gateways are randomly selected and have no persistent relationship with clients and no knowledge of whether a client's packets are decoys or not. When client traffic through a gateway is slow, the node additionally generates decoy traffic.
Service nodes provide functionality requested by clients. They are logically positioned at the deepest point of the mix network, with incoming queries and outgoing replies both needing to traverse all n layers of mix nodes. A service node's functionality may involve storing messages, publishing information outside of the mixnet, interfacing with a blockchain node, and so on. Service nodes also process decoy packets.
Client applications should be designed so that the following conditions are met:
-
Separate service requests from a client are unlinkable. Repeating the same request may be lead to linkability.
-
Service nodes and clients have no persistent relationship.
-
Clients generate a stream of packets addressed to random or pseudorandom services regardless of whether a real service request is being made. Most of these packets will be decoy traffic.
-
Traffic from a client to a service node must be correctly coupled with decoy traffic. This can mean that the service node is chosen independently from traffic history, or that the transmitted packet replaces a decoy packet that was meant to go to the desired service.
Katzenpost currently includes several client applications. All applications make extensive use of Sphinx single-use reply blocks (SURBs), which enable service nodes to send replies without knowing the location of the client. Newer clients require a connection through the client connector, which provides multiplexing and privilege separation with a consequent reduction in processing overhead.
The following client applications are available.
Table 1. Katzenpost clients
Name |
Needs connector |
Description |
Code |
---|---|---|---|
Ping |
no |
The mix network equivalent of an ICMP ping utility, used for network testing. |
GitHub: ping |
Fetch |
no |
Fetches the PKI document containing peer information. |
GitHub: fetch |
Katzen |
no |
A text chat client with file-transfer support. |
GitHub: katzen |
Status |
yes |
An HTML page containing status information about the mix network. |
GitHub: status |
Worldmap |
yes |
An HTML page with a world map showing geographic locations of mix network nodes. |
GitHub: worldmap |
This section documents the configuration parameters for each type of Katzenpost server node. Each node has its own configuration file in TOML format.
The following configuration is drawn from the reference implementation in
katzenpost/docker/dirauth_mixnet/auth1/authority.toml
. In a
real-world mixnet, the component hosts would not be sharing a single IP address. For
more information about the test mixnet, see Using the Katzenpost
Docker test network.
The Server
section configures mandatory basic parameters for each
directory authority.
[Server] Identifier = "auth1" WireKEMScheme = "xwing" PKISignatureScheme = "Ed25519 Sphincs+" Addresses = ["tcp://127.0.0.1:30001"] DataDir = "/dirauth_mixnet/auth1"
-
Identifier
Specifies the human-readable identifier for a node, and must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
WireKEMScheme
Specifies the key encapsulation mechanism (KEM) scheme for the PQ Noise-based wire protocol (link layer) that nodes use to communicate with each other. PQ Noise is a post-quantum variation of the Noise protocol framework, which algebraically transforms ECDH handshake patterns into KEM encapsulate/decapsulate operations.
This configuration option supports the optional use of hybrid post-quantum cryptography to strengthen security. The following KEM schemes are supported:
-
Classical: "x25519", "x448"
Note X25519 and X448 are actually non-interactive key-exchanges (NIKEs), not KEMs. Katzenpost uses a hashed ElGamal cryptographic construction to convert them from NIKEs to KEMs.
-
Post-quantum: "mlkem768","sntrup4591761", "frodo640shake", "mceliece348864", "mceliece348864f", "mceliece460896", "mceliece460896f", "mceliece6688128", "mceliece6688128f", "mceliece6960119", "mceliece6960119f", "mceliece8192128", "mceliece8192128f", "CTIDH511", "CTIDH512", "CTIDH1024", "CTIDH2048",
-
Hybrid post-quantum: "xwing", "Kyber768-X25519", "MLKEM768-X25519", "MLKEM768-X448", "FrodoKEM-640-SHAKE-X448", "sntrup4591761-X448", "mceliece348864-X25519", "mceliece348864f-X25519", "mceliece460896-X25519", "mceliece460896f-X25519", "mceliece6688128-X25519", "mceliece6688128f-X25519", "mceliece6960119-X25519", "mceliece6960119f-X25519", "mceliece8192128-X25519", "mceliece8192128f-X25519", "CTIDH512-X25519", "CTIDH512-X25519"
Type: string
Required: Yes
-
-
PKISignatureScheme
Specifies the cryptographic signature scheme which will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
The following signature schemes are supported: "ed25519", "ed448", "Ed25519 Sphincs+", "Ed448-Sphincs+", "Ed25519-Dilithium2", "Ed448-Dilithium3"
Type: string
Required: Yes
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the node will bind to for incoming connections. Katzenpost supports URLs with that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"].
Type: []string
Required: Yes
-
DataDir
Specifies the absolute path to a node's state directory. This is where
persistence.db
is written to disk and where a node stores its cryptographic key materials when started with the "-g" command-line option.Type: string
Required: Yes
An Authorities
section is configured for each peer authority. We
recommend using TOML's style
for multi-line quotations for key materials.
[[Authorities]] Identifier = "auth1" IdentityPublicKey = """ -----BEGIN ED25519 PUBLIC KEY----- dYpXpbozjFfqhR45ZC2q97SOOsXMANdHaEdXrP42CJk= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """ -----BEGIN XWING PUBLIC KEY----- ooQBPYNdmfwnxXmvnljPA2mG5gWgurfHhbY87DMRY2tbMeZpinJ5BlSiIecprnmm QqxcS9o36IS62SVMlOUkw+XEZGVvc9wJqHpgEgVJRAs1PCR8cUAdM6QIYLWt/lkf SPKDCtZ3GiSIOzMuaglo2tarIPEv1AY7r9B0xXOgSKMkGyBkCfw1VBZf46MM26NL ... gHtNyQJnXski52O03JpZRIhR40pFOhAAcMMAZDpMTVoxlcdR6WA4SlBiSceeJBgY Yp9PlGhCimx9am99TrdLoLCdTHB6oowt8tss3POpIOxaSlguyeym/sBhkUrnXOgN ldMtDsvvc9KUfE4I0+c+XQ== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30001"]
-
Identifier
Specifies the human-readable identifier for the node which must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKey
String containing the node's public identity key in PEM format.
IdentityPublicKey
is the node's permanent identifier and is used to verify cryptographic signatures produced by its private identity key.Type: string
Required: Yes
-
PKISignatureScheme
Specifies the cryptographic signature scheme used by all directory authority nodes.
PKISignatureScheme
must match the scheme specified in theServer
section of the configuration.Type: string
Required: Yes
-
LinkPublicKey
String containing the peer's public link-layer key in PEM format.
LinkPublicKey
must match the specifiedWireKEMScheme
.Type: string
Required: Yes
-
WireKEMScheme
Specifies the key encapsulation mechanism (KEM) scheme for the PQ Noise-based wire protocol (link layer) that nodes use to communicate with each other. PQ Noise is a post-quantum variation of the Noise protocol framework, which algebraically transforms ECDH handshake patterns into KEM encapsulate/decapsulate operations.
This configuration option supports the optional use of hybrid post-quantum cryptography to strengthen security. The following KEM schemes are supported:
-
Classical: "x25519", "x448"
Note X25519 and X448 are actually non-interactive key-exchanges (NIKEs), not KEMs. Katzenpost uses a hashed ElGamal cryptographic construction to convert them from NIKEs to KEMs.
-
Post-quantum: "mlkem768","sntrup4591761", "frodo640shake", "mceliece348864", "mceliece348864f", "mceliece460896", "mceliece460896f", "mceliece6688128", "mceliece6688128f", "mceliece6960119", "mceliece6960119f", "mceliece8192128", "mceliece8192128f", "CTIDH511", "CTIDH512", "CTIDH1024", "CTIDH2048",
-
Hybrid post-quantum: "xwing", "Kyber768-X25519", "MLKEM768-X25519", "MLKEM768-X448", "FrodoKEM-640-SHAKE-X448", "sntrup4591761-X448", "mceliece348864-X25519", "mceliece348864f-X25519", "mceliece460896-X25519", "mceliece460896f-X25519", "mceliece6688128-X25519", "mceliece6688128f-X25519", "mceliece6960119-X25519", "mceliece6960119f-X25519", "mceliece8192128-X25519", "mceliece8192128f-X25519", "CTIDH512-X25519", "CTIDH512-X25519"
Type: string
Required: Yes
-
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the node will bind to for incoming connections. Katzenpost supports URLs with that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"].
Type: []string
Required: Yes
The Logging
configuration section controls logging behavior across Katzenpost.
[Logging] Disable = false File = "katzenpost.log" Level = "INFO"
-
Disable
If true, logging is disabled.
Type: bool
Required: No
-
File
Specifies the log file. If omitted,
stdout
is used.An absolute or relative file path can be specified. A relative path is relative to the DataDir specified in the
Server
section of the configuration.Type: string
Required: No
-
Level
Supported logging level values are ERROR | WARNING | NOTICE |INFO | DEBUG.
Type: string
Required: No
Warning The DEBUG log level is unsafe for production use.
The Parameters
section contains the network parameters.
[Parameters] SendRatePerMinute = 0 Mu = 0.005 MuMaxDelay = 1000 LambdaP = 0.001 LambdaPMaxDelay = 1000 LambdaL = 0.0005 LambdaLMaxDelay = 1000 LambdaD = 0.0005 LambdaDMaxDelay = 3000 LambdaM = 0.0005 LambdaG = 0.0 LambdaMMaxDelay = 100 LambdaGMaxDelay = 100
-
SendRatePerMinute
Specifies the maximum allowed rate of packets per client per gateway node. Rate limiting is done on the gateway nodes.
Type: uint64
Required: Yes
-
Mu
Specifies the inverse of the mean of the exponential distribution from which the Sphinx packet per-hop mixing delay will be sampled.
Type: float64
Required: Yes
-
MuMaxDelay
Specifies the maximum Sphinx packet per-hop mixing delay in milliseconds.
Type: uint64
Required: Yes
-
LambdaP
Specifies the inverse of the mean of the exponential distribution that clients sample to determine the time interval between sending messages, whether actual messages from the FIFO egress queue or decoy messages if the queue is empty.
Type: float64
Required: Yes
-
LambdaPMaxDelay
Specifies the maximum send delay interval for LambdaP in milliseconds.
Type: uint64
Required: Yes
-
LambdaL
Specifies the inverse of the mean of the exponential distribution that clients sample to determine the delay interval between loop decoys.
Type: float64
Required: Yes
-
LambdaLMaxDelay
Specifies the maximum send delay interval for LambdaL in milliseconds.
Type: uint64
Required: Yes
-
LambdaD
LambdaD is the inverse of the mean of the exponential distribution that clients sample to determine the delay interval between decoy drop messages.
Type: float64
Required: Yes
-
LambdaDMaxDelay
Specifies the maximum send interval in for LambdaD in milliseconds.
Type: uint64
Required: Yes
-
LambdaM
LambdaM is the inverse of the mean of the exponential distribution that mix nodes sample to determine the delay between mix loop decoys.
Type: float64
Required: Yes
-
LambdaG
LambdaG is the inverse of the mean of the exponential distribution that gateway nodes to select the delay between gateway node decoys.
Warning Do not set this value manually in the TOML configuration file. The field is used internally by the dirauth server state machine.
Type: float64
Required: Yes
-
LambdaMMaxDelay
Specifies the maximum delay for LambdaM in milliseconds.
Type: uint64
Required: Yes
-
LambdaGMaxDelay
Specifies the maximum delay for LambdaG in milliseconds.
Type: uint64
Required: Yes
[Debug] Layers = 3 MinNodesPerLayer = 1 GenerateOnly = false
-
Layers
Specifies the number of non-service-provider layers in the network topology.
Type: int
Required: Yes
-
MinNodesrPerLayer
Specifies the minimum number of nodes per layer required to form a valid consensus document.
Type: int
Required: Yes
-
GenerateOnly
If true, the server halts and cleans up the data directory immediately after long-term key generation.
Type: bool
Required: No
The Mixes
configuration sections list mix nodes that are known to
the authority.
[[Mixes]] Identifier = "mix1" IdentityPublicKeyPem = "../mix1/identity.public.pem" [[Mixes]] Identifier = "mix2" IdentityPublicKeyPem = "../mix2/identity.public.pem" [[Mixes]] Identifier = "mix3" IdentityPublicKeyPem = "../mix3/identity.public.pem"
-
Identifier
Specifies the human-readable identifier for a mix node, and must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKeyPem
Path and file name of a mix node's public identity signing key, also known as the identity key, in PEM format.
Type: string
Required: Yes
The GatewayNodes
sections list gateway nodes that are known to
the authority.
[[GatewayNodes]] Identifier = "gateway1" IdentityPublicKeyPem = "../gateway1/identity.public.pem"
-
Identifier
Specifies the human-readable identifier for a gateway node, and must be unique per mixnet. Identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKeyPem
Path and file name of a gateway node's public identity signing key, also known as the identity key, in PEM format.
Type: string
Required: Yes
The ServiceNodes
sections list service nodes that are known to
the authority.
[[ServiceNodes]] Identifier = "servicenode1" IdentityPublicKeyPem = "../servicenode1/identity.public.pem"
-
Identifier
Specifies the human-readable identifier for a service node, and must be unique per mixnet. Identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKeyPem
Path and file name of a service node's public identity signing key, also known as the identity key, in PEM format.
Type: string
Required: Yes
The Topology
section defines the layers of the mix network and
the mix nodes in each layer.
[Topology] [[Topology.Layers]] [[Topology.Layers.Nodes]] Identifier = "mix1" IdentityPublicKeyPem = "../mix1/identity.public.pem" [[Topology.Layers]] [[Topology.Layers.Nodes]] Identifier = "mix2" IdentityPublicKeyPem = "../mix2/identity.public.pem" [[Topology.Layers]] [[Topology.Layers.Nodes]] Identifier = "mix3" IdentityPublicKeyPem = "../mix3/identity.public.pem"
-
Identifier
Specifies the human-readable identifier for a node, and must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
-
IdentityPublicKeyPem
Path and file name of a mix node's public identity signing key, also known as the identity key, in PEM format.
Type: string
Required: Yes
Sphinx is an encrypted nested-packet format designed primarily for mixnets. The original Sphinx paper described a non-interactive key exchange (NIKE) employing classical encryption. The Katzenpost implementation strongly emphasizes configurability, supporting key encapsulation mechanisms (KEMs) as well as NIKEs, and enabling the use of either classical or hybrid post-quantum cryptography. Hybrid constructions offset the newness of post-quantum algorithms by offering heavily tested classical algorithms as a fallback.
![]() |
Note |
---|---|
Sphinx, the nested-packet format, should not be confused with Sphincs or Sphincs+, which are post-quantum signature schemes. |
Katzenpost Sphinx also relies on the following classical cryptographic primitives:
-
CTR-AES256, a stream cipher
-
HMAC-SHA256, a message authentication code (MAC) function
-
HKDF-SHA256, a key derivation function (KDF)
-
AEZv5, a strong pseudorandom permutation (SPRP)
All dirauths must be configured to use the same SphinxGeometry
parameters. Any geometry not advertised by the PKI document will fail. Each
dirauth publishes the hash of its SphinxGeometry
parameters in the
PKI document for validation by its peer dirauths.
The SphinxGeometry
section defines parameters for the Sphinx
encrypted nested-packet format used internally by Katzenpost.
The settings in this section are generated by the gensphinx utility, which computes the Sphinx geometry based on the following user-supplied directives:
-
The number of mix node layers (not counting gateway and service nodes)
-
The length of the application-usable packet payload
-
The selected NIKE or KEM scheme
![]() |
Warning |
---|---|
The values in the |
The gensphinx output in TOML should then be pasted unchanged into the node's configuration file, as shown below.
[SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = ""
-
PacketLength
The length of a Sphinx packet in bytes.
Type: int
Required: Yes
-
NrHops
The number of hops a Sphinx packet takes through the mixnet. Because packet headers hold destination information for each hop, the size of the header increases linearly with the number of hops.
Type: int
Required: Yes
-
HeaderLength
The total length of the Sphinx packet header in bytes.
Type: int
Required: Yes
-
RoutingInfoLength
The total length of the routing information portion of the Sphinx packet header.
Type: int
Required: Yes
-
PerHopRoutingInfoLength
The length of the per-hop routing information in the Sphinx packet header.
Type: int
Required: Yes
-
SURBLength
The length of a single-use reply block (SURB).
Type: int
Required: Yes
-
SphinxPlaintextHeaderLength
The length of the plaintext Sphinx packet header.
Type: int
Required: Yes
-
PayloadTagLength
The length of the payload tag.
Type: int
Required: Yes
-
ForwardPayloadLength
The total size of the payload.
Type: int
Required: Yes
-
UserForwardPayloadLength
The size of the usable payload.
Type: int
Required: Yes
-
NextNodeHopLength
The
NextNodeHopLength
is derived from the largest routing-information block that we expect to encounter. Other packets haveNextNodeHop
+NodeDelay
sections, or aRecipient
section, both of which are shorter.Type: int
Required: Yes
-
SPRPKeyMaterialLength
The length of the strong pseudo-random permutation (SPRP) key.
Type: int
Required: Yes
-
NIKEName
The name of the non-interactive key exchange (NIKE) scheme used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
-
KEMName
The name of the key encapsulation mechanism (KEM) used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
The following configuration is drawn from the reference implementation in
katzenpost/docker/dirauth_mixnet/mix1/katzenpost.toml
. In a
real-world mixnet, the component hosts would not be sharing a single IP address. For
more information about the test mixnet, see Using the Katzenpost Docker test network.
The Server
section configures mandatory basic parameters for each
server node.
[Server] Identifier = "mix1" WireKEM = "xwing" PKISignatureScheme = "Ed25519" Addresses = ["127.0.0.1:30008"] OnlyAdvertiseAltAddresses = false MetricsAddress = "127.0.0.1:30009" DataDir = "/dirauth_mixnet/mix1" IsGatewayNode = false IsServiceNode = false [Server.AltAddresses]
-
Identifier
Specifies the human-readable identifier for a node, and must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
WireKEM
WireKEM specifies the key encapsulation mechanism (KEM) scheme for the PQ Noise-based wire protocol (link layer) that nodes use to communicate with each other. PQ Noise is a post-quantum variation of the Noise protocol framework, which algebraically transforms ECDH handshake patterns into KEM encapsulate/decapsulate operations.
This configuration option supports the optional use of hybrid post-quantum cryptography to strengthen security. The following KEM schemes are supported:
-
Classical: "x25519", "x448"
Note X25519 and X448 are actually non-interactive key-exchanges (NIKEs), not KEMs. Katzenpost uses a hashed ElGamal cryptographic construction to convert them from NIKEs to KEMs.
-
Post-quantum: "mlkem768","sntrup4591761", "frodo640shake", "mceliece348864", "mceliece348864f", "mceliece460896", "mceliece460896f", "mceliece6688128", "mceliece6688128f", "mceliece6960119", "mceliece6960119f", "mceliece8192128", "mceliece8192128f", "CTIDH511", "CTIDH512", "CTIDH1024", "CTIDH2048",
-
Hybrid post-quantum: "xwing", "Kyber768-X25519", "MLKEM768-X25519", "MLKEM768-X448", "FrodoKEM-640-SHAKE-X448", "sntrup4591761-X448", "mceliece348864-X25519", "mceliece348864f-X25519", "mceliece460896-X25519", "mceliece460896f-X25519", "mceliece6688128-X25519", "mceliece6688128f-X25519", "mceliece6960119-X25519", "mceliece6960119f-X25519", "mceliece8192128-X25519", "mceliece8192128f-X25519", "CTIDH512-X25519", "CTIDH512-X25519"
Type: string
Required: Yes
-
-
PKISignatureScheme
Specifies the cryptographic signature scheme that will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
The following signature schemes are supported:
-
Classical: "ed25519", "ed448"
-
Hybrid post-quantum: "Ed25519 Sphincs+", "Ed448-Sphincs+", "Ed25519-Dilithium2", "Ed448-Dilithium3"
Type: string
Required: Yes
-
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the server will bind to for incoming connections. Katzenpost supports URLs with that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"].
Addresses is overridden if BindAddresses is true. In that scenario, one or more advertised, external addresses is provided as the value of Addresses, and is advertised in the PKI document.
Note that BindAddresses, below, holds the address values for non-adverstised, internal-only listeners. The addition of BindAddresses to the node configuration is required for hosts connecting to the Internet through network address translation (NAT).
Type: []string
Required: Yes
-
BindAddresses
If true, allows setting of listener addresses that the server will bind to and accept connections on. These addresses are not advertised in the PKI document. For more information, see Addresses, above.
Type: bool, []string
Required: No
-
MetricsAddress
Specifies the address/port to bind the Prometheus metrics endpoint to.
Type: string
Required: No
-
DataDir
Specifies the absolute path to a node's state directory. This is where persistence.db is written to disk and where a node stores its cryptographic key materials when started with the "-g" commmand-line option.
Type: string
Required: Yes
-
IsGatewayNode
If true, the server is a gateway node.
Type: bool
Required: No
-
IsServiceNode
If true, the server is a service node.
Type: bool
Required: No
The Logging
configuration section controls logging behavior across Katzenpost.
[Logging] Disable = false File = "katzenpost.log" Level = "INFO"
-
Disable
If true, logging is disabled.
Type: bool
Required: No
-
File
Specifies the log file. If omitted,
stdout
is used.An absolute or relative file path can be specified. A relative path is relative to the DataDir specified in the
Server
section of the configuration.Type: string
Required: No
-
Level
Supported logging level values are ERROR | WARNING | NOTICE |INFO | DEBUG.
Type: string
Required: No
Warning The DEBUG log level is unsafe for production use.
The PKI
section contains the directory authority configuration for a mix, gateway, or service
node.
[PKI] [PKI.dirauth] [[PKI.dirauth.Authorities]] Identifier = "auth1" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- tqN6tpOVotHWXKCszVn2kS7vAZjQpvJjQF3Qz/Qwhyg= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """-----BEGIN XWING PUBLIC KEY----- JnJ8ztQEIjAkKJcpuZvJAdkWjBim/5G5d8yoosEQHeGJeeBqNPdm2AitUbpiQPcd tNCo9DxuC9Ieqmsfw0YpV6AtOOsaInA6QnHDYcuBfZcQL5MU4+t2TzpBZQYlrSED hPCKrAG+8GEUl6akseG371WQzEtPpEWWCJCJOiS/VDFZT7eKrldlumN6gfiB84sR ... arFh/WKwYJUj+aGBsFYSqGdzC6MdY4x/YyFe2ze0MJEjThQE91y1d/LCQ3Sb7Ri+ u6PBi3JU2qzlPEejDKwK0t5tMNEAkq8iNrpRTdD/hS0gR+ZIN8Z9QKh7Xf94FWG2 H+r8OaqImQhgHabrWRDyLg== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30001"] [[PKI.dirauth.Authorities]] Identifier = "auth2" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- O51Ty2WLu4C1ETMa29s03bMXV72gnjJfTfwLV++LVBI= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """-----BEGIN XWING PUBLIC KEY----- TtQkg2XKUnY602FFBaPJ+zpN0Twy20cwyyFxh7FNUjaXA9MAJXs0vUwFbJc6BjYv f+olKnlIKFSmDvcF74U6w1F0ObugwTNKNxeYKPKhX4FiencUbRwkHoYHdtZdSctz TKy08qKQyCAccqCRpdo6ZtYXPAU+2rthjYTOL7Zn+7SHUKCuJClcPnvEYjVcJxtZ ... ubJIe5U4nMJbBkOqr7Kq6niaEkiLODa0tkpB8tKMYTMBdcYyHSXCzpo7U9sb6LAR HktiTBDtRXviu2vbw7VRXhkMW2kjYZDtReQ5sAse04DvmD49zgTp1YxYW+wWFaL3 37X7/SNuLdHX4PHZXIWHBQ== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30002"] [[PKI.dirauth.Authorities]] Identifier = "auth3" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- zQvydRYJq3npeLcg1NqIf+SswEKE5wFmiwNsI9Z1whQ= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """ -----BEGIN XWING PUBLIC KEY----- OYK9FiC53xwZ1VST3jDOO4tR+cUMSVRSekmigZMChSjDCPZbKut8TblxtlUfc/yi Ugorz4NIvYPMWUt3QPwS2UWq8/HMWXNGPUiAevg12+oV+jOJXaJeCfY24UekJnSw TNcdGaFZFSR0FocFcPBBnrK1M2B8w8eEUKQIsXRDM3x/8aRIuDif+ve8rSwpgKeh ... OdVD3yw7OOS8uPZLORGQFyJbHtVmFPVvwja4G/o2gntAoHUZ2LiJJakpVhhlSyrI yuzvwwFtZVfWtNb5gAKZCyg0aduR3qgd7MPerRF+YopZk3OCRpC02YxfUZrHv398 FZWJFK0R8iU52CEUxVpXTA== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30003"]
-
Identifier
Specifies the human-readable identifier for a node, which must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKey
String containing the node's public identity key in PEM format.
IdentityPublicKey
is the node's permanent identifier and is used to verify cryptographic signatures produced by its private identity key.Type: string
Required: Yes
-
PKISignatureScheme
Specifies the cryptographic signature scheme that will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
Type: string
Required: Yes
-
LinkPublicKey
String containing the peer's public link-layer key in PEM format.
LinkPublicKey
must match the specifiedWireKEMScheme
.Type: string
Required: Yes
-
WireKEMScheme
The name of the wire protocol key-encapsulation mechanism (KEM) to use.
Type: string
Required: Yes
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the server will bind to for incoming connections. Katzenpost supports URLs that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"]. The value of Addresses is advertised in the PKI document.
Type: []string
Required: Yes
The Management
section specifies connectivity information for the
Katzenpost control protocol which can be used to make run-time configuration
changes. A configuration resembles the following, substituting the node's configured
DataDir
value as part of the Path
value:
[Management]
Enable = false
Path = "/node_datadir
/management_sock"
-
Enable
If true, the management interface is enabled.
Type: bool
Required: No
-
Path
Specifies the path to the management interface socket. If left empty, then
management_sock
is located in the configuration's definedDataDir
.Type: string
Required: No
The SphinxGeometry
section defines parameters for the Sphinx
encrypted nested-packet format used internally by Katzenpost.
The settings in this section are generated by the gensphinx utility, which computes the Sphinx geometry based on the following user-supplied directives:
-
The number of mix node layers (not counting gateway and service nodes)
-
The length of the application-usable packet payload
-
The selected NIKE or KEM scheme
![]() |
Warning |
---|---|
The values in the |
The gensphinx output in TOML should then be pasted unchanged into the node's configuration file, as shown below.
[SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = ""
-
PacketLength
The length of a Sphinx packet in bytes.
Type: int
Required: Yes
-
NrHops
The number of hops a Sphinx packet takes through the mixnet. Because packet headers hold destination information for each hop, the size of the header increases linearly with the number of hops.
Type: int
Required: Yes
-
HeaderLength
The total length of the Sphinx packet header in bytes.
Type: int
Required: Yes
-
RoutingInfoLength
The total length of the routing information portion of the Sphinx packet header.
Type: int
Required: Yes
-
PerHopRoutingInfoLength
The length of the per-hop routing information in the Sphinx packet header.
Type: int
Required: Yes
-
SURBLength
The length of a single-use reply block (SURB).
Type: int
Required: Yes
-
SphinxPlaintextHeaderLength
The length of the plaintext Sphinx packet header.
Type: int
Required: Yes
-
PayloadTagLength
The length of the payload tag.
Type: int
Required: Yes
-
ForwardPayloadLength
The total size of the payload.
Type: int
Required: Yes
-
UserForwardPayloadLength
The size of the usable payload.
Type: int
Required: Yes
-
NextNodeHopLength
The
NextNodeHopLength
is derived from the largest routing-information block that we expect to encounter. Other packets haveNextNodeHop
+NodeDelay
sections, or aRecipient
section, both of which are shorter.Type: int
Required: Yes
-
SPRPKeyMaterialLength
The length of the strong pseudo-random permutation (SPRP) key.
Type: int
Required: Yes
-
NIKEName
The name of the non-interactive key exchange (NIKE) scheme used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
-
KEMName
The name of the key encapsulation mechanism (KEM) used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
The Debug
section is the Katzenpost server debug configuration
for advanced tuning.
[Debug] NumSphinxWorkers = 16 NumServiceWorkers = 3 NumGatewayWorkers = 3 NumKaetzchenWorkers = 3 SchedulerExternalMemoryQueue = false SchedulerQueueSize = 0 SchedulerMaxBurst = 16 UnwrapDelay = 250 GatewayDelay = 500 ServiceDelay = 500 KaetzchenDelay = 750 SchedulerSlack = 150 SendSlack = 50 DecoySlack = 15000 ConnectTimeout = 60000 HandshakeTimeout = 30000 ReauthInterval = 30000 SendDecoyTraffic = false DisableRateLimit = false GenerateOnly = false
-
NumSphinxWorkers
Specifies the number of worker instances to use for inbound Sphinx packet processing.
Type: int
Required: No
-
NumProviderWorkers
Specifies the number of worker instances to use for provider specific packet processing.
Type: int
Required: No
-
NumKaetzchenWorkers
Specifies the number of worker instances to use for Kaetzchen-specific packet processing.
Type: int
Required: No
-
SchedulerExternalMemoryQueue
If true, the experimental disk-backed external memory queue is enabled.
Type: bool
Required: No
-
SchedulerQueueSize
Specifies the maximum scheduler queue size before random entries will start getting dropped. A value less than or equal to zero is treated as unlimited.
Type: int
Required: No
-
SchedulerMaxBurst
Specifies the maximum number of packets that will be dispatched per scheduler wakeup event.
Type:
Required: No
-
UnwrapDelay
Specifies the maximum unwrap delay due to queueing in milliseconds.
Type: int
Required: No
-
GatewayDelay
Specifies the maximum gateway node worker delay due to queueing in milliseconds.
Type: int
Required: No
-
ServiceDelay
Specifies the maximum provider delay due to queueing in milliseconds.
Type: int
Required: No
-
KaetzchenDelay
Specifies the maximum kaetzchen delay due to queueing in milliseconds.
Type: int
Required: No
-
SchedulerSlack
Specifies the maximum scheduler slack due to queueing and/or processing in milliseconds.
Type: int
Required: No
-
SendSlack
Specifies the maximum send-queue slack due to queueing and/or congestion in milliseconds.
Type: int
Required: No
-
DecoySlack
Specifies the maximum decoy sweep slack due to external delays such as latency before a loop decoy packet will be considered lost.
Type: int
Required: No
-
ConnectTimeout
Specifies the maximum time a connection can take to establish a TCP/IP connection in milliseconds.
Type: int
Required: No
-
HandshakeTimeout
Specifies the maximum time a connection can take for a link-protocol handshake in milliseconds.
Type: int
Required: No
-
ReauthInterval
Specifies the interval at which a connection will be reauthenticated in milliseconds.
Type: int
Required: No
-
SendDecoyTraffic
If true, decoy traffic is enabled. This parameter is experimental and untuned, and is disabled by default.
Note This option will be removed once decoy traffic is fully implemented.
Type: bool
Required: No
-
DisableRateLimit
If true, the per-client rate limiter is disabled.
Note This option should only be used for testing.
Type: bool
Required: No
-
GenerateOnly
If true, the server immediately halts and cleans up after long-term key generation.
Type: bool
Required: No
The following configuration is drawn from the reference implementation in
katzenpost/docker/dirauth_mixnet/gateway1/katzenpost.toml
.
In a real-world mixnet, the component hosts would not be sharing a single IP
address. For more information about the test mixnet, see Using the Katzenpost Docker test network.
The Server
section configures mandatory basic parameters for each
server node.
[Server] Identifier = "gateway1" WireKEM = "xwing" PKISignatureScheme = "Ed25519" Addresses = ["127.0.0.1:30004"] OnlyAdvertiseAltAddresses = false MetricsAddress = "127.0.0.1:30005" DataDir = "/dirauth_mixnet/gateway1" IsGatewayNode = true IsServiceNode = false [Server.AltAddresses] TCP = ["localhost:30004"]
-
Identifier
Specifies the human-readable identifier for a node, and must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
WireKEM
WireKEM specifies the key encapsulation mechanism (KEM) scheme for the PQ Noise-based wire protocol (link layer) that nodes use to communicate with each other. PQ Noise is a post-quantum variation of the Noise protocol framework, which algebraically transforms ECDH handshake patterns into KEM encapsulate/decapsulate operations.
This configuration option supports the optional use of hybrid post-quantum cryptography to strengthen security. The following KEM schemes are supported:
-
Classical: "x25519", "x448"
Note X25519 and X448 are actually non-interactive key-exchanges (NIKEs), not KEMs. Katzenpost uses a hashed ElGamal cryptographic construction to convert them from NIKEs to KEMs.
-
Post-quantum: "mlkem768","sntrup4591761", "frodo640shake", "mceliece348864", "mceliece348864f", "mceliece460896", "mceliece460896f", "mceliece6688128", "mceliece6688128f", "mceliece6960119", "mceliece6960119f", "mceliece8192128", "mceliece8192128f", "CTIDH511", "CTIDH512", "CTIDH1024", "CTIDH2048",
-
Hybrid post-quantum: "xwing", "Kyber768-X25519", "MLKEM768-X25519", "MLKEM768-X448", "FrodoKEM-640-SHAKE-X448", "sntrup4591761-X448", "mceliece348864-X25519", "mceliece348864f-X25519", "mceliece460896-X25519", "mceliece460896f-X25519", "mceliece6688128-X25519", "mceliece6688128f-X25519", "mceliece6960119-X25519", "mceliece6960119f-X25519", "mceliece8192128-X25519", "mceliece8192128f-X25519", "CTIDH512-X25519", "CTIDH512-X25519"
Type: string
Required: Yes
-
-
PKISignatureScheme
Specifies the cryptographic signature scheme that will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
The following signature schemes are supported:
-
Classical: "ed25519", "ed448"
-
Hybrid post-quantum: "Ed25519 Sphincs+", "Ed448-Sphincs+", "Ed25519-Dilithium2", "Ed448-Dilithium3"
Type: string
Required: Yes
-
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the server will bind to for incoming connections. Katzenpost supports URLs with that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"].
Addresses is overridden if BindAddresses is true. In that scenario, one or more advertised, external addresses is provided as the value of Addresses, and is advertised in the PKI document.
Note that BindAddresses, below, holds the address values for non-adverstised, internal-only listeners. The addition of BindAddresses to the node configuration is required for hosts connecting to the Internet through network address translation (NAT).
Type: []string
Required: Yes
-
BindAddresses
If true, allows setting of listener addresses that the server will bind to and accept connections on. These addresses are not advertised in the PKI document. For more information, see Addresses, above.
Type: bool, []string
Required: No
-
MetricsAddress
Specifies the address/port to bind the Prometheus metrics endpoint to.
Type: string
Required: No
-
DataDir
Specifies the absolute path to a node's state directory. This is where persistence.db is written to disk and where a node stores its cryptographic key materials when started with the "-g" commmand-line option.
Type: string
Required: Yes
-
IsGatewayNode
If true, the server is a gateway node.
Type: bool
Required: No
-
IsServiceNode
If true, the server is a service node.
Type: bool
Required: No
The Logging
configuration section controls logging behavior across Katzenpost.
[Logging] Disable = false File = "katzenpost.log" Level = "INFO"
-
Disable
If true, logging is disabled.
Type: bool
Required: No
-
File
Specifies the log file. If omitted,
stdout
is used.An absolute or relative file path can be specified. A relative path is relative to the DataDir specified in the
Server
section of the configuration.Type: string
Required: No
-
Level
Supported logging level values are ERROR | WARNING | NOTICE |INFO | DEBUG.
Type: string
Required: No
Warning The DEBUG log level is unsafe for production use.
The Gateway
section of the configuration is required for configuring a Gateway
node. The section must contain UserDB
and SpoolDB
definitions. Bolt is an
embedded database library for the Go programming language that Katzenpost
has used in the past for its user and spool databases. Because Katzenpost
currently persists data on Service nodes instead of Gateways, these databases
will probably be deprecated in favour of in-memory concurrency structures. In
the meantime, it remains necessary to configure a Gateway node as shown below,
only changing the file paths as needed:
[Gateway] [Gateway.UserDB] Backend = "bolt" [Gateway.UserDB.Bolt] UserDB = "/dirauth_mixnet/gateway1/users.db" [Gateway.SpoolDB] Backend = "bolt" [Gateway.SpoolDB.Bolt] SpoolDB = "/dirauth_mixnet/gateway1/spool.db"
The PKI
section contains the directory authority configuration for a mix, gateway, or service
node.
[PKI] [PKI.dirauth] [[PKI.dirauth.Authorities]] Identifier = "auth1" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- tqN6tpOVotHWXKCszVn2kS7vAZjQpvJjQF3Qz/Qwhyg= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """-----BEGIN XWING PUBLIC KEY----- JnJ8ztQEIjAkKJcpuZvJAdkWjBim/5G5d8yoosEQHeGJeeBqNPdm2AitUbpiQPcd tNCo9DxuC9Ieqmsfw0YpV6AtOOsaInA6QnHDYcuBfZcQL5MU4+t2TzpBZQYlrSED hPCKrAG+8GEUl6akseG371WQzEtPpEWWCJCJOiS/VDFZT7eKrldlumN6gfiB84sR ... arFh/WKwYJUj+aGBsFYSqGdzC6MdY4x/YyFe2ze0MJEjThQE91y1d/LCQ3Sb7Ri+ u6PBi3JU2qzlPEejDKwK0t5tMNEAkq8iNrpRTdD/hS0gR+ZIN8Z9QKh7Xf94FWG2 H+r8OaqImQhgHabrWRDyLg== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30001"] [[PKI.dirauth.Authorities]] Identifier = "auth2" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- O51Ty2WLu4C1ETMa29s03bMXV72gnjJfTfwLV++LVBI= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """-----BEGIN XWING PUBLIC KEY----- TtQkg2XKUnY602FFBaPJ+zpN0Twy20cwyyFxh7FNUjaXA9MAJXs0vUwFbJc6BjYv f+olKnlIKFSmDvcF74U6w1F0ObugwTNKNxeYKPKhX4FiencUbRwkHoYHdtZdSctz TKy08qKQyCAccqCRpdo6ZtYXPAU+2rthjYTOL7Zn+7SHUKCuJClcPnvEYjVcJxtZ ... ubJIe5U4nMJbBkOqr7Kq6niaEkiLODa0tkpB8tKMYTMBdcYyHSXCzpo7U9sb6LAR HktiTBDtRXviu2vbw7VRXhkMW2kjYZDtReQ5sAse04DvmD49zgTp1YxYW+wWFaL3 37X7/SNuLdHX4PHZXIWHBQ== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30002"] [[PKI.dirauth.Authorities]] Identifier = "auth3" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- zQvydRYJq3npeLcg1NqIf+SswEKE5wFmiwNsI9Z1whQ= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """ -----BEGIN XWING PUBLIC KEY----- OYK9FiC53xwZ1VST3jDOO4tR+cUMSVRSekmigZMChSjDCPZbKut8TblxtlUfc/yi Ugorz4NIvYPMWUt3QPwS2UWq8/HMWXNGPUiAevg12+oV+jOJXaJeCfY24UekJnSw TNcdGaFZFSR0FocFcPBBnrK1M2B8w8eEUKQIsXRDM3x/8aRIuDif+ve8rSwpgKeh ... OdVD3yw7OOS8uPZLORGQFyJbHtVmFPVvwja4G/o2gntAoHUZ2LiJJakpVhhlSyrI yuzvwwFtZVfWtNb5gAKZCyg0aduR3qgd7MPerRF+YopZk3OCRpC02YxfUZrHv398 FZWJFK0R8iU52CEUxVpXTA== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30003"]
-
Identifier
Specifies the human-readable identifier for a node, which must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKey
String containing the node's public identity key in PEM format.
IdentityPublicKey
is the node's permanent identifier and is used to verify cryptographic signatures produced by its private identity key.Type: string
Required: Yes
-
PKISignatureScheme
Specifies the cryptographic signature scheme that will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
Type: string
Required: Yes
-
LinkPublicKey
String containing the peer's public link-layer key in PEM format.
LinkPublicKey
must match the specifiedWireKEMScheme
.Type: string
Required: Yes
-
WireKEMScheme
The name of the wire protocol key-encapsulation mechanism (KEM) to use.
Type: string
Required: Yes
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the server will bind to for incoming connections. Katzenpost supports URLs that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"]. The value of Addresses is advertised in the PKI document.
Type: []string
Required: Yes
The Management
section specifies connectivity information for the
Katzenpost control protocol which can be used to make run-time configuration
changes. A configuration resembles the following, substituting the node's configured
DataDir
value as part of the Path
value:
[Management]
Enable = false
Path = "/node_datadir
/management_sock"
-
Enable
If true, the management interface is enabled.
Type: bool
Required: No
-
Path
Specifies the path to the management interface socket. If left empty, then
management_sock
is located in the configuration's definedDataDir
.Type: string
Required: No
The SphinxGeometry
section defines parameters for the Sphinx
encrypted nested-packet format used internally by Katzenpost.
The settings in this section are generated by the gensphinx utility, which computes the Sphinx geometry based on the following user-supplied directives:
-
The number of mix node layers (not counting gateway and service nodes)
-
The length of the application-usable packet payload
-
The selected NIKE or KEM scheme
![]() |
Warning |
---|---|
The values in the |
The gensphinx output in TOML should then be pasted unchanged into the node's configuration file, as shown below.
[SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = ""
-
PacketLength
The length of a Sphinx packet in bytes.
Type: int
Required: Yes
-
NrHops
The number of hops a Sphinx packet takes through the mixnet. Because packet headers hold destination information for each hop, the size of the header increases linearly with the number of hops.
Type: int
Required: Yes
-
HeaderLength
The total length of the Sphinx packet header in bytes.
Type: int
Required: Yes
-
RoutingInfoLength
The total length of the routing information portion of the Sphinx packet header.
Type: int
Required: Yes
-
PerHopRoutingInfoLength
The length of the per-hop routing information in the Sphinx packet header.
Type: int
Required: Yes
-
SURBLength
The length of a single-use reply block (SURB).
Type: int
Required: Yes
-
SphinxPlaintextHeaderLength
The length of the plaintext Sphinx packet header.
Type: int
Required: Yes
-
PayloadTagLength
The length of the payload tag.
Type: int
Required: Yes
-
ForwardPayloadLength
The total size of the payload.
Type: int
Required: Yes
-
UserForwardPayloadLength
The size of the usable payload.
Type: int
Required: Yes
-
NextNodeHopLength
The
NextNodeHopLength
is derived from the largest routing-information block that we expect to encounter. Other packets haveNextNodeHop
+NodeDelay
sections, or aRecipient
section, both of which are shorter.Type: int
Required: Yes
-
SPRPKeyMaterialLength
The length of the strong pseudo-random permutation (SPRP) key.
Type: int
Required: Yes
-
NIKEName
The name of the non-interactive key exchange (NIKE) scheme used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
-
KEMName
The name of the key encapsulation mechanism (KEM) used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
The Debug
section is the Katzenpost server debug configuration
for advanced tuning.
[Debug] NumSphinxWorkers = 16 NumServiceWorkers = 3 NumGatewayWorkers = 3 NumKaetzchenWorkers = 3 SchedulerExternalMemoryQueue = false SchedulerQueueSize = 0 SchedulerMaxBurst = 16 UnwrapDelay = 250 GatewayDelay = 500 ServiceDelay = 500 KaetzchenDelay = 750 SchedulerSlack = 150 SendSlack = 50 DecoySlack = 15000 ConnectTimeout = 60000 HandshakeTimeout = 30000 ReauthInterval = 30000 SendDecoyTraffic = false DisableRateLimit = false GenerateOnly = false
-
NumSphinxWorkers
Specifies the number of worker instances to use for inbound Sphinx packet processing.
Type: int
Required: No
-
NumProviderWorkers
Specifies the number of worker instances to use for provider specific packet processing.
Type: int
Required: No
-
NumKaetzchenWorkers
Specifies the number of worker instances to use for Kaetzchen-specific packet processing.
Type: int
Required: No
-
SchedulerExternalMemoryQueue
If true, the experimental disk-backed external memory queue is enabled.
Type: bool
Required: No
-
SchedulerQueueSize
Specifies the maximum scheduler queue size before random entries will start getting dropped. A value less than or equal to zero is treated as unlimited.
Type: int
Required: No
-
SchedulerMaxBurst
Specifies the maximum number of packets that will be dispatched per scheduler wakeup event.
Type:
Required: No
-
UnwrapDelay
Specifies the maximum unwrap delay due to queueing in milliseconds.
Type: int
Required: No
-
GatewayDelay
Specifies the maximum gateway node worker delay due to queueing in milliseconds.
Type: int
Required: No
-
ServiceDelay
Specifies the maximum provider delay due to queueing in milliseconds.
Type: int
Required: No
-
KaetzchenDelay
Specifies the maximum kaetzchen delay due to queueing in milliseconds.
Type: int
Required: No
-
SchedulerSlack
Specifies the maximum scheduler slack due to queueing and/or processing in milliseconds.
Type: int
Required: No
-
SendSlack
Specifies the maximum send-queue slack due to queueing and/or congestion in milliseconds.
Type: int
Required: No
-
DecoySlack
Specifies the maximum decoy sweep slack due to external delays such as latency before a loop decoy packet will be considered lost.
Type: int
Required: No
-
ConnectTimeout
Specifies the maximum time a connection can take to establish a TCP/IP connection in milliseconds.
Type: int
Required: No
-
HandshakeTimeout
Specifies the maximum time a connection can take for a link-protocol handshake in milliseconds.
Type: int
Required: No
-
ReauthInterval
Specifies the interval at which a connection will be reauthenticated in milliseconds.
Type: int
Required: No
-
SendDecoyTraffic
If true, decoy traffic is enabled. This parameter is experimental and untuned, and is disabled by default.
Note This option will be removed once decoy traffic is fully implemented.
Type: bool
Required: No
-
DisableRateLimit
If true, the per-client rate limiter is disabled.
Note This option should only be used for testing.
Type: bool
Required: No
-
GenerateOnly
If true, the server immediately halts and cleans up after long-term key generation.
Type: bool
Required: No
The following configuration is drawn from the reference implementation in
katzenpost/docker/dirauth_mixnet/servicenode1/authority.toml
.
In a real-world mixnet, the component hosts would not be sharing a single IP
address. For more information about the test mixnet, see Using the Katzenpost Docker test network.
The Server
section configures mandatory basic parameters for each
server node.
[Server] Identifier = "servicenode1" WireKEM = "xwing" PKISignatureScheme = "Ed25519" Addresses = ["127.0.0.1:30006"] OnlyAdvertiseAltAddresses = false MetricsAddress = "127.0.0.1:30007" DataDir = "/dirauth_mixnet/servicenode1" IsGatewayNode = false IsServiceNode = true [Server.AltAddresses]
-
Identifier
Specifies the human-readable identifier for a node, and must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
WireKEM
WireKEM specifies the key encapsulation mechanism (KEM) scheme for the PQ Noise-based wire protocol (link layer) that nodes use to communicate with each other. PQ Noise is a post-quantum variation of the Noise protocol framework, which algebraically transforms ECDH handshake patterns into KEM encapsulate/decapsulate operations.
This configuration option supports the optional use of hybrid post-quantum cryptography to strengthen security. The following KEM schemes are supported:
-
Classical: "x25519", "x448"
Note X25519 and X448 are actually non-interactive key-exchanges (NIKEs), not KEMs. Katzenpost uses a hashed ElGamal cryptographic construction to convert them from NIKEs to KEMs.
-
Post-quantum: "mlkem768","sntrup4591761", "frodo640shake", "mceliece348864", "mceliece348864f", "mceliece460896", "mceliece460896f", "mceliece6688128", "mceliece6688128f", "mceliece6960119", "mceliece6960119f", "mceliece8192128", "mceliece8192128f", "CTIDH511", "CTIDH512", "CTIDH1024", "CTIDH2048",
-
Hybrid post-quantum: "xwing", "Kyber768-X25519", "MLKEM768-X25519", "MLKEM768-X448", "FrodoKEM-640-SHAKE-X448", "sntrup4591761-X448", "mceliece348864-X25519", "mceliece348864f-X25519", "mceliece460896-X25519", "mceliece460896f-X25519", "mceliece6688128-X25519", "mceliece6688128f-X25519", "mceliece6960119-X25519", "mceliece6960119f-X25519", "mceliece8192128-X25519", "mceliece8192128f-X25519", "CTIDH512-X25519", "CTIDH512-X25519"
Type: string
Required: Yes
-
-
PKISignatureScheme
Specifies the cryptographic signature scheme that will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
The following signature schemes are supported:
-
Classical: "ed25519", "ed448"
-
Hybrid post-quantum: "Ed25519 Sphincs+", "Ed448-Sphincs+", "Ed25519-Dilithium2", "Ed448-Dilithium3"
Type: string
Required: Yes
-
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the server will bind to for incoming connections. Katzenpost supports URLs with that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"].
Addresses is overridden if BindAddresses is true. In that scenario, one or more advertised, external addresses is provided as the value of Addresses, and is advertised in the PKI document.
Note that BindAddresses, below, holds the address values for non-adverstised, internal-only listeners. The addition of BindAddresses to the node configuration is required for hosts connecting to the Internet through network address translation (NAT).
Type: []string
Required: Yes
-
BindAddresses
If true, allows setting of listener addresses that the server will bind to and accept connections on. These addresses are not advertised in the PKI document. For more information, see Addresses, above.
Type: bool, []string
Required: No
-
MetricsAddress
Specifies the address/port to bind the Prometheus metrics endpoint to.
Type: string
Required: No
-
DataDir
Specifies the absolute path to a node's state directory. This is where persistence.db is written to disk and where a node stores its cryptographic key materials when started with the "-g" commmand-line option.
Type: string
Required: Yes
-
IsGatewayNode
If true, the server is a gateway node.
Type: bool
Required: No
-
IsServiceNode
If true, the server is a service node.
Type: bool
Required: No
The Logging
configuration section controls logging behavior across Katzenpost.
[Logging] Disable = false File = "katzenpost.log" Level = "INFO"
-
Disable
If true, logging is disabled.
Type: bool
Required: No
-
File
Specifies the log file. If omitted,
stdout
is used.An absolute or relative file path can be specified. A relative path is relative to the DataDir specified in the
Server
section of the configuration.Type: string
Required: No
-
Level
Supported logging level values are ERROR | WARNING | NOTICE |INFO | DEBUG.
Type: string
Required: No
Warning The DEBUG log level is unsafe for production use.
The ServiceNode
section contains configurations for each network
service that Katzenpost supports.
Services, termed Kaetzchen, can be divided into built-in and external services. External services are provided through the CBORPlugin, a Go programming language implementation of the Concise Binary Object Representation (CBOR), a binary data serialization format. While native services need simply to be activated, external services are invoked by a separate command and connected to the mixnet over a Unix socket. The plugin allows mixnet services to be added in any programming language.
[ServiceNode] [[ServiceNode.Kaetzchen]] Capability = "echo" Endpoint = "+echo" Disable = false [[ServiceNode.CBORPluginKaetzchen]] Capability = "spool" Endpoint = "+spool" Command = "/dirauth_mixnet/memspool.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] data_store = "/dirauth_mixnet/servicenode1/memspool.storage" log_dir = "/dirauth_mixnet/servicenode1" [[ServiceNode.CBORPluginKaetzchen]] Capability = "pigeonhole" Endpoint = "+pigeonhole" Command = "/dirauth_mixnet/pigeonhole.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] db = "/dirauth_mixnet/servicenode1/map.storage" log_dir = "/dirauth_mixnet/servicenode1" [[ServiceNode.CBORPluginKaetzchen]] Capability = "panda" Endpoint = "+panda" Command = "/dirauth_mixnet/panda_server.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] fileStore = "/dirauth_mixnet/servicenode1/panda.storage" log_dir = "/dirauth_mixnet/servicenode1" log_level = "INFO" [[ServiceNode.CBORPluginKaetzchen]] Capability = "http" Endpoint = "+http" Command = "/dirauth_mixnet/proxy_server.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] host = "localhost:4242" log_dir = "/dirauth_mixnet/servicenode1" log_level = "DEBUG"
Common parameters:
-
Capability
Specifies the protocol capability exposed by the agent.
Type: string
Required: Yes
-
Endpoint
Specifies the provider-side Endpoint where the agent will accept requests. While not required by the specification, this server only supports Endpoints that are lower-case local parts of an email address.
Type: string
Required: Yes
-
Command
Specifies the full path to the external plugin program that implements this
Kaetzchen
service.Type: string
Required: Yes
-
MaxConcurrency
Specifies the number of worker goroutines to start for this service.
Type: int
Required: Yes
-
Config
Specifies extra per-agent arguments to be passed to the agent's initialization routine.
Type: map[string]interface{}
Required: Yes
-
Disable
If true, disables a configured agent.
Type: bool
Required: No
Per-service parameters:
-
echo
The internal
echo
service must be enabled on every service node of a production mixnet for decoy traffic to work properly. -
spool
The
spool
service supports thecatshadow
storage protocol, which is required by the Katzen chat client. The example configuration above shows spool enabled with the setting:Disable = false
Note Spool
, properlymemspool
, should not be confused with the spool database on gateway nodes.-
data_store
Specifies the full path to the service database file.
Type: string
Required: Yes
-
log_dir
Specifies the path to the node's log directory.
Type: string
Required: Yes
-
-
pigeonhole
The
pigeonhole
courier service supports the Blinding-and-Capability scheme (BACAP)-based unlinkable messaging protocols detailed in Place-holder for research paper link. Most of our future protocols will use thepigeonhole
courier service.-
db
Specifies the full path to the service database file.
Type: string
Required: Yes
-
log_dir
Specifies the path to the node's log directory.
Type: string
Required: Yes
-
-
panda
The
panda
storage and authentication service currently does not work properly.-
fileStore
Specifies the full path to the service database file.
Type: string
Required: Yes
-
log_dir
Specifies the path to the node's log directory.
Type: string
Required: Yes
-
log_level
Supported values are ERROR | WARNING | NOTICE |INFO | DEBUG.
Warning The DEBUG log level is unsafe for production use.
Type: string
Required: Yes
Required: Yes
-
-
http
The
http
service is completely optional, but allows the mixnet to be used as an HTTP proxy. This may be useful for integrating with existing software systems.-
host
The host name and TCP port of the service.
Type: string
Required: Yes
-
log_dir
Specifies the path to the node's log directory.
Type: string
Required: Yes
-
log_level
Supported values are ERROR | WARNING | NOTICE |INFO | DEBUG.
Type: string
Required: Yes
Required: Yes
Warning The DEBUG log level is unsafe for production use.
Type: string
Required: Yes
-
The PKI
section contains the directory authority configuration for a mix, gateway, or service
node.
[PKI] [PKI.dirauth] [[PKI.dirauth.Authorities]] Identifier = "auth1" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- tqN6tpOVotHWXKCszVn2kS7vAZjQpvJjQF3Qz/Qwhyg= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """-----BEGIN XWING PUBLIC KEY----- JnJ8ztQEIjAkKJcpuZvJAdkWjBim/5G5d8yoosEQHeGJeeBqNPdm2AitUbpiQPcd tNCo9DxuC9Ieqmsfw0YpV6AtOOsaInA6QnHDYcuBfZcQL5MU4+t2TzpBZQYlrSED hPCKrAG+8GEUl6akseG371WQzEtPpEWWCJCJOiS/VDFZT7eKrldlumN6gfiB84sR ... arFh/WKwYJUj+aGBsFYSqGdzC6MdY4x/YyFe2ze0MJEjThQE91y1d/LCQ3Sb7Ri+ u6PBi3JU2qzlPEejDKwK0t5tMNEAkq8iNrpRTdD/hS0gR+ZIN8Z9QKh7Xf94FWG2 H+r8OaqImQhgHabrWRDyLg== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30001"] [[PKI.dirauth.Authorities]] Identifier = "auth2" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- O51Ty2WLu4C1ETMa29s03bMXV72gnjJfTfwLV++LVBI= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """-----BEGIN XWING PUBLIC KEY----- TtQkg2XKUnY602FFBaPJ+zpN0Twy20cwyyFxh7FNUjaXA9MAJXs0vUwFbJc6BjYv f+olKnlIKFSmDvcF74U6w1F0ObugwTNKNxeYKPKhX4FiencUbRwkHoYHdtZdSctz TKy08qKQyCAccqCRpdo6ZtYXPAU+2rthjYTOL7Zn+7SHUKCuJClcPnvEYjVcJxtZ ... ubJIe5U4nMJbBkOqr7Kq6niaEkiLODa0tkpB8tKMYTMBdcYyHSXCzpo7U9sb6LAR HktiTBDtRXviu2vbw7VRXhkMW2kjYZDtReQ5sAse04DvmD49zgTp1YxYW+wWFaL3 37X7/SNuLdHX4PHZXIWHBQ== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30002"] [[PKI.dirauth.Authorities]] Identifier = "auth3" IdentityPublicKey = """-----BEGIN ED25519 PUBLIC KEY----- zQvydRYJq3npeLcg1NqIf+SswEKE5wFmiwNsI9Z1whQ= -----END ED25519 PUBLIC KEY----- """ PKISignatureScheme = "Ed25519" LinkPublicKey = """ -----BEGIN XWING PUBLIC KEY----- OYK9FiC53xwZ1VST3jDOO4tR+cUMSVRSekmigZMChSjDCPZbKut8TblxtlUfc/yi Ugorz4NIvYPMWUt3QPwS2UWq8/HMWXNGPUiAevg12+oV+jOJXaJeCfY24UekJnSw TNcdGaFZFSR0FocFcPBBnrK1M2B8w8eEUKQIsXRDM3x/8aRIuDif+ve8rSwpgKeh ... OdVD3yw7OOS8uPZLORGQFyJbHtVmFPVvwja4G/o2gntAoHUZ2LiJJakpVhhlSyrI yuzvwwFtZVfWtNb5gAKZCyg0aduR3qgd7MPerRF+YopZk3OCRpC02YxfUZrHv398 FZWJFK0R8iU52CEUxVpXTA== -----END XWING PUBLIC KEY----- """ WireKEMScheme = "xwing" Addresses = ["127.0.0.1:30003"]
-
Identifier
Specifies the human-readable identifier for a node, which must be unique per mixnet. The identifier can be an FQDN but does not have to be.
Type: string
Required: Yes
-
IdentityPublicKey
String containing the node's public identity key in PEM format.
IdentityPublicKey
is the node's permanent identifier and is used to verify cryptographic signatures produced by its private identity key.Type: string
Required: Yes
-
PKISignatureScheme
Specifies the cryptographic signature scheme that will be used by all components of the mix network when interacting with the PKI system. Mix nodes sign their descriptors using this signature scheme, and dirauth nodes similarly sign PKI documents using the same scheme.
Type: string
Required: Yes
-
LinkPublicKey
String containing the peer's public link-layer key in PEM format.
LinkPublicKey
must match the specifiedWireKEMScheme
.Type: string
Required: Yes
-
WireKEMScheme
The name of the wire protocol key-encapsulation mechanism (KEM) to use.
Type: string
Required: Yes
-
Addresses
Specifies a list of one or more address URLs in a format that contains the transport protocol, IP address, and port number that the server will bind to for incoming connections. Katzenpost supports URLs that start with either "tcp://" or "quic://" such as: ["tcp://192.168.1.1:30001"] and ["quic://192.168.1.1:40001"]. The value of Addresses is advertised in the PKI document.
Type: []string
Required: Yes
The Management
section specifies connectivity information for the
Katzenpost control protocol which can be used to make run-time configuration
changes. A configuration resembles the following, substituting the node's configured
DataDir
value as part of the Path
value:
[Management]
Enable = false
Path = "/node_datadir
/management_sock"
-
Enable
If true, the management interface is enabled.
Type: bool
Required: No
-
Path
Specifies the path to the management interface socket. If left empty, then
management_sock
is located in the configuration's definedDataDir
.Type: string
Required: No
The SphinxGeometry
section defines parameters for the Sphinx
encrypted nested-packet format used internally by Katzenpost.
The settings in this section are generated by the gensphinx utility, which computes the Sphinx geometry based on the following user-supplied directives:
-
The number of mix node layers (not counting gateway and service nodes)
-
The length of the application-usable packet payload
-
The selected NIKE or KEM scheme
![]() |
Warning |
---|---|
The values in the |
The gensphinx output in TOML should then be pasted unchanged into the node's configuration file, as shown below.
[SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = ""
-
PacketLength
The length of a Sphinx packet in bytes.
Type: int
Required: Yes
-
NrHops
The number of hops a Sphinx packet takes through the mixnet. Because packet headers hold destination information for each hop, the size of the header increases linearly with the number of hops.
Type: int
Required: Yes
-
HeaderLength
The total length of the Sphinx packet header in bytes.
Type: int
Required: Yes
-
RoutingInfoLength
The total length of the routing information portion of the Sphinx packet header.
Type: int
Required: Yes
-
PerHopRoutingInfoLength
The length of the per-hop routing information in the Sphinx packet header.
Type: int
Required: Yes
-
SURBLength
The length of a single-use reply block (SURB).
Type: int
Required: Yes
-
SphinxPlaintextHeaderLength
The length of the plaintext Sphinx packet header.
Type: int
Required: Yes
-
PayloadTagLength
The length of the payload tag.
Type: int
Required: Yes
-
ForwardPayloadLength
The total size of the payload.
Type: int
Required: Yes
-
UserForwardPayloadLength
The size of the usable payload.
Type: int
Required: Yes
-
NextNodeHopLength
The
NextNodeHopLength
is derived from the largest routing-information block that we expect to encounter. Other packets haveNextNodeHop
+NodeDelay
sections, or aRecipient
section, both of which are shorter.Type: int
Required: Yes
-
SPRPKeyMaterialLength
The length of the strong pseudo-random permutation (SPRP) key.
Type: int
Required: Yes
-
NIKEName
The name of the non-interactive key exchange (NIKE) scheme used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
-
KEMName
The name of the key encapsulation mechanism (KEM) used by Sphinx packets.
NIKEName
andKEMName
are mutually exclusive.Type: string
Required: Yes
The Debug
section is the Katzenpost server debug configuration
for advanced tuning.
[Debug] NumSphinxWorkers = 16 NumServiceWorkers = 3 NumGatewayWorkers = 3 NumKaetzchenWorkers = 3 SchedulerExternalMemoryQueue = false SchedulerQueueSize = 0 SchedulerMaxBurst = 16 UnwrapDelay = 250 GatewayDelay = 500 ServiceDelay = 500 KaetzchenDelay = 750 SchedulerSlack = 150 SendSlack = 50 DecoySlack = 15000 ConnectTimeout = 60000 HandshakeTimeout = 30000 ReauthInterval = 30000 SendDecoyTraffic = false DisableRateLimit = false GenerateOnly = false
-
NumSphinxWorkers
Specifies the number of worker instances to use for inbound Sphinx packet processing.
Type: int
Required: No
-
NumProviderWorkers
Specifies the number of worker instances to use for provider specific packet processing.
Type: int
Required: No
-
NumKaetzchenWorkers
Specifies the number of worker instances to use for Kaetzchen-specific packet processing.
Type: int
Required: No
-
SchedulerExternalMemoryQueue
If true, the experimental disk-backed external memory queue is enabled.
Type: bool
Required: No
-
SchedulerQueueSize
Specifies the maximum scheduler queue size before random entries will start getting dropped. A value less than or equal to zero is treated as unlimited.
Type: int
Required: No
-
SchedulerMaxBurst
Specifies the maximum number of packets that will be dispatched per scheduler wakeup event.
Type:
Required: No
-
UnwrapDelay
Specifies the maximum unwrap delay due to queueing in milliseconds.
Type: int
Required: No
-
GatewayDelay
Specifies the maximum gateway node worker delay due to queueing in milliseconds.
Type: int
Required: No
-
ServiceDelay
Specifies the maximum provider delay due to queueing in milliseconds.
Type: int
Required: No
-
KaetzchenDelay
Specifies the maximum kaetzchen delay due to queueing in milliseconds.
Type: int
Required: No
-
SchedulerSlack
Specifies the maximum scheduler slack due to queueing and/or processing in milliseconds.
Type: int
Required: No
-
SendSlack
Specifies the maximum send-queue slack due to queueing and/or congestion in milliseconds.
Type: int
Required: No
-
DecoySlack
Specifies the maximum decoy sweep slack due to external delays such as latency before a loop decoy packet will be considered lost.
Type: int
Required: No
-
ConnectTimeout
Specifies the maximum time a connection can take to establish a TCP/IP connection in milliseconds.
Type: int
Required: No
-
HandshakeTimeout
Specifies the maximum time a connection can take for a link-protocol handshake in milliseconds.
Type: int
Required: No
-
ReauthInterval
Specifies the interval at which a connection will be reauthenticated in milliseconds.
Type: int
Required: No
-
SendDecoyTraffic
If true, decoy traffic is enabled. This parameter is experimental and untuned, and is disabled by default.
Note This option will be removed once decoy traffic is fully implemented.
Type: bool
Required: No
-
DisableRateLimit
If true, the per-client rate limiter is disabled.
Note This option should only be used for testing.
Type: bool
Required: No
-
GenerateOnly
If true, the server immediately halts and cleans up after long-term key generation.
Type: bool
Required: No
1.5 -
Table of Contents
Any Katzenpost server node can be configured to run behind a properly configured router that supports network address translation (NAT) and similar network topologies that traverse public and private network boundaries. This applies to directory authorities, gateways that allow clients to connect to the network, mix nodes, and service nodes that provide protocols over the mix network such as ping and spool services for storing messages or rendezvous information.
Typically, the router connecting a LAN with the Internet blocks incoming connections by default, and must be configured to forward traffic from the Internet to a destination host based on port number. These target addresses are most often drawn from RFC 6598 private address space, although more exotic topologies involving public IP address may also be targeted. (Router configuration for NAT topologies in general is beyond the scope of this topic.) For such cases, where the host listens on a LAN-side address:port but is accessed publicly using a different address:port, Katzenpost provides mechanisms to specify both addresses.
In a direct network connection, the values defined in the server
Addresses
parameter define the addresses on which the node
listens for incoming connections, and which are advertised to other mixnet components
in
the PKI document. By supplying the optional BindAddresses
parameter, you can define a second address group: LAN-side addresses that are
not advertised in the PKI document. This is useful for NAT
scenarios, which involve both public and private address spaces.
![]() |
Note |
---|---|
The |
The following table shows the details for these two parameters. For more information about node configuration, see Components and configuration of the Katzenpost mixnet.
Table 1. Addresses
and BindAddresses
parameters
Parameter |
Required | Description |
---|---|---|
Addresses |
Yes |
Specifies a list of one or more address URIs in a format that contains the transport protocol (typically TCP), an IP address, and a port number that the node will bind to for incoming connections. This value is advertised in the PKI document. |
BindAddresses |
No |
If true (that is, if this
parameter is present), this parameter sets listener
address:port values that the server
will bind to and accept connections on, but that are not
advertised in the PKI document. In this case,
|
![]() |
Note |
---|---|
Directory authorities do not support the |
This section provides an example of a Katzenpost topology that make use of the
BindAddresses
parameter. In this scenario, a mix node behind
NAT listens on local addresses for connections, while advertising a public address
and
port to its peer, a directory authority, that is assumed to have a publicly routable
address.
Key observations
-
The configuration file on the NATed mix node is
katzenpost.toml
. -
The relevant section of the configuration file is
[Server]
. -
The
Addresses
parameter specifies the publicly routable address:port, 203.0.113.10:1234, over which the mix node can be reached from the Internet. This value is periodically advertised in the PKI document to other components of the mix network. -
The
BindAddresses
parameter specifies the LAN address:port, 192.168.0.2:1234, on which the node listens for incoming Sphinx packets from peers. -
The NAT router has two configured addresses, public address 203.0.113.10 and private LAN address 192.168.0.1.
-
The NAT router forwards traffic for 203.0.113.10:1234 to the mix node's LAN address:port, 192.168.0.2:1234, where the configured listener is bound.
The configuration in this example applies equally well to a NATed gateway node or service provider. A NATed gateway node would also be reachable by a client with knowledge of the gateway's public address.
Directory authorities have no support for the BindAddresses
parameter. They also do not adverstise an address in the PKI document, because peers
must already know the address in order to fetch the document, which means that addresses
for dirauths must be provided out-of-band.
Consequently, the Addresses
parameter for dirauths performs the same
function as BindAddresses
on the other node types, that is, to define the
node's listening address:port values, but not an advertised
address. In a NAT scenario, these addresses can refer to any target that is situated
on
the LAN side of the NAT router.
Key observations
-
The configuration file on the NATed dirauth is
authority.toml
. -
The relevant section of the configuration file is
[Server]
. -
The
Addresses
parameter specifies a private RFC 6598address:port, 192.168.0.2:1234. By definition, this address cannot be reached directly from the Internet. -
There is no
BindAddresses
parameter. -
The NAT device has two configured addresses, public address 198.51.100.50, and LAN address 192.168.0.1.
-
The NAT device routes traffic targeting 198.51.100.50:1234 to the address:port specified in
Addresses
, 192.168.0.2:1234. -
The dirauth does not advertise its address on the mix network. The address must provided to peers out-of-band.
1.6 -
Table of Contents
As an aid to adminstrators implementing a Katzenpost mixnet, this appendix provides lightly edited examples of configuration files for each Katzenpost node type. These files are drawn from a built instance of the Docker test mixnet. These code listings are meant to be used as a reference alongside the detailed configuration documentation in Components and configuration of the Katzenpost mixnet. You cannot use these listings as a drop-in solution in your own mixnets for reasons explained in the Network topology and components section of the Docker test mixnet documentation.
Source: ../katzenpost/docker/voting_mixnet/auth1/authority.toml
[Server] Identifier = "auth1" WireKEMScheme = "xwing" PKISignatureScheme = "Ed448-Dilithium3" Addresses = ["tcp://127.0.0.1:30001"] DataDir = "/voting_mixnet/auth1" [[Authorities]] Identifier = "auth1" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nfvcvAfUpeu7lMHjQBw [...] Gpi8ovBXl9ENIHLwA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nsxxS04mftoEmwjxE/w [...] expP2fbERpGQwVNg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30001"] [[Authorities]] Identifier = "auth2" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\n5nsy6uFQ1782fZ+iYn [...] Sdr2xoinylYJr/3AA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nkQzCJvaS6jg06szLea [...] PG1Bzx1JwHGFxRBQ==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30002"] [[Authorities]] Identifier = "auth3" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nJzkFpS035de1PmA2MM [...] jo6Z7is9GLs0YxVQA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\n+pIUsgEGwHa8k4GZcb [...] 1mxoc+4kcgZWuOAg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30003"] [Logging] Disable = false File = "katzenpost.log" Level = "INFO" [Parameters] SendRatePerMinute = 0 Mu = 0.005 MuMaxDelay = 1000 LambdaP = 0.001 LambdaPMaxDelay = 1000 LambdaL = 0.0005 LambdaLMaxDelay = 1000 LambdaD = 0.0005 LambdaDMaxDelay = 3000 LambdaM = 0.0005 LambdaG = 0.0 LambdaMMaxDelay = 100 LambdaGMaxDelay = 100 [Debug] Layers = 3 MinNodesPerLayer = 1 GenerateOnly = false [[Mixes]] Identifier = "mix1" IdentityPublicKeyPem = "../mix1/identity.public.pem" [[Mixes]] Identifier = "mix2" IdentityPublicKeyPem = "../mix2/identity.public.pem" [[Mixes]] Identifier = "mix3" IdentityPublicKeyPem = "../mix3/identity.public.pem" [[GatewayNodes]] Identifier = "gateway1" IdentityPublicKeyPem = "../gateway1/identity.public.pem" [[ServiceNodes]] Identifier = "servicenode1" IdentityPublicKeyPem = "../servicenode1/identity.public.pem" [Topology] [[Topology.Layers]] [[Topology.Layers.Nodes]] Identifier = "mix1" IdentityPublicKeyPem = "../mix1/identity.public.pem" [[Topology.Layers]] [[Topology.Layers.Nodes]] Identifier = "mix2" IdentityPublicKeyPem = "../mix2/identity.public.pem" [[Topology.Layers]] [[Topology.Layers.Nodes]] Identifier = "mix3" IdentityPublicKeyPem = "../mix3/identity.public.pem" [SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = ""
Source: ../katzenpost/docker/voting_mixnet/mix1/katzenpost.toml
[Server] Identifier = "mix1" WireKEM = "xwing" PKISignatureScheme = "Ed448-Dilithium3" Addresses = ["tcp://127.0.0.1:30010", "quic://[::1]:30011"] MetricsAddress = "127.0.0.1:30012" DataDir = "/voting_mixnet/mix1" IsGatewayNode = false IsServiceNode = false [Logging] Disable = false File = "katzenpost.log" Level = "INFO" [PKI] [PKI.Voting] [[PKI.Voting.Authorities]] Identifier = "auth1" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nfvcvAfUpeu7lMHjQBw [...] Gpi8ovBXl9ENIHLwA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nsxxS04mftoEmwjxE/w [...] expP2fbERpGQwVNg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30001"] [[PKI.Voting.Authorities]] Identifier = "auth2" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\n5nsy6uFQ1782fZ+iYn [...] Sdr2xoinylYJr/3AA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nkQzCJvaS6jg06szLea [...] PG1Bzx1JwHGFxRBQ==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30002"] [[PKI.Voting.Authorities]] Identifier = "auth3" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nJzkFpS035de1PmA2M [...] jo6Z7is9GLs0YxVQA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\n+pIUsgEGwHa8k4GZcb [...] 1mxoc+4kcgZWuOAg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30003"] [Management] Enable = false Path = "/voting_mixnet/mix1/management_sock" [SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = "" [Debug] NumSphinxWorkers = 16 NumServiceWorkers = 3 NumGatewayWorkers = 3 NumKaetzchenWorkers = 3 SchedulerExternalMemoryQueue = false SchedulerQueueSize = 0 SchedulerMaxBurst = 16 UnwrapDelay = 250 GatewayDelay = 500 ServiceDelay = 500 KaetzchenDelay = 750 SchedulerSlack = 150 SendSlack = 50 DecoySlack = 15000 ConnectTimeout = 60000 HandshakeTimeout = 30000 ReauthInterval = 30000 SendDecoyTraffic = false DisableRateLimit = false GenerateOnly = false
Source: ../katzenpost/docker/voting_mixnet/gateway1/katzenpost.toml
[Server] Identifier = "gateway1" WireKEM = "xwing" PKISignatureScheme = "Ed448-Dilithium3" Addresses = ["tcp://127.0.0.1:30004", "quic://[::1]:30005", "onion://thisisjustatestoniontoverifythatconfigandpkiworkproperly.onion:4242"] BindAddresses = ["tcp://127.0.0.1:30004", "quic://[::1]:30005"] MetricsAddress = "127.0.0.1:30006" DataDir = "/voting_mixnet/gateway1" IsGatewayNode = true IsServiceNode = false [Logging] Disable = false File = "katzenpost.log" Level = "INFO" [Gateway] [Gateway.UserDB] Backend = "bolt" [Gateway.UserDB.Bolt] UserDB = "/voting_mixnet/gateway1/users.db" [Gateway.SpoolDB] Backend = "bolt" [Gateway.SpoolDB.Bolt] SpoolDB = "/voting_mixnet/gateway1/spool.db" [PKI] [PKI.Voting] [[PKI.Voting.Authorities]] Identifier = "auth1" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nfvcvAfUpeu7lMHjQBw [...] Gpi8ovBXl9ENIHLwA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nsxxS04mftoEmwjxE/w [...] expP2fbERpGQwVNg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30001"] [[PKI.Voting.Authorities]] Identifier = "auth2" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\n5nsy6uFQ1782fZ+iYn [...] Sdr2xoinylYJr/3AA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nkQzCJvaS6jg06szLea [...] PG1Bzx1JwHGFxRBQ==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30002"] [[PKI.Voting.Authorities]] Identifier = "auth3" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nJzkFpS035de1PmA2MM [...] jo6Z7is9GLs0YxVQA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\n+pIUsgEGwHa8k4GZcb [...] 1mxoc+4kcgZWuOAg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30003"] [Management] Enable = true Path = "/voting_mixnet/gateway1/management_sock" [SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = "" [Debug] NumSphinxWorkers = 16 NumServiceWorkers = 3 NumGatewayWorkers = 3 NumKaetzchenWorkers = 3 SchedulerExternalMemoryQueue = false SchedulerQueueSize = 0 SchedulerMaxBurst = 16 UnwrapDelay = 250 GatewayDelay = 500 ServiceDelay = 500 KaetzchenDelay = 750 SchedulerSlack = 150 SendSlack = 50 DecoySlack = 15000 ConnectTimeout = 60000 HandshakeTimeout = 30000 ReauthInterval = 30000 SendDecoyTraffic = false DisableRateLimit = false GenerateOnly = false
Source:
../katzenpost/docker/voting_mixnet/servicenode1/katzenpost.toml
[Server] Identifier = "servicenode1" WireKEM = "xwing" PKISignatureScheme = "Ed448-Dilithium3" Addresses = ["tcp://127.0.0.1:30007", "quic://[::1]:30008"] MetricsAddress = "127.0.0.1:30009" DataDir = "/voting_mixnet/servicenode1" IsGatewayNode = false IsServiceNode = true [Logging] Disable = false File = "katzenpost.log" Level = "INFO" [ServiceNode] [[ServiceNode.Kaetzchen]] Capability = "echo" Endpoint = "+echo" Disable = false [[ServiceNode.Kaetzchen]] Capability = "testdest" Endpoint = "+testdest" Disable = false [[ServiceNode.CBORPluginKaetzchen]] Capability = "spool" Endpoint = "+spool" Command = "/voting_mixnet/memspool.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] data_store = "/voting_mixnet/servicenode1/memspool.storage" log_dir = "/voting_mixnet/servicenode1" [[ServiceNode.CBORPluginKaetzchen]] Capability = "pigeonhole" Endpoint = "+pigeonhole" Command = "/voting_mixnet/pigeonhole.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] db = "/voting_mixnet/servicenode1/map.storage" log_dir = "/voting_mixnet/servicenode1" [[ServiceNode.CBORPluginKaetzchen]] Capability = "panda" Endpoint = "+panda" Command = "/voting_mixnet/panda_server.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] fileStore = "/voting_mixnet/servicenode1/panda.storage" log_dir = "/voting_mixnet/servicenode1" log_level = "INFO" [[ServiceNode.CBORPluginKaetzchen]] Capability = "http" Endpoint = "+http" Command = "/voting_mixnet/proxy_server.alpine" MaxConcurrency = 1 Disable = false [ServiceNode.CBORPluginKaetzchen.Config] host = "localhost:4242" log_dir = "/voting_mixnet/servicenode1" log_level = "DEBUG" [PKI] [PKI.Voting] [[PKI.Voting.Authorities]] Identifier = "auth1" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nfvcvAfUpeu7lMHjQBw [...] Gpi8ovBXl9ENIHLwA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nsxxS04mftoEmwjxE/w [...] expP2fbERpGQwVNg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30001"] [[PKI.Voting.Authorities]] Identifier = "auth2" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\n5nsy6uFQ1782fZ+iYn [...] Sdr2xoinylYJr/3AA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\nkQzCJvaS6jg06szLea [...] PG1Bzx1JwHGFxRBQ==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30002"] [[PKI.Voting.Authorities]] Identifier = "auth3" IdentityPublicKey = "-----BEGIN ED448-DILITHIUM3 PUBLIC KEY-----\nJzkFpS035de1PmA2MM [...] jo6Z7is9GLs0YxVQA=\n-----END ED448-DILITHIUM3 PUBLIC KEY-----\n" PKISignatureScheme = "Ed448-Dilithium3" LinkPublicKey = "-----BEGIN XWING PUBLIC KEY-----\n+pIUsgEGwHa8k4GZcb [...] 1mxoc+4kcgZWuOAg==\n-----END XWING PUBLIC KEY-----\n" WireKEMScheme = "xwing" Addresses = ["tcp://127.0.0.1:30003"] [Management] Enable = true Path = "/voting_mixnet/servicenode1/management_sock" [SphinxGeometry] PacketLength = 3082 NrHops = 5 HeaderLength = 476 RoutingInfoLength = 410 PerHopRoutingInfoLength = 82 SURBLength = 572 SphinxPlaintextHeaderLength = 2 PayloadTagLength = 32 ForwardPayloadLength = 2574 UserForwardPayloadLength = 2000 NextNodeHopLength = 65 SPRPKeyMaterialLength = 64 NIKEName = "x25519" KEMName = "" [Debug] NumSphinxWorkers = 16 NumServiceWorkers = 3 NumGatewayWorkers = 3 NumKaetzchenWorkers = 4 SchedulerExternalMemoryQueue = false SchedulerQueueSize = 0 SchedulerMaxBurst = 16 UnwrapDelay = 250 GatewayDelay = 500 ServiceDelay = 500 KaetzchenDelay = 750 SchedulerSlack = 150 SendSlack = 50 DecoySlack = 15000 ConnectTimeout = 60000 HandshakeTimeout = 30000 ReauthInterval = 30000 SendDecoyTraffic = false DisableRateLimit = false GenerateOnly = false
2 - Specifications
These technical specifications describe the specifics of Katzenpost protocols and implementations, and are aimed primarily at software developers.
Title | Description | Link(s) | |
---|---|---|---|
📖 | Certificate format | PKI Certificate format. | HTML / PDF |
📖 | Client2 | Connector library design. | HTML / PDF |
📖 | KEM Sphinx packet format | The KEM Sphinx variation of Sphinx. | HTML / PDF |
📖 | Mixnet | Describes the overall mixnet design. | HTML / PDF |
📖 | Mix decoy stats propagation | Mix decoy stats propagation. | HTML / PDF |
📖 | Public Key Infrastructure | Every mixnet must have a PKI, this doc describes ours. | HTML / PDF |
📖 | Provider-side autoresponder extension | Autoresponder agent that runs on provider nodes. | HTML / PDF |
📖 | Sphinx packet format | Sphinx packet format, a nested cryptographic packet format designed for mix networks. | HTML / PDF |
📖 | Sphinx Replay Detection | Sphinx replay detection. | HTML / PDF |
📖 | Wire Protocol | A detailed design specification for our PQ Noise based wire protocol, which is used for transport encryption between all the mix nodes and dirauth nodes. | HTML / PDF |
📖 | Glossary | Consolidated list of terms defined in the specifications. | HTML / PDF |
📖 | References | Consolidated list of references cited in the specifications. | HTML / PDF |
2.1 -
Abstract
This document proposes a certificate format that Katzenpost mix server, directory authority server and clients will use.
Table of Contents
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC2119.
Mixes and Directory Authority servers need to have key agility in the sense of operational abilities such as key rotation and key revocation. That is, we wish for mixes and authorities to periodically utilize a long-term signing key for generating certificates for new short-term signing keys.
Yet another use-case for these certificate is to replace the use of JOSE RFC7515 in the voting Directory Authority system KATZMIXPKI for the multi-signature documents exchanged for voting and consensus.
The CBOR RFC7049 serialization format is used to serialize certificates:
Signature is a cryptographic signature which has an associated signer ID.
type Signature struct { // Identity is the identity of the signer. Identity []byte // Signature is the actual signature value. Signature []byte }
Certificate structure for serializing certificates.
type certificate struct { // Version is the certificate format version. Version uint32 // Expiration is seconds since Unix epoch. Expiration int64 // KeyType indicates the type of key // that is certified by this certificate. KeyType string // Certified is the data that is certified by // this certificate. Certified []byte // Signatures are the signature of the certificate. Signatures []Signature }
That is, one or more signatures sign the certificate. However the
Certified
field is not the only information that is signed.
The Certified
field along with the other non-signature fields are
all concatenated together and signed. Before serialization the signatures are sorted
by their identity so that the output is binary deterministic.
The certificate type
field indicates the type of certificate.
So far we have only two types:
-
identity key certificate
-
directory authority certificate
Both mixes and directory authority servers have a secret, long-term identity key. This key is ideally stored encrypted and offline, it’s used to sign key certificate documents. Key certificates contain a medium-term signing key that is used to sign other documents. In the case of an “authority signing key”, it is used to sign vote and consensus documents whereas the “mix singing key” is used to sign mix descriptors which are uploaded to the directory authority servers.
It’s more practical to continue using Ed25519 ED25519 keys but it’s also possible that in the future we could upgrade to a stateless hash based post quantum cryptographic signature scheme such as SPHINCS-256 or SPHINCS+. SPHINCS256
-
https://godoc.org/github.com/katzenpost/katzenpost/core/crypto/cert
Our golang implementation is agnostic to the specific cryptographic signature scheme which is used. Cert can handle single and multiple signatures per document and has a variety of helper functions that ease use for multi signature use cases.
This specification was inspired by Tor Project’s certificate format specification document:
-
https://gitweb.torproject.org/torspec.git/tree/cert-spec.txt
C. Bormannm, P. Hoffman, “Concise Binary Object Representation (CBOR)”, Internet Engineering Task Force (IETF), October 2013, https://www.rfc-editor.org/info/rfc7049.
Jones, M., Bradley, J., Sakimura, N., “JSON Web Signature (JWS)”, May 2015, https://www.rfc-editor.org/info/rfc7515.
Angel, Y., Piotrowska, A., Stainton, D., “Katzenpost Mix Network Public Key Infrastructure Specification”, December 2017, https://katzenpost.network/docs/specs/pdf/pki.pdf.
Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, http://www.rfc-editor.org/info/rfc2119.
Saarinen, M-J., Ed., and J-P. Aumasson, “The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)”, RFC 7693, DOI 10.17487/RFC7693, November 2015, http://www.rfc-editor.org/info/rfc7693.
https://www.rfc-editor.org/rfc/rfc8032.
Bernstein, D., Hopwood, D., Hulsing, A., Lange, T., Niederhagen, R., Papachristodoulou, L., Schwabe, P., Wilcox O'Hearn, Z., “SPHINCS: practical stateless hash-based signatures”, http://sphincs.cr.yp.to/sphincs-20141001.pdf.
2.2 -
Abstract
Here I present a modification of the Sphinx cryptographic packet format that uses a KEM instead of a NIKE whilst preserving the properties of bitwise unlinkability, constant packet size and route length hiding.
Table of Contents
We’ll express our KEM Sphinx header in pseudo code. The Sphinx body will be exactly the same as the section called “References” Our basic KEM API has three functions:
-
PRIV_KEY, PUB_KEY = GEN_KEYPAIR(RNG)
-
ct, ss = ENCAP(PUB_KEY)
- Encapsulate generates a shared secret, ss, for the public key and encapsulates it into a ciphertext. -
ss = DECAP(PRIV_KEY, ct)
- Decapsulate computes the shared key, ss, encapsulated in the ciphertext, ct, for the private key.
Additional notation includes:
-
||
= concatenate two binary blobs together -
PRF
= pseudo random function, a cryptographic hash function, e.g.Blake2b
.
Therefore we must embed these KEM ciphertexts in the KEMSphinx header, one KEM ciphertext per mix hop.
Special care must be taken in order correctly compose a hybrid post quantum KEM that is IND-CCA2 robust.
The hybrid post quantum KEMs found in Cloudflare’s circl library are suitable to be used with Noise or TLS but not with KEM Sphinx because they are not IND-CCA2 robust. Noise and TLS achieve IND-CCA2 security by mixing in the public keys and ciphertexts into the hash object and therefore do not require an IND-CCA2 KEM.
Firstly, our post quantum KEM is IND-CCA2 however we must specifically take care to make our NIKE to KEM adapter have semantic security. Secondly, we must make a security preserving KEM combiner.
We easily achieve our IND-CCA2 security by means of hashing together the DH shared secret along with both of the public keys:
func ENCAPSULATE(their_pubkey publickey) ([]byte, []byte) { my_privkey, my_pubkey = GEN_KEYPAIR(RNG) ss = DH(my_privkey, their_pubkey) ss2 = PRF(ss || their_pubkey || my_pubkey) return my_pubkey, ss2 } func DECAPSULATE(my_privkey, their_pubkey) []byte { s = DH(my_privkey, their_pubkey) shared_key = PRF(ss || my_pubkey || their_pubkey) return shared_key }
The KEM Combiners paper ??? makes the observation that if a KEM combiner is not security preserving then the resulting hybrid KEM will not have IND-CCA2 security if one of the composing KEMs does not have IND-CCA2 security. Likewise the paper points out that when using a security preserving KEM combiner, if only one of the composing KEMs has IND-CCA2 security then the resulting hybrid KEM will have IND-CCA2 security.
Our KEM combiner uses the split PRF design from the paper when combining two KEM shared secrets together we use a hash function to also mix in the values of both KEM ciphertexts. In this pseudo code example we are hashing together the two shared secrets from the two underlying KEMs, ss1 and ss2. Additionally the two ciphertexts from the underlying KEMs, cct1 and cct2, are also hashed together:
func SplitPRF(ss1, ss2, cct1, cct2 []byte) []byte { cct := cct1 || cct2 return PRF(ss1 || cct) XOR PRF(ss2 || cct) }
Which simplifies to:
SplitPRF := PRF(ss1 || cct2) XOR PRF(ss2 || cct1)
The Split PRF can be used to combine an arbitrary number of KEMs. Here’s what it looks like with three KEMs:
func SplitPRF(ss1, ss2, ss3, cct1, cct2, cct3 []byte) []byte { cct := cct1 || cct2 || cct3 return PRF(ss1 || cct) XOR PRF(ss2 || cct) XOR PRF(ss3 || cct) }
NIKE Sphinx header elements:
-
Version number (MACed but not encrypted)
-
Group element
-
Encrypted per routing commands
-
MAC for this hop (authenticates header fields 1 thru 4)
KEM Sphinx header elements:
-
Version number (MACed but not encrypted)
-
One KEM ciphertext for use with the next hop
-
Encrypted per routing commands AND KEM ciphtertexts, one for each additional hop
-
MAC for this hop (authenticates header fields 1 thru 4)
We can say that KEMSphinx differs from NIKE Sphinx by replacing the header’s group element (e.g. an X25519 public key) field with the KEM ciphertext. Subsequent KEM ciphertexts for each hop are stored inside the Sphinx header “routing information” section.
First we must have a data type to express a mix hop, and we can use lists of these hops to express a route:
type PathHop struct { public_key kem.PublicKey routing_commands Commands }
Here’s how we construct a KEMSphinx packet header where path is a list of PathHop, and indicates the route through the network:
-
Derive the KEM ciphertexts for each hop.
route_keys = [] route_kems = [] for i := 0; i < num_hops; i++ { kem_ct, ss := ENCAP(path[i].public_key) route_kems += kem_ct route_keys += ss }
-
Derive the routing_information keystream and encrypted padding for each hop.
Same as in the section called “References” except for the fact that each routing info slot is now increased by the size of the KEM ciphertext.
-
Create the routing_information block.
Here we modify the Sphinx implementation to pack the next KEM ciphertext into each routing information block. Each of these blocks is decrypted for each mix mix hop which will decrypt the KEM ciphertext for the next hop in the route.
-
Assemble the completed Sphinx Packet Header and Sphinx Packet Payload SPRP key vector. Same as in SPHINXSPEC except the
kem_element
field is set to the first KEM ciphertext,route_kems[0]
:
var sphinx_header SphinxHeader sphinx_header.additional_data = version sphinx_header.kem_element = route_kems[0] sphinx_header.routing_info = routing_info sphinx_header.mac = mac <<<<<<< HEAD
Most of the design here will be exactly the same as in SPHINXSPEC. However there are a few notable differences:
The shared secret is derived from the KEM ciphertext instead of a DH.
Next hop’s KEM ciphertext stored in the encrypted routing information.
I would like to thank Peter Schwabe for the original idea of simply replacing the Sphinx NIKE with a KEM and for answering all my questions. I’d also like to thank Bas Westerbaan for answering questions.
KEMCOMB. Federico Giacon, Felix Heuer, Bertram Poettering, "KEM Combiners", 2018. https://link.springer.com/chapter/10.1007/978-3-319-76578-5_7
SPHINX09. Danezis, G., Goldberg, I., "Sphinx: A Compact and Provably Secure Mix Format\", DOI 10.1109/SP.2009.15, May 2009. https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf
SPHINXSPEC. Angel, Y., Danezis, G., Diaz, C., Piotrowska, A., Stainton, D., "Sphinx Mix Network Cryptographic Packet Format Specification" July 2017. https://katzenpost.network/docs/specs/sphinx/
Most of the design here will be exactly the same as in SPHINXSPEC. However there are a few notable differences:
-
The shared secret is derived from the KEM ciphertext instead of a DH.
-
Next hop’s KEM ciphertext stored in the encrypted routing information.
I would like to thank Peter Schwabe for the original idea of simply replacing the Sphinx NIKE with a KEM and for answering all my questions. I’d also like to thank Bas Westerbaan for answering questions.
Federico Giacon, Felix Heuer, Bertram Poettering, “KEM Combiners”, 2018, https://link.springer.com/chapter/10.1007/978-3-319-76578-5_7
Danezis, G., Goldberg, I., “Sphinx: A Compact and Provably Secure Mix Format”, DOI 10.1109/SP.2009.15, May 2009, https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf.
Angel, Y., Danezis, G., Diaz, C., Piotrowska, A., Stainton, D., “Sphinx Mix Network Cryptographic Packet Format Specification”, July 2017, https://katzenpost.network/docs/specs/pdf/sphinx.pdf.