ZK/SEC Research notes from zkSecurity
All posts
security · zk · audit

Public report of Reclaim protocol's ChaCha20 circuit

We have audited the ChaCha20 circuits of the Reclaim protocol.

reclaim protocol

You can find the report here as well as Reclaim's own announcement here.

Through two audit iterations, it became evident that the word-based circuit approach still posed challenges, as several bit-level operations could not be efficiently constrained. This realization led us to revisit the initial individual-bits approach, optimize it, and undergo the audit for the third, final, time.

The outcome of this comprehensive audit was a significant performance improvement. Our new circuit, validated as both secure and efficient, emerged as a 10% smaller and faster alternative to the word-based approach. This achievement not only enhances the performance of our cryptographic solutions but also underscores our commitment to providing the best security practices to our users.

The implementation used Circom, targeting the Groth16 proof system, and offering a flexible API for the user to use. In addition, it purposefully does not provide authentication over the ciphertext as the Reclaim protocol does not need it. Oracles can see the ciphertexts and authenticate their integrity by passing them as public inputs to the circuit when verifying the proofs.

The audit followed RFC 7539: ChaCha20 and Poly1305 for IETF Protocols in order to ensure that the implementation matched the specification.

To address original findings and subsequent reviews, Questbook ended up changing its approach to encode all bitwise logic in the circuits with bit-level constraints. We directly copy below the overview of the changes and the reasoning behind them as written in the report.

Questbook uses the Groth16 proof system, which works with circuits that are encoded using the R1CS arithmetization. Such an arithmetization allows someone to encode a circuit as a list of quadratic constraints which are often referred to as the rows or constraints of the circuits. Specifically, each constraint has full access to a list of "witness values" (that usually starts with the public input values), and can encode the multiplication of two linear combinations of that witness list and enforce that it is equal to another linear combination of that same witness list.

In other words, for a witness vector $\vec{w} = (w_0, w_1, \cdots)$ we can add new constraints by hardcoding the scalar values $a_i, b_i, c_i$ which will enforce the following:

$$ (a_0 w_0 + a_1 w_1 + \cdots)(b_0 w_0 + b_1 w_1 + \cdots) = c_0 w_0 + c_1 w_1 + \cdots $$

Circuit size is usually paramount to a zero-knowledge application, as it directly impacts the proof size and prover time. As such, reducing a circuit is often a priority.

Non-snark friendly cryptography primitives, like ChaCha20 implemented here, are often full of bitwise operations (e.g. XOR, ROT, etc.) which are not efficient to encode in such arithmetization systems. This often leads to circuits blowing up.

Some systems accelerate unfriendly low-level operations by making use of lookup tables or higher-degree constraints (that fits in a single row). Groth16 does not support any of these. For this reason, bitwise operations in the ChaCha20 circuit have to be encoded by first unpacking values down to their bit representations, and then acting on the bits directly. Later, bits can be packed back into field elements if needed.

Originally, the ChaCha20 circuit attempted to save constraints by avoiding the packing and unpacking of values. Dealing with bits directly is undesirable as each value being unpacked leads to a growth of the witness size by the number of bits. Then each bit must be constrained to ensure that they can only be 0 or 1 and nothing else. These "bit constraints" are quadratic constraints (i.e. $x(x-1) = 0$) and thus cost one constraint/row in R1CS for each of them. That is unlike the packing and unpacking operations ($x = \sum_i x_i 2^i$) which are linear and thus can be encoded in a single constraint/row.

Unfortunately, as it was found in the original review contained which forms the base of this report, that approach was found not to be sound.

The circuits were subsequently refactored to perform all bitwise operations on the bits of the values directly. In addition, packing and unpacking operations are removed as bits can be passed from one operation to the other without paying the cost of constantly re-encoding.

Keep reading
Latest

The Year Finding and Exploiting Bugs Became Cheap, and What to Do About It

The economics of security have changed. AI has made finding and exploiting bugs cheaper, including in cryptographic and zero-knowledge code, while validation and remediation remain slow. Here is our view of what happened, what comes next, and how teams should change the way they secure their stack to defend against AI-assisted attackers.

Stefanos Chaliasos · September 07, 2026

Variants of KZG: Part V, Multilinear Commitments with Mercury

In this final post of the series, we extend univariate KZG commitments to multilinear polynomials through Mercury. Building on Gemini, we fold half of the variables at once, use polynomial division to bind this large fold to the original commitment and reduce the remaining multilinear evaluations to a batched inner product check. We then walk through the end-to-end opening protocol. We conclude by examining its proof size, prover cost and verifier cost.

Varun Thakore · August 17, 2026

Variants of KZG: Part IV, Multilinear Commitments with Gemini

In this blog post, we extend univariate KZG commitments to multilinear polynomials through Gemini. We introduce recursive partial evaluations, derive the split-and-fold identity and express each fold as a univariate identity that can be checked using KZG openings. We then walk through the end-to-end opening protocol. We conclude by examining its proof size, prover cost, and verifier cost.

Varun Thakore · August 12, 2026
Recommended

Renegade Audit: When ZK meets MPC

We recently had the pleasure of auditing Renegade's circuits and smart contracts, and it was a great experience. Over three weeks, our team explored their top-notch code and documentation, with the Renegade team providing awesome support throughout. Curious how it all went? Dive into our full report for the inside scoop!

ZK/SEC · July 22, 2024

Auditing Self: Collaborating with Celo on Privacy and Identity Infrastructure

We recently teamed up with Celo for an in-depth security audit of the Self project, exploring its innovative approach to on-chain identity using biometric passports and zero-knowledge proofs. During our three-week dive, we examined everything from cryptographic primitives to smart contract architecture and a unique proof delegation system using AWS Nitro Enclaves. The Celo team impressed us with their commitment and responsiveness, and we collaborated on refining the system with strategic improvements. Curious about the nitty-gritty details and our insights? Check out the full report!

ZK/SEC · March 24, 2025

Bug Hunt: Zero-Knowledge, Full-Paranoia, and the AI That Stares Back

Over the past year, we've been diving into whether AI can effectively identify bugs in zero-knowledge circuits and applications, sparking questions about the future of auditing. This led us to develop SnarkSentinel, an AI-powered auditing tool. We'll share what worked, what didn’t, and how our journey with AI could impact auditing. From early challenges with Circom to innovative methods like retrieval-augmented generation and agent-led probing, we'll give you a peek into our findings, including both successes and setbacks with bug detection. Discover how AI might enhance or change the landscape of auditing and what this means for developers and security pros alike.

ZK/SEC · July 03, 2025
More to explore

zkBitcoin: Use Zero-Knowledge Applications (zkapps) on Bitcoin

We're excited to introduce zkBitcoin, a new tool that lets you create zero-knowledge applications on Bitcoin using a minimal layer 2 protocol. This innovation opens up a world of complex, privacy-focused apps by enhancing Bitcoin's scripting capabilities. We're currently on testnet, so you can jump in and explore the possibilities. Check out our whitepaper or watch some videos for a deeper dive. It's an exciting time for Bitcoin development, and we can't wait to see what you'll build!

David Wong · January 31, 2024

Sigma dance: commit, challenge, respond

Learn the fundamentals of Σ-protocols through the classic Schnorr protocol, exploring the three-step dance of commit, challenge, and respond. This post walks through knowledge soundness and witness extraction, then shows how to compose Sigma proofs with AND/OR logic and Pedersen commitments. See working SageMath implementations, discover how Fiat-Shamir transforms interactive proofs into non-interactive signatures, and understand the deeper mathematical structure as proofs of knowledge of homomorphism pre-images.

teddav · November 18, 2025

zkVM Security: What Could Go Wrong?

Ever wondered how zkVMs simplify the use of zero-knowledge proofs in coding? We dive into how they let developers focus more on application logic by abstracting complex cryptographic aspects, using familiar languages like Rust or C++. But hold on, it's not all smooth sailing. Despite these benefits, a single bug anywhere in the complex system of compilers, proof systems, or verification can lead to serious security issues. In the post, we break down the zkVM workflow, explore common vulnerabilities at each phase, and highlight the importance of understanding these layers to build more secure, zk-powered applications. Curious about how this all plays out? Let’s unravel it together!

Suneal Gong · November 21, 2024