From b7d58bdac813b8ed31374cac35b36e042cd64aec Mon Sep 17 00:00:00 2001 From: cedoor Date: Fri, 15 Dec 2023 11:56:20 +0100 Subject: [PATCH] refactor: hash message and scope --- packages/contracts/contracts/Semaphore.sol | 11 +++++++++-- packages/proof/package.json | 4 ++++ packages/proof/src/generate-proof.ts | 19 +++++++++++-------- packages/proof/src/hash.ts | 16 ++++++++++++++++ packages/proof/src/index.test.ts | 5 +++-- packages/proof/src/verify-proof.ts | 3 ++- yarn.lock | 4 ++++ 7 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 packages/proof/src/hash.ts diff --git a/packages/contracts/contracts/Semaphore.sol b/packages/contracts/contracts/Semaphore.sol index ea349e27b..9008a8096 100644 --- a/packages/contracts/contracts/Semaphore.sol +++ b/packages/contracts/contracts/Semaphore.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.4; import "./interfaces/ISemaphore.sol"; import "./interfaces/ISemaphoreVerifier.sol"; -import "./base/SemaphoreGroups.sol"; +import {SemaphoreGroups} from "./base/SemaphoreGroups.sol"; /// @title Semaphore /// @dev This contract uses the Semaphore base contracts to provide a complete service @@ -189,7 +189,7 @@ contract Semaphore is ISemaphore, SemaphoreGroups { [proof[0], proof[1]], [[proof[2], proof[3]], [proof[4], proof[5]]], [proof[6], proof[7]], - [merkleTreeRoot, nullifier, message, scope] + [merkleTreeRoot, nullifier, _hash(message), _hash(scope)] ) ) { revert Semaphore__InvalidProof(); @@ -199,4 +199,11 @@ contract Semaphore is ISemaphore, SemaphoreGroups { emit ProofVerified(groupId, merkleTreeRoot, nullifier, message, scope, proof); } + + /// @dev Creates a keccak256 hash of a message compatible with the SNARK scalar modulus. + /// @param message: Message to be hashed. + /// @return Message digest. + function _hash(uint256 message) private pure returns (uint256) { + return uint256(keccak256(abi.encodePacked(message))) >> 8; + } } diff --git a/packages/proof/package.json b/packages/proof/package.json index 798b97bd2..cdd8242d5 100644 --- a/packages/proof/package.json +++ b/packages/proof/package.json @@ -30,6 +30,7 @@ "access": "public" }, "devDependencies": { + "@ethersproject/strings": "^5.7.0", "@rollup/plugin-commonjs": "^24.1.0", "@rollup/plugin-json": "^5.0.1", "@rollup/plugin-node-resolve": "^15.0.2", @@ -46,6 +47,9 @@ "@semaphore-protocol/identity": "3.15.2" }, "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", "@zk-kit/groth16": "0.5.0", "download": "^8.0.0", "tmp": "^0.2.1" diff --git a/packages/proof/src/generate-proof.ts b/packages/proof/src/generate-proof.ts index da74914f7..d92e8eaee 100644 --- a/packages/proof/src/generate-proof.ts +++ b/packages/proof/src/generate-proof.ts @@ -1,9 +1,12 @@ +import { BigNumber } from "@ethersproject/bignumber" +import { BytesLike, Hexable } from "@ethersproject/bytes" import { Group } from "@semaphore-protocol/group" import type { Identity } from "@semaphore-protocol/identity" -import { prove } from "@zk-kit/groth16" +import { NumericString, prove } from "@zk-kit/groth16" import getSnarkArtifacts from "./get-snark-artifacts.node" +import hash from "./hash" import packProof from "./pack-proof" -import { BigNumberish, SemaphoreProof, SnarkArtifacts } from "./types" +import { SemaphoreProof, SnarkArtifacts } from "./types" /** * Generates a Semaphore proof. @@ -18,8 +21,8 @@ import { BigNumberish, SemaphoreProof, SnarkArtifacts } from "./types" export default async function generateProof( identity: Identity, group: Group, - message: BigNumberish, - scope: BigNumberish, + message: BytesLike | Hexable | number | bigint, + scope: BytesLike | Hexable | number | bigint, treeDepth?: number, snarkArtifacts?: SnarkArtifacts ): Promise { @@ -54,8 +57,8 @@ export default async function generateProof( treeDepth: merkleProofLength, treeIndices, treeSiblings, - scope, - message + scope: hash(scope), + message: hash(message) }, snarkArtifacts.wasmFilePath, snarkArtifacts.zkeyFilePath @@ -64,8 +67,8 @@ export default async function generateProof( return { treeRoot: publicSignals[0], nullifier: publicSignals[1], - message: publicSignals[2], - scope: publicSignals[3], + message: BigNumber.from(message).toString() as NumericString, + scope: BigNumber.from(scope).toString() as NumericString, proof: packProof(proof) } } diff --git a/packages/proof/src/hash.ts b/packages/proof/src/hash.ts new file mode 100644 index 000000000..2b7225342 --- /dev/null +++ b/packages/proof/src/hash.ts @@ -0,0 +1,16 @@ +import { BigNumber } from "@ethersproject/bignumber" +import { BytesLike, Hexable, zeroPad } from "@ethersproject/bytes" +import { keccak256 } from "@ethersproject/keccak256" +import { NumericString } from "@zk-kit/groth16" + +/** + * Creates a keccak256 hash of a message compatible with the SNARK scalar modulus. + * @param message The message to be hashed. + * @returns The message digest. + */ +export default function hash(message: BytesLike | Hexable | number | bigint): NumericString { + message = BigNumber.from(message).toTwos(256).toHexString() + message = zeroPad(message, 32) + + return (BigInt(keccak256(message)) >> BigInt(8)).toString() as NumericString +} diff --git a/packages/proof/src/index.test.ts b/packages/proof/src/index.test.ts index d572b84b1..9bee0b6ee 100644 --- a/packages/proof/src/index.test.ts +++ b/packages/proof/src/index.test.ts @@ -1,3 +1,4 @@ +import { formatBytes32String } from "@ethersproject/strings" import { Group } from "@semaphore-protocol/group" import { Identity } from "@semaphore-protocol/identity" import { getCurveFromName } from "ffjavascript" @@ -10,8 +11,8 @@ import verifyProof from "./verify-proof" describe("Proof", () => { const treeDepth = 10 - const message = 1 - const scope = 2 + const message = formatBytes32String("Hello world") + const scope = formatBytes32String("Scope") const identity = new Identity(42) diff --git a/packages/proof/src/verify-proof.ts b/packages/proof/src/verify-proof.ts index 4a6333987..7c86f07bf 100644 --- a/packages/proof/src/verify-proof.ts +++ b/packages/proof/src/verify-proof.ts @@ -2,6 +2,7 @@ import { verify } from "@zk-kit/groth16" import { SemaphoreProof } from "./types" import unpackProof from "./unpack-proof" import verificationKeys from "./verification-keys.json" +import hash from "./hash" /** * Verifies a Semaphore proof. @@ -21,7 +22,7 @@ export default function verifyProof({ treeRoot, nullifier, message, scope, proof } return verify(verificationKey, { - publicSignals: [treeRoot, nullifier, message, scope], + publicSignals: [treeRoot, nullifier, hash(message), hash(scope)], proof: unpackProof(proof) }) } diff --git a/yarn.lock b/yarn.lock index c3a5bad41..4a8319f27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8499,6 +8499,10 @@ __metadata: version: 0.0.0-use.local resolution: "@semaphore-protocol/proof@workspace:packages/proof" dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/strings": ^5.7.0 "@rollup/plugin-commonjs": ^24.1.0 "@rollup/plugin-json": ^5.0.1 "@rollup/plugin-node-resolve": ^15.0.2