forked from emmaguo13/zk-blind
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
262 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// @ts-ignore | ||
import { program } from 'commander'; | ||
import { generateJWT } from '../../helpers/src/jwt'; // JWTを生成する関数のパスを指定 | ||
import { generateJWTVerifierInputs } from '../../helpers/src/input-generators'; // 入力を生成する関数のパスを指定 | ||
import { splitJWT } from "../../helpers/src/utils"; | ||
import fs from "fs"; | ||
const snarkjs = require("snarkjs"); | ||
import { promisify } from "util"; | ||
import path from "path"; | ||
const relayerUtils = require("@zk-email/relayer-utils"); | ||
import https from 'https'; | ||
|
||
program | ||
.requiredOption( | ||
"--input-file <string>", | ||
"Path of a json file to write the generated input" | ||
) | ||
.requiredOption('-a, --accountCode <string>', 'Account code as bigint string') | ||
.option('-h, --header <string>', 'JWT header as JSON string') | ||
.option('-p, --payload <string>', 'JWT payload as JSON string') | ||
.option('-m, --maxMessageLength <number>', 'Maximum message length', '1024') | ||
.option("--silent", "No console logs") | ||
.option("--prove", "Also generate proof"); | ||
|
||
program.parse(process.argv); | ||
|
||
const options = program.opts(); | ||
|
||
function log(...message: any) { | ||
if (!options.silent) { | ||
console.log(...message); | ||
} | ||
} | ||
|
||
async function main() { | ||
const kid = BigInt("0x5aaff47c21d06e266cce395b2145c7c6d4730ea5"); | ||
const issuer = "random.website.com"; | ||
const timestamp = 1694989812; | ||
const azp = "demo-client-id"; | ||
const email = "[email protected]"; | ||
|
||
const defaultHeader = { | ||
alg: "RS256", | ||
typ: "JWT", | ||
kid: kid.toString(16), | ||
}; | ||
const header = defaultHeader; | ||
const defaultPayload = { | ||
email, | ||
iat: timestamp, | ||
azp, | ||
iss: issuer, | ||
}; | ||
const payload = defaultPayload; | ||
const accountCode = BigInt(options.accountCode); | ||
const maxMessageLength = parseInt(options.maxMessageLength, 1024); | ||
|
||
|
||
const { rawJWT, publicKey } = generateJWT(header, { | ||
...payload, | ||
nonce: "Send 0.1 ETH to [email protected]", | ||
}); | ||
|
||
const jwtVerifierInputs = await generateJWTVerifierInputs( | ||
rawJWT, | ||
publicKey, | ||
accountCode, | ||
{ maxMessageLength } | ||
); | ||
|
||
console.log('JWT Verifier Inputs:', jwtVerifierInputs); | ||
|
||
const publicKeyHash = relayerUtils.publicKeyHash( | ||
"0x" + Buffer.from(publicKey.n, "base64").toString("hex") | ||
); | ||
console.log("publicKeyHash"); | ||
console.log(publicKeyHash); | ||
const [, , signature] = splitJWT(rawJWT); | ||
const expectedJwtNullifier = relayerUtils.emailNullifier( | ||
"0x" + Buffer.from(signature, "base64").toString("hex") | ||
); | ||
console.log("expectedJwtNullifier"); | ||
console.log(expectedJwtNullifier); | ||
|
||
if (!options.inputFile.endsWith(".json")) { | ||
throw new Error("--input file path arg must end with .json"); | ||
} | ||
|
||
// log("Generating Inputs for:", options); | ||
|
||
// const circuitInputs = await genEmailCircuitInput(args.emailFile, args.accountCode, { | ||
// maxHeaderLength: 1024, | ||
// ignoreBodyHashCheck: true | ||
// }); | ||
// log("\n\nGenerated Inputs:", circuitInputs, "\n\n"); | ||
const processedInputs = convertBigIntFieldsToString(jwtVerifierInputs); | ||
|
||
await promisify(fs.writeFile)(options.inputFile, JSON.stringify(processedInputs, null, 2)); | ||
|
||
log("Inputs written to", options.inputFile); | ||
|
||
if (options.prove) { | ||
console.log("generate pub signal"); | ||
const fileContent = fs.readFileSync(options.inputFile as string, 'utf-8'); | ||
const jsonData = JSON.parse(fileContent); | ||
const payload = JSON.stringify({ input: jsonData }); | ||
const urlObject = new URL("https://zkemail--jwt-prover-v0-1-0-flask-app.modal.run/prove/jwt"); | ||
const reqOptions = { | ||
hostname: urlObject.hostname, | ||
path: urlObject.pathname, | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Content-Length': Buffer.byteLength(payload) | ||
} | ||
}; | ||
await new Promise<void>((resolve, reject) => { | ||
const req = https.request(reqOptions, (res) => { | ||
let data = ''; | ||
|
||
res.on('data', (chunk) => { | ||
data += chunk; | ||
}); | ||
|
||
res.on('end', async () => { | ||
try { | ||
const dir = path.dirname(options.inputFile); | ||
const responseJson = JSON.parse(data); | ||
const proof = responseJson.proof; | ||
// console.log(proof); | ||
const publicSignals = responseJson.pub_signals; | ||
|
||
await fs.promises.writeFile( | ||
path.join(dir, "proof.json"), | ||
JSON.stringify(convertBigIntFieldsToString(proof), null, 2) | ||
); | ||
|
||
await fs.promises.writeFile( | ||
path.join(dir, "public.json"), | ||
JSON.stringify(convertBigIntFieldsToString(publicSignals), null, 2) | ||
); | ||
console.log('Files written successfully'); | ||
resolve(); | ||
} catch (error) { | ||
console.error('Error processing response:', error); | ||
reject(error); | ||
} | ||
}); | ||
}); | ||
|
||
req.on('error', (error) => { | ||
console.error('Error posting JSON data:', error); | ||
reject(error); | ||
}); | ||
|
||
req.write(payload); | ||
req.end(); | ||
}); | ||
|
||
}; | ||
// Create the request | ||
|
||
process.exit(0); | ||
} | ||
|
||
function convertBigIntFieldsToString(obj: any): any { | ||
if (typeof obj === 'object' && obj !== null) { | ||
return Object.fromEntries( | ||
Object.entries(obj).map(([key, value]) => [ | ||
key, | ||
typeof value === 'bigint' ? value.toString() : value | ||
]) | ||
); | ||
} | ||
return obj; | ||
} | ||
|
||
main().catch((err) => { | ||
console.error("Error generating inputs", err); | ||
process.exit(1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.12; | ||
|
||
import "forge-std/Test.sol"; | ||
import "forge-std/console.sol"; | ||
import {EmailProof, JwtVerifier} from "../../src/utils/JwtVerifier.sol"; | ||
import {JwtGroth16Verifier} from "../../src/utils/JwtGroth16Verifier.sol"; | ||
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
|
||
contract JwtVerifierTest_verifyEmailProof is Test { | ||
JwtVerifier verifier; | ||
|
||
constructor() {} | ||
|
||
function setUp() public { | ||
JwtVerifier verifierImpl = new JwtVerifier(); | ||
JwtGroth16Verifier groth16Verifier = new JwtGroth16Verifier(); | ||
ERC1967Proxy verifierProxy = new ERC1967Proxy( | ||
address(verifierImpl), | ||
abi.encodeCall( | ||
verifierImpl.initialize, | ||
(msg.sender, address(groth16Verifier)) | ||
) | ||
); | ||
verifier = JwtVerifier(address(verifierProxy)); | ||
} | ||
|
||
function test_verifyEmailProof() public { | ||
// account code = 16351800486276213158813915254152097017375347006603152442842997572625254103242 | ||
console.log("test_verifyEmailProof"); | ||
EmailProof memory emailProof; | ||
emailProof.domainName = "0x5aaff47c21d06e266cce395b2145c7c6d4730ea5|random.website.com|demo-client-id"; | ||
emailProof.publicKeyHash = bytes32(0x17b17b71ba34d6771b91f2689fddf7266d561d4dcc5d43174d0e100468d89685); | ||
emailProof.timestamp = 1694989812; | ||
emailProof.maskedCommand = "Send 0.1 ETH to [email protected]"; | ||
emailProof.emailNullifier = bytes32(0x24dc5e63ebcbbe243ef41484ec2d97a6ea130387702a9cad6aea2193457d5aec); | ||
emailProof.accountSalt = bytes32(0x2426ca85629574124b746006d8d50f7e7c0e3a0d91a9cdf6c477e314ed14b8ca); | ||
emailProof.isCodeExist = true; | ||
emailProof.proof = proofToBytes( | ||
string.concat( | ||
vm.projectRoot(), | ||
"/test/jwt_proofs/proof.json" | ||
) | ||
); | ||
verifier.verifyEmailProof(emailProof); | ||
} | ||
|
||
function proofToBytes( | ||
string memory proofPath | ||
) internal view returns (bytes memory) { | ||
string memory proofFile = vm.readFile(proofPath); | ||
string[] memory pi_a = abi.decode( | ||
vm.parseJson(proofFile, ".pi_a"), | ||
(string[]) | ||
); | ||
uint256[2] memory pA = [vm.parseUint(pi_a[0]), vm.parseUint(pi_a[1])]; | ||
string[][] memory pi_b = abi.decode( | ||
vm.parseJson(proofFile, ".pi_b"), | ||
(string[][]) | ||
); | ||
uint256[2][2] memory pB = [ | ||
[vm.parseUint(pi_b[0][1]), vm.parseUint(pi_b[0][0])], | ||
[vm.parseUint(pi_b[1][1]), vm.parseUint(pi_b[1][0])] | ||
]; | ||
string[] memory pi_c = abi.decode( | ||
vm.parseJson(proofFile, ".pi_c"), | ||
(string[]) | ||
); | ||
uint256[2] memory pC = [vm.parseUint(pi_c[0]), vm.parseUint(pi_c[1])]; | ||
bytes memory proof = abi.encode(pA, pB, pC); | ||
return proof; | ||
} | ||
|
||
} |