🏹Semaphore: Missing Smart Contract Range Check
Summary
Related Vulnerabilities: 3. Arithmetic Over/Under Flows
Identified By: PSE Security Team
The Semaphore smart contracts performed range checks in some places but not others. The range checks were to ensure that all public inputs were less than the snark scalar field order. However, these checks weren’t enforced in all the necessary places. This could cause new Semaphore group owners to unknowingly create a group that will always fail verification.
Background
Semaphore is a dapp built on Ethereum that allows users to prove their membership of a group and send signals such as votes or endorsements without revealing their original identity. Essentially, trusted coordinators create a group (with a group Id) and add members via the smart contracts. Members can then submit zero knowledge proofs to the coordinator that prove their membership of the group and optionally contain a signal with it.
The Vulnerability
Since the Solidity uint256 type can hold numbers larger than the snark scalar field order, it is important to be weary of overflows. In order to prevent unwanted overflows, the Semaphore verifier smart contract automatically fails if a public input is greater than the snark scalar field order:
When a coordinator creates a new group, they can input any valid uint256 value as the id. This is a problem since the id is a public input for the zk proof. If the id is greater than the snark scalar field order, the verifier will always revert and the group isn’t operable.
The Fix
To prevent an inoperable group from being created, a check on the group Id is needed. This check needs to ensure that the new group id will be less than the snark scalar field order:
Conclusion:
Issue with uint256: In Ethereum's Solidity language, the
uint256
type can store values up to 2^256 - 1. This range is greater than the snark scalar field order (a predefined value relevant to zk-SNARKs, which is essential for the zero-knowledge proofs used in Semaphore). Hence, there's a potential risk if any of the inputs exceed the snark scalar field order, causing unwanted overflows.Check in Verifier: In the outdated version of the Semaphore verifier contract, there's a check that any public input used in a zk proof must be less than the snark scalar field order. If not, the verification process fails.
Problem with Group IDs: When a coordinator establishes a new group, they can set any value as the group ID, provided it's within the valid range of
uint256
. However, because this ID acts as a public input for the zk proof, there's a significant risk. If the coordinator unintentionally sets an ID greater than the snark scalar field order, all zk proofs involving that group will fail.In simpler terms, imagine a coordinator setting up a voting group but choosing an ID so high that every single vote (zk proof) from that group gets automatically rejected by the system. The entire group becomes useless.
The Fix:
GroupId Check: The fix involves adding a check when creating a group to ensure the group's ID is smaller than the snark scalar field order. If it isn't, the creation of the group fails. This requirement prevents the accidental creation of inoperable groups and ensures all created groups can successfully interact with the zk proofs.
So now, when a coordinator tries to set up a group, the system immediately warns them if they're about to choose an unusable ID. The process is halted, preventing potential issues down the line.
In essence, the vulnerability stemmed from the flexibility given to coordinators to select any group ID, without ensuring that this ID was compatible with the zk proof verification system. The fix introduces a necessary validation step, ensuring all group IDs are zk-proof friendly.
References
Last updated