Skip to content
cloudflareresearchPublic

About

Post-quantum anonymous credentials in the VOLE-in-the-Head framework

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

vole-mole

C++23 library for post-quantum anonymous tokens. A client gets a token from an issuer, then presents it later without the issuer linking the two steps.

All protocols use UOV signatures and the VOLE-in-the-Head (VOLEitH) proof system from FAEST.

Warning

Experimental and not audited. Do not use in production. APIs and wire formats will change as the specifications evolve.

Protocols

Protocol Use it when Header
PomFRIT You need a blind signature: the issuer signs a message it never sees. vole_mole/pomfrit.hpp
Moussaka A verifier trusts several issuers and must not learn which one issued a token. vole_mole/moussaka.hpp
Ratatouille A token carries a hidden credit balance that the issuer updates on each spend. vole_mole/ratatouille.hpp

PomFRIT implements Algorithm 2 of ePrint 2026/109, a Fischlin-style blind signature. Moussaka is an Issuer-Hiding Anonymous Token (draft-authors-mole-vole-ihat). Ratatouille is an Anonymous Credit Token (draft-authors-mole-vole-act). Moussaka and Ratatouille were designed for MOLE, but do not depend on it.

Build

You need Meson 1.4 or later, Ninja, Python 3, and Clang 21+ or GCC 16+. Apple Clang 17 is too old. Meson downloads the other dependencies.

Supported CPUs are aarch64 with the crypto extensions and x86_64. On x86_64 the build uses -march=native, so binaries may not run on other machines.

meson setup build
meson compile -C build
meson test -C build --print-errorlogs

On macOS, install a recent LLVM with Homebrew and select it before setup:

brew install meson ninja llvm@22
export CC=$(brew --prefix llvm@22)/bin/clang
export CXX=$(brew --prefix llvm@22)/bin/clang++

Quick start

Issue and verify a PomFRIT blind signature:

#include <vole_mole/pomfrit.hpp>
#include <vole_mole/uov.hpp>

using namespace vole_mole;

// Issuer: generate a key pair once and publish pk.
const uov::SecretKey sk(uov::generate_secret_key_bytes());
const uov::PublicKey pk(sk.public_key_bytes());

// Client: blind the message. The Issuer never sees msg.
auto [pending, request] = pomfrit::issue_request(pk, msg);

// Issuer: sign the blinded request.
const auto response = pomfrit::issue_response(sk, request);

// Client: unblind. Returns std::nullopt if the response is invalid.
const auto sig = std::move(pending).finalize_issue(pk, response);
if (!sig) return EXIT_FAILURE;

// Verifier: check the signature. It cannot be linked to the issuance.
const bool ok = pomfrit::verify(pk, msg, *sig);

msg is a std::span<const uint8_t>. Each public header documents its functions, including what they return and throw.

Examples

Example Shows
keys.cpp UOV key generation, encoding, and expansion
pomfrit.cpp Blind signature issuance and verification
moussaka.cpp Issuance from one of several Anchors and redemption, with replay rejection
ratatouille.cpp Issuance, spending credits, and double-spend rejection

Build as above, then run:

./build/examples/example_pomfrit

Use in a Meson project

Create subprojects/vole-mole.wrap in your project:

[wrap-git]
url = https://github.com/cloudflareresearch/vole-mole.git
revision = <commit>
depth = 1

Set revision to a commit hash. The API is unstable, so don't track a branch. Meson clones the repository and its dependencies during meson setup. Then add to your meson.build:

vole_mole_dep = subproject('vole-mole',
  default_options : ['tests=false', 'benchmarks=false', 'examples=false'],
).get_variable('vole_mole_dep')

There is no installed package or pkg-config file yet.

Security considerations

This code has not been audited.

The library does not prevent double spending. Moussaka finalize_redeem and Ratatouille spend_response return a nullifier for each accepted token. Store it and reject any request that reuses it, for as long as you accept tokens for those contexts. For Moussaka, one record must cover every Anchor Set you accept, since a token is valid under any set that contains its Anchor. Expiring contexts, such as an issuance epoch, bound the storage. PomFRIT has no built-in nullifier; to make signatures single-use, put one in the signed message.

Performance

Client messages carrying proofs are 5–17 KB. On an Apple M4 Pro, every step takes under 2 ms, except Moussaka redemption, which grows with the number of Anchors (about 7 ms for 64). See docs/perf.md for full numbers, the smaller-proof parameter trade-off (4-12KB), and how to run the benchmarks.

License

BSD 3-Clause, see LICENSE. Meson downloads faest-arch-opt (MIT) and pqov (CC0 or Apache-2.0) as subprojects. src/voleith.hpp is adapted from faest-arch-opt and keeps its MIT notice.

Acknowledgements

This library is based on a prototype by Tianyu Zhang, written during his Summer 2026 internship at Cloudflare Research. Tianyu also contributed to the circuit and protocol designs.

About

Post-quantum anonymous credentials in the VOLE-in-the-Head framework

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages