Announcement #4: Tornado Cash is safe and NOT vulnerable to CVE-2023–33252 (snarkjs <= 6.11)

We are not affected.

On May 5th (one month ago) a critical vulnerability in the Javascript code of snarkjs has been disclosed on iden3’s snarkjs Github page [1]. Approximately 20 hours ago, in [2] this issue has also been disclosed by Beosin on Twitter.

For snarkjs versions lower than or equal to 6.11, an attacker is able to generate for any arbitrary commitment (think of, for example a deposit) any number of additional valid public inputs beyond the one that could in a safe scenario legitimately be used. So this is a classical double spend issue. An example process of an attacker executing such an attack can be seen below.

Why Tornado Cash is safe

Let us examine the snarkjs patch which can be found in [3] first. The notable thing is that in [4] a check has been added on whether the public inputs given to the groth16Verify function are, as shown in [5], strictly smaller than the number of elements of the prime finite field over which the curve used for the zkSNARK implementation is defined.

You can consider the “prime finite field” a set of the only values which we allow to be used with our program. If our program were to allow allow any larger values, meaning not part of the field, which is the basis of our security assumptions, to be inputted, then our security assumptions would be totally void.

If we now look at the Tornado Verifier Contract in [6] (line 214) which is used for Tornado Instances we will find the exact following function:

 // Make sure that every input is less than the snark scalar field
for (uint256 i = 0; i < input.length; i++) {
    require(input[i] < SNARK_SCALAR_FIELD, "verifier-gte-snark-scalar-field");
    vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}

This function, just like the patch, assures that all of the inputs are within the SNARK_SCALAR_FIELD - the prime order of the finite field (of scalars). Would this check not exist, then the all security assumptions about the contract would be void. The snarkjs patch function can be seen below:

function publicInputsAreValid(curve, publicInputs) {
    for(let i = 0; i < publicInputs.length; i++) {
        if(!Scalar.lt(publicInputs[i], curve.r)) {
            return false;
        }
    }
    return true;
}

I’ve verified all of the Tornado Cash instances, and all of them use the same verifier implementing the above function which can be seen in [6] (you must scroll to line 214).

What about our snarkjs library?

So, all Tornado Cash frontends out there, including the IPFS pinned ones use snarkjs <= 6.11. But this does not matter, these frontends do not verify the proofs, they only build them. As such, this is currently not an issue, but we are aware of the issue of our dependencies being pinned at a version below 6.11.

As such, if you are intent on using our snarkjs dependency for off-chain snark verification, you will have to patch this issue until we do not do it.

Links: [1], [2], [3], [4], [5], [6]

3 Likes