diff --git a/.gitignore b/.gitignore index a514905..c844a79 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ # dependencies node_modules/ +# build artifacts +src/circuits/*_js/ + # Expo .expo/ dist/ diff --git a/package.json b/package.json index 5fdac38..92c1d53 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "test": "jest", "test:web": "jest --config=jest.web.config.js", "test:ci": "jest --ci --colors", + "compile:zk": "circom ./src/circuits/circle.circom --verbose --wasm --json -l node_modules -o ./src/circuits/; mv ./src/circuits/circle_constraints.json ./src/circuits/circle_js/ ", "postinstall": "patch-package && rn-nodeify --install all --hack", "android": "expo run:android --variant developmentDebug -d", "build:android": "eas build -p android", diff --git a/src/_circuits/Nargo.toml b/src/_circuits/Nargo.toml deleted file mode 100644 index 618830a..0000000 --- a/src/_circuits/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "circuits" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/src/_circuits/Prover.toml b/src/_circuits/Prover.toml deleted file mode 100644 index 6e149d9..0000000 --- a/src/_circuits/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "51313" -y = "1241" diff --git a/src/_circuits/README.md b/src/_circuits/README.md deleted file mode 100644 index 2242e1c..0000000 --- a/src/_circuits/README.md +++ /dev/null @@ -1,82 +0,0 @@ -## Circom - -1. Set Up Your Environment - Before you begin coding, ensure you have the necessary tools installed: - Rust: Required for building Circom. - Circom: The main library for writing circuits. - SnarkJS: A JavaScript library for generating and verifying ZKPs. - Run the following commands to set up your environment: - bash - curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh - git clone https://github.com/iden3/circom.git - cd circom - cargo build --release - cargo install --path circom - npm install -g snarkjs - -2. Write Your Circuit - Create a .circom file and define your circuit. Here’s a simple example of a circuit that hashes a private key using the Poseidon hash function: - text - pragma circom 2.0.0; - -include "circomlib/circuits/poseidon.circom"; - -template privateKeyHasher() { -signal input privateKey; -signal output publicKey; - - component poseidonComponent = Poseidon(1); - poseidonComponent.inputs <== privateKey; - publicKey <== poseidonComponent.out; - -} - -component main = privateKeyHasher(); - -3. Compile the Circuit - Compile your circuit using the Circom compiler to generate the R1CS representation and WASM files: - bash - circom yourCircuit.circom --r1cs --wasm --sym --c - -4. Generate Witness - Create an input JSON file (e.g., input.json) containing the values for your inputs. Then, use the generated WASM file to compute the witness: - bash - node yourCircuit_js/generate_witness.js yourCircuit_js/yourCircuit.wasm input.json witness.wtns - -5. Set Up Trusted Setup - You will need to perform a trusted setup to generate the proving and verifying keys. This can be done using SnarkJS: - bash - snarkjs setup yourCircuit.r1cs pot12_final.zkey - -6. Generate Proof - With the witness and proving key, generate the proof: - bash - snarkjs prove pot12_final.zkey witness.wtns proof.json public.json - -7. Verify Proof - To verify the proof, use SnarkJS with the verification key generated during setup: - bash - snarkjs verify verification_key.json public.json proof.json - -8. Integrate with Smart Contracts - You can autogenerate a Solidity verifier contract from your circuit, which allows you to verify proofs on-chain. This is done using SnarkJS as well: - bash - snarkjs export solidityverifier pot12_final.zkey verifier.sol - -## Baby Jubjub Curve - -Curve Specifications -Curve Equation: The Baby Jubjub curve is represented by the equation: -ax^2 + y^2 = 1 + dx^2y^2 - -where: -Coefficient a: 168700 -Coefficient d: 168696 -Field Modulus: -r = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - -Order: -n = 21888242871839275222246405745257275088614511777268538073601725287587578984328 - -Subgroup Cofactor: 8 -Generator Point: Specific coordinates are used for cryptographic operations. diff --git a/src/_circuits/src/main.nr b/src/_circuits/src/main.nr deleted file mode 100644 index daf7375..0000000 --- a/src/_circuits/src/main.nr +++ /dev/null @@ -1,38 +0,0 @@ -fn genProof(msg: Field, jmid: Field) -> pub boolean { - // generate proof that msg was signed by jmid - // and that jmid is part of original list provided - // and that - // - msg -} - -fn main(pid: Field, jubs: [Field; N]) -> pub Field { - let mut result = 0; - - for i in 0..len(jubs) { - let proof = genProof(msg, jubs[i]); - // sick to do aggregated proof everytime its true - // so can prove n jubs signed off on you at once - if(proof) { - result = proof; - break; - } - } - - result -} - -// #[test] -// fn test_main() { -// assert(None, main("me", ["0x0000"])); -// assert(True, main("me", ["0x0000", "0x2020"])); -// assert(None, main("notme", ["0x0000", "0x2020"])); -// } - - -// merkle tree inclusion example in noir -// fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - // let leaf = std::hash::hash_to_field(message.as_slice()); - // let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); - // assert(merkle_root == root); -// } \ No newline at end of file diff --git a/src/_circuits/target/circuits.json b/src/_circuits/target/circuits.json deleted file mode 100644 index eaaeef2..0000000 --- a/src/_circuits/target/circuits.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "noir_version": "0.33.0+325dac54efb6f99201de9fdeb0a507d45189607d", - "hash": 16226285208704693228, - "abi": { - "parameters": [ - { "name": "x", "type": { "kind": "field" }, "visibility": "private" }, - { "name": "y", "type": { "kind": "field" }, "visibility": "public" } - ], - "return_type": null, - "error_types": {} - }, - "bytecode": "H4sIAAAAAAAA/7VUwQ3DIAyEhKb9dhM7QDC/rlJUsv8IjRSo3IRfzEmWEZaO47DRaofZ4qbOGEt+lWxhcS6HOaPFN8wxkQfn00JI6Ml/ZrI2k6MQUwwQ0dmMq492hR2cC64BtaCuQU4XVB91w0998BOuASV168a7dxE8duA1wk3a496GGSzEC6pjcxkl21zmoJOvW8Py27yXPDVqFY8thk4G12ngP2Q9y7D6xLRoeS3IKP+GqOKpzv58AckTiVriBQAA", - "debug_symbols": "lZBBCoQwEAT/0ucc1l1lIV9ZFhk1SiBMxERBgn93oviA3Kam69KdMJhunVrLow/QvwTne4rWs1DC63qFmThTiLRE6LpRMDxAN9WhMFpn5Pwef4WqTH+X6Z8yvS7RBTZaLHXO5OI5W7l/dhCM+3wn4p4=", - "file_map": { - "57": { - "source": "fn main(x: Field, y: pub Field) {\n assert(x != y);\n}\n\n#[test]\nfn test_main() {\n main(1, 2);\n\n // Uncomment to make test fail\n // main(1, 1);\n}\n", - "path": "/Users/kibagateaux/code/jinni/mobile-app/circuits/src/main.nr" - } - }, - "names": ["main"] -} diff --git a/src/_circuits/target/witness-name.gz b/src/_circuits/target/witness-name.gz deleted file mode 100644 index a323ef0..0000000 Binary files a/src/_circuits/target/witness-name.gz and /dev/null differ diff --git a/src/_circuits/thing.sym b/src/_circuits/thing.sym deleted file mode 100644 index e69de29..0000000 diff --git a/src/_circuits/yourCircuit.sym b/src/_circuits/yourCircuit.sym deleted file mode 100644 index e69de29..0000000 diff --git a/src/circuits/VerifyMessage.circom b/src/circuits/VerifyMessage.circom deleted file mode 100644 index 9201868..0000000 --- a/src/circuits/VerifyMessage.circom +++ /dev/null @@ -1,42 +0,0 @@ -// msg to be concatenated with `summon:` beforehand - -template SignatureVerification(msg) { - signal input signedMessage; // The signed message from the Ethereum wallet - signal input playerId; // The player's username - signal input addressList[10]; // List of Ethereum addresses (up to 10 for simplicity) - signal output isValid; // Output signal indicating validity of the signature - - // Construct the message by adding the "summon:" prefix - // signal message; - // message <== concat("summon:", playerId); // Concatenate "summon:" with playerId - - // Hash the message (using Keccak256) - signal hashMsg; - hashMsg <== sha256(msg); // Replace with appropriate hash function - - // Extract the signer address from the signed message - signal signerAddress; // This will hold the address of the signer - signerAddress <== ecdsaRecover(signedMessage, hashMsg); // Recover the signer address from the signed message - - // Check if the signer address is in the address list - signal isSignerInList; - isSignerInList <== 0; // Initialize to false - for (var i = 0; i < 10; i++) { - isSignerInList <== isSignerInList + (signerAddress === addressList[i] ? 1 : 0); - } - - // The output is valid if the signer is in the list and the signature is correct - isValid <== isSignerInList > 0 && verifySignature(signedMessage, hashMsg, signerAddress); // Ensure the signature is valid -} - -template A(param1, param2) { - signal input X; - signal output z; -} - template B { - signal input a; - signal output b; - - component subTemp = A(a, param2); - b <== subTemp.z; - } \ No newline at end of file diff --git a/src/circuits/VerifyMsg.circom b/src/circuits/VerifyMsg.circom index 4fea6fc..45e4c5f 100644 --- a/src/circuits/VerifyMsg.circom +++ b/src/circuits/VerifyMsg.circom @@ -1,4 +1,7 @@ -// https://github.com/cursive-team/babyjubjub-ecdsa/blob/main/packages/circuits/baby-jubjub-ecdsa/pubkey_membership.circom +// https://github.com/cursive-team/babyjubjub-ecdsa/blob/b9e76d1083d21ee267b8a1b460397c4d90b9fa2b/packages/circuits/baby-jubjub-ecdsa/valid_tap.circom +// can actually just reuse this pretty sure. Use summoner pub key instead of cursive team hrdcoded ones. +// Just Have to figure out nullifiers which i should probs do anyway. pretty sure its generated per proof? + include "ecc.circom"; // Include elliptic curve functions diff --git a/src/circuits/baby_jubjub_ecdsa.circom b/src/circuits/baby_jubjub_ecdsa.circom new file mode 100644 index 0000000..7091da9 --- /dev/null +++ b/src/circuits/baby_jubjub_ecdsa.circom @@ -0,0 +1,58 @@ +// https://github.com/cursive-team/babyjubjub-ecdsa/blob/main/packages/circuits/baby-jubjub-ecdsa/baby_jubjub_ecdsa.circom +pragma circom 2.1.2; + +include "../../node_modules/circomlib/circuits/babyjub.circom"; +include "../../node_modules/circomlib/circuits/bitify.circom"; +include "../../node_modules/circomlib/circuits/escalarmulany.circom"; + +/** + * BabyJubJubECDSA + * ==================== + * + * Converts inputted efficient ECDSA signature to an public key. There is no + * public key validation included. Takes in points in Twisted Edwards form + * and uses Edwards addition and scalar multiplication. Returns computed + * public key in Edwards form. + */ +template BabyJubJubECDSA() { + var bits = 256; + signal input s; + signal input Tx; // T = r^-1 * R + signal input Ty; // T is represented in Twisted Edwards form + signal input Ux; // U = -(m * r^-1 * G) + signal input Uy; // U is represented in Twisted Edwards form + + signal output pubKeyX; // Represented in Twisted Edwards form + signal output pubKeyY; + + // bitify s + component sBits = Num2Bits(bits); + sBits.in <== s; + + // check T, U are on curve + component checkT = BabyCheck(); + checkT.x <== Tx; + checkT.y <== Ty; + component checkU = BabyCheck(); + checkU.x <== Ux; + checkU.y <== Uy; + + // sMultT = s * T + component sMultT = EscalarMulAny(bits); + var i; + for (i=0; i 0 && verifySignature(signedMessage, hashMsg, signerAddress); // Ensure the signature is valid +// // } + +// // template A(param1, param2) { +// // signal input X; +// // signal output z; +// // } +// // template B { +// // signal input a; +// // signal output b; + +// // component subTemp = A(a, param2); +// // b <== subTemp.z; +// // } \ No newline at end of file diff --git a/src/circuits/circle_js/circle.wasm b/src/circuits/circle_js/circle.wasm new file mode 100644 index 0000000..1b952a5 Binary files /dev/null and b/src/circuits/circle_js/circle.wasm differ diff --git a/src/circuits/circle_js/generate_witness.js b/src/circuits/circle_js/generate_witness.js new file mode 100644 index 0000000..2c6656d --- /dev/null +++ b/src/circuits/circle_js/generate_witness.js @@ -0,0 +1,21 @@ +const wc = require("./witness_calculator.js"); +const { readFileSync, writeFile } = require("fs"); + +if (process.argv.length != 5) { + console.log("Usage: node generate_witness.js "); +} else { + const input = JSON.parse(readFileSync(process.argv[3], "utf8")); + + const buffer = readFileSync(process.argv[2]); + wc(buffer).then(async witnessCalculator => { + const w= await witnessCalculator.calculateWitness(input,0); + /* + for (let i=0; i< w.length; i++){ + console.log(w[i]); + }*/ + const buff= await witnessCalculator.calculateWTNSBin(input,0); + writeFile(process.argv[4], buff, function(err) { + if (err) throw err; + }); + }); +} diff --git a/src/circuits/circle_js/witness_calculator.js b/src/circuits/circle_js/witness_calculator.js new file mode 100644 index 0000000..1560c9a --- /dev/null +++ b/src/circuits/circle_js/witness_calculator.js @@ -0,0 +1,384 @@ +module.exports = async function builder(code, options) { + + options = options || {}; + + let wasmModule; + try { + wasmModule = await WebAssembly.compile(code); + } catch (err) { + console.log(err); + console.log("\nTry to run circom --c in order to generate c++ code instead\n"); + throw new Error(err); + } + + let wc; + + let errStr = ""; + let msgStr = ""; + + const instance = await WebAssembly.instantiate(wasmModule, { + runtime: { + printDebug : function(value) { + console.log("printDebug:",value); + }, + exceptionHandler : function(code) { + let err; + if (code == 1) { + err = "Signal not found.\n"; + } else if (code == 2) { + err = "Too many signals set.\n"; + } else if (code == 3) { + err = "Signal already set.\n"; + } else if (code == 4) { + err = "Assert Failed.\n"; + } else if (code == 5) { + err = "Not enough memory.\n"; + } else if (code == 6) { + err = "Input signal array access exceeds the size.\n"; + } else { + err = "Unknown error.\n"; + } + throw new Error(err + errStr); + }, + printErrorMessage : function() { + errStr += getMessage() + "\n"; + // console.error(getMessage()); + }, + writeBufferMessage : function() { + const msg = getMessage(); + // Any calls to `log()` will always end with a `\n`, so that's when we print and reset + if (msg === "\n") { + console.log(msgStr); + msgStr = ""; + } else { + // If we've buffered other content, put a space in between the items + if (msgStr !== "") { + msgStr += " " + } + // Then append the message to the message we are creating + msgStr += msg; + } + }, + showSharedRWMemory : function() { + printSharedRWMemory (); + } + + } + }); + + const sanityCheck = + options +// options && +// ( +// options.sanityCheck || +// options.logGetSignal || +// options.logSetSignal || +// options.logStartComponent || +// options.logFinishComponent +// ); + + + wc = new WitnessCalculator(instance, sanityCheck); + return wc; + + function getMessage() { + var message = ""; + var c = instance.exports.getMessageChar(); + while ( c != 0 ) { + message += String.fromCharCode(c); + c = instance.exports.getMessageChar(); + } + return message; + } + + function printSharedRWMemory () { + const shared_rw_memory_size = instance.exports.getFieldNumLen32(); + const arr = new Uint32Array(shared_rw_memory_size); + for (let j=0; j { + const h = fnvHash(k); + const hMSB = parseInt(h.slice(0,8), 16); + const hLSB = parseInt(h.slice(8,16), 16); + const fArr = flatArray(input[k]); + let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB); + if (signalSize < 0){ + throw new Error(`Signal ${k} not found\n`); + } + if (fArr.length < signalSize) { + throw new Error(`Not enough values for input signal ${k}\n`); + } + if (fArr.length > signalSize) { + throw new Error(`Too many values for input signal ${k}\n`); + } + for (let i=0; i 0) { + let t = typeof a[0]; + for (let i = 1; i { + let new_prefix = prefix == ""? k : prefix + "." + k; + qualify_input(new_prefix,input[k],input1); + }); + } else { + input1[prefix] = input; + } +} + +function toArray32(rem,size) { + const res = []; //new Uint32Array(size); //has no unshift + const radix = BigInt(0x100000000); + while (rem) { + res.unshift( Number(rem % radix)); + rem = rem / radix; + } + if (size) { + var i = size - res.length; + while (i>0) { + res.unshift(0); + i--; + } + } + return res; +} + +function fromArray32(arr) { //returns a BigInt + var res = BigInt(0); + const radix = BigInt(0x100000000); + for (let i = 0; i> i) & 1; + } + for (i=nBits+8; i> i) & 1; + } + for (i=0; i<8; i++) { + out[blockSize-8+i] <== aux.out[i]; + } + for (i=0; i>shr) + signal input a[n]; + signal input b[n]; + signal output out[n]; + var i; + + component aux0 = ShR(64, shr); + for (i=0; i<64; i++) { + aux0.in[i] <== a[i]; + } + component aux1 = ShL(64, shl); + for (i=0; i<64; i++) { + aux1.in[i] <== a[i]; + } + component aux2 = OrArray(64); + for (i=0; i<64; i++) { + aux2.a[i] <== aux0.out[i]; + aux2.b[i] <== aux1.out[i]; + } + component aux3 = XorArray(64); + for (i=0; i<64; i++) { + aux3.a[i] <== b[i]; + aux3.b[i] <== aux2.out[i]; + } + for (i=0; i<64; i++) { + out[i] <== aux3.out[i]; + } +} + +template Theta() { + signal input in[25*64]; + signal output out[25*64]; + + var i; + + component c0 = Xor5(64); + for (i=0; i<64; i++) { + c0.a[i] <== in[i]; + c0.b[i] <== in[5*64+i]; + c0.c[i] <== in[10*64+i]; + c0.d[i] <== in[15*64+i]; + c0.e[i] <== in[20*64+i]; + } + + component c1 = Xor5(64); + for (i=0; i<64; i++) { + c1.a[i] <== in[1*64+i]; + c1.b[i] <== in[6*64+i]; + c1.c[i] <== in[11*64+i]; + c1.d[i] <== in[16*64+i]; + c1.e[i] <== in[21*64+i]; + } + + component c2 = Xor5(64); + for (i=0; i<64; i++) { + c2.a[i] <== in[2*64+i]; + c2.b[i] <== in[7*64+i]; + c2.c[i] <== in[12*64+i]; + c2.d[i] <== in[17*64+i]; + c2.e[i] <== in[22*64+i]; + } + + component c3 = Xor5(64); + for (i=0; i<64; i++) { + c3.a[i] <== in[3*64+i]; + c3.b[i] <== in[8*64+i]; + c3.c[i] <== in[13*64+i]; + c3.d[i] <== in[18*64+i]; + c3.e[i] <== in[23*64+i]; + } + + component c4 = Xor5(64); + for (i=0; i<64; i++) { + c4.a[i] <== in[4*64+i]; + c4.b[i] <== in[9*64+i]; + c4.c[i] <== in[14*64+i]; + c4.d[i] <== in[19*64+i]; + c4.e[i] <== in[24*64+i]; + } + + // d = c4 ^ (c1<<1 | c1>>(64-1)) + component d0 = D(64, 1, 64-1); + for (i=0; i<64; i++) { + d0.a[i] <== c1.out[i]; + d0.b[i] <== c4.out[i]; + } + // r[0] = a[0] ^ d + component r0 = XorArray(64); + for (i=0; i<64; i++) { + r0.a[i] <== in[i]; + r0.b[i] <== d0.out[i]; + } + for (i=0; i<64; i++) { + out[i] <== r0.out[i]; + } + // r[5] = a[5] ^ d + component r5 = XorArray(64); + for (i=0; i<64; i++) { + r5.a[i] <== in[5*64+i]; + r5.b[i] <== d0.out[i]; + } + for (i=0; i<64; i++) { + out[5*64+i] <== r5.out[i]; + } + // r[10] = a[10] ^ d + component r10 = XorArray(64); + for (i=0; i<64; i++) { + r10.a[i] <== in[10*64+i]; + r10.b[i] <== d0.out[i]; + } + for (i=0; i<64; i++) { + out[10*64+i] <== r10.out[i]; + } + // r[15] = a[15] ^ d + component r15 = XorArray(64); + for (i=0; i<64; i++) { + r15.a[i] <== in[15*64+i]; + r15.b[i] <== d0.out[i]; + } + for (i=0; i<64; i++) { + out[15*64+i] <== r15.out[i]; + } + // r[20] = a[20] ^ d + component r20 = XorArray(64); + for (i=0; i<64; i++) { + r20.a[i] <== in[20*64+i]; + r20.b[i] <== d0.out[i]; + } + for (i=0; i<64; i++) { + out[20*64+i] <== r20.out[i]; + } + + // d = c0 ^ (c2<<1 | c2>>(64-1)) + component d1 = D(64, 1, 64-1); + for (i=0; i<64; i++) { + d1.a[i] <== c2.out[i]; + d1.b[i] <== c0.out[i]; + } + // r[1] = a[1] ^ d + component r1 = XorArray(64); + for (i=0; i<64; i++) { + r1.a[i] <== in[1*64+i]; + r1.b[i] <== d1.out[i]; + } + for (i=0; i<64; i++) { + out[1*64+i] <== r1.out[i]; + } + // r[6] = a[6] ^ d + component r6 = XorArray(64); + for (i=0; i<64; i++) { + r6.a[i] <== in[6*64+i]; + r6.b[i] <== d1.out[i]; + } + for (i=0; i<64; i++) { + out[6*64+i] <== r6.out[i]; + } + // r[11] = a[11] ^ d + component r11 = XorArray(64); + for (i=0; i<64; i++) { + r11.a[i] <== in[11*64+i]; + r11.b[i] <== d1.out[i]; + } + for (i=0; i<64; i++) { + out[11*64+i] <== r11.out[i]; + } + // r[16] = a[16] ^ d + component r16 = XorArray(64); + for (i=0; i<64; i++) { + r16.a[i] <== in[16*64+i]; + r16.b[i] <== d1.out[i]; + } + for (i=0; i<64; i++) { + out[16*64+i] <== r16.out[i]; + } + // r[21] = a[21] ^ d + component r21 = XorArray(64); + for (i=0; i<64; i++) { + r21.a[i] <== in[21*64+i]; + r21.b[i] <== d1.out[i]; + } + for (i=0; i<64; i++) { + out[21*64+i] <== r21.out[i]; + } + + // d = c1 ^ (c3<<1 | c3>>(64-1)) + component d2 = D(64, 1, 64-1); + for (i=0; i<64; i++) { + d2.a[i] <== c3.out[i]; + d2.b[i] <== c1.out[i]; + } + // r[2] = a[2] ^ d + component r2 = XorArray(64); + for (i=0; i<64; i++) { + r2.a[i] <== in[2*64+i]; + r2.b[i] <== d2.out[i]; + } + for (i=0; i<64; i++) { + out[2*64+i] <== r2.out[i]; + } + // r[7] = a[7] ^ d + component r7 = XorArray(64); + for (i=0; i<64; i++) { + r7.a[i] <== in[7*64+i]; + r7.b[i] <== d2.out[i]; + } + for (i=0; i<64; i++) { + out[7*64+i] <== r7.out[i]; + } + // r[12] = a[12] ^ d + component r12 = XorArray(64); + for (i=0; i<64; i++) { + r12.a[i] <== in[12*64+i]; + r12.b[i] <== d2.out[i]; + } + for (i=0; i<64; i++) { + out[12*64+i] <== r12.out[i]; + } + // r[17] = a[17] ^ d + component r17 = XorArray(64); + for (i=0; i<64; i++) { + r17.a[i] <== in[17*64+i]; + r17.b[i] <== d2.out[i]; + } + for (i=0; i<64; i++) { + out[17*64+i] <== r17.out[i]; + } + // r[22] = a[22] ^ d + component r22 = XorArray(64); + for (i=0; i<64; i++) { + r22.a[i] <== in[22*64+i]; + r22.b[i] <== d2.out[i]; + } + for (i=0; i<64; i++) { + out[22*64+i] <== r22.out[i]; + } + + // d = c2 ^ (c4<<1 | c4>>(64-1)) + component d3 = D(64, 1, 64-1); + for (i=0; i<64; i++) { + d3.a[i] <== c4.out[i]; + d3.b[i] <== c2.out[i]; + } + // r[3] = a[3] ^ d + component r3 = XorArray(64); + for (i=0; i<64; i++) { + r3.a[i] <== in[3*64+i]; + r3.b[i] <== d3.out[i]; + } + for (i=0; i<64; i++) { + out[3*64+i] <== r3.out[i]; + } + // r[8] = a[8] ^ d + component r8 = XorArray(64); + for (i=0; i<64; i++) { + r8.a[i] <== in[8*64+i]; + r8.b[i] <== d3.out[i]; + } + for (i=0; i<64; i++) { + out[8*64+i] <== r8.out[i]; + } + // r[13] = a[13] ^ d + component r13 = XorArray(64); + for (i=0; i<64; i++) { + r13.a[i] <== in[13*64+i]; + r13.b[i] <== d3.out[i]; + } + for (i=0; i<64; i++) { + out[13*64+i] <== r13.out[i]; + } + // r[18] = a[18] ^ d + component r18 = XorArray(64); + for (i=0; i<64; i++) { + r18.a[i] <== in[18*64+i]; + r18.b[i] <== d3.out[i]; + } + for (i=0; i<64; i++) { + out[18*64+i] <== r18.out[i]; + } + // r[23] = a[23] ^ d + component r23 = XorArray(64); + for (i=0; i<64; i++) { + r23.a[i] <== in[23*64+i]; + r23.b[i] <== d3.out[i]; + } + for (i=0; i<64; i++) { + out[23*64+i] <== r23.out[i]; + } + + // d = c3 ^ (c0<<1 | c0>>(64-1)) + component d4 = D(64, 1, 64-1); + for (i=0; i<64; i++) { + d4.a[i] <== c0.out[i]; + d4.b[i] <== c3.out[i]; + } + // r[4] = a[4] ^ d + component r4 = XorArray(64); + for (i=0; i<64; i++) { + r4.a[i] <== in[4*64+i]; + r4.b[i] <== d4.out[i]; + } + for (i=0; i<64; i++) { + out[4*64+i] <== r4.out[i]; + } + // r[9] = a[9] ^ d + component r9 = XorArray(64); + for (i=0; i<64; i++) { + r9.a[i] <== in[9*64+i]; + r9.b[i] <== d4.out[i]; + } + for (i=0; i<64; i++) { + out[9*64+i] <== r9.out[i]; + } + // r[14] = a[14] ^ d + component r14 = XorArray(64); + for (i=0; i<64; i++) { + r14.a[i] <== in[14*64+i]; + r14.b[i] <== d4.out[i]; + } + for (i=0; i<64; i++) { + out[14*64+i] <== r14.out[i]; + } + // r[19] = a[19] ^ d + component r19 = XorArray(64); + for (i=0; i<64; i++) { + r19.a[i] <== in[19*64+i]; + r19.b[i] <== d4.out[i]; + } + for (i=0; i<64; i++) { + out[19*64+i] <== r19.out[i]; + } + // r[24] = a[24] ^ d + component r24 = XorArray(64); + for (i=0; i<64; i++) { + r24.a[i] <== in[24*64+i]; + r24.b[i] <== d4.out[i]; + } + for (i=0; i<64; i++) { + out[24*64+i] <== r24.out[i]; + } +} + +// RhoPi + +template stepRhoPi(shl, shr) { + // out = a<>shr + signal input a[64]; + signal output out[64]; + var i; + + component aux0 = ShR(64, shr); + for (i=0; i<64; i++) { + aux0.in[i] <== a[i]; + } + component aux1 = ShL(64, shl); + for (i=0; i<64; i++) { + aux1.in[i] <== a[i]; + } + component aux2 = OrArray(64); + for (i=0; i<64; i++) { + aux2.a[i] <== aux0.out[i]; + aux2.b[i] <== aux1.out[i]; + } + for (i=0; i<64; i++) { + out[i] <== aux2.out[i]; + } +} +template RhoPi() { + signal input in[25*64]; + signal output out[25*64]; + + var i; + + // r[10] = a[1]<<1|a[1]>>(64-1) + component s10 = stepRhoPi(1, 64-1); + for (i=0; i<64; i++) { + s10.a[i] <== in[1*64+i]; + } + // r[7] = a[10]<<3|a[10]>>(64-3) + component s7 = stepRhoPi(3, 64-3); + for (i=0; i<64; i++) { + s7.a[i] <== in[10*64+i]; + } + // r[11] = a[7]<<6|a[7]>>(64-6) + component s11 = stepRhoPi(6, 64-6); + for (i=0; i<64; i++) { + s11.a[i] <== in[7*64+i]; + } + // r[17] = a[11]<<10|a[11]>>(64-10) + component s17 = stepRhoPi(10, 64-10); + for (i=0; i<64; i++) { + s17.a[i] <== in[11*64+i]; + } + // r[18] = a[17]<<15|a[17]>>(64-15) + component s18 = stepRhoPi(15, 64-15); + for (i=0; i<64; i++) { + s18.a[i] <== in[17*64+i]; + } + // r[3] = a[18]<<21|a[18]>>(64-21) + component s3 = stepRhoPi(21, 64-21); + for (i=0; i<64; i++) { + s3.a[i] <== in[18*64+i]; + } + // r[5] = a[3]<<28|a[3]>>(64-28) + component s5 = stepRhoPi(28, 64-28); + for (i=0; i<64; i++) { + s5.a[i] <== in[3*64+i]; + } + // r[16] = a[5]<<36|a[5]>>(64-36) + component s16 = stepRhoPi(36, 64-36); + for (i=0; i<64; i++) { + s16.a[i] <== in[5*64+i]; + } + // r[8] = a[16]<<45|a[16]>>(64-45) + component s8 = stepRhoPi(45, 64-45); + for (i=0; i<64; i++) { + s8.a[i] <== in[16*64+i]; + } + // r[21] = a[8]<<55|a[8]>>(64-55) + component s21 = stepRhoPi(55, 64-55); + for (i=0; i<64; i++) { + s21.a[i] <== in[8*64+i]; + } + // r[24] = a[21]<<2|a[21]>>(64-2) + component s24 = stepRhoPi(2, 64-2); + for (i=0; i<64; i++) { + s24.a[i] <== in[21*64+i]; + } + // r[4] = a[24]<<14|a[24]>>(64-14) + component s4 = stepRhoPi(14, 64-14); + for (i=0; i<64; i++) { + s4.a[i] <== in[24*64+i]; + } + // r[15] = a[4]<<27|a[4]>>(64-27) + component s15 = stepRhoPi(27, 64-27); + for (i=0; i<64; i++) { + s15.a[i] <== in[4*64+i]; + } + // r[23] = a[15]<<41|a[15]>>(64-41) + component s23 = stepRhoPi(41, 64-41); + for (i=0; i<64; i++) { + s23.a[i] <== in[15*64+i]; + } + // r[19] = a[23]<<56|a[23]>>(64-56) + component s19 = stepRhoPi(56, 64-56); + for (i=0; i<64; i++) { + s19.a[i] <== in[23*64+i]; + } + // r[13] = a[19]<<8|a[19]>>(64-8) + component s13 = stepRhoPi(8, 64-8); + for (i=0; i<64; i++) { + s13.a[i] <== in[19*64+i]; + } + // r[12] = a[13]<<25|a[13]>>(64-25) + component s12 = stepRhoPi(25, 64-25); + for (i=0; i<64; i++) { + s12.a[i] <== in[13*64+i]; + } + // r[2] = a[12]<<43|a[12]>>(64-43) + component s2 = stepRhoPi(43, 64-43); + for (i=0; i<64; i++) { + s2.a[i] <== in[12*64+i]; + } + // r[20] = a[2]<<62|a[2]>>(64-62) + component s20 = stepRhoPi(62, 64-62); + for (i=0; i<64; i++) { + s20.a[i] <== in[2*64+i]; + } + // r[14] = a[20]<<18|a[20]>>(64-18) + component s14 = stepRhoPi(18, 64-18); + for (i=0; i<64; i++) { + s14.a[i] <== in[20*64+i]; + } + // r[22] = a[14]<<39|a[14]>>(64-39) + component s22 = stepRhoPi(39, 64-39); + for (i=0; i<64; i++) { + s22.a[i] <== in[14*64+i]; + } + // r[9] = a[22]<<61|a[22]>>(64-61) + component s9 = stepRhoPi(61, 64-61); + for (i=0; i<64; i++) { + s9.a[i] <== in[22*64+i]; + } + // r[6] = a[9]<<20|a[9]>>(64-20) + component s6 = stepRhoPi(20, 64-20); + for (i=0; i<64; i++) { + s6.a[i] <== in[9*64+i]; + } + // r[1] = a[6]<<44|a[6]>>(64-44) + component s1 = stepRhoPi(44, 64-44); + for (i=0; i<64; i++) { + s1.a[i] <== in[6*64+i]; + } + + for (i=0; i<64; i++) { + out[i] <== in[i]; + out[10*64+i] <== s10.out[i]; + out[7*64+i] <== s7.out[i]; + out[11*64+i] <== s11.out[i]; + out[17*64+i] <== s17.out[i]; + out[18*64+i] <== s18.out[i]; + out[3*64+i] <== s3.out[i]; + out[5*64+i] <== s5.out[i]; + out[16*64+i] <== s16.out[i]; + out[8*64+i] <== s8.out[i]; + out[21*64+i] <== s21.out[i]; + out[24*64+i] <== s24.out[i]; + out[4*64+i] <== s4.out[i]; + out[15*64+i] <== s15.out[i]; + out[23*64+i] <== s23.out[i]; + out[19*64+i] <== s19.out[i]; + out[13*64+i] <== s13.out[i]; + out[12*64+i] <== s12.out[i]; + out[2*64+i] <== s2.out[i]; + out[20*64+i] <== s20.out[i]; + out[14*64+i] <== s14.out[i]; + out[22*64+i] <== s22.out[i]; + out[9*64+i] <== s9.out[i]; + out[6*64+i] <== s6.out[i]; + out[1*64+i] <== s1.out[i]; + } +} + + +// Chi + +template stepChi() { + // out = a ^ (^b) & c + signal input a[64]; + signal input b[64]; + signal input c[64]; + signal output out[64]; + var i; + + // ^b + component bXor = XorArraySingle(64); + for (i=0; i<64; i++) { + bXor.a[i] <== b[i]; + } + // (^b)&c + component bc = AndArray(64); + for (i=0; i<64; i++) { + bc.a[i] <== bXor.out[i]; + bc.b[i] <== c[i]; + } + // a^(^b)&c + component abc = XorArray(64); + for (i=0; i<64; i++) { + abc.a[i] <== a[i]; + abc.b[i] <== bc.out[i]; + } + for (i=0; i<64; i++) { + out[i] <== abc.out[i]; + } +} + +template Chi() { + signal input in[25*64]; + signal output out[25*64]; + + var i; + + component r0 = stepChi(); + for (i=0; i<64; i++) { + r0.a[i] <== in[i]; + r0.b[i] <== in[1*64+i]; + r0.c[i] <== in[2*64+i]; + } + component r1 = stepChi(); + for (i=0; i<64; i++) { + r1.a[i] <== in[1*64+i]; + r1.b[i] <== in[2*64+i]; + r1.c[i] <== in[3*64+i]; + } + component r2 = stepChi(); + for (i=0; i<64; i++) { + r2.a[i] <== in[2*64+i]; + r2.b[i] <== in[3*64+i]; + r2.c[i] <== in[4*64+i]; + } + component r3 = stepChi(); + for (i=0; i<64; i++) { + r3.a[i] <== in[3*64+i]; + r3.b[i] <== in[4*64+i]; + r3.c[i] <== in[0*64+i]; + } + component r4 = stepChi(); + for (i=0; i<64; i++) { + r4.a[i] <== in[4*64+i]; + r4.b[i] <== in[i]; + r4.c[i] <== in[1*64+i]; + } + + component r5 = stepChi(); + for (i=0; i<64; i++) { + r5.a[i] <== in[5*64+i]; + r5.b[i] <== in[6*64+i]; + r5.c[i] <== in[7*64+i]; + } + component r6 = stepChi(); + for (i=0; i<64; i++) { + r6.a[i] <== in[6*64+i]; + r6.b[i] <== in[7*64+i]; + r6.c[i] <== in[8*64+i]; + } + component r7 = stepChi(); + for (i=0; i<64; i++) { + r7.a[i] <== in[7*64+i]; + r7.b[i] <== in[8*64+i]; + r7.c[i] <== in[9*64+i]; + } + component r8 = stepChi(); + for (i=0; i<64; i++) { + r8.a[i] <== in[8*64+i]; + r8.b[i] <== in[9*64+i]; + r8.c[i] <== in[5*64+i]; + } + component r9 = stepChi(); + for (i=0; i<64; i++) { + r9.a[i] <== in[9*64+i]; + r9.b[i] <== in[5*64+i]; + r9.c[i] <== in[6*64+i]; + } + + component r10 = stepChi(); + for (i=0; i<64; i++) { + r10.a[i] <== in[10*64+i]; + r10.b[i] <== in[11*64+i]; + r10.c[i] <== in[12*64+i]; + } + component r11 = stepChi(); + for (i=0; i<64; i++) { + r11.a[i] <== in[11*64+i]; + r11.b[i] <== in[12*64+i]; + r11.c[i] <== in[13*64+i]; + } + component r12 = stepChi(); + for (i=0; i<64; i++) { + r12.a[i] <== in[12*64+i]; + r12.b[i] <== in[13*64+i]; + r12.c[i] <== in[14*64+i]; + } + component r13 = stepChi(); + for (i=0; i<64; i++) { + r13.a[i] <== in[13*64+i]; + r13.b[i] <== in[14*64+i]; + r13.c[i] <== in[10*64+i]; + } + component r14 = stepChi(); + for (i=0; i<64; i++) { + r14.a[i] <== in[14*64+i]; + r14.b[i] <== in[10*64+i]; + r14.c[i] <== in[11*64+i]; + } + + component r15 = stepChi(); + for (i=0; i<64; i++) { + r15.a[i] <== in[15*64+i]; + r15.b[i] <== in[16*64+i]; + r15.c[i] <== in[17*64+i]; + } + component r16 = stepChi(); + for (i=0; i<64; i++) { + r16.a[i] <== in[16*64+i]; + r16.b[i] <== in[17*64+i]; + r16.c[i] <== in[18*64+i]; + } + component r17 = stepChi(); + for (i=0; i<64; i++) { + r17.a[i] <== in[17*64+i]; + r17.b[i] <== in[18*64+i]; + r17.c[i] <== in[19*64+i]; + } + component r18 = stepChi(); + for (i=0; i<64; i++) { + r18.a[i] <== in[18*64+i]; + r18.b[i] <== in[19*64+i]; + r18.c[i] <== in[15*64+i]; + } + component r19 = stepChi(); + for (i=0; i<64; i++) { + r19.a[i] <== in[19*64+i]; + r19.b[i] <== in[15*64+i]; + r19.c[i] <== in[16*64+i]; + } + + component r20 = stepChi(); + for (i=0; i<64; i++) { + r20.a[i] <== in[20*64+i]; + r20.b[i] <== in[21*64+i]; + r20.c[i] <== in[22*64+i]; + } + component r21 = stepChi(); + for (i=0; i<64; i++) { + r21.a[i] <== in[21*64+i]; + r21.b[i] <== in[22*64+i]; + r21.c[i] <== in[23*64+i]; + } + component r22 = stepChi(); + for (i=0; i<64; i++) { + r22.a[i] <== in[22*64+i]; + r22.b[i] <== in[23*64+i]; + r22.c[i] <== in[24*64+i]; + } + component r23 = stepChi(); + for (i=0; i<64; i++) { + r23.a[i] <== in[23*64+i]; + r23.b[i] <== in[24*64+i]; + r23.c[i] <== in[20*64+i]; + } + component r24 = stepChi(); + for (i=0; i<64; i++) { + r24.a[i] <== in[24*64+i]; + r24.b[i] <== in[20*64+i]; + r24.c[i] <== in[21*64+i]; + } + + for (i=0; i<64; i++) { + out[i] <== r0.out[i]; + out[1*64+i] <== r1.out[i]; + out[2*64+i] <== r2.out[i]; + out[3*64+i] <== r3.out[i]; + out[4*64+i] <== r4.out[i]; + + out[5*64+i] <== r5.out[i]; + out[6*64+i] <== r6.out[i]; + out[7*64+i] <== r7.out[i]; + out[8*64+i] <== r8.out[i]; + out[9*64+i] <== r9.out[i]; + + out[10*64+i] <== r10.out[i]; + out[11*64+i] <== r11.out[i]; + out[12*64+i] <== r12.out[i]; + out[13*64+i] <== r13.out[i]; + out[14*64+i] <== r14.out[i]; + + out[15*64+i] <== r15.out[i]; + out[16*64+i] <== r16.out[i]; + out[17*64+i] <== r17.out[i]; + out[18*64+i] <== r18.out[i]; + out[19*64+i] <== r19.out[i]; + + out[20*64+i] <== r20.out[i]; + out[21*64+i] <== r21.out[i]; + out[22*64+i] <== r22.out[i]; + out[23*64+i] <== r23.out[i]; + out[24*64+i] <== r24.out[i]; + } +} + +// Iota + +template RC(r) { + signal output out[64]; + var rc[24] = [ + 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, + 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, + 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800A, 0x800000008000000A, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + ]; + for (var i=0; i<64; i++) { + out[i] <== (rc[r] >> i) & 1; + } +} + +template Iota(r) { + signal input in[25*64]; + signal output out[25*64]; + var i; + + component rc = RC(r); + + component iota = XorArray(64); + for (var i=0; i<64; i++) { + iota.a[i] <== in[i]; + iota.b[i] <== rc.out[i]; + } + for (i=0; i<64; i++) { + out[i] <== iota.out[i]; + } + for (i=64; i<25*64; i++) { + out[i] <== in[i]; + } +} diff --git a/src/circuits/keccak/utils.circom b/src/circuits/keccak/utils.circom new file mode 100644 index 0000000..c87622f --- /dev/null +++ b/src/circuits/keccak/utils.circom @@ -0,0 +1,116 @@ +// https://github.com/0xPARC/circom-ecdsa/blob/master/circuits/vocdoni-keccak/utils.circom +pragma circom 2.0.2; + +include "../../../node_modules/circomlib/circuits/gates.circom"; +include "../../../node_modules/circomlib/circuits/sha256/xor3.circom"; +include "../../../node_modules/circomlib/circuits/sha256/shift.circom"; // contains ShiftRight + +template Xor5(n) { + signal input a[n]; + signal input b[n]; + signal input c[n]; + signal input d[n]; + signal input e[n]; + signal output out[n]; + var i; + + component xor3 = Xor3(n); + for (i=0; i { + // ensure valid 0x hexstr before converting + const hexBigInt = BigInt(hexStr.startsWith('0x') ? hexStr : `0x${hexStr}`); + + // reduce the BigInt modulo the field prime (if necessary) + const fieldElement = Scalar.mod( + hexBigInt, + Scalar.fromString( + // field prime for babyjubjub curve used in circuit + '21888242871839275222246405745257275088548364400416034343698204186575808495617', + ), + ); + + return fieldElement.toString(); +}; + +export const proveCircleSummoning = async (pid: string, sig: JubjubSignature) => { + // Initialize Poseidon + if (!poseidon) poseidon = await buildPoseidon(); + if (!babyjub) babyjub = await buildBabyjub(); + + // @DEV pretty sure VerifyTap assumes every sig is different (aka chips incrementing nonce) for valid nullifiers + const sigNullifierRandomness = randomUUID(); + + // Convert playerID hex string to a field element + const playerIDFieldElement = hexStringToFieldElement(pid); + + // Convert r, s, and msg to field elements + const tapPubKeys = convertSignatureToEdwards(sig); + + // Compute nullifier + // const sigNullifier = poseidon([ + // tapPubKeys.tapS, + // BigInt(sigNullifierRandomness), + // ]); + + const [tapPubKeyX, tapPubKeyY] = babyjub.mulPointEscalar(babyjub.Base8, tapPubKeys.tapS); + + const F = babyjub.F; + const circuitInputs = { + ...mapValues(tapPubKeys, (val) => val.toString()), + sigNullifierRandomness, + sigNullifier: F.toObject(sigNullifierRandomness).toString(), + playerID: playerIDFieldElement, // Field element representation of the hex string + tapPubKeyX: F.toObject(tapPubKeyX).toString(), + tapPubKeyY: F.toObject(tapPubKeyY).toString(), + }; + + return circuitInputs; +}; + +/** + * + * @param sig + * @returns Twisted Edwards points for public keys of signer + */ +export const convertSignatureToEdwards = (sig: JubjubSignature) => { + if (!poseidon) return {}; // cant generate so return null + if (!babyjub) return {}; // cant generate so return null + + // Extract r, s, v from the signature + const r = sig.raw.r; + const s = sig.raw.s; + + // Convert r, s, and msg to field elements + const r_bn = BigInt(r); + const s_bn = BigInt(s); + const msg_bn = BigInt(sig.ether); + + const tapTx = poseidon([msg_bn]); + const tapTy = poseidon([msg_bn]); + const tapUx = poseidon([r_bn]); + const tapUy = poseidon([r_bn]); + + const F = babyjub.F; + return { + tapS: F.toObject(F.e(s_bn)), + tapTx: F.toObject(tapTx), + tapTy: F.toObject(tapTy), + tapUx: F.toObject(tapUx), + tapUy: F.toObject(tapUy), + }; +}; diff --git a/src/utils/zkpid.android.ts b/src/utils/zkpid.android.ts index ee7ad87..d138264 100644 --- a/src/utils/zkpid.android.ts +++ b/src/utils/zkpid.android.ts @@ -17,7 +17,7 @@ export const signWithId = async (id: string | Identity): Promise