├── circuits ├── circom │ ├── test │ │ ├── negate_test.circom │ │ ├── xden_test.circom │ │ ├── xnum_test.circom │ │ ├── yden_test.circom │ │ ├── ynum_test.circom │ │ ├── cmov_test.circom │ │ ├── inv0_test.circom │ │ ├── iso_map_test.circom │ │ ├── sgn0_test.circom │ │ ├── sha256Hash_test.circom │ │ ├── sha256raw_test.circom │ │ ├── startsWith_test.circom │ │ ├── u_squared_test.circom │ │ ├── checkZeroPad_test.circom │ │ ├── point_add_test.circom │ │ ├── tv2_plus_tv1_test.circom │ │ ├── map_to_curve_test.circom │ │ ├── strxor_test.circom │ │ ├── hash_b0_to_b1_test.circom │ │ ├── hash_b1_to_b2_test.circom │ │ ├── hash_b2_to_b3_test.circom │ │ ├── hash_to_curve_test.circom │ │ ├── hash_to_field_test.circom │ │ ├── msg_prime_test.circom │ │ ├── verifyPaddedBits_test.circom │ │ ├── x_y2_selector_test.circom │ │ ├── z_mul_u_squared_test.circom │ │ ├── hash_to_curve_133_test.circom │ │ ├── hash_to_curve_16_test.circom │ │ ├── bytes_to_registers_test.circom │ │ ├── expand_msg_xmd_test.circom │ │ ├── zero_sandwich_test.circom │ │ ├── expand_msg_xmd2_test.circom │ │ ├── verify_msg_prime_test.circom │ │ └── hash_msg_prime_to_b0_test.circom │ ├── calculateTotal.circom │ ├── point_add.circom │ ├── selector.circom │ ├── hash_to_curve.circom │ ├── hash_to_field.circom │ ├── arith.circom │ ├── constants.circom │ ├── Sha256.circom │ ├── iso_map.circom │ ├── map_to_curve.circom │ └── expand_message_xmd.circom ├── circomHelperConfig.json ├── jest.config.js ├── tsconfig.json ├── ts │ ├── constants.ts │ ├── __tests__ │ │ ├── HashToField.test.ts │ │ ├── HashToCurve.test.ts │ │ ├── PointAdd.test.ts │ │ ├── Sha256.test.ts │ │ ├── IsoMap.test.ts │ │ ├── ExpandMessageXmd.test.ts │ │ └── MapToCurve.test.ts │ ├── iso_map.ts │ ├── utils.ts │ └── generate_inputs.ts └── package.json ├── .gitignore └── README.md /circuits/circom/test/negate_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../arith.circom"; 4 | 5 | component main = Negate(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/xden_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../iso_map.circom"; 4 | 5 | component main = XDen(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/xnum_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../iso_map.circom"; 4 | 5 | component main = XNum(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/yden_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../iso_map.circom"; 4 | 5 | component main = YDen(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/ynum_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../iso_map.circom"; 4 | 5 | component main = YNum(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/cmov_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = CMov(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/inv0_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = Inv0(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/iso_map_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../iso_map.circom"; 4 | 5 | component main = IsoMap(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/sgn0_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = Sgn0(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/sha256Hash_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../Sha256.circom"; 3 | 4 | component main = Sha256Hash(512); 5 | -------------------------------------------------------------------------------- /circuits/circom/test/sha256raw_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../Sha256.circom"; 3 | 4 | component main = Sha256Raw(512); 5 | -------------------------------------------------------------------------------- /circuits/circom/test/startsWith_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../Sha256.circom"; 3 | 4 | component main = StartsWith(5); 5 | -------------------------------------------------------------------------------- /circuits/circom/test/u_squared_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../arith.circom"; 4 | 5 | component main = Square(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/checkZeroPad_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../Sha256.circom"; 3 | 4 | component main = CheckZeroPad(5); 5 | -------------------------------------------------------------------------------- /circuits/circom/test/point_add_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../point_add.circom"; 4 | 5 | component main = PointAdd(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/tv2_plus_tv1_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = Add(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/map_to_curve_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = MapToCurve(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/strxor_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = StrXor(8); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_b0_to_b1_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = HashB(1); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_b1_to_b2_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = HashBi(2); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_b2_to_b3_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = HashBi(3); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_to_curve_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../hash_to_curve.circom"; 4 | 5 | component main = HashToCurve(3); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_to_field_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../hash_to_field.circom"; 4 | 5 | component main = HashToField(3); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/msg_prime_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = MsgPrime(3); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/verifyPaddedBits_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../Sha256.circom"; 3 | 4 | component main = VerifyPaddedBits(512); 5 | -------------------------------------------------------------------------------- /circuits/circom/test/x_y2_selector_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = XY2Selector(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/z_mul_u_squared_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../map_to_curve.circom"; 4 | 5 | component main = ZMulUSquared(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_to_curve_133_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../hash_to_curve.circom"; 4 | 5 | component main = HashToCurve(133); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_to_curve_16_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../hash_to_curve.circom"; 4 | 5 | component main = HashToCurve(16); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/bytes_to_registers_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../hash_to_field.circom"; 4 | 5 | component main = BytesToRegisters(); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/expand_msg_xmd_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = ExpandMessageXmd(3); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/zero_sandwich_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = ZeroSandwich(4, 8); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/expand_msg_xmd2_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = ExpandMessageXmd2(192); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/verify_msg_prime_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = VerifyMsgPrime(120); 6 | -------------------------------------------------------------------------------- /circuits/circom/test/hash_msg_prime_to_b0_test.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "../expand_message_xmd.circom"; 4 | 5 | component main = HashMsgPrimeToB0(3); 6 | -------------------------------------------------------------------------------- /circuits/circomHelperConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "circom": "../../.local/bin/circom", 3 | "snarkjs": "./node_modules/snarkjs/build/cli.cjs", 4 | "circuitDirs": [ 5 | "./circom/test" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /circuits/circom/calculateTotal.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | // Output the sum of the input signals. 4 | template CalculateTotal(n) { 5 | assert(n > 0); 6 | signal input in[n]; 7 | signal output out; 8 | 9 | signal totals[n]; 10 | totals[0] <== in[0]; 11 | 12 | for (var i = 1; i < n; i ++) { 13 | totals[i] <== totals[i - 1] + in[i]; 14 | } 15 | 16 | out <== totals[n - 1]; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /circuits/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | transform: { 4 | "^.+\\.tsx?$": ['ts-jest', {}] 5 | }, 6 | testPathIgnorePatterns: [ 7 | "/build/", 8 | "/node_modules/", 9 | ], 10 | testRegex: '/__tests__/.*\\.test\\.ts$', 11 | moduleFileExtensions: [ 12 | 'ts', 13 | 'tsx', 14 | 'js', 15 | 'jsx', 16 | 'json', 17 | 'node' 18 | ], 19 | globals: { 20 | }, 21 | testEnvironment: 'node' 22 | } 23 | -------------------------------------------------------------------------------- /circuits/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "experimentalDecorators": true, 5 | "alwaysStrict": true, 6 | "allowJs": true, 7 | "noImplicitAny": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "strict": true, 15 | "outDir": "./build" 16 | }, 17 | "exclude": [ 18 | "node_modules/**" 19 | ], 20 | "include": [ 21 | "./ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /circuits/circom/point_add.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../node_modules/circom-ecdsa/circuits/secp256k1.circom"; 3 | 4 | template PointAdd() { 5 | signal input a[2][4]; 6 | signal input b[2][4]; 7 | signal output out[2][4]; 8 | 9 | component add_unequal = Secp256k1AddUnequal(64, 4); 10 | for (var i = 0; i < 4; i ++) { 11 | add_unequal.a[0][i] <== a[0][i]; 12 | add_unequal.a[1][i] <== a[1][i]; 13 | add_unequal.b[0][i] <== b[0][i]; 14 | add_unequal.b[1][i] <== b[1][i]; 15 | } 16 | 17 | for (var i = 0; i < 4; i ++) { 18 | out[0][i] <== add_unequal.out[0][i]; 19 | out[1][i] <== add_unequal.out[1][i]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /circuits/circom/selector.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/comparators.circom"; 3 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/bitify.circom"; 4 | 5 | /* 6 | * Given a list of items and an index, output the item at the position denoted 7 | * by the index. The index must be less than the number of items. 8 | */ 9 | template Selector(length) { 10 | signal input in[length]; 11 | // Assumes that index < length 12 | signal input index; 13 | signal output out; 14 | 15 | signal totals[length + 1]; 16 | totals[0] <== 0; 17 | 18 | component eqs[length]; 19 | for (var i = 0; i < length; i ++) { 20 | eqs[i] = IsEqual(); 21 | eqs[i].in[0] <== i; 22 | eqs[i].in[1] <== index; 23 | totals[i + 1] <== eqs[i].out * in[i] + totals[i]; 24 | } 25 | out <== totals[length]; 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/build/* 3 | */target 4 | 5 | # Generated by Cargo 6 | # will have compiled files and executables 7 | debug/ 8 | target/ 9 | 10 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 11 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 12 | Cargo.lock 13 | 14 | # These are backup files generated by rustfmt 15 | **/*.rs.bk 16 | 17 | # MSVC Windows builds of rustc generate these, which store debugging information 18 | *.pdb 19 | 20 | # Swap 21 | [._]*.s[a-v][a-z] 22 | !*.svg # comment out if you don't need vector files 23 | [._]*.sw[a-p] 24 | [._]s[a-rt-v][a-z] 25 | [._]ss[a-gi-z] 26 | [._]sw[a-p] 27 | 28 | # Session 29 | Session.vim 30 | Sessionx.vim 31 | 32 | # Temporary 33 | .netrwhist 34 | *~ 35 | # Auto-generated tag files 36 | tags 37 | # Persistent undo 38 | [._]*.un~ 39 | 40 | 41 | # Added by cargo 42 | 43 | /target 44 | -------------------------------------------------------------------------------- /circuits/ts/constants.ts: -------------------------------------------------------------------------------- 1 | const dst_prime = [ 2 | 81, 85, 85, 88, 45, 86, 48, 49, 45, 67, 83, 48, 50, 45, 119, 105, 116, 3 | 104, 45, 115, 101, 99, 112, 50, 53, 54, 107, 49, 95, 88, 77, 68, 58, 4 | 83, 72, 65, 45, 50, 53, 54, 95, 83, 83, 87, 85, 95, 82, 79, 95, 49 5 | ] 6 | const z_pad = [ 7 | 0, 0, 0, 0, 0, 0, 0, 0, 8 | 0, 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0, 0, 0, 0, 0, 0, 11 | 0, 0, 0, 0, 0, 0, 0, 0, 12 | 0, 0, 0, 0, 0, 0, 0, 0, 13 | 0, 0, 0, 0, 0, 0, 0, 0, 14 | 0, 0, 0, 0, 0, 0, 0, 0 15 | ] 16 | const lib_str = [0, 96] 17 | const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F') 18 | const Z = BigInt('115792089237316195423570985008687907853269984665640564039457584007908834671652') 19 | const c1 = BigInt('5324262023205125242632636178842408935272934169651804884418803605709653231043') 20 | const c2 = BigInt('31579660701086235115519359547823974869073632181538335647124795638520591274090') 21 | const A = BigInt('28734576633528757162648956269730739219262246272443394170905244663053633733939') 22 | const B = BigInt('1771') 23 | 24 | export { 25 | dst_prime, 26 | z_pad, 27 | lib_str, 28 | p, 29 | Z, 30 | c1, 31 | c2, 32 | A, 33 | B, 34 | } 35 | -------------------------------------------------------------------------------- /circuits/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secp256k1_hash_to_curve_circom", 3 | "version": "0.0.1", 4 | "description": "circom circuits for the secp256k1_XMD:SHA-256_SSWU_RO_ hash-to-curve algorithm", 5 | "scripts": { 6 | "circom-helper": "circom-helper -c ./circomHelperConfig.json -b ./build/test/ -p 9001 -nc", 7 | "test": "jest --runInBand", 8 | "test-expandMessageXmd": "jest ExpandMessageXmd.test.ts", 9 | "test-expandMessageXmd-debug": "node --inspect-brk ./node_modules/.bin/jest ExpandMessageXmd.test.ts", 10 | "test-hashToField": "jest HashToField.test.ts", 11 | "test-hashToField-debug": "node --inspect-brk ./node_modules/.bin/jest HashToField.test.ts", 12 | "test-mapToCurve": "jest MapToCurve.test.ts", 13 | "test-mapToCurve-debug": "node --inspect-brk ./node_modules/.bin/jest MapToCurve.test.ts", 14 | "test-isomap": "jest IsoMap.test.ts", 15 | "test-isomap-debug": "node --inspect-brk ./node_modules/.bin/jest IsoMap.test.ts", 16 | "test-pointAdd": "jest PointAdd.test.ts", 17 | "test-pointAdd-debug": "node --inspect-brk ./node_modules/.bin/jest PointAdd.test.ts", 18 | "test-hashToCurve": "jest HashToCurve.test.ts", 19 | "test-hashToCurve-debug": "node --inspect-brk ./node_modules/.bin/jest HashToCurve.test.ts", 20 | "test-sha256": "jest Sha256.test.ts", 21 | "test-sha256-debug": "node --inspect-brk ./node_modules/.bin/jest Sha256.test.ts" 22 | }, 23 | "author": "Koh Wei Jie", 24 | "license": "MIT", 25 | "dependencies": { 26 | "@noble/secp256k1": "1.3.4", 27 | "circom-ecdsa": "git://github.com/0xPARC/circom-ecdsa.git#d87eb7068cb35c951187093abe966275c1839ead", 28 | "circomlib": "git://github.com/weijiekoh/circomlib.git#ac85e82c1914d47789e2032fb11ceb2cfdd38a2b", 29 | "snarkjs": "^0.5.0" 30 | }, 31 | "devDependencies": { 32 | "@types/jest": "^29.0.3", 33 | "circom_tester": "^0.0.19", 34 | "circom-helper": "0.3.5", 35 | "jest": "^29.0.3", 36 | "ts-jest": "^29.0.1", 37 | "typescript": "^4.8.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /circuits/circom/hash_to_curve.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "./hash_to_field.circom"; 3 | include "./map_to_curve.circom"; 4 | include "./iso_map.circom"; 5 | include "./point_add.circom"; 6 | 7 | template HashToCurve(msg_length) { 8 | signal input msg[msg_length]; 9 | 10 | signal input q0_gx1_sqrt[4]; 11 | signal input q0_gx2_sqrt[4]; 12 | signal input q0_y_pos[4]; 13 | signal input q0_x_mapped[4]; 14 | signal input q0_y_mapped[4]; 15 | 16 | signal input q1_gx1_sqrt[4]; 17 | signal input q1_gx2_sqrt[4]; 18 | signal input q1_y_pos[4]; 19 | signal input q1_x_mapped[4]; 20 | signal input q1_y_mapped[4]; 21 | 22 | signal output out[2][4]; 23 | 24 | // Step 1: u = hash_to_field(msg) 25 | component h2f = HashToField(msg_length); 26 | for (var i = 0; i < msg_length; i ++) { 27 | h2f.msg[i] <== msg[i]; 28 | } 29 | 30 | // Step 2: Q0 = map_to_curve(u[0]) 31 | component m2c_q0 = MapToCurve(); 32 | for (var i = 0; i < 4; i ++) { 33 | m2c_q0.u[i] <== h2f.u[0][i]; 34 | m2c_q0.gx1_sqrt[i] <== q0_gx1_sqrt[i]; 35 | m2c_q0.gx2_sqrt[i] <== q0_gx2_sqrt[i]; 36 | m2c_q0.y_pos[i] <== q0_y_pos[i]; 37 | m2c_q0.x_mapped[i] <== q0_x_mapped[i]; 38 | m2c_q0.y_mapped[i] <== q0_y_mapped[i]; 39 | } 40 | 41 | // Step 3: Q1 = map_to_curve(u[1]) 42 | component m2c_q1 = MapToCurve(); 43 | for (var i = 0; i < 4; i ++) { 44 | m2c_q1.u[i] <== h2f.u[1][i]; 45 | m2c_q1.gx1_sqrt[i] <== q1_gx1_sqrt[i]; 46 | m2c_q1.gx2_sqrt[i] <== q1_gx2_sqrt[i]; 47 | m2c_q1.y_pos[i] <== q1_y_pos[i]; 48 | m2c_q1.x_mapped[i] <== q1_x_mapped[i]; 49 | m2c_q1.y_mapped[i] <== q1_y_mapped[i]; 50 | } 51 | 52 | // Step 4: return A + B 53 | component point_add = PointAdd(); 54 | for (var i = 0; i < 4; i ++) { 55 | point_add.a[0][i] <== q0_x_mapped[i]; 56 | point_add.a[1][i] <== q0_y_mapped[i]; 57 | point_add.b[0][i] <== q1_x_mapped[i]; 58 | point_add.b[1][i] <== q1_y_mapped[i]; 59 | } 60 | 61 | for (var i = 0; i < 4; i ++) { 62 | out[0][i] <== point_add.out[0][i]; 63 | out[1][i] <== point_add.out[1][i]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /circuits/circom/hash_to_field.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "./constants.circom"; 3 | include "./expand_message_xmd.circom"; 4 | 5 | template HashToField(msg_length) { 6 | signal input msg[msg_length]; 7 | signal output u[2][4]; 8 | 9 | component expand_message_xmd = ExpandMessageXmd(msg_length); 10 | for (var i = 0; i < msg_length; i ++) { 11 | expand_message_xmd.msg[i] <== msg[i]; 12 | } 13 | 14 | component u0_bytes_to_registers = BytesToRegisters(); 15 | component u1_bytes_to_registers = BytesToRegisters(); 16 | 17 | for (var i = 0; i < 48; i ++) { 18 | u0_bytes_to_registers.bytes[i] <== expand_message_xmd.out[i]; 19 | u1_bytes_to_registers.bytes[i] <== expand_message_xmd.out[i + 48]; 20 | } 21 | 22 | for (var i = 0; i < 4; i ++) { 23 | u[0][i] <== u0_bytes_to_registers.out[i]; 24 | u[1][i] <== u1_bytes_to_registers.out[i]; 25 | } 26 | } 27 | 28 | // Converts a 48-byte array into a 4-register BigInt modulo the secp256k1 prime 29 | template BytesToRegisters() { 30 | signal input bytes[48]; 31 | signal output out[4]; 32 | 33 | // Split each byte in bytes into bits 34 | component n2b[48]; 35 | for (var i = 0; i < 6; i ++) { 36 | for (var j = 0 ; j < 8; j ++) { 37 | var idx = i * 8 + j; 38 | n2b[idx] = Num2Bits(8); 39 | n2b[idx].in <== bytes[idx]; 40 | } 41 | } 42 | 43 | // Convert each chunk of 64 bits into a register 44 | component b2n[6]; 45 | for (var i = 0; i < 6; i ++) { 46 | b2n[i] = Bits2Num(64); 47 | for (var j = 0; j < 8; j ++) { 48 | for (var k = 0; k < 8; k ++) { 49 | b2n[i].in[(7-j) * 8 + k] <== n2b[i * 8 + j].out[k]; 50 | } 51 | } 52 | } 53 | 54 | // Input the registers into BigMod 55 | component m = BigMod(64, 4); 56 | for (var i = 0; i < 6; i ++) { 57 | m.a[i] <== b2n[5 - i].out; 58 | } 59 | for (var i = 6; i < 8; i ++) { 60 | m.a[i] <== 0; 61 | } 62 | 63 | var p[4] = get_secp256k1_p(); 64 | for (var i = 0; i < 4; i ++) { 65 | m.b[i] <== p[i]; 66 | } 67 | 68 | for (var i = 0; i < 4; i ++) { 69 | out[i] <== m.mod[i]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /circuits/circom/arith.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "./constants.circom"; 4 | include "../node_modules/circom-ecdsa/circuits/bigint.circom"; 5 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/mux1.circom"; 6 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/comparators.circom"; 7 | 8 | template Add() { 9 | signal input a[4]; 10 | signal input b[4]; 11 | signal output out[4]; 12 | 13 | var p[4] = get_secp256k1_p(); 14 | 15 | component adder = BigAdd(64, 4); 16 | for (var i = 0; i < 4; i ++) { 17 | adder.a[i] <== a[i]; 18 | adder.b[i] <== b[i]; 19 | } 20 | 21 | component mod = BigMod(64, 4); 22 | for (var i = 0; i < 4 + 1; i ++) { 23 | mod.a[i] <== adder.out[i]; 24 | } 25 | 26 | for (var i = 4 + 1; i < 4 * 2; i ++) { 27 | mod.a[i] <== 0; 28 | } 29 | 30 | for (var i = 0; i < 4; i ++) { 31 | mod.b[i] <== p[i]; 32 | } 33 | 34 | for (var i = 0; i < 4; i ++) { 35 | out[i] <== mod.mod[i]; 36 | } 37 | } 38 | 39 | template Negate() { 40 | signal input in[4]; 41 | signal output out[4]; 42 | 43 | var p[4] = get_secp256k1_p(); 44 | 45 | component sub = BigSubModP(64, 4); 46 | for (var i = 0; i < 4; i ++) { 47 | sub.a[i] <== p[i]; 48 | sub.b[i] <== in[i]; 49 | sub.p[i] <== p[i]; 50 | } 51 | 52 | for (var i = 0; i < 4; i ++) { 53 | out[i] <== sub.out[i]; 54 | } 55 | } 56 | 57 | template Multiply() { 58 | signal input a[4]; 59 | signal input b[4]; 60 | signal output out[4]; 61 | var p[4] = get_secp256k1_p(); 62 | 63 | component mul_mod_p = BigMultModP(64, 4); 64 | for (var i = 0; i < 4; i ++) { 65 | mul_mod_p.a[i] <== a[i]; 66 | mul_mod_p.b[i] <== b[i]; 67 | mul_mod_p.p[i] <== p[i]; 68 | } 69 | 70 | for (var i = 0; i < 4; i ++) { 71 | out[i] <== mul_mod_p.out[i]; 72 | } 73 | } 74 | 75 | template Square() { 76 | signal input in[4]; 77 | signal output out[4]; 78 | 79 | component mul = Multiply(); 80 | for (var i = 0; i < 4; i ++) { 81 | mul.a[i] <== in[i]; 82 | mul.b[i] <== in[i]; 83 | } 84 | 85 | for (var i = 0; i < 4; i ++) { 86 | out[i] <== mul.out[i]; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `secp256k1_XMD:SHA-256_SSWU_RO_` hash-to-curve in circom 2 | 3 | ## Quick start 4 | 5 | First, install `circom-helper` dependencies following [these 6 | instructions](https://github.com/weijiekoh/circom-helper). Note that you'll 7 | need an Intel machine running Linux (ideally the Debian, Ubuntu or OpenSuse 8 | distributions). 9 | 10 | 1. Clone this repository and navigate to the project root. 11 | 2. Run `npm i` 12 | 3. In a different terminal, navigate to the project root and run `npm run circom-helper`. 13 | 3. In a different terminal, navigate to the project root and run `npm run test`. 14 | 15 | ## About the circuits 16 | 17 | The `HashToCurve` circuit, parameterised by the message length (in bytes), is 18 | located at `circuits/circom/hash_to_curve.circom`. It implements the 19 | `secp256k1_XMD:SHA-256_SSWU_RO_` hash-to-curve suite described 20 | [here](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#appendix-J.8.1). 21 | 22 | ## Constraints 23 | 24 | A circuit parameterised for a message with length 3 will have 2217282 25 | constraints, and a circuit for a message of length 64 will have 2218762 26 | constraints. In practice, messages will be longer, and the number of 27 | constraints will be accordingly larger. The bulk of the constraints come from 28 | the `hash-to-field` circuit which involves multiple SHA256 hashes. 29 | 30 | ## The algorithm 31 | 32 | The algorithm it follows is as such: 33 | 34 | ``` 35 | hash_to_curve(msg) 36 | 37 | Input: msg, an arbitrary-length byte string. 38 | Output: P, a point in the secp256k1 curve. 39 | 40 | Steps: 41 | 1. u = hash_to_field(msg) 42 | 2. Q0 = map_to_curve(u[0]) 43 | 3. Q1 = map_to_curve(u[1]) 44 | 4. R = iso_map(Q0) + iso_map(Q1) 45 | 5. return P 46 | ``` 47 | 48 | ### `hash-to-field` 49 | 50 | Implemented in `circuits/circom/hash_to_field.circom`. Follows the algorithm 51 | described 52 | [here](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#hashtofield). 53 | 54 | Constraints: 1881345 where the message length is 3. 55 | 56 | ### `map_to_curve` 57 | 58 | Implemented in `circuits/circom/map_to_curve.circom`. Follows the SSWU 59 | algorithm described 60 | [here](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#simple-swu). 61 | 62 | Constraints: 68792 63 | 64 | ### `iso_map` 65 | 66 | Implemented in `circuits/circom/iso_map.circom`. Follows the algorithm 67 | described 68 | [here](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#appx-iso-secp256k1). 69 | 70 | Constraints: 95331 71 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/HashToField.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(120000) 2 | const ff = require('ffjavascript') 3 | const stringifyBigInts = ff.utils.stringifyBigInts 4 | import { 5 | callGenWitness as genWitness, 6 | callGetSignalByName as getSignalByName, 7 | } from 'circom-helper' 8 | import { bigint_to_array } from '../utils' 9 | import { 10 | bytes_to_registers, 11 | } from '../generate_inputs' 12 | 13 | describe('HashToField', () => { 14 | const msg = [97, 98, 99] // "abc" 15 | const u0_bytes = [ 16 | 232, 52, 124, 173, 72, 171, 78, 49, 157, 123, 39, 85, 32, 234, 129, 17 | 207, 18, 138, 171, 93, 54, 121, 161, 247, 96, 30, 59, 222, 172, 18 | 154, 81, 208, 197, 77, 255, 208, 84, 39, 78, 219, 36, 136, 85, 230, 19 | 17, 144, 196, 98 20 | ] 21 | 22 | const expected_u0_registers = bytes_to_registers(u0_bytes) 23 | 24 | const expected_u1_registers = [ 25 | BigInt('5596462452035853824'), 26 | BigInt('5988634572027431684'), 27 | BigInt('1427920136467682780'), 28 | BigInt('6383771510115767720'), 29 | ] 30 | 31 | it('HashToField', async () => { 32 | const circuit = 'hash_to_field_test' 33 | const circuitInputs = stringifyBigInts({ msg }) 34 | const witness = await genWitness(circuit, circuitInputs) 35 | 36 | // u0 37 | const u0_registers: bigint[] = [] 38 | for (let i = 0; i < 4; i ++) { 39 | const out = BigInt(await getSignalByName(circuit, witness, 'main.u[0][' + i.toString() + ']')) 40 | u0_registers.push(out) 41 | expect(out).toEqual(expected_u0_registers[i]) 42 | } 43 | expect(bigint_to_array(64, 4, BigInt('8386638881075453792406600069412283052291822895580742641789327979312054938465'))).toEqual(u0_registers) 44 | // u1 45 | const u1_registers: bigint[] = [] 46 | for (let i = 0; i < 4; i ++) { 47 | const out = BigInt(await getSignalByName(circuit, witness, 'main.u[1][' + i.toString() + ']')) 48 | u1_registers.push(out) 49 | expect(out).toEqual(expected_u1_registers[i]) 50 | } 51 | expect(bigint_to_array(64, 4, BigInt('40071583224459737250239606232440854032076176808341187421679277989916398099968'))).toEqual(u1_registers) 52 | }) 53 | 54 | const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F') 55 | it('BytesToRegisters (all bytes are 255)', async () => { 56 | const circuit = 'bytes_to_registers_test' 57 | const bytes: number[] = [] 58 | for (let i = 0; i < 48; i ++) { 59 | bytes.push(255) 60 | } 61 | 62 | const expected_registers = bigint_to_array( 63 | 64, 64 | 4, 65 | BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') % p 66 | ) 67 | const circuitInputs = stringifyBigInts({ bytes }) 68 | const witness = await genWitness(circuit, circuitInputs) 69 | for (let i = 0; i < 4; i ++) { 70 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 71 | expect(out).toEqual(expected_registers[i]) 72 | } 73 | }) 74 | 75 | it('BytesToRegisters (for u0)', async () => { 76 | const circuit = 'bytes_to_registers_test' 77 | const circuitInputs = stringifyBigInts({ bytes: u0_bytes }) 78 | const witness = await genWitness(circuit, circuitInputs) 79 | const registers: bigint[] = [] 80 | for (let i = 0; i < 4; i ++) { 81 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 82 | expect(out.toString()).toEqual(expected_u0_registers[i].toString()) 83 | } 84 | }) 85 | }) 86 | 87 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/HashToCurve.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(360000) 2 | const crypto = require('crypto') 3 | import { join } from 'path'; 4 | import { wasm as wasm_tester } from 'circom_tester' 5 | const ff = require('ffjavascript') 6 | const stringifyBigInts = ff.utils.stringifyBigInts 7 | import { 8 | callGenWitness as genWitness, 9 | callGetSignalByName as getSignalByName, 10 | } from 'circom-helper' 11 | import { bigint_to_array } from '../utils' 12 | import { generate_inputs } from '../generate_inputs' 13 | 14 | const test_suites = [ 15 | { 16 | circuit: 'hash_to_curve_test', 17 | msg: 'abc', 18 | expected_x: BigInt('0x3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b'), 19 | expected_y: BigInt('0x7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6'), 20 | check_constraints: true, 21 | }, 22 | { 23 | circuit: 'hash_to_curve_16_test', 24 | msg: 'abcdef0123456789', 25 | expected_x: BigInt('0xbac54083f293f1fe08e4a70137260aa90783a5cb84d3f35848b324d0674b0e3a'), 26 | expected_y: BigInt('0x4436476085d4c3c4508b60fcf4389c40176adce756b398bdee27bca19758d828'), 27 | }, 28 | { 29 | circuit: 'hash_to_curve_133_test', 30 | msg: 'q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq', 31 | expected_x: BigInt('0xe2167bc785333a37aa562f021f1e881defb853839babf52a7f72b102e41890e9'), 32 | expected_y: BigInt('0xf2401dd95cc35867ffed4f367cd564763719fbc6a53e969fb8496a1e6685d873'), 33 | }, 34 | // The circuit for the following test case may not work with circom-helper 35 | // because of the large amount of JS heap memory it needs to run `snarkjs 36 | // r1cs info`. 37 | //{ 38 | //circuit: 'hash_to_curve_517_test', 39 | //msg: 'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 40 | //expected_x: BigInt('0xe3c8d35aaaf0b9b647e88a0a0a7ee5d5bed5ad38238152e4e6fd8c1f8cb7c998'), 41 | //expected_y: BigInt('0x8446eeb6181bf12f56a9d24e262221cc2f0c4725c7e3803024b5888ee5823aa6'), 42 | //}, 43 | ] 44 | 45 | describe('HashToCurve', () => { 46 | for (const suite of test_suites) { 47 | it('msg = " ' + suite.msg + '"', async () => { 48 | const circuit = suite.circuit 49 | const msg = suite.msg 50 | const expected_x = suite.expected_x 51 | const expected_y = suite.expected_y 52 | const expected_x_array = bigint_to_array(64, 4, expected_x) 53 | const expected_y_array = bigint_to_array(64, 4, expected_y) 54 | const inputs = generate_inputs(msg) 55 | 56 | const circuitInputs = stringifyBigInts(inputs) 57 | const witness = await genWitness(circuit, circuitInputs) 58 | 59 | for (let i = 0; i < 4; i ++) { 60 | const out_x = BigInt(await getSignalByName(circuit, witness, 'main.out[0][' + i.toString() + ']')) 61 | expect(out_x).toEqual(expected_x_array[i]) 62 | const out_y = BigInt(await getSignalByName(circuit, witness, 'main.out[1][' + i.toString() + ']')) 63 | expect(out_y).toEqual(expected_y_array[i]) 64 | } 65 | 66 | if (suite.check_constraints) { 67 | const p = join(__dirname, "../../circom/test", circuit + '.circom') 68 | const c = await wasm_tester(p, {"json":true, "sym": true}) 69 | const w = await c.calculateWitness(circuitInputs) 70 | await c.checkConstraints(w) 71 | } 72 | }) 73 | } 74 | }) 75 | -------------------------------------------------------------------------------- /circuits/ts/iso_map.ts: -------------------------------------------------------------------------------- 1 | // Implements the isogeny map for the secp256k1 hash-to-curve suite 2 | // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#appendix-E.1 3 | const ff = require('ffjavascript') 4 | 5 | // Constants used to compute x_num 6 | const k_1_0 = BigInt('0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7') 7 | const k_1_1 = BigInt('0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581') 8 | const k_1_2 = BigInt('0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262') 9 | const k_1_3 = BigInt('0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c') 10 | 11 | // Constants used to compute x_den 12 | const k_2_0 = BigInt('0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b') 13 | const k_2_1 = BigInt('0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14') 14 | 15 | // Constants used to compute y_num 16 | const k_3_0 = BigInt('0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c') 17 | const k_3_1 = BigInt('0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3') 18 | const k_3_2 = BigInt('0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931') 19 | const k_3_3 = BigInt('0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84') 20 | 21 | // Constants used to compute y_den 22 | const k_4_0 = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b') 23 | const k_4_1 = BigInt('0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573') 24 | const k_4_2 = BigInt('0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f') 25 | 26 | const constants = { 27 | k_1_0, 28 | k_1_1, 29 | k_1_2, 30 | k_1_3, 31 | k_2_0, 32 | k_2_1, 33 | k_3_0, 34 | k_3_1, 35 | k_3_2, 36 | k_3_3, 37 | k_4_0, 38 | k_4_1, 39 | k_4_2, 40 | } 41 | 42 | const compute_x_num = (x: bigint, p: bigint): bigint => { 43 | const x_2 = (x * x) % p 44 | const x_3 = (x * x * x) % p 45 | 46 | // Compute x_num = k_(1,3) * x'^3 + k_(1,2) * x'^2 + k_(1,1) * x' + k_(1,0) 47 | const x_num = (k_1_3 * x_3 + k_1_2 * x_2 + k_1_1 * x + k_1_0) % p 48 | return x_num 49 | } 50 | 51 | const compute_x_den = (x: bigint, p: bigint): bigint => { 52 | // Compute x_den = x'^2 + k_(2,1) * x' + k_(2,0) 53 | const x_2 = (x * x) % p 54 | const x_den = (x_2 + k_2_1 * x + k_2_0) % p 55 | return x_den 56 | } 57 | 58 | const compute_y_num = (x: bigint, p: bigint): bigint => { 59 | // Compute y_num = k_(3,3) * x'^3 + k_(3,2) * x'^2 + k_(3,1) * x' + k_(3,0) 60 | const x_2 = (x * x) % p 61 | const x_3 = (x * x * x) % p 62 | const y_num = (k_3_3 * x_3 + k_3_2 * x_2 + k_3_1 * x + k_3_0) % p 63 | return y_num 64 | } 65 | 66 | const compute_y_den = (x: bigint, p: bigint): bigint => { 67 | // Compute y_den = x'^3 + k_(4,2) * x'^2 + k_(4,1) * x' + k_(4,0) 68 | const x_2 = (x * x) % p 69 | const x_3 = (x * x * x) % p 70 | const y_den = (x_3 + k_4_2 * x_2 + k_4_1 * x + k_4_0) % p 71 | 72 | return y_den 73 | } 74 | 75 | function bigint_to_array(n: number, k: number, x: bigint) { 76 | let mod: bigint = BigInt(1); 77 | for (var idx = 0; idx < n; idx++) { 78 | mod = mod * BigInt(2); 79 | } 80 | 81 | let ret: bigint[] = []; 82 | var x_temp: bigint = x; 83 | for (var idx = 0; idx < k; idx++) { 84 | ret.push(x_temp % mod); 85 | x_temp = x_temp / mod; 86 | } 87 | return ret; 88 | } 89 | 90 | 91 | // Converts (x', y') on E' to (x, y) on E 92 | const iso_map = (x: bigint, y: bigint, p: bigint) => { 93 | const x_2 = (x * x) % p 94 | const x_3 = (x * x * x) % p 95 | 96 | const x_num = compute_x_num(x, p) 97 | const x_den = compute_x_den(x, p) 98 | const y_num = compute_y_num(x, p) 99 | const y_den = compute_y_den(x, p) 100 | 101 | const field = new ff.F1Field(p) 102 | const x_mapped = field.div(x_num, x_den) 103 | const y_mapped = field.mul(y, field.div(y_num, y_den)) 104 | 105 | return { x: x_mapped, y: y_mapped } 106 | } 107 | 108 | export { 109 | iso_map, 110 | constants, 111 | compute_x_num, 112 | compute_x_den, 113 | compute_y_num, 114 | compute_y_den, 115 | } 116 | -------------------------------------------------------------------------------- /circuits/ts/utils.ts: -------------------------------------------------------------------------------- 1 | const ff = require('ffjavascript') 2 | 3 | const sqrt_mod_p = (n: bigint, p: bigint): bigint => { 4 | const F = new ff.F1Field(p) 5 | return F.sqrt(n) 6 | } 7 | 8 | // From circom-ecdsa 9 | function bigint_to_array(n: number, k: number, x: bigint) { 10 | let mod: bigint = BigInt(1); 11 | for (var idx = 0; idx < n; idx++) { 12 | mod = mod * BigInt(2); 13 | } 14 | 15 | let ret: bigint[] = []; 16 | var x_temp: bigint = x; 17 | for (var idx = 0; idx < k; idx++) { 18 | ret.push(x_temp % mod); 19 | x_temp = x_temp / mod; 20 | } 21 | return ret; 22 | } 23 | 24 | const sgn0 = (input: bigint): bigint => { 25 | return input % BigInt(2) 26 | } 27 | 28 | /* 29 | * Converts a buffer into an array of bits. Each bit is represented by a Number 30 | * (1 or 0). E.g. will be converted to 31 | * [ 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0 ] 32 | */ 33 | const buffer2bitArray = (b: Buffer): number[] => { 34 | const res: number[] = [] 35 | for (let i = 0; i < b.length; i ++) { 36 | for (let j = 0; j < 8; j ++) { 37 | res.push((b[i] >> (7 - j) & 1)) 38 | } 39 | } 40 | return res 41 | } 42 | 43 | const bufToPaddedBytes = (buf: Buffer): number[] => { 44 | // Convert the buffer to bits 45 | const result: number[] = buffer2bitArray(buf) 46 | const len = result.length 47 | 48 | result.push(1) 49 | 50 | const nBlocks = Math.floor((len + 64) / 512) + 1 51 | 52 | while (result.length < nBlocks * 512 - 64) { 53 | result.push(0) 54 | } 55 | 56 | const lenBitArr: number[] = [] 57 | let lengthInBits = BigInt(len).toString(2) 58 | for (let i = 0; i < lengthInBits.length; i ++) { 59 | lenBitArr.push(Number(lengthInBits[i])) 60 | } 61 | 62 | while (lenBitArr.length < 64) { 63 | lenBitArr.unshift(0) 64 | } 65 | 66 | for (let i = 0; i < 64; i ++) { 67 | result.push(lenBitArr[i]) 68 | } 69 | 70 | const p: number[] = [] 71 | 72 | for (var i = 0; i < result.length / 8; i ++) { 73 | const b = Number('0b' + result.slice(i * 8, i * 8 + 8).join('')) 74 | p.push(b) 75 | } 76 | return p 77 | } 78 | 79 | /* 80 | * The SHA256 hash function accepts a plaintext (`str`) and its first operation 81 | * is to pad it. RFC4634, section 4.1 describes how this is done. 82 | */ 83 | const strToPaddedBytes = (str: string): number[] => { 84 | // Convert the input string to a buffer 85 | const buf = Buffer.from(str, 'utf8') 86 | return bufToPaddedBytes(buf) 87 | 88 | } 89 | 90 | /* 91 | * The SHA256 hash function accepts a plaintext (`str`) and its first operation 92 | * is to pad it. RFC4634, section 4.1 describes how this is done. 93 | */ 94 | const strToSha256PaddedBitArr = (str: string): string => { 95 | // Convert the input string to a buffer 96 | const buf = Buffer.from(str, 'utf8') 97 | return bufToSha256PaddedBitArr(buf) 98 | } 99 | 100 | const msgToSha256PaddedBitArr = (msg: string): string => { 101 | const l = msg.length 102 | const s = l + 1 103 | 104 | let total_length = 512 105 | 106 | while (total_length < l + 1) { 107 | total_length += 512 108 | } 109 | 110 | if (((total_length - s) % 512) < 64) { 111 | total_length += 512 112 | } 113 | 114 | const buf = Buffer.alloc(total_length / 8) 115 | const msg_buf = Buffer.from(msg) 116 | for (let i = 0; i < msg_buf.length; i ++) { 117 | buf[i] = msg_buf[i] 118 | } 119 | 120 | return buffer2bitArray(buf).join('') 121 | } 122 | 123 | const bufToSha256PaddedBitArr = (buf: Buffer): string => { 124 | // Convert the buffer to bits 125 | const bits: number[] = buffer2bitArray(buf) 126 | 127 | let result: number[] = [] 128 | for (let i = 0; i < bits.length; i ++) { 129 | result.push(bits[i]) 130 | } 131 | 132 | result.push(1) 133 | 134 | const nBlocks = Math.floor((bits.length + 64) / 512) + 1 135 | 136 | while (result.length < nBlocks * 512 - 64) { 137 | result.push(0) 138 | } 139 | 140 | let lengthInBits = BigInt(bits.length).toString(2) 141 | while (lengthInBits.length < 64) { 142 | lengthInBits = '0' + lengthInBits 143 | } 144 | 145 | return result.join('') + lengthInBits 146 | } 147 | 148 | export { 149 | sqrt_mod_p, 150 | bigint_to_array, 151 | sgn0, 152 | buffer2bitArray, 153 | strToPaddedBytes, 154 | bufToPaddedBytes, 155 | strToSha256PaddedBitArr, 156 | bufToSha256PaddedBitArr, 157 | msgToSha256PaddedBitArr, 158 | } 159 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/PointAdd.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(120000) 2 | import { getPublicKey, Point } from '@noble/secp256k1'; 3 | const crypto = require('crypto') 4 | const ff = require('ffjavascript') 5 | const stringifyBigInts = ff.utils.stringifyBigInts 6 | import { 7 | callGenWitness as genWitness, 8 | callGetSignalByName as getSignalByName, 9 | } from 'circom-helper' 10 | 11 | import { bigint_to_array } from '../utils' 12 | 13 | const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F') 14 | 15 | const sk0 = BigInt('88549154299169935420064281163296845505587953610183896504176354567359434168161') 16 | const pk0 = Point.fromPrivateKey(sk0) 17 | const sk1 = BigInt('37706893564732085918706190942542566344879680306879183356840008504374628845468') 18 | const pk1 = Point.fromPrivateKey(sk1) 19 | 20 | describe('PointAdd', () => { 21 | const circuit = 'point_add_test' 22 | it('a + b where a != b', async () => { 23 | // From https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#appendix-J.8.1 24 | const expected_x_array = bigint_to_array(64, 4, BigInt('0xc1cae290e291aee617ebaef1be6d73861479c48b841eaba9b7b5852ddfeb1346')) 25 | const expected_y_array = bigint_to_array(64, 4, BigInt('0x64fa678e07ae116126f08b022a94af6de15985c996c3a91b64c406a960e51067')) 26 | 27 | const a: any[] = [ 28 | bigint_to_array(64, 4, BigInt('0x74519ef88b32b425a095e4ebcc84d81b64e9e2c2675340a720bb1a1857b99f1e')), 29 | bigint_to_array(64, 4, BigInt('0xc174fa322ab7c192e11748beed45b508e9fdb1ce046dee9c2cd3a2a86b410936')), 30 | ] 31 | const b: any[] = [ 32 | bigint_to_array(64, 4, BigInt('0x44548adb1b399263ded3510554d28b4bead34b8cf9a37b4bd0bd2ba4db87ae63')), 33 | bigint_to_array(64, 4, BigInt('0x96eb8e2faf05e368efe5957c6167001760233e6dd2487516b46ae725c4cce0c6')), 34 | ] 35 | 36 | const circuitInputs = stringifyBigInts({ a, b }) 37 | const witness = await genWitness(circuit, circuitInputs) 38 | for (let i = 0; i < 4; i ++) { 39 | const out0 = BigInt(await getSignalByName(circuit, witness, 'main.out[0][' + i.toString() + ']')) 40 | expect(out0).toEqual(expected_x_array[i]) 41 | const out1 = BigInt(await getSignalByName(circuit, witness, 'main.out[1][' + i.toString() + ']')) 42 | expect(out1).toEqual(expected_y_array[i]) 43 | } 44 | }) 45 | 46 | it('Q0 and Q1 where msg = "abc"', async () => { 47 | // From https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#appendix-J.8.1 48 | const expected_x_array = bigint_to_array(64, 4, BigInt('0x3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b')) 49 | const expected_y_array = bigint_to_array(64, 4, BigInt('0x7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6')) 50 | 51 | const a: any[] = [ 52 | bigint_to_array(64, 4, BigInt('0x07dd9432d426845fb19857d1b3a91722436604ccbbbadad8523b8fc38a5322d7')), 53 | bigint_to_array(64, 4, BigInt('0x604588ef5138cffe3277bbd590b8550bcbe0e523bbaf1bed4014a467122eb33f')), 54 | ] 55 | const b: any[] = [ 56 | bigint_to_array(64, 4, BigInt('0xe9ef9794d15d4e77dde751e06c182782046b8dac05f8491eb88764fc65321f78')), 57 | bigint_to_array(64, 4, BigInt('0xcb07ce53670d5314bf236ee2c871455c562dd76314aa41f012919fe8e7f717b3')), 58 | ] 59 | 60 | const circuitInputs = stringifyBigInts({ a, b }) 61 | const witness = await genWitness(circuit, circuitInputs) 62 | for (let i = 0; i < 4; i ++) { 63 | const out0 = BigInt(await getSignalByName(circuit, witness, 'main.out[0][' + i.toString() + ']')) 64 | expect(out0).toEqual(expected_x_array[i]) 65 | const out1 = BigInt(await getSignalByName(circuit, witness, 'main.out[1][' + i.toString() + ']')) 66 | expect(out1).toEqual(expected_y_array[i]) 67 | } 68 | }) 69 | 70 | it('a + b where a == b should not work', async () => { 71 | const expected_point = pk0.add(pk0) 72 | const expected_x_array = bigint_to_array(64, 4, expected_point.x) 73 | const expected_y_array = bigint_to_array(64, 4, expected_point.y) 74 | 75 | const a: any[] = [ 76 | bigint_to_array(64, 4, pk0.x), 77 | bigint_to_array(64, 4, pk0.y), 78 | ] 79 | const b: any[] = [ 80 | bigint_to_array(64, 4, pk0.x), 81 | bigint_to_array(64, 4, pk0.y), 82 | ] 83 | 84 | const circuitInputs = stringifyBigInts({ a, b }) 85 | const witness = await genWitness(circuit, circuitInputs) 86 | for (let i = 0; i < 4; i ++) { 87 | const out0 = BigInt(await getSignalByName(circuit, witness, 'main.out[0][' + i.toString() + ']')) 88 | expect(out0).not.toEqual(expected_x_array[i]) 89 | const out1 = BigInt(await getSignalByName(circuit, witness, 'main.out[1][' + i.toString() + ']')) 90 | expect(out1).not.toEqual(expected_y_array[i]) 91 | } 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/Sha256.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(360000) 2 | const crypto = require('crypto') 3 | const ff = require('ffjavascript') 4 | const stringifyBigInts = ff.utils.stringifyBigInts 5 | import { 6 | callGenWitness as genWitness, 7 | callGetSignalByName as getSignalByName, 8 | } from 'circom-helper' 9 | import { 10 | strToPaddedBytes, 11 | buffer2bitArray, 12 | strToSha256PaddedBitArr, 13 | bufToSha256PaddedBitArr, 14 | msgToSha256PaddedBitArr, 15 | } from '../utils' 16 | import { 17 | str_to_array, 18 | gen_msg_prime, 19 | } from '../generate_inputs' 20 | 21 | describe('Sha256', () => { 22 | it('checkZeroPad_test (valid)', async () => { 23 | const circuit = 'checkZeroPad_test' 24 | const circuitInputs = stringifyBigInts({ 25 | in: [0, 2, 0, 0, 5], 26 | start: 2, 27 | end: 4, 28 | }) 29 | const witness = await genWitness(circuit, circuitInputs) 30 | }) 31 | 32 | it('checkZeroPad_test (invalid)', async () => { 33 | const circuit = 'checkZeroPad_test' 34 | try { 35 | const circuitInputs = stringifyBigInts({ 36 | in: [0, 2, 1, 0, 5], 37 | start: 2, 38 | end: 4, 39 | }) 40 | const witness = await genWitness(circuit, circuitInputs) 41 | expect(false).toBeTruthy() 42 | } catch (e) { 43 | expect(true).toBeTruthy() 44 | } 45 | 46 | try { 47 | const circuitInputs = stringifyBigInts({ 48 | in: [0, 2, 0, 1, 0], 49 | start: 2, 50 | end: 4, 51 | }) 52 | const witness = await genWitness(circuit, circuitInputs) 53 | expect(false).toBeTruthy() 54 | } catch (e) { 55 | expect(true).toBeTruthy() 56 | } 57 | 58 | expect.assertions(2) 59 | }) 60 | 61 | it('startsWith_test (valid)', async () => { 62 | const circuit = 'startsWith_test' 63 | const circuitInputs = stringifyBigInts({ 64 | a: [1, 2, 3, 0, 0], 65 | b: [1, 2, 3, 4, 0], 66 | num_elements: 3, 67 | }) 68 | const witness = await genWitness(circuit, circuitInputs) 69 | }) 70 | 71 | it('startsWith_test (invalid)', async () => { 72 | const circuit = 'startsWith_test' 73 | const circuitInputs = stringifyBigInts({ 74 | a: [1, 2, 3, 0, 0], 75 | b: [1, 2, 0, 4, 5], 76 | num_elements: 3, 77 | }) 78 | try { 79 | const witness = await genWitness(circuit, circuitInputs) 80 | expect(false).toBeTruthy() 81 | } catch (e) { 82 | expect(true).toBeTruthy() 83 | } 84 | 85 | expect.assertions(1) 86 | }) 87 | 88 | it('verifyPaddedBits_test', async () => { 89 | const circuit = 'verifyPaddedBits_test' 90 | const msg = 'abc' 91 | const padded_bits = strToSha256PaddedBitArr(msg) 92 | const circuitInputs = stringifyBigInts({ 93 | padded_bits: padded_bits.split(''), 94 | msg: msgToSha256PaddedBitArr(msg).split(''), 95 | }) 96 | const witness = await genWitness(circuit, circuitInputs) 97 | }) 98 | 99 | it('Sha256Raw circuit', async () => { 100 | const circuit = 'sha256raw_test' 101 | const msg = 'abc' 102 | const padded_bits = strToSha256PaddedBitArr(msg) 103 | const circuitInputs = stringifyBigInts({ 104 | padded_bits: padded_bits.split(''), 105 | }) 106 | const witness = await genWitness(circuit, circuitInputs) 107 | let outBits = '' 108 | for (let i = 0; i < 256; i ++) { 109 | //const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 110 | const out = BigInt(witness[i + 1]) 111 | outBits += out 112 | } 113 | 114 | const hash = crypto.createHash("sha256") 115 | .update(Buffer.from(msg)) 116 | .digest('hex') 117 | 118 | expect(BigInt('0b' + outBits).toString(16)).toEqual(hash) 119 | }) 120 | 121 | it('Sha256Hash circuit', async () => { 122 | const circuit = 'sha256Hash_test' 123 | const msg = 'abc' 124 | const paddedIn = strToSha256PaddedBitArr(msg) 125 | const circuitInputs = stringifyBigInts({ 126 | padded_bits: paddedIn.split(''), 127 | msg: msgToSha256PaddedBitArr(msg).split(''), 128 | }) 129 | const witness = await genWitness(circuit, circuitInputs) 130 | let outBits = '' 131 | for (let i = 0; i < 256; i ++) { 132 | //const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 133 | const out = BigInt(witness[i + 1]) 134 | outBits += out 135 | } 136 | 137 | const hash = crypto.createHash("sha256") 138 | .update(Buffer.from(msg)) 139 | .digest('hex') 140 | 141 | expect(BigInt('0b' + outBits).toString(16)).toEqual(hash) 142 | }) 143 | }) 144 | -------------------------------------------------------------------------------- /circuits/circom/constants.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | function get_dst_prime() { 4 | var dst_prime[50] = [ 5 | 81, 85, 85, 88, 45, 86, 48, 49, 45, 67, 83, 48, 50, 45, 119, 105, 116, 6 | 104, 45, 115, 101, 99, 112, 50, 53, 54, 107, 49, 95, 88, 77, 68, 58, 7 | 83, 72, 65, 45, 50, 53, 54, 95, 83, 83, 87, 85, 95, 82, 79, 95, 49 8 | ]; 9 | return dst_prime; 10 | } 11 | 12 | function get_lib_str() { 13 | var lib_str[2] = [0, 96]; 14 | return lib_str; 15 | } 16 | 17 | function get_z_pad() { 18 | var z_pad[64]; 19 | for (var i = 0; i < 64; i ++) { 20 | z_pad[i] = 0; 21 | } 22 | return z_pad; 23 | } 24 | 25 | 26 | function get_secp256k1_p() { 27 | var p[4] = [ 28 | 18446744069414583343, 29 | 18446744073709551615, 30 | 18446744073709551615, 31 | 18446744073709551615 32 | ]; 33 | return p; 34 | } 35 | 36 | function get_Z() { 37 | // 115792089237316195423570985008687907853269984665640564039457584007908834671652 38 | var z[4] = [ 39 | 18446744069414583332, 40 | 18446744073709551615, 41 | 18446744073709551615, 42 | 18446744073709551615 43 | ]; 44 | return z; 45 | } 46 | 47 | function get_A() { 48 | // 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533 49 | // 28734576633528757162648956269730739219262246272443394170905244663053633733939 50 | var a[4] = [ 51 | 4635408806871057715, 52 | 16813014259472469597, 53 | 11568152433342665330, 54 | 4577682160469023452 55 | ]; 56 | return a; 57 | } 58 | 59 | function get_B() { 60 | var b[4] = [1771, 0, 0, 0]; 61 | return b; 62 | } 63 | 64 | function get_C1() { 65 | var c1[4] = [ 66 | 12250307269654431171, 67 | 7923238676646950141, 68 | 11532678464006552332, 69 | 848203876191778994 70 | ]; 71 | return c1; 72 | } 73 | 74 | 75 | function get_C2() { 76 | var c2[4] = [ 77 | 1676976732802240618, 78 | 15092790605762360413, 79 | 6707906935894382405, 80 | 5030930201920786804 81 | ]; 82 | return c2; 83 | 84 | } 85 | 86 | function get_k_1_0() { 87 | // 8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7 88 | var k[4] = [ 89 | 10248191149674768583, 90 | 4099276460824344803, 91 | 16397105843297379214, 92 | 10248191152060862008 93 | ]; 94 | return k; 95 | } 96 | 97 | function get_k_1_1() { 98 | // 7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581 99 | var k[4] = [ 100 | 16140637477814429057, 101 | 15390439281582816146, 102 | 13399077293683197125, 103 | 564028334007329237 104 | ]; 105 | return k; 106 | } 107 | 108 | function get_k_1_2() { 109 | // 534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262 110 | var k[4] = [ 111 | 5677861232072053346, 112 | 16451756383528566833, 113 | 16331199996347402988, 114 | 6002227985152881894 115 | ]; 116 | return k; 117 | } 118 | 119 | function get_k_1_3() { 120 | // 8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c 121 | var k[4] = [ 122 | 10248191149674768524, 123 | 4099276460824344803, 124 | 16397105843297379214, 125 | 10248191152060862008 126 | ]; 127 | return k; 128 | } 129 | 130 | function get_k_2_0() { 131 | // d35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b 132 | var k[4] = [ 133 | 11522098205669897371, 134 | 9713490981125900413, 135 | 11286949528964841693, 136 | 15228765018197889418 137 | ]; 138 | return k; 139 | } 140 | 141 | function get_k_2_1() { 142 | // edadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14 143 | var k[4] = [ 144 | 14207262949819313428, 145 | 491854862080688571, 146 | 17853591451159765588, 147 | 17126563718956833821 148 | ]; 149 | return k; 150 | } 151 | 152 | function get_k_3_0() { 153 | // 4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c 154 | var k[4] = [ 155 | 11614616637729727036, 156 | 3416063717353620669, 157 | 7515340178177965473, 158 | 5465701947765793071 159 | ]; 160 | return k; 161 | } 162 | 163 | function get_k_3_1() { 164 | // c75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3 165 | var k[4] = [ 166 | 16139934577133973923, 167 | 7240293169244854895, 168 | 12236461929419286229, 169 | 14365933273833241615 170 | ]; 171 | return k; 172 | } 173 | 174 | function get_k_3_2() { 175 | // 29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931 176 | var k[4] = [ 177 | 12062302652890802481, 178 | 8225878191764283416, 179 | 8165599998173701494, 180 | 3001113992576440947 181 | ]; 182 | return k; 183 | } 184 | 185 | function get_k_3_3() { 186 | // 2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84 187 | var k[4] = [ 188 | 9564978407794773380, 189 | 13664254869414482678, 190 | 11614616639002310276, 191 | 3416063717353620669 192 | ]; 193 | return k; 194 | } 195 | 196 | function get_k_4_0() { 197 | // fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b 198 | var k[4] = [ 199 | 18446744069414582587, 200 | 18446744073709551615, 201 | 18446744073709551615, 202 | 18446744073709551615 203 | ]; 204 | return k; 205 | } 206 | 207 | function get_k_4_1() { 208 | // 7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573 209 | var k[4] = [ 210 | 16119550551890077043, 211 | 10693728869668149624, 212 | 15414104513184973464, 213 | 8792806907174565023 214 | ]; 215 | return k; 216 | } 217 | 218 | function get_k_4_2() { 219 | // 6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f 220 | var k[4] = [ 221 | 12087522392169162607, 222 | 737782293121032857, 223 | 17557015139884872574, 224 | 7243101504725699116 225 | ]; 226 | return k; 227 | } 228 | -------------------------------------------------------------------------------- /circuits/ts/generate_inputs.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto' 2 | const assert = require('assert') 3 | import { 4 | dst_prime, 5 | z_pad, 6 | lib_str, 7 | p, 8 | Z, 9 | c1, 10 | c2, 11 | A, 12 | B, 13 | } from './constants' 14 | const ff = require('ffjavascript') 15 | import { sgn0, bigint_to_array } from './utils' 16 | import { iso_map } from './iso_map' 17 | import { Point } from '@noble/secp256k1'; 18 | 19 | const str_to_array = (msg: string): number[] => { 20 | return msg.split('').map((x) => Buffer.from(x)[0]) 21 | } 22 | 23 | const buf_to_array = (buf: Buffer): number[] => { 24 | const r: number[] = [] 25 | for (let i = 0; i < buf.length; i ++) { 26 | r.push(Number(buf[i])) 27 | } 28 | 29 | return r 30 | } 31 | 32 | const strxor = (a: number[], b: number[]): number[] => { 33 | const result: number[] = [] 34 | for (let i = 0; i < a.length; i ++) { 35 | result.push(a[i] ^ b[i]) 36 | } 37 | return result 38 | } 39 | 40 | const gen_msg_prime = (msg_array: number[]): any => { 41 | return z_pad.concat(msg_array).concat(lib_str).concat([0]).concat(dst_prime) 42 | } 43 | 44 | const gen_b0 = (msg_prime: number[]) => { 45 | const buff = Buffer.from(msg_prime) 46 | const hash = crypto.createHash("sha256").update(buff).digest() 47 | return buf_to_array(hash) 48 | } 49 | 50 | const gen_b1 = (b0: number[]) => { 51 | const buff = Buffer.from(b0.concat([1]).concat(dst_prime)) 52 | const hash = crypto.createHash("sha256").update(buff).digest() 53 | return buf_to_array(hash) 54 | } 55 | 56 | const gen_b2 = (b0: number[], b1: number[]) => { 57 | const buff = Buffer.from(strxor(b0, b1).concat([2]).concat(dst_prime)) 58 | const hash = crypto.createHash("sha256").update(buff).digest() 59 | return buf_to_array(hash) 60 | } 61 | 62 | const gen_b3 = (b0: number[], b2: number[]) => { 63 | const buff = Buffer.from(strxor(b0, b2).concat([3]).concat(dst_prime)) 64 | const hash = crypto.createHash("sha256").update(buff).digest() 65 | return buf_to_array(hash) 66 | } 67 | 68 | const expand_msg_xmd = (msg_array: number[]): any => { 69 | const msg_prime = gen_msg_prime(msg_array) 70 | const b0 = gen_b0(msg_prime) 71 | const b1 = gen_b1(b0) 72 | const b2 = gen_b2(b0, b1) 73 | const b3 = gen_b3(b0, b2) 74 | return b1.concat(b2).concat(b3) 75 | } 76 | 77 | const field = new ff.F1Field(p) 78 | 79 | const bytes_to_registers = (bytes: number[]) => { 80 | const blah = ff.utils.beBuff2int(Buffer.from(bytes)) % p 81 | return bigint_to_array(64, 4, blah) 82 | } 83 | 84 | const map_to_curve = (u: bigint) => { 85 | // Step 1 86 | const step1_tv1 = (Z * (u * u)) % p 87 | // Step 2 88 | const step2_tv2 = (step1_tv1 * step1_tv1) % p 89 | // Step 3 90 | const step3_tv1_plus_tv2 = (step1_tv1 + step2_tv2) % p 91 | // Step 4 92 | const step4_inv0_x1 = field.inv(step3_tv1_plus_tv2) 93 | // Step 6 94 | const step6_x1_plus_1 = step4_inv0_x1 + BigInt(1) 95 | // Step 8 check 96 | const step8_x1_mul_c1 = (step6_x1_plus_1 * c1) % p 97 | // Step 9 98 | const gx1 = (step8_x1_mul_c1 * step8_x1_mul_c1) % p 99 | // Step 10 100 | const step10_gx1 = (gx1 + A) % p 101 | // Step 11 102 | const step11_gx1_mul_x1 = (step10_gx1 * step8_x1_mul_c1) % p 103 | // Step 12 104 | const step12_gx1 = (step11_gx1_mul_x1 + B) % p 105 | // Step 13 106 | const step13_x2 = (step1_tv1 * step8_x1_mul_c1) % p 107 | // Step 14 108 | const step14_tv2 = (step1_tv1 * step2_tv2) % p 109 | // Step 15 110 | const step15_gx2 = (step12_gx1 * step14_tv2) % p 111 | // Step 16-18 112 | let gx1_sqrt = field.sqrt(step12_gx1) 113 | let gx2_sqrt = field.sqrt(step15_gx2) 114 | let step16_expected_x 115 | let step16_expected_y2 116 | if (gx1_sqrt == null) { 117 | gx1_sqrt = BigInt(1) 118 | step16_expected_x = step13_x2 119 | step16_expected_y2 = step15_gx2 120 | } 121 | 122 | if (gx2_sqrt == null) { 123 | gx2_sqrt = BigInt(1) 124 | step16_expected_x = step8_x1_mul_c1 125 | step16_expected_y2 = step12_gx1 126 | } 127 | // Step 19 128 | const step19_sqrt_y2 = field.sqrt(step16_expected_y2) 129 | const sgn0_u = sgn0(u) 130 | const sgn0_y = sgn0(step19_sqrt_y2) 131 | 132 | const step20_e3 = sgn0_u === sgn0_y ? BigInt(1) : BigInt(0) 133 | let expected_y 134 | if (step20_e3 === BigInt(1)) { 135 | expected_y = step19_sqrt_y2 136 | } else { 137 | expected_y = p - step19_sqrt_y2 138 | } 139 | const expected_y_array = bigint_to_array(64, 4, expected_y) 140 | 141 | const x_out = step16_expected_x 142 | const y_out = expected_y 143 | 144 | const mapped = iso_map(x_out, y_out, p) 145 | 146 | return { 147 | x: mapped.x, 148 | y: mapped.y, 149 | gx1_sqrt, 150 | gx2_sqrt, 151 | y_pos: step19_sqrt_y2, 152 | } 153 | } 154 | 155 | const generate_inputs = (msg: string): any => { 156 | const msg_array = str_to_array(msg) 157 | return generate_inputs_from_array(msg_array) 158 | } 159 | 160 | const generate_inputs_from_array = (msg: number[]): any => { 161 | const uniform_bytes = expand_msg_xmd(msg) 162 | 163 | const u0_bytes = uniform_bytes.slice(0, 48) 164 | const u1_bytes = uniform_bytes.slice(48) 165 | 166 | const u0 = ff.utils.beBuff2int(Buffer.from(u0_bytes)) % p 167 | const u1 = ff.utils.beBuff2int(Buffer.from(u1_bytes)) % p 168 | 169 | const q0 = map_to_curve(u0) 170 | const q1 = map_to_curve(u1) 171 | 172 | return { 173 | msg: msg, 174 | q0_gx1_sqrt: bigint_to_array(64, 4, q0.gx1_sqrt), 175 | q0_gx2_sqrt: bigint_to_array(64, 4, q0.gx2_sqrt), 176 | q0_y_pos: bigint_to_array(64, 4, q0.y_pos), 177 | q1_gx1_sqrt: bigint_to_array(64, 4, q1.gx1_sqrt), 178 | q1_gx2_sqrt: bigint_to_array(64, 4, q1.gx2_sqrt), 179 | q1_y_pos: bigint_to_array(64, 4, q1.y_pos), 180 | q0_x_mapped: bigint_to_array(64, 4, q0.x), 181 | q0_y_mapped: bigint_to_array(64, 4, q0.y), 182 | q1_x_mapped: bigint_to_array(64, 4, q1.x), 183 | q1_y_mapped: bigint_to_array(64, 4, q1.y), 184 | } 185 | 186 | //const q0_mapped_pt = new Point(q0_mapped.x, q0_mapped.y) 187 | //const q1_mapped_pt = new Point(q1_mapped.x, q1_mapped.y) 188 | 189 | //const point = q0_mapped_pt.add(q1_mapped_pt) 190 | //return point 191 | } 192 | 193 | export { 194 | gen_msg_prime, 195 | gen_b0, 196 | gen_b1, 197 | gen_b2, 198 | gen_b3, 199 | generate_inputs, 200 | generate_inputs_from_array, 201 | strxor, 202 | str_to_array, 203 | expand_msg_xmd, 204 | bytes_to_registers, 205 | sgn0, 206 | map_to_curve, 207 | } 208 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/IsoMap.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(120000) 2 | const crypto = require('crypto') 3 | const ff = require('ffjavascript') 4 | const stringifyBigInts = ff.utils.stringifyBigInts 5 | import { 6 | callGenWitness as genWitness, 7 | callGetSignalByName as getSignalByName, 8 | } from 'circom-helper' 9 | 10 | import { bigint_to_array } from '../utils' 11 | import { 12 | iso_map, 13 | compute_x_num, 14 | compute_x_den, 15 | compute_y_num, 16 | compute_y_den, 17 | } from '../iso_map' 18 | import { sqrt_mod_p } from '../utils' 19 | 20 | const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F') 21 | const test_cases: any[] = [ 22 | { 23 | x_out: BigInt('109847960678874069395357325452026395105140293515447328048593769534097129836981'), 24 | y_out: BigInt('26747645647114029413939469563206094538114691466623191143901133893381837269477'), 25 | expected_x: BigInt('0x07dd9432d426845fb19857d1b3a91722436604ccbbbadad8523b8fc38a5322d7'), 26 | expected_y: BigInt('0x604588ef5138cffe3277bbd590b8550bcbe0e523bbaf1bed4014a467122eb33f'), 27 | }, 28 | { 29 | x_out: BigInt('95747712435547668378765549993819480138391130494145532428970724587426680705447'), 30 | y_out: BigInt('24586358576631698449083237381902966339991415142283656103974565296768092405604'), 31 | expected_x: BigInt('0xe9ef9794d15d4e77dde751e06c182782046b8dac05f8491eb88764fc65321f78'), 32 | expected_y: BigInt('0xcb07ce53670d5314bf236ee2c871455c562dd76314aa41f012919fe8e7f717b3'), 33 | } 34 | ] 35 | 36 | describe('IsoMap', () => { 37 | it('XNum circuit', async () => { 38 | const circuit = 'xnum_test' 39 | for (const test_case of test_cases) { 40 | const x = BigInt(test_case.x_out) 41 | const x_2 = (x * x) % p 42 | const x_3 = (x * x * x) % p 43 | const expected_x_num = compute_x_num(x, p) 44 | const expected_x_num_array = bigint_to_array(64, 4, expected_x_num) 45 | const circuitInputs = stringifyBigInts({ 46 | x: bigint_to_array(64, 4, x), 47 | x_2: bigint_to_array(64, 4, x_2), 48 | x_3: bigint_to_array(64, 4, x_3), 49 | }) 50 | const witness = await genWitness(circuit, circuitInputs) 51 | for (let i = 0; i < 4; i ++) { 52 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 53 | expect(out).toEqual(expected_x_num_array[i]) 54 | } 55 | } 56 | }) 57 | 58 | it('XDen circuit', async () => { 59 | const circuit = 'xden_test' 60 | for (const test_case of test_cases) { 61 | const x = BigInt(test_case.x_out) 62 | const x_2 = (x * x) % p 63 | const expected_x_den = compute_x_den(x, p) 64 | const expected_x_den_array = bigint_to_array(64, 4, expected_x_den) 65 | const circuitInputs = stringifyBigInts({ 66 | x: bigint_to_array(64, 4, x), 67 | x_2: bigint_to_array(64, 4, x_2), 68 | }) 69 | const witness = await genWitness(circuit, circuitInputs) 70 | for (let i = 0; i < 4; i ++) { 71 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 72 | expect(out).toEqual(expected_x_den_array[i]) 73 | } 74 | } 75 | }) 76 | 77 | it('YNum circuit', async () => { 78 | const circuit = 'ynum_test' 79 | for (const test_case of test_cases) { 80 | const x = BigInt(test_case.x_out) 81 | const x_2 = (x * x) % p 82 | const x_3 = (x * x * x) % p 83 | const expected_y_num = compute_y_num(x, p) 84 | const expected_y_num_array = bigint_to_array(64, 4, expected_y_num) 85 | const circuitInputs = stringifyBigInts({ 86 | x: bigint_to_array(64, 4, x), 87 | x_2: bigint_to_array(64, 4, x_2), 88 | x_3: bigint_to_array(64, 4, x_3), 89 | }) 90 | const witness = await genWitness(circuit, circuitInputs) 91 | for (let i = 0; i < 4; i ++) { 92 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 93 | expect(out).toEqual(expected_y_num_array[i]) 94 | } 95 | } 96 | }) 97 | 98 | it('YDen circuit', async () => { 99 | const circuit = 'yden_test' 100 | for (const test_case of test_cases) { 101 | const x = BigInt(test_case.x_out) 102 | const x_2 = (x * x) % p 103 | const x_3 = (x * x * x) % p 104 | const expected_y_den = compute_y_den(x, p) 105 | const expected_y_den_array = bigint_to_array(64, 4, expected_y_den) 106 | const circuitInputs = stringifyBigInts({ 107 | x: bigint_to_array(64, 4, x), 108 | x_2: bigint_to_array(64, 4, x_2), 109 | x_3: bigint_to_array(64, 4, x_3), 110 | }) 111 | const witness = await genWitness(circuit, circuitInputs) 112 | for (let i = 0; i < 4; i ++) { 113 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 114 | expect(out).toEqual(expected_y_den_array[i]) 115 | } 116 | } 117 | }) 118 | 119 | it('iso_map()', async () => { 120 | for (const test_case of test_cases) { 121 | const { x, y } = iso_map(test_case.x_out, test_case.y_out, p) 122 | expect(x).toEqual(test_case.expected_x) 123 | expect(y).toEqual(test_case.expected_y) 124 | } 125 | }) 126 | 127 | it('IsoMap circuit', async () => { 128 | const circuit = 'iso_map_test' 129 | for (const test_case of test_cases) { 130 | const x = BigInt(test_case.x_out) 131 | const y = BigInt(test_case.y_out) 132 | const mapped = iso_map(test_case.x_out, test_case.y_out, p) 133 | const circuitInputs = stringifyBigInts({ 134 | x: bigint_to_array(64, 4, x), 135 | y: bigint_to_array(64, 4, y), 136 | x_mapped: bigint_to_array(64, 4, mapped.x), 137 | y_mapped: bigint_to_array(64, 4, mapped.y), 138 | }) 139 | const witness = await genWitness(circuit, circuitInputs) 140 | } 141 | }) 142 | 143 | it('IsoMap circuit', async () => { 144 | const circuit = 'iso_map_test' 145 | const u1 = BigInt('52561607698273230922893701749432844571766812627334290650941810523569022097944') 146 | const x = BigInt('64194511559750490853478619256035491267414370428644296524684110489113862940138') 147 | const y = BigInt('102352885279598387049455667780191904281516840171708369876930321669597547584074') 148 | const q1 = { 149 | x_mapped: BigInt('112451734755426834716648727358299441967457432883933738090080934024500761239544'), 150 | y_mapped: BigInt('81063536041005174460194413543313074086488650157769284612643288531187436138327'), 151 | } 152 | 153 | const m = iso_map(x, y, p) 154 | const circuitInputs = stringifyBigInts({ 155 | x: bigint_to_array(64, 4, x), 156 | y: bigint_to_array(64, 4, y), 157 | x_mapped: bigint_to_array(64, 4, q1.x_mapped), 158 | y_mapped: bigint_to_array(64, 4, q1.y_mapped), 159 | }) 160 | const witness = await genWitness(circuit, circuitInputs) 161 | }) 162 | }) 163 | -------------------------------------------------------------------------------- /circuits/circom/Sha256.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "./calculateTotal.circom"; 3 | include "./selector.circom"; 4 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/sha256/sha256.circom"; 5 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/bitify.circom"; 6 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/mux1.circom"; 7 | 8 | // Given the number of bits of a message to hash, compute the number of padded 9 | // bits to supply to the Sha256Raw circuit. 10 | function calc_padded_bits_length(msg_bits_length) { 11 | var s = msg_bits_length + 1; 12 | var total_bits_length = 512; 13 | 14 | while (total_bits_length < s) { 15 | total_bits_length += 512; 16 | } 17 | 18 | if ((total_bits_length - s) % 512 < 64) { 19 | total_bits_length += 512; 20 | } 21 | return total_bits_length; 22 | } 23 | 24 | // Checks that the first num_elements of signal array a are the same as that of 25 | // signal array b. 26 | template StartsWith(length) { 27 | // Assumes that all inputs are 0 or 1 28 | signal input a[length]; 29 | signal input b[length]; 30 | signal input num_elements; 31 | 32 | component num_elements_lt_length = LessThan(252); 33 | num_elements_lt_length.in[0] <== num_elements; 34 | num_elements_lt_length.in[1] <== length; 35 | num_elements_lt_length.out === 1; 36 | 37 | /* 38 | Example: 39 | num_elements = 3 40 | a = [1, 2, 3, 0, 0] 41 | b = [1, 2, 3, 4, 0] 42 | 43 | s = [1, 1, 1, 0, 0] <- [i < num_elements for i in length] 44 | c = [1, 1, 1, 0, 1] <- [a[i] == b[i] for i in length] 45 | d = [1, 1, 1, 0, 0] <- [s[i] == 1 ? c[i] : 0 for i in length] 46 | 47 | 3 === num_elements <- the sum of the first num_elements elements in c 48 | */ 49 | 50 | component s[length]; 51 | component c[length]; 52 | component d[length]; 53 | signal total[length + 1]; 54 | total[0] <== 0; 55 | for (var i = 0; i < length; i ++) { 56 | s[i] = LessThan(252); 57 | s[i].in[0] <== i; 58 | s[i].in[1] <== num_elements; 59 | 60 | c[i] = IsEqual(); 61 | c[i].in[0] <== a[i]; 62 | c[i].in[1] <== b[i]; 63 | 64 | d[i] = Mux1(); 65 | d[i].c[0] <== 0; 66 | d[i].c[1] <== c[i].out; 67 | d[i].s <== s[i].out; 68 | 69 | total[i + 1] <== total[i] + d[i].out; 70 | } 71 | total[length] === num_elements; 72 | } 73 | 74 | template CheckZeroPad(length) { 75 | // Assumes that all inputs are 0 or 1 76 | signal input in[length]; 77 | signal input start; 78 | signal input end; 79 | 80 | // Check that end > start 81 | component start_lt_end = LessThan(252); 82 | start_lt_end.in[0] <== start; 83 | start_lt_end.in[1] <== end; 84 | start_lt_end.out === 1; 85 | 86 | // start: 2, end: 4 87 | // in: [0, 2, 0, 0, 5] 88 | // a: [0, 0, 1, 1, 1] <- [gte(i, start) for i in length] 89 | // b: [1, 1, 1, 1, 0] <- [lt(i, end) for i in length] 90 | // c: [0, 0, 1, 1, 0] <- [a[i] + b[i] == 2 for i in length] 91 | // d: [1, 0, 1, 1, 0] <- [in[i] == 0 for i in length] 92 | // e: [0, 0, 1, 1, 0] <- [c[i] * d[i] for i in length] 93 | // sum(d) === end - start 94 | 95 | component gte[length]; 96 | component lt[length]; 97 | component eq_c[length]; 98 | component eq_d[length]; 99 | component total = CalculateTotal(length); 100 | for (var i = 0; i < length; i ++) { 101 | gte[i] = GreaterEqThan(252); 102 | gte[i].in[0] <== i; 103 | gte[i].in[1] <== start; 104 | 105 | lt[i] = LessThan(252); 106 | lt[i].in[0] <== i; 107 | lt[i].in[1] <== end; 108 | 109 | eq_c[i] = IsEqual(); 110 | eq_c[i].in[0] <== gte[i].out + lt[i].out; 111 | eq_c[i].in[1] <== 2; 112 | 113 | eq_d[i] = IsEqual(); 114 | eq_d[i].in[0] <== 0; 115 | eq_d[i].in[1] <== in[i]; 116 | 117 | total.in[i] <== eq_c[i].out * eq_d[i].out; 118 | } 119 | total.out === end - start; 120 | } 121 | 122 | template VerifyPaddedBits(padded_length) { 123 | var num_blocks = padded_length / 512; 124 | assert(num_blocks * 512 == padded_length); 125 | 126 | signal input padded_bits[padded_length]; 127 | signal input msg[padded_length]; 128 | 129 | // Step 1: Convert the last 64 bits to a field element 130 | component step1_b2n = Bits2Num(64); 131 | for (var i = 0; i < 64; i ++) { 132 | step1_b2n.in[63 - i] <== padded_bits[padded_length - 64 + i]; 133 | } 134 | 135 | signal num_msg_bits; 136 | num_msg_bits <== step1_b2n.out; 137 | 138 | // Step 2: Check that this field element is less than padded_length 139 | component step2_lt = LessThan(252); 140 | step2_lt.in[0] <== num_msg_bits; 141 | step2_lt.in[1] <== padded_length; 142 | step2_lt.out === 1; 143 | 144 | // Step 3: Select the bit at the position denoted by said field element 145 | component step3_selector = Selector(padded_length - 64); 146 | step3_selector.index <== num_msg_bits; 147 | for (var i = 0; i < padded_length - 64; i ++) { 148 | step3_selector.in[i] <== padded_bits[i]; 149 | } 150 | 151 | // Step 4: Ensure that the bit at this position is 1 152 | step3_selector.out === 1; 153 | 154 | // Step 5: Check that num_msg_bits bits of padded_bits starts with the 155 | // num_msg_bits of msg 156 | // Note that this takes up the bulk of constraints of this circuit 157 | component step5_sw = StartsWith(padded_length - 64); 158 | step5_sw.num_elements <== num_msg_bits; 159 | for (var i = 0; i < padded_length - 64; i ++) { 160 | step5_sw.a[i] <== msg[i]; 161 | step5_sw.b[i] <== padded_bits[i]; 162 | } 163 | 164 | // Step 6: Check that the bits between (num_msg_bits + 1) and 165 | // (padded_length - 64) are 0 166 | component step6_czp = CheckZeroPad(padded_length - 64); 167 | step6_czp.start <== num_msg_bits + 1; 168 | step6_czp.end <== padded_length - 64; 169 | for (var i = 0; i < padded_length - 64; i ++) { 170 | step6_czp.in[i] <== padded_bits[i]; 171 | } 172 | 173 | // Step 7: Check that each value in padded_bits and msg is either 0 or 1 174 | component step7_is_bin_a[padded_length]; 175 | component step7_is_bin_b[padded_length]; 176 | for (var i = 0; i < padded_length; i ++) { 177 | step7_is_bin_a[i] = IsBinary(); 178 | step7_is_bin_a[i].in <== padded_bits[i]; 179 | 180 | step7_is_bin_b[i] = IsBinary(); 181 | step7_is_bin_b[i].in <== msg[i]; 182 | } 183 | } 184 | 185 | // Ensures that the input is either 0 or 1 186 | template IsBinary() { 187 | signal input in; 188 | in * (in - 1) === 0; 189 | } 190 | 191 | // Outputs the SHA256 hash of padded_bits. 192 | // msg must be the message to hash, in bits, right-padded with zeros. 193 | template Sha256Hash(padded_length) { 194 | var num_blocks = padded_length / 512; 195 | assert(num_blocks * 512 == padded_length); 196 | 197 | signal input padded_bits[padded_length]; 198 | signal input msg[padded_length]; 199 | signal output out[256]; 200 | 201 | component vpb = VerifyPaddedBits(padded_length); 202 | for (var i = 0; i < padded_length; i ++) { 203 | vpb.padded_bits[i] <== padded_bits[i]; 204 | vpb.msg[i] <== msg[i]; 205 | } 206 | 207 | component sha256Raw = Sha256Raw(padded_length); 208 | for (var i = 0; i < padded_length; i ++) { 209 | sha256Raw.padded_bits[i] <== padded_bits[i]; 210 | } 211 | 212 | for (var i = 0; i < 256; i ++) { 213 | out[i] <== sha256Raw.out[i]; 214 | } 215 | } 216 | 217 | // Output the SHA256 hash of the padded_bits. i.e. the input message is already 218 | // padded. 219 | template Sha256Raw(num_bits) { 220 | // num_bits is the number of padded bits. It must be a multiple of 512. 221 | var nBlocks = num_bits \ 512; 222 | assert(nBlocks * 512 == num_bits); 223 | signal input padded_bits[num_bits]; 224 | signal output out[256]; 225 | 226 | component sha256compression[nBlocks]; 227 | component h[8]; 228 | 229 | for (var i = 0; i < 8; i ++) { 230 | h[i] = H(i); 231 | } 232 | 233 | for (var i = 0; i < nBlocks; i ++) { 234 | sha256compression[i] = Sha256compression(); 235 | 236 | if (i == 0) { 237 | for (var j = 0; j < 32; j ++) { 238 | for (var k = 0; k < 8; k ++) { 239 | sha256compression[i].hin[k * 32 + j] <== h[k].out[j]; 240 | } 241 | } 242 | } else { 243 | for (var j = 0; j < 32; j ++) { 244 | for (var k = 0; k < 8; k ++) { 245 | sha256compression[i].hin[32 * k + j] <== sha256compression[i-1].out[32 * k + 31 - j]; 246 | } 247 | } 248 | } 249 | 250 | for (var j = 0; j < 512; j ++) { 251 | sha256compression[i].inp[j] <== padded_bits[i * 512 + j]; 252 | } 253 | } 254 | 255 | for (var i = 0; i < 256; i++) { 256 | out[i] <== sha256compression[nBlocks - 1].out[i]; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /circuits/circom/iso_map.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "./constants.circom"; 4 | include "./arith.circom"; 5 | include "../node_modules/circom-ecdsa/circuits/bigint.circom"; 6 | 7 | template IsoMap() { 8 | signal input x[4]; 9 | signal input y[4]; 10 | signal input x_mapped[4]; 11 | signal input y_mapped[4]; 12 | 13 | // Step 1: calculate x^2 14 | component x_2 = Square(); 15 | for (var i = 0; i < 4; i ++) { 16 | x_2.in[i] <== x[i]; 17 | } 18 | 19 | // Step 2: calculate x^3 20 | component x_3 = Multiply(); 21 | for (var i = 0; i < 4; i ++) { 22 | x_3.a[i] <== x_2.out[i]; 23 | x_3.b[i] <== x[i]; 24 | } 25 | 26 | // Step 3: calculate x_num 27 | component x_num = XNum(); 28 | for (var i = 0; i < 4; i ++) { 29 | x_num.x[i] <== x[i]; 30 | x_num.x_2[i] <== x_2.out[i]; 31 | x_num.x_3[i] <== x_3.out[i]; 32 | } 33 | 34 | // Step 4: calculate x_den 35 | component x_den = XDen(); 36 | for (var i = 0; i < 4; i ++) { 37 | x_den.x[i] <== x[i]; 38 | x_den.x_2[i] <== x_2.out[i]; 39 | } 40 | 41 | // Step 5: calculate y_num 42 | component y_num = YNum(); 43 | for (var i = 0; i < 4; i ++) { 44 | y_num.x[i] <== x[i]; 45 | y_num.x_2[i] <== x_2.out[i]; 46 | y_num.x_3[i] <== x_3.out[i]; 47 | } 48 | 49 | // Step 6: calculate y_den 50 | component y_den = YDen(); 51 | for (var i = 0; i < 4; i ++) { 52 | y_den.x[i] <== x[i]; 53 | y_den.x_2[i] <== x_2.out[i]; 54 | y_den.x_3[i] <== x_3.out[i]; 55 | } 56 | 57 | // Step 7: x_mapped * x_den === x_num 58 | component x_check = Multiply(); 59 | for (var i = 0; i < 4; i ++) { 60 | x_check.a[i] <== x_mapped[i]; 61 | x_check.b[i] <== x_den.out[i]; 62 | } 63 | 64 | // Step 8: y_mapped = y' * y_num / y_den 65 | // y_mapped * y_den === y' * y_num 66 | component y_check = Multiply(); 67 | for (var i = 0; i < 4; i ++) { 68 | y_check.a[i] <== y_mapped[i]; 69 | y_check.b[i] <== y_den.out[i]; 70 | } 71 | 72 | component y_check2 = Multiply(); 73 | for (var i = 0; i < 4; i ++) { 74 | y_check2.a[i] <== y[i]; 75 | y_check2.b[i] <== y_num.out[i]; 76 | } 77 | 78 | // Ensure that the provided x_mapped and y_mapped values are correct 79 | for (var i = 0; i < 4; i ++) { 80 | x_check.out[i] === x_num.out[i]; 81 | y_check.out[i] === y_check2.out[i]; 82 | } 83 | } 84 | 85 | template XNum() { 86 | signal input x[4]; 87 | signal input x_2[4]; // Assumes that this value is correct 88 | signal input x_3[4]; // Assumes that this value is correct 89 | signal output out[4]; 90 | /* 91 | k_(1,3) * x'^3 + 92 | k_(1,2) * x'^2 + 93 | k_(1,1) * x' + 94 | k_(1,0) 95 | */ 96 | 97 | var k_1_3[4] = get_k_1_3(); 98 | var k_1_2[4] = get_k_1_2(); 99 | var k_1_1[4] = get_k_1_1(); 100 | var k_1_0[4] = get_k_1_0(); 101 | 102 | // Step 1: a = k_(1,3) * x'^3 103 | component step1_a = Multiply(); 104 | for (var i = 0; i < 4; i ++) { 105 | step1_a.a[i] <== k_1_3[i]; 106 | step1_a.b[i] <== x_3[i]; 107 | } 108 | 109 | // Step 2: b = k_(1,2) * x'^2 + 110 | component step2_b = Multiply(); 111 | for (var i = 0; i < 4; i ++) { 112 | step2_b.a[i] <== k_1_2[i]; 113 | step2_b.b[i] <== x_2[i]; 114 | } 115 | 116 | // Step 3: c = k_(1,1) * x' + 117 | component step3_c = Multiply(); 118 | for (var i = 0; i < 4; i ++) { 119 | step3_c.a[i] <== k_1_1[i]; 120 | step3_c.b[i] <== x[i]; 121 | } 122 | 123 | // Step 4: a + b 124 | component step4_a_plus_b = Add(); 125 | for (var i = 0; i < 4; i ++) { 126 | step4_a_plus_b.a[i] <== step1_a.out[i]; 127 | step4_a_plus_b.b[i] <== step2_b.out[i]; 128 | } 129 | 130 | // Step 5: a + b + c 131 | component step5_a_plus_b_plus_c = Add(); 132 | for (var i = 0; i < 4; i ++) { 133 | step5_a_plus_b_plus_c.a[i] <== step4_a_plus_b.out[i]; 134 | step5_a_plus_b_plus_c.b[i] <== step3_c.out[i]; 135 | } 136 | 137 | // Step 6: a + b + c + k_1_0 138 | component step6_a_plus_b_plus_c_plus_k_1_0 = Add(); 139 | for (var i = 0; i < 4; i ++) { 140 | step6_a_plus_b_plus_c_plus_k_1_0.a[i] <== step5_a_plus_b_plus_c.out[i]; 141 | step6_a_plus_b_plus_c_plus_k_1_0.b[i] <== k_1_0[i]; 142 | } 143 | 144 | for (var i = 0; i < 4; i ++) { 145 | out[i] <== step6_a_plus_b_plus_c_plus_k_1_0.out[i]; 146 | } 147 | } 148 | 149 | template XDen() { 150 | signal input x[4]; 151 | signal input x_2[4]; // Assumes that this value is correct 152 | signal output out[4]; 153 | 154 | var k_2_0[4] = get_k_2_0(); 155 | var k_2_1[4] = get_k_2_1(); 156 | /* 157 | x'^2 + 158 | k_(2,1) * x' + 159 | k_(2,0) 160 | */ 161 | 162 | // Step 1: a = x_2 + k_2_0 163 | component step1_a = Add(); 164 | for (var i = 0; i < 4; i ++) { 165 | step1_a.a[i] <== x_2[i]; 166 | step1_a.b[i] <== k_2_0[i]; 167 | } 168 | 169 | // Step 2: b = x * k_2_1 170 | component step2_b = Multiply(); 171 | for (var i = 0; i < 4; i ++) { 172 | step2_b.a[i] <== k_2_1[i]; 173 | step2_b.b[i] <== x[i]; 174 | } 175 | 176 | // Step 3: c = a + b 177 | component step3_c = Add(); 178 | for (var i = 0; i < 4; i ++) { 179 | step3_c.a[i] <== step1_a.out[i]; 180 | step3_c.b[i] <== step2_b.out[i]; 181 | } 182 | 183 | for (var i = 0; i < 4; i ++) { 184 | out[i] <== step3_c.out[i]; 185 | } 186 | } 187 | 188 | template YNum() { 189 | signal input x[4]; 190 | signal input x_2[4]; // Assumes that this value is correct 191 | signal input x_3[4]; // Assumes that this value is correct 192 | signal output out[4]; 193 | 194 | var k_3_3[4] = get_k_3_3(); 195 | var k_3_2[4] = get_k_3_2(); 196 | var k_3_1[4] = get_k_3_1(); 197 | var k_3_0[4] = get_k_3_0(); 198 | /* 199 | k_(3,3) * x'^3 + 200 | k_(3,2) * x'^2 + 201 | k_(3,1) * x' + 202 | k_(3,0) 203 | */ 204 | 205 | // Step 1: a = k_3_3 * x_3 206 | component step1_a = Multiply(); 207 | for (var i = 0; i < 4; i ++) { 208 | step1_a.a[i] <== k_3_3[i]; 209 | step1_a.b[i] <== x_3[i]; 210 | } 211 | 212 | // Step 2: b = k_3_2 * x_2 213 | component step2_b = Multiply(); 214 | for (var i = 0; i < 4; i ++) { 215 | step2_b.a[i] <== k_3_2[i]; 216 | step2_b.b[i] <== x_2[i]; 217 | } 218 | 219 | // Step 3: c = k_3_1 * x 220 | component step3_c = Multiply(); 221 | for (var i = 0; i < 4; i ++) { 222 | step3_c.a[i] <== k_3_1[i]; 223 | step3_c.b[i] <== x[i]; 224 | } 225 | 226 | // Step 4: a + b 227 | component step4_a_plus_b = Add(); 228 | for (var i = 0; i < 4; i ++) { 229 | step4_a_plus_b.a[i] <== step1_a.out[i]; 230 | step4_a_plus_b.b[i] <== step2_b.out[i]; 231 | } 232 | 233 | // Step 5: a + b + c 234 | component step5_a_plus_b_plus_c = Add(); 235 | for (var i = 0; i < 4; i ++) { 236 | step5_a_plus_b_plus_c.a[i] <== step4_a_plus_b.out[i]; 237 | step5_a_plus_b_plus_c.b[i] <== step3_c.out[i]; 238 | } 239 | // Step 6: a + b + c + k_3_0 240 | component step6_a_plus_b_plus_c_plus_k_3_0 = Add(); 241 | for (var i = 0; i < 4; i ++) { 242 | step6_a_plus_b_plus_c_plus_k_3_0.a[i] <== step5_a_plus_b_plus_c.out[i]; 243 | step6_a_plus_b_plus_c_plus_k_3_0.b[i] <== k_3_0[i]; 244 | } 245 | 246 | for (var i = 0; i < 4; i ++) { 247 | out[i] <== step6_a_plus_b_plus_c_plus_k_3_0.out[i]; 248 | } 249 | } 250 | 251 | template YDen() { 252 | signal input x[4]; 253 | signal input x_2[4]; // Assumes that this value is correct 254 | signal input x_3[4]; // Assumes that this value is correct 255 | signal output out[4]; 256 | 257 | var k_4_0[4] = get_k_4_0(); 258 | var k_4_1[4] = get_k_4_1(); 259 | var k_4_2[4] = get_k_4_2(); 260 | 261 | /* 262 | x'^3 + 263 | k_(4,2) * x'^2 + 264 | k_(4,1) * x' + 265 | k_(4,0) 266 | */ 267 | 268 | // Step 1: a = x_3 + k_4_0 269 | component step1_a = Add(); 270 | for (var i = 0; i < 4; i ++) { 271 | step1_a.a[i] <== x_3[i]; 272 | step1_a.b[i] <== k_4_0[i]; 273 | } 274 | 275 | // Step 2: b = k_4_2 * x_2 276 | component step2_b = Multiply(); 277 | for (var i = 0; i < 4; i ++) { 278 | step2_b.a[i] <== k_4_2[i]; 279 | step2_b.b[i] <== x_2[i]; 280 | } 281 | 282 | // Step 3: c = k_4_1 * x 283 | component step3_c = Multiply(); 284 | for (var i = 0; i < 4; i ++) { 285 | step3_c.a[i] <== k_4_1[i]; 286 | step3_c.b[i] <== x[i]; 287 | } 288 | 289 | // Step 4: a + b 290 | component step4_a_plus_b = Add(); 291 | for (var i = 0; i < 4; i ++) { 292 | step4_a_plus_b.a[i] <== step1_a.out[i]; 293 | step4_a_plus_b.b[i] <== step2_b.out[i]; 294 | } 295 | 296 | // Step 5: a + b + c 297 | component step5_a_plus_b_plus_c = Add(); 298 | for (var i = 0; i < 4; i ++) { 299 | step5_a_plus_b_plus_c.a[i] <== step4_a_plus_b.out[i]; 300 | step5_a_plus_b_plus_c.b[i] <== step3_c.out[i]; 301 | } 302 | 303 | for (var i = 0; i < 4; i ++) { 304 | out[i] <== step5_a_plus_b_plus_c.out[i]; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /circuits/circom/map_to_curve.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | 3 | include "./constants.circom"; 4 | include "./arith.circom"; 5 | include "./iso_map.circom"; 6 | include "../node_modules/circom-ecdsa/circuits/bigint.circom"; 7 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/mux1.circom"; 8 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/comparators.circom"; 9 | 10 | template CMov() { 11 | // The spec says "CMOV(a, b, c): If c is False, CMOV returns a, otherwise 12 | // it returns b." 13 | 14 | // As such, if c is 0, output a. Otherwise, output b. 15 | signal input a[4]; 16 | signal input b[4]; 17 | signal input c; 18 | signal output out[4]; 19 | 20 | component mux[4]; 21 | for (var i = 0; i < 4; i ++) { 22 | mux[i] = Mux1(); 23 | mux[i].c[0] <== a[i]; 24 | mux[i].c[1] <== b[i]; 25 | mux[i].s <== c; 26 | } 27 | 28 | for (var i = 0; i < 4; i ++) { 29 | out[i] <== mux[i].out; 30 | } 31 | } 32 | 33 | template Inv0() { 34 | signal input a[4]; 35 | signal output out[4]; 36 | 37 | var p[4] = get_secp256k1_p(); 38 | component modinv = BigModInv(64, 4); 39 | for (var i = 0; i < 4; i ++) { 40 | modinv.in[i] <== a[i]; 41 | modinv.p[i] <== p[i]; 42 | } 43 | 44 | for (var i = 0; i < 4; i ++) { 45 | out[i] <== modinv.out[i]; 46 | } 47 | } 48 | 49 | template ZMulUSquared() { 50 | signal input u_squared[4]; 51 | signal output out[4]; 52 | var z[4] = get_Z(); 53 | 54 | component mul = Multiply(); 55 | for (var i = 0; i < 4; i ++) { 56 | mul.a[i] <== u_squared[i]; 57 | mul.b[i] <== z[i]; 58 | } 59 | 60 | for (var i = 0; i < 4; i ++) { 61 | out[i] <== mul.out[i]; 62 | } 63 | } 64 | 65 | template IsEqualBigInt() { 66 | signal input a[4]; 67 | signal input b[4]; 68 | signal output out; 69 | 70 | component is_eq[4]; 71 | signal sum[5]; 72 | sum[0] <== 0; 73 | for (var i = 0; i < 4; i ++) { 74 | is_eq[i] = IsEqual(); 75 | is_eq[i].in[0] <== a[i]; 76 | is_eq[i].in[1] <== b[i]; 77 | sum[i + 1] <== sum[i] + is_eq[i].out; 78 | } 79 | component result = IsEqual(); 80 | result.in[0] <== sum[4]; 81 | result.in[1] <== 4; 82 | out <== result.out; 83 | } 84 | 85 | // Output 0 if the input is even, and 1 if it is odd 86 | template Sgn0() { 87 | signal input in[4]; 88 | signal output out; 89 | 90 | // Only need to test the 0th bigint register 91 | signal val; 92 | val <== in[0]; 93 | 94 | signal r <-- val % 2; 95 | signal q <-- val \ 2; 96 | 97 | // Ensure that r is 0 xor 1 98 | r * (r - 1) === 0; 99 | 100 | // Ensure that q * 2 + r equals the input 101 | q * 2 + r === val; 102 | 103 | component num2bits = Num2Bits(64); 104 | num2bits.in <== in[0]; 105 | r === num2bits.out[0]; 106 | 107 | // If the remainder is 0, output 0; if it is 1, output 1 108 | out <== r; 109 | } 110 | 111 | template XY2Selector() { 112 | // Either gx1 or gx2 are square. 113 | signal input gx1[4]; 114 | signal input gx1_sqrt[4]; 115 | signal input gx2[4]; 116 | signal input gx2_sqrt[4]; 117 | signal input x1[4]; 118 | signal input x2[4]; 119 | signal output x[4]; 120 | signal output y2[4]; 121 | 122 | // Step 1: square gx1_sqrt 123 | component sq_gx1_sqrt = Square(); 124 | for (var i = 0; i < 4; i ++) { 125 | sq_gx1_sqrt.in[i] <== gx1_sqrt[i]; 126 | } 127 | 128 | // Step 2: square gx2_sqrt 129 | component sq_gx2_sqrt = Square(); 130 | for (var i = 0; i < 4; i ++) { 131 | sq_gx2_sqrt.in[i] <== gx2_sqrt[i]; 132 | } 133 | 134 | // Step 3: s1 = IsEqual(gx1, sq_gx1_sqrt) 135 | component s1 = IsEqualBigInt(); 136 | for (var i = 0; i < 4; i ++) { 137 | s1.a[i] <== gx1[i]; 138 | s1.b[i] <== sq_gx1_sqrt.out[i]; 139 | } 140 | 141 | // Step 4: s2 = IsEqual(gx2, sq_gx2_sqrt) 142 | component s2 = IsEqualBigInt(); 143 | for (var i = 0; i < 4; i ++) { 144 | s2.a[i] <== gx2[i]; 145 | s2.b[i] <== sq_gx2_sqrt.out[i]; 146 | } 147 | 148 | // Step 5: Constrain s1 + s2 === 1 149 | s1.out + s2.out === 1; 150 | 151 | // Step 6: x <== s1 == 1 ? x1 : x2 152 | component x_cmov = CMov(); 153 | x_cmov.c <== s1.out; 154 | for (var i = 0; i < 4; i ++) { 155 | x_cmov.a[i] <== x2[i]; 156 | x_cmov.b[i] <== x1[i]; 157 | } 158 | 159 | // Step 7: y2 <== s1 == 1 ? gx1 : gx2 160 | component y2_cmov = CMov(); 161 | y2_cmov.c <== s1.out; 162 | for (var i = 0; i < 4; i ++) { 163 | y2_cmov.a[i] <== gx2[i]; 164 | y2_cmov.b[i] <== gx1[i]; 165 | } 166 | 167 | for (var i = 0; i < 4; i ++) { 168 | x[i] <== x_cmov.out[i]; 169 | y2[i] <== y2_cmov.out[i]; 170 | } 171 | } 172 | 173 | template MapToCurve() { 174 | signal input u[4]; 175 | signal input gx1_sqrt[4]; 176 | signal input gx2_sqrt[4]; 177 | signal input y_pos[4]; 178 | signal input x_mapped[4]; 179 | signal input y_mapped[4]; 180 | signal output x[4]; 181 | signal output y[4]; 182 | 183 | /////////////////////////////////////////////////////////////////////////// 184 | // Step 1: tv1 = Z * u^2 185 | component step1_u_sq = Square(); 186 | for (var i = 0; i < 4; i ++) { 187 | step1_u_sq.in[i] <== u[i]; 188 | } 189 | component step1_tv1 = ZMulUSquared(); 190 | for (var i = 0; i < 4; i ++) { 191 | step1_tv1.u_squared[i] <== step1_u_sq.out[i]; 192 | } 193 | 194 | /////////////////////////////////////////////////////////////////////////// 195 | // Step 2: tv2 = tv1^2 196 | component step2_tv2 = Square(); 197 | for (var i = 0; i < 4; i ++) { 198 | step2_tv2.in[i] <== step1_tv1.out[i]; 199 | } 200 | 201 | /////////////////////////////////////////////////////////////////////////// 202 | // Step 3: x1 = tv1 + tv2 203 | component step3_tv1_plus_tv2 = Add(); 204 | for (var i = 0; i < 4; i ++) { 205 | step3_tv1_plus_tv2.a[i] <== step1_tv1.out[i]; 206 | step3_tv1_plus_tv2.b[i] <== step2_tv2.out[i]; 207 | } 208 | 209 | /////////////////////////////////////////////////////////////////////////// 210 | // Step 4: x1 = inv0(x1) 211 | component step4_inv0_x1 = Inv0(); 212 | for (var i = 0; i < 4; i ++) { 213 | step4_inv0_x1.a[i] <== step3_tv1_plus_tv2.out[i]; 214 | } 215 | 216 | /////////////////////////////////////////////////////////////////////////// 217 | // Step 5: e1 = x1 == 0 218 | component step5_is_zeroes[4]; 219 | for (var i = 0; i < 4; i ++) { 220 | step5_is_zeroes[i] = IsZero(); 221 | step5_is_zeroes[i].in <== step4_inv0_x1.out[i]; 222 | } 223 | component step5_e1 = IsEqual(); 224 | step5_e1.in[0] <== 225 | step5_is_zeroes[0].out + 226 | step5_is_zeroes[1].out + 227 | step5_is_zeroes[2].out + 228 | step5_is_zeroes[3].out; 229 | step5_e1.in[1] <== 4; 230 | 231 | /////////////////////////////////////////////////////////////////////////// 232 | // Step 6: x1 = x1 + 1 233 | component step6_x1_plus_1 = Add(); 234 | for (var i = 0; i < 4; i ++) { 235 | step6_x1_plus_1.a[i] <== step4_inv0_x1.out[i]; 236 | } 237 | step6_x1_plus_1.b[0] <== 1; 238 | step6_x1_plus_1.b[1] <== 0; 239 | step6_x1_plus_1.b[2] <== 0; 240 | step6_x1_plus_1.b[3] <== 0; 241 | 242 | /////////////////////////////////////////////////////////////////////////// 243 | // Step 7: x1 = CMOV(x1, c2, e1) # If (tv1 + tv2) == 0, set x1 = -1 / Z 244 | var c2[4] = get_C2(); 245 | component step7_cmov = CMov(); 246 | step7_cmov.c <== step5_e1.out; 247 | for (var i = 0; i < 4; i ++) { 248 | step7_cmov.a[i] <== step6_x1_plus_1.out[i]; 249 | step7_cmov.b[i] <== c2[i]; 250 | } 251 | 252 | /////////////////////////////////////////////////////////////////////////// 253 | // Step 8: x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2))) 254 | component step8_x1_mul_c1 = Multiply(); 255 | var c1[4] = get_C1(); 256 | for (var i = 0; i < 4; i ++) { 257 | step8_x1_mul_c1.a[i] <== step7_cmov.out[i]; 258 | step8_x1_mul_c1.b[i] <== c1[i]; 259 | } 260 | 261 | /////////////////////////////////////////////////////////////////////////// 262 | // Step 9: gx1 = x1^2 263 | component step9_gx1 = Square(); 264 | for (var i = 0; i < 4; i ++) { 265 | step9_gx1.in[i] <== step8_x1_mul_c1.out[i]; 266 | } 267 | 268 | /////////////////////////////////////////////////////////////////////////// 269 | // Step 10: gx1 = gx1 + A 270 | var a[4] = get_A(); 271 | component step10_gx1 = Add(); 272 | for (var i = 0; i < 4; i ++) { 273 | step10_gx1.a[i] <== step9_gx1.out[i]; 274 | step10_gx1.b[i] <== a[i]; 275 | } 276 | 277 | /////////////////////////////////////////////////////////////////////////// 278 | // Step 11: gx1 = gx1 * x1 279 | component step11_gx1_mul_x1 = Multiply(); 280 | for (var i = 0; i < 4; i ++) { 281 | step11_gx1_mul_x1.a[i] <== step10_gx1.out[i]; 282 | step11_gx1_mul_x1.b[i] <== step8_x1_mul_c1.out[i]; 283 | } 284 | 285 | /////////////////////////////////////////////////////////////////////////// 286 | // Step 12: gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B 287 | var b[4] = get_B(); 288 | component step12_gx1 = Add(); 289 | for (var i = 0; i < 4; i ++) { 290 | step12_gx1.a[i] <== step11_gx1_mul_x1.out[i]; 291 | step12_gx1.b[i] <== b[i]; 292 | } 293 | 294 | /////////////////////////////////////////////////////////////////////////// 295 | // Step 13: x2 = tv1 * x1 # x2 = Z * u^2 * x1 296 | component step13_x2 = Multiply(); 297 | for (var i = 0; i < 4; i ++) { 298 | step13_x2.a[i] <== step1_tv1.out[i]; 299 | step13_x2.b[i] <== step8_x1_mul_c1.out[i]; 300 | } 301 | 302 | /////////////////////////////////////////////////////////////////////////// 303 | // Step 14: tv2 = tv1 * tv2 304 | component step14_tv2 = Multiply(); 305 | for (var i = 0; i < 4; i ++) { 306 | step14_tv2.a[i] <== step1_tv1.out[i]; 307 | step14_tv2.b[i] <== step2_tv2.out[i]; 308 | } 309 | 310 | /////////////////////////////////////////////////////////////////////////// 311 | // Step 15: gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1 312 | component step15_gx2 = Multiply(); 313 | for (var i = 0; i < 4; i ++) { 314 | step15_gx2.a[i] <== step12_gx1.out[i]; 315 | step15_gx2.b[i] <== step14_tv2.out[i]; 316 | } 317 | 318 | /////////////////////////////////////////////////////////////////////////// 319 | // Steps 16-18: 320 | // e2 = is_square(gx1) 321 | // x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2 322 | // y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2 323 | component step16_x_y2_selector = XY2Selector(); 324 | for (var i = 0; i < 4; i ++) { 325 | step16_x_y2_selector.gx1[i] <== step12_gx1.out[i]; 326 | step16_x_y2_selector.gx1_sqrt[i] <== gx1_sqrt[i]; 327 | step16_x_y2_selector.gx2[i] <== step15_gx2.out[i]; 328 | step16_x_y2_selector.gx2_sqrt[i] <== gx2_sqrt[i]; 329 | step16_x_y2_selector.x1[i] <== step8_x1_mul_c1.out[i]; 330 | step16_x_y2_selector.x2[i] <== step13_x2.out[i]; 331 | } 332 | 333 | /////////////////////////////////////////////////////////////////////////// 334 | // Step 19: y = sqrt(y2) 335 | component step19_expected_y2 = Square(); 336 | for (var i = 0; i < 4; i ++) { 337 | step19_expected_y2.in[i] <== y_pos[i]; 338 | } 339 | // Ensure that the square of the input signal y equals step16_x_y2_selector.y2 340 | for (var i = 0; i < 4; i ++) { 341 | step16_x_y2_selector.y2[i] === step19_expected_y2.out[i]; 342 | } 343 | 344 | /////////////////////////////////////////////////////////////////////////// 345 | // Step 20: e3 = sgn0(u) == sgn0(y) # Fix sign of y 346 | component sgn0_u = Sgn0(); 347 | component sgn0_y = Sgn0(); 348 | for (var i = 0; i < 4; i ++) { 349 | sgn0_u.in[i] <== u[i]; 350 | sgn0_y.in[i] <== y_pos[i]; 351 | } 352 | component step20_e3 = IsEqual(); 353 | step20_e3.in[0] <== sgn0_u.out; 354 | step20_e3.in[1] <== sgn0_y.out; 355 | 356 | /////////////////////////////////////////////////////////////////////////// 357 | // Step 21: y = CMOV(-y, y, e3) 358 | component neg_y = Negate(); 359 | for (var i = 0; i < 4; i ++) { 360 | neg_y.in[i] <== y_pos[i]; 361 | } 362 | 363 | component step21_y = CMov(); 364 | step21_y.c <== step20_e3.out; 365 | for (var i = 0; i < 4; i ++) { 366 | step21_y.a[i] <== neg_y.out[i]; 367 | step21_y.b[i] <== y_pos[i]; 368 | } 369 | 370 | component isomap = IsoMap(); 371 | for (var i = 0; i < 4; i ++) { 372 | isomap.x[i] <== step16_x_y2_selector.x[i]; 373 | isomap.y[i] <== step21_y.out[i]; 374 | isomap.x_mapped[i] <== x_mapped[i]; 375 | isomap.y_mapped[i] <== y_mapped[i]; 376 | } 377 | 378 | for (var i = 0; i < 4; i ++) { 379 | x[i] <== x_mapped[i]; 380 | y[i] <== y_mapped[i]; 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/ExpandMessageXmd.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(120000) 2 | const ff = require('ffjavascript') 3 | const stringifyBigInts = ff.utils.stringifyBigInts 4 | import { 5 | callGenWitness as genWitness, 6 | callGetSignalByName as getSignalByName, 7 | } from 'circom-helper' 8 | import { 9 | dst_prime, 10 | z_pad, 11 | lib_str, 12 | } from '../constants' 13 | import { 14 | str_to_array, 15 | gen_msg_prime, 16 | gen_b0, 17 | gen_b1, 18 | gen_b2, 19 | gen_b3, 20 | strxor, 21 | expand_msg_xmd, 22 | } from '../generate_inputs' 23 | import { 24 | buffer2bitArray, 25 | bufToPaddedBytes, 26 | bufToSha256PaddedBitArr 27 | } from '../utils' 28 | import { 29 | } from '../generate_inputs' 30 | 31 | describe('ExpandMessageXmd', () => { 32 | const msg = 'abc' 33 | const expected_msg_prime = gen_msg_prime(str_to_array(msg)) 34 | const expected_msg_prime2 = [ 35 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 38 | 99, 0, 96, 0, 81, 85, 85, 88, 45, 86, 48, 49, 45, 67, 83, 48, 50, 39 | 45, 119, 105, 116, 104, 45, 115, 101, 99, 112, 50, 53, 54, 107, 49, 40 | 95, 88, 77, 68, 58, 83, 72, 65, 45, 50, 53, 54, 95, 83, 83, 87, 85, 41 | 95, 82, 79, 95, 49 42 | ] 43 | const expected_b0 = [ 44 | 99, 4, 75, 36, 124, 254, 65, 234, 207, 65, 212, 122, 206, 186, 87, 45 | 48, 157, 28, 243, 255, 59, 178, 30, 40, 136, 85, 202, 99, 135, 177, 46 | 127, 169 47 | ] 48 | const expected_hash_b0_bits = buffer2bitArray(Buffer.from(expected_b0)) 49 | 50 | const expected_b1 = [ 51 | 232, 52, 124, 173, 72, 171, 78, 49, 157, 123, 39, 85, 32, 234, 129, 52 | 207, 18, 138, 171, 93, 54, 121, 161, 247, 96, 30, 59, 222, 172, 154, 53 | 81, 208 54 | ] 55 | 56 | const expected_b2 = [ 57 | 197, 77, 255, 208, 84, 39, 78, 219, 36, 136, 85, 230, 17, 144, 196, 98, 58 | 167, 187, 97, 236, 186, 142, 64, 10, 154, 118, 213, 174, 1, 78, 135, 59 | 255 60 | ] 61 | 62 | const expected_b3 = [ 63 | 88, 151, 182, 93, 163, 181, 149, 168, 19, 208, 253, 203, 206, 13, 49, 64 | 111, 118, 108, 238, 235, 111, 248, 76, 222, 204, 214, 155, 224, 231, 65 | 179, 153, 209 66 | ] 67 | 68 | const expected_u0_registers = [ 69 | BigInt('5220784225879993185'), 70 | BigInt('4488152950487114122'), 71 | BigInt('6926039108402729460'), 72 | BigInt('1336068656303022583'), 73 | ] 74 | 75 | const expected_u1_registers = [ 76 | BigInt('5596462452035853824'), 77 | BigInt('5988634572027431684'), 78 | BigInt('1427920136467682780'), 79 | BigInt('6383771510115767720'), 80 | ] 81 | 82 | //it('strxor test', async () => { 83 | //const a = [0, 10, 20, 30, 40, 50, 60, 70] 84 | //const b = [255, 254, 253, 252, 251, 250, 249, 248] 85 | //const expected_out: number[] = [255, 244, 233, 226, 211, 200, 197, 190] 86 | //for (let i = 0; i < a.length; i ++) { 87 | //expect(a[i] ^ b[i]).toEqual(expected_out[i]) 88 | //} 89 | 90 | //const circuit = 'strxor_test' 91 | //const circuitInputs = stringifyBigInts({ a, b }) 92 | //const witness = await genWitness(circuit, circuitInputs) 93 | //const result: number[] = [] 94 | //for (let i = 0; i < 8; i ++) { 95 | //const out = Number(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 96 | //result.push(out) 97 | //} 98 | //expect(result.join('')).toEqual(expected_out.join('')) 99 | //}) 100 | 101 | //// Test the MsgPrime circuit 102 | //it('msg_prime', async () => { 103 | //const circuit = 'msg_prime_test' 104 | //const circuitInputs = stringifyBigInts({ msg: str_to_array(msg) }) 105 | 106 | //const witness = await genWitness(circuit, circuitInputs) 107 | //for (let i = 0; i < 120; i ++) { 108 | //const out = Number(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 109 | //expect(out).toEqual(expected_msg_prime[i]) 110 | //expect(out).toEqual(expected_msg_prime2[i]) 111 | //} 112 | //}) 113 | 114 | //// Hash msg_prime with SHA256 115 | //it('b0 = h(msg_prime)', async () => { 116 | //const hash = gen_b0(expected_msg_prime) 117 | 118 | //for (let i = 0; i < 32; i ++) { 119 | //expect(hash[i]).toEqual(expected_b0[i]) 120 | //} 121 | 122 | //const circuitInputs = stringifyBigInts({ msg_prime: expected_msg_prime }) 123 | 124 | //const hash_bits = buffer2bitArray(Buffer.from(hash)) 125 | //expect(hash_bits.join('')).toEqual(expected_hash_b0_bits.join('')) 126 | 127 | //const circuit = 'hash_msg_prime_to_b0_test' 128 | //const witness = await genWitness(circuit, circuitInputs) 129 | 130 | //const bits: number[] = [] 131 | //for (let i = 0; i < 256; i ++) { 132 | ////const out = Number(await getSignalByName(circuit, witness, 'main.hash[' + i.toString() + ']')) 133 | //const out = Number(witness[1 + i]) 134 | //bits.push(out) 135 | //} 136 | //expect(bits.join('')).toEqual(hash_bits.join('')) 137 | //}) 138 | 139 | //it('b1 = h(b0 || 1 || dst_prime)', async () => { 140 | //const hash = gen_b1(expected_b0) 141 | //for (let i = 0; i < 32; i ++) { 142 | //expect(hash[i]).toEqual(expected_b1[i]) 143 | //} 144 | //const b_bits = buffer2bitArray(Buffer.from(expected_b0)) 145 | //const circuitInputs = stringifyBigInts({ b_bits }) 146 | //const circuit = 'hash_b0_to_b1_test' 147 | //const witness = await genWitness(circuit, circuitInputs) 148 | 149 | //const b1_bits = buffer2bitArray(Buffer.from(hash)) 150 | //const bits: number[] = [] 151 | //for (let i = 0; i < 256; i ++) { 152 | ////const out = Number(await getSignalByName(circuit, witness, 'main.bi_bits[' + i.toString() + ']')) 153 | //const out = Number(witness[1 + i]) 154 | //bits.push(out) 155 | //} 156 | //expect(bits.join('')).toEqual(b1_bits.join('')) 157 | //}) 158 | 159 | //it('b2 = h(strxor(b0, b1) || 2 || dst_prime)', async () => { 160 | //const hash = gen_b2(expected_b0, expected_b1) 161 | //for (let i = 0; i < 32; i ++) { 162 | //expect(hash[i]).toEqual(expected_b2[i]) 163 | //} 164 | //const b0_bits = buffer2bitArray(Buffer.from(expected_b0)) 165 | //const bi_minus_one_bits = buffer2bitArray(Buffer.from(expected_b1)) 166 | //const circuitInputs = stringifyBigInts({ b0_bits, bi_minus_one_bits }) 167 | //const circuit = 'hash_b1_to_b2_test' 168 | //const witness = await genWitness(circuit, circuitInputs) 169 | //const b2_bits = buffer2bitArray(Buffer.from(hash)) 170 | //const bits: number[] = [] 171 | //for (let i = 0; i < 256; i ++) { 172 | ////const out = Number(await getSignalByName(circuit, witness, 'main.bi_bits[' + i.toString() + ']')) 173 | //const out = Number(witness[1 + i]) 174 | //bits.push(out) 175 | //} 176 | //expect(bits.join('')).toEqual(b2_bits.join('')) 177 | //}) 178 | 179 | //it('b3 = h(strxor(b0, b2) || 3 || dst_prime)', async () => { 180 | //const hash = gen_b3(expected_b0, expected_b2) 181 | //for (let i = 0; i < 32; i ++) { 182 | //expect(hash[i]).toEqual(expected_b3[i]) 183 | //} 184 | //const b0_bits = buffer2bitArray(Buffer.from(expected_b0)) 185 | //const bi_minus_one_bits = buffer2bitArray(Buffer.from(expected_b2)) 186 | //const circuitInputs = stringifyBigInts({ b0_bits, bi_minus_one_bits }) 187 | //const circuit = 'hash_b2_to_b3_test' 188 | //const witness = await genWitness(circuit, circuitInputs) 189 | //const b2_bits = buffer2bitArray(Buffer.from(hash)) 190 | //const bits: number[] = [] 191 | //for (let i = 0; i < 256; i ++) { 192 | ////const out = Number(await getSignalByName(circuit, witness, 'main.bi_bits[' + i.toString() + ']')) 193 | //const out = Number(witness[1 + i]) 194 | //bits.push(out) 195 | //} 196 | //expect(bits.join('')).toEqual(b2_bits.join('')) 197 | //}) 198 | 199 | //it('ExpandMessageXmd', async () => { 200 | //const circuit = 'expand_msg_xmd_test' 201 | //const circuitInputs = stringifyBigInts({ msg: str_to_array(msg) }) 202 | 203 | //const witness = await genWitness(circuit, circuitInputs) 204 | 205 | //const bytes: number[] = [] 206 | //for (let i = 0; i < 96; i ++) { 207 | ////const out = Number(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 208 | //const out = Number(witness[1 + i]) 209 | //bytes.push(out) 210 | //} 211 | 212 | //const expected = expand_msg_xmd(msg) 213 | //expect(expected.length).toEqual(bytes.length) 214 | //for (let i = 0; i < 96; i ++) { 215 | //expect(bytes[i]).toEqual(expected[i]) 216 | //} 217 | //}) 218 | 219 | //it('ZeroSandwich (valid)', async () => { 220 | //const circuit = 'zero_sandwich_test' 221 | //const circuitInputs = stringifyBigInts({ 222 | //in: [0, 0, 0, 0, 5, 6, 0, 0], 223 | //substring_length: 2, 224 | //}) 225 | 226 | //const witness = await genWitness(circuit, circuitInputs) 227 | //}) 228 | 229 | //it('ZeroSandwich (invalid)', async () => { 230 | //const circuit = 'zero_sandwich_test' 231 | //const circuitInputs = stringifyBigInts({ 232 | //in: [0, 0, 0, 0, 5, 6, 3, 0], 233 | //substring_length: 2, 234 | //}) 235 | 236 | //try { 237 | //const witness = await genWitness(circuit, circuitInputs) 238 | //expect(false).toBeTruthy() 239 | //} catch { 240 | //expect(true).toBeTruthy() 241 | //} 242 | //expect.assertions(1) 243 | //}) 244 | 245 | //it('ZeroSandwich (invalid)', async () => { 246 | //const circuit = 'zero_sandwich_test' 247 | //const circuitInputs = stringifyBigInts({ 248 | //in: [0, 0, 0, 1, 5, 6, 0, 0], 249 | //substring_length: 2, 250 | //}) 251 | 252 | //try { 253 | //const witness = await genWitness(circuit, circuitInputs) 254 | //expect(false).toBeTruthy() 255 | //} catch { 256 | //expect(true).toBeTruthy() 257 | //} 258 | //expect.assertions(1) 259 | //}) 260 | 261 | //it('VerifyMsgPrime', async () => { 262 | //const circuit = 'verify_msg_prime_test' 263 | //const msg_prime = gen_msg_prime(msg) 264 | //const b = bufToPaddedBytes(Buffer.from(msg_prime)) 265 | ////console.log(msg_prime.length) 266 | ////console.log(Buffer.from(expected_msg_prime).toString('hex')) 267 | ////console.log(Buffer.from(b).toString('hex')) 268 | 269 | //let offset_msg_buf = Buffer.alloc(msg_prime.length) 270 | //for (let i = 64; i < 64 + msg.length; i ++) { 271 | //offset_msg_buf[i] = Buffer.from(msg[i - 64])[0] 272 | //} 273 | //let offset_msg: Number[] = [] 274 | //for (let i = 0; i < offset_msg_buf.length; i ++) { 275 | //offset_msg.push(Number(offset_msg_buf[i])) 276 | //} 277 | 278 | //const circuitInputs = stringifyBigInts({ 279 | //msg_prime, 280 | //offset_msg, 281 | //msg_length: msg.length 282 | //}) 283 | //const witness = await genWitness(circuit, circuitInputs) 284 | //}) 285 | 286 | it('ExpandMessageXmd2', async () => { 287 | const circuit = 'expand_msg_xmd2_test' 288 | const msg_prime = gen_msg_prime(str_to_array(msg)) 289 | const padded_msg_prime = bufToPaddedBytes(Buffer.from(msg_prime)) 290 | 291 | let offset_msg_buf = Buffer.alloc(padded_msg_prime.length) 292 | for (let i = 64; i < 64 + msg.length; i ++) { 293 | offset_msg_buf[i] = Buffer.from(msg[i - 64])[0] 294 | } 295 | let offset_msg: Number[] = [] 296 | for (let i = 0; i < offset_msg_buf.length; i ++) { 297 | offset_msg.push(Number(offset_msg_buf[i])) 298 | } 299 | 300 | const circuitInputs = stringifyBigInts({ 301 | msg_prime: padded_msg_prime, 302 | offset_msg, 303 | msg_length: msg.length, 304 | padded_msg_prime: bufToPaddedBytes(Buffer.from(msg_prime)), 305 | }) 306 | const witness = await genWitness(circuit, circuitInputs) 307 | 308 | //const hash = gen_b0(expected_msg_prime) 309 | 310 | //const hash_bits = buffer2bitArray(Buffer.from(hash)) 311 | 312 | //const bits: number[] = [] 313 | //for (let i = 0; i < 256; i ++) { 314 | ////const out = Number(await getSignalByName(circuit, witness, 'main.hash[' + i.toString() + ']')) 315 | //const out = Number(witness[1 + i]) 316 | //bits.push(out) 317 | //} 318 | //expect(bits.join('')).toEqual(hash_bits.join('')) 319 | 320 | const bytes: number[] = [] 321 | for (let i = 0; i < 96; i ++) { 322 | //const out = Number(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 323 | const out = Number(witness[1 + i]) 324 | bytes.push(out) 325 | } 326 | 327 | const expected = expand_msg_xmd(str_to_array(msg)) 328 | expect(expected.length).toEqual(bytes.length) 329 | for (let i = 0; i < 96; i ++) { 330 | expect(bytes[i]).toEqual(expected[i]) 331 | } 332 | }) 333 | }) 334 | -------------------------------------------------------------------------------- /circuits/circom/expand_message_xmd.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.0.0; 2 | include "./constants.circom"; 3 | include "./calculateTotal.circom"; 4 | include "./Sha256.circom"; 5 | include "../node_modules/circom-ecdsa/circuits/bigint.circom"; 6 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/comparators.circom"; 7 | include "../node_modules/circom-ecdsa/node_modules/circomlib/circuits/sha256/sha256.circom"; 8 | 9 | function calc_msg_prime_output_length(msg_length) { 10 | return msg_length + 64 + 2 + 50 + 1; 11 | } 12 | 13 | template MsgPrime(msg_length) { 14 | var output_length = calc_msg_prime_output_length(msg_length); 15 | 16 | signal input msg[msg_length]; 17 | signal output out[output_length]; 18 | 19 | signal msg_prime[output_length]; 20 | 21 | var z_pad[64] = get_z_pad(); 22 | var lib_str[2] = get_lib_str(); 23 | var dst_prime[50] = get_dst_prime(); 24 | 25 | // msg_prme = z_pad ... 26 | for (var i = 0; i < 64; i ++) { 27 | msg_prime[i] <== z_pad[i]; 28 | } 29 | 30 | // msg_prme = z_pad || msg ... 31 | for (var i = 0; i < msg_length; i ++) { 32 | msg_prime[64 + i] <== msg[i]; 33 | } 34 | 35 | // msg_prme = z_pad || msg || lib_str ... 36 | for (var i = 0; i < 2; i ++) { 37 | msg_prime[64 + msg_length + i] <== lib_str[i]; 38 | } 39 | 40 | // msg_prme = z_pad || msg || lib_str || 0 ... 41 | msg_prime[64 + msg_length + 2] <== 0; 42 | 43 | // msg_prme = z_pad || msg || lib_str || 0 || dst_prime 44 | for (var i = 0; i < 50; i ++) { 45 | msg_prime[64 + msg_length + 2 + 1 + i] <== dst_prime[i]; 46 | } 47 | 48 | for (var i = 0; i < output_length; i ++) { 49 | out[i] <== msg_prime[i]; 50 | } 51 | } 52 | 53 | template HashMsgPrimeToB0(msg_length) { 54 | var msg_prime_length = calc_msg_prime_output_length(msg_length); 55 | signal input msg_prime[msg_prime_length]; 56 | signal output hash[256]; 57 | 58 | component hasher = Sha256(msg_prime_length * 8); 59 | component n2b[msg_prime_length]; 60 | for (var i = 0; i < msg_prime_length; i ++) { 61 | n2b[i] = Num2Bits(8); 62 | n2b[i].in <== msg_prime[i]; 63 | for (var j = 0; j < 8; j ++) { 64 | hasher.in[i * 8 + (7 - j)] <== n2b[i].out[j]; 65 | } 66 | } 67 | 68 | for (var i = 0; i < 256; i ++) { 69 | hash[i] <== hasher.out[i]; 70 | } 71 | } 72 | 73 | template HashBi(b_idx) { 74 | signal input b0_bits[256]; 75 | signal input bi_minus_one_bits[256]; 76 | signal output bi_bits[256]; 77 | 78 | component strxor = StrXor(256); 79 | component hashb = HashB(b_idx); 80 | 81 | for (var i = 0; i < 256; i ++) { 82 | strxor.a[i] <== b0_bits[i]; 83 | strxor.b[i] <== bi_minus_one_bits[i]; 84 | } 85 | 86 | for (var i = 0; i < 256; i ++) { 87 | hashb.b_bits[i] <== strxor.out[i]; 88 | } 89 | 90 | for (var i = 0; i < 256; i ++) { 91 | bi_bits[i] <== hashb.bi_bits[i]; 92 | } 93 | } 94 | 95 | template HashB(b_idx) { 96 | assert(b_idx < 8); 97 | 98 | signal input b_bits[256]; 99 | signal output bi_bits[256]; 100 | 101 | var num_preimage_bits = 256 + 8 + (50 * 8); 102 | component hasher = Sha256(num_preimage_bits); 103 | for (var i = 0; i < 32; i ++) { 104 | for (var j = 0; j < 8; j ++) { 105 | hasher.in[i * 8 + (j)] <== b_bits[i * 8 + (j)]; 106 | } 107 | } 108 | 109 | component digit_n2b = Num2Bits(8); 110 | digit_n2b.in <== b_idx; 111 | for (var i = 0; i < 8; i ++) { 112 | hasher.in[256 + i] <== digit_n2b.out[7 - i]; 113 | } 114 | 115 | var dst_prime[50] = get_dst_prime(); 116 | component n2b[50]; 117 | for (var i = 0; i < 50; i ++) { 118 | n2b[i] = Num2Bits(8); 119 | n2b[i].in <== dst_prime[i]; 120 | for (var j = 0; j < 8; j ++) { 121 | hasher.in[256 + 8 + (i * 8) + (j)] <== n2b[i].out[7 - j]; 122 | } 123 | } 124 | for (var i = 0; i < 256; i ++) { 125 | bi_bits[i] <== hasher.out[i]; 126 | } 127 | } 128 | 129 | template StrXor(n) { 130 | // TODO: For safety, should the inputs be constrained to be 0 <= n <= 255? 131 | signal input a[n]; 132 | signal input b[n]; 133 | signal output out[n]; 134 | 135 | component xor[n][8]; 136 | component n2b_a[n]; 137 | component n2b_b[n]; 138 | component b2n[n]; 139 | for (var i = 0; i < n; i ++) { 140 | n2b_a[i] = Num2Bits(8); 141 | n2b_a[i].in <== a[i]; 142 | n2b_b[i] = Num2Bits(8); 143 | n2b_b[i].in <== b[i]; 144 | b2n[i] = Bits2Num(8); 145 | for (var j = 0; j < 8; j ++) { 146 | xor[i][j] = XOR(); 147 | xor[i][j].a <== n2b_a[i].out[j]; 148 | xor[i][j].b <== n2b_b[i].out[j]; 149 | b2n[i].in[j] <== xor[i][j].out; 150 | } 151 | } 152 | for (var i = 0; i < n; i ++) { 153 | out[i] <== b2n[i].out; 154 | } 155 | } 156 | 157 | template ExpandMessageXmd(msg_length) { 158 | signal input msg[msg_length]; 159 | signal output out[96]; 160 | 161 | component msg_prime = MsgPrime(msg_length); 162 | for (var i = 0; i < msg_length; i ++) { 163 | msg_prime.msg[i] <== msg[i]; 164 | } 165 | 166 | component b0 = HashMsgPrimeToB0(msg_length); 167 | var msg_prime_length = calc_msg_prime_output_length(msg_length); 168 | for (var i = 0; i < msg_prime_length; i ++) { 169 | b0.msg_prime[i] <== msg_prime.out[i]; 170 | } 171 | 172 | component b1 = HashB(1); 173 | for (var i = 0; i < 256; i ++) { 174 | b1.b_bits[i] <== b0.hash[i]; 175 | } 176 | 177 | component b2 = HashBi(2); 178 | for (var i = 0; i < 256; i ++) { 179 | b2.b0_bits[i] <== b0.hash[i]; 180 | b2.bi_minus_one_bits[i] <== b1.bi_bits[i]; 181 | } 182 | 183 | component b3 = HashBi(3); 184 | for (var i = 0; i < 256; i ++) { 185 | b3.b0_bits[i] <== b0.hash[i]; 186 | b3.bi_minus_one_bits[i] <== b2.bi_bits[i]; 187 | } 188 | 189 | component b2n_b1[32]; 190 | for (var i = 0; i < 32; i ++) { 191 | b2n_b1[i] = Bits2Num(8); 192 | for (var j = 0; j < 8; j ++) { 193 | b2n_b1[i].in[j] <== b1.bi_bits[i * 8 + (7 - j)]; 194 | } 195 | out[i] <== b2n_b1[i].out; 196 | } 197 | 198 | component b2n_b2[32]; 199 | for (var i = 0; i < 32; i ++) { 200 | b2n_b2[i] = Bits2Num(8); 201 | for (var j = 0; j < 8; j ++) { 202 | b2n_b2[i].in[j] <== b2.bi_bits[i * 8 + (7 - j)]; 203 | } 204 | out[32 + i] <== b2n_b2[i].out; 205 | } 206 | 207 | component b2n_b3[32]; 208 | for (var i = 0; i < 32; i ++) { 209 | b2n_b3[i] = Bits2Num(8); 210 | for (var j = 0; j < 8; j ++) { 211 | b2n_b3[i].in[j] <== b3.bi_bits[i * 8 + (7 - j)]; 212 | } 213 | out[64 + i] <== b2n_b3[i].out; 214 | } 215 | } 216 | 217 | // msg_prime_length is in bytes 218 | template VerifyMsgPrime(padded_length) { 219 | signal input msg_prime[padded_length]; 220 | signal input offset_msg[padded_length]; 221 | signal input msg_length; // in bytes 222 | 223 | var offset = 64; 224 | 225 | // msg_prime = z_pad || msg || lib_str || 0 || dst_prime 226 | 227 | // Step 1: Check that msg_prime starts with z_pad (64 zeroes) 228 | for (var i = 0; i < offset; i ++) { 229 | msg_prime[i] === 0; 230 | } 231 | 232 | // Step 2: Verify that offset_msg is valid 233 | component zs = ZeroSandwich(offset, padded_length); 234 | zs.substring_length <== msg_length; 235 | for (var i = 0; i < padded_length; i ++) { 236 | zs.in[i] <== offset_msg[i]; 237 | } 238 | 239 | // Step 3: Check that msg_prime starts with offset_msg values from offset 240 | // onwards 241 | /* 242 | msg_length = 4 243 | [1, 2, 3, 4, 5, 0, 0, 8] <- msg_prime 244 | [1, 2, 3, 4, 0, 0, 0, 0] <- offset_msg 245 | a: [1, 1, 1, 1, 0, 1, 1, 0] <- [msg_prime[i] == offset_msg[i] for i in length] 246 | b: [1, 1, 1, 1, 0, 0, 0, 0] <- [i < msg_length ? 1 : 0 for i in length] 247 | c: [1, 1, 1, 1, 0, 0, 0, 0] <- [a[i] * b[i] for i in length] 248 | sum(c) === msg_length 249 | */ 250 | 251 | component iseq_a[padded_length - offset]; 252 | component lt_b[padded_length - offset]; 253 | component ct_c = CalculateTotal(padded_length - offset); 254 | 255 | for (var i = 0; i < padded_length - offset; i ++) { 256 | iseq_a[i] = IsEqual(); 257 | iseq_a[i].in[0] <== msg_prime[offset + i]; 258 | iseq_a[i].in[1] <== offset_msg[offset + i]; 259 | 260 | // TODO: save on constraints by first checking that msg_length is 261 | // less than length (using LessThan(252) outside the loop), then using 262 | // LessThan(log2(msg_length)) inside the loop 263 | lt_b[i] = LessThan(252); 264 | lt_b[i].in[0] <== offset + i; 265 | lt_b[i].in[1] <== offset + msg_length; 266 | 267 | ct_c.in[i] <== iseq_a[i].out * lt_b[i].out; 268 | } 269 | 270 | ct_c.out === msg_length; 271 | 272 | // Step 4: Check that msg_prime contains lib_str || 0 || dst_prime from 273 | component lib_str_selector[2]; 274 | component zero_selector = Selector(padded_length); 275 | component dst_prime_selector[50]; 276 | 277 | var lib_str[2] = get_lib_str(); 278 | var dst_prime[50] = get_dst_prime(); 279 | 280 | for (var i = 0; i < 2; i ++) { 281 | lib_str_selector[i] = Selector(padded_length); 282 | lib_str_selector[i].index <== offset + msg_length + i; 283 | for (var j = 0; j < padded_length; j ++) { 284 | lib_str_selector[i].in[j] <== msg_prime[j]; 285 | } 286 | lib_str_selector[i].out === lib_str[i]; 287 | } 288 | 289 | zero_selector.index <== offset + msg_length + 2; 290 | for (var i = 0; i < padded_length; i ++) { 291 | zero_selector.in[i] <== msg_prime[i]; 292 | } 293 | zero_selector.out === 0; 294 | 295 | for (var i = 0; i < 50; i ++) { 296 | dst_prime_selector[i] = Selector(padded_length); 297 | dst_prime_selector[i].index <== offset + msg_length + 2 + 1 + i; 298 | for (var j = 0; j < padded_length; j ++) { 299 | dst_prime_selector[i].in[j] <== msg_prime[j]; 300 | } 301 | dst_prime_selector[i].out === dst_prime[i]; 302 | } 303 | } 304 | 305 | // padded_msg_prime_length is in bytes 306 | template ExpandMessageXmd2(padded_msg_prime_length) { 307 | // offset_msg must be offset by 64 zeros and end with 0s. e.g. if msg = [1, 308 | // 2, 3], then offset_msg = [0, 0, ... 0, 1, 2, 3, 0, 0...] 309 | signal input offset_msg[padded_msg_prime_length]; 310 | signal input msg_length; // in bytes 311 | 312 | signal input msg_prime[padded_msg_prime_length]; 313 | signal input padded_msg_prime[padded_msg_prime_length]; 314 | 315 | signal output out[96]; 316 | 317 | var offset = 64; // the length of z_prime 318 | 319 | // Step 1: Ensure that each value in offset_msg, msg_prime, and 320 | // padded_msg_prime are < 256 321 | // TODO 322 | 323 | // Step 2: Verify that msg_prime is valid 324 | component v = VerifyMsgPrime(padded_msg_prime_length); 325 | v.msg_length <== msg_length; 326 | for (var i = 0; i < padded_msg_prime_length; i ++) { 327 | v.msg_prime[i] <== msg_prime[i]; 328 | v.offset_msg[i] <== offset_msg[i]; 329 | } 330 | 331 | // Step 3: Convert padded_msg_prime and msg_prime_bits to bits 332 | signal padded_msg_prime_bits[padded_msg_prime_length * 8]; 333 | signal msg_prime_bits[padded_msg_prime_length * 8]; 334 | component n2b_a[padded_msg_prime_length]; 335 | component n2b_b[padded_msg_prime_length]; 336 | for (var i = 0; i < padded_msg_prime_length; i ++) { 337 | n2b_a[i] = Num2Bits(8); 338 | n2b_b[i] = Num2Bits(8); 339 | n2b_a[i].in <== padded_msg_prime[i]; 340 | n2b_b[i].in <== msg_prime[i]; 341 | for (var j = 0; j < 8; j ++) { 342 | padded_msg_prime_bits[i * 8 + (7 - j)] <== n2b_a[i].out[j]; 343 | msg_prime_bits[i * 8 + (7 - j)] <== n2b_b[i].out[j]; 344 | } 345 | } 346 | 347 | // Step 4: Hash padded_msg_prime to derive B0 348 | component b0 = Sha256Hash(padded_msg_prime_length * 8); 349 | for (var i = 0; i < padded_msg_prime_length * 8; i ++) { 350 | b0.padded_bits[i] <== padded_msg_prime_bits[i]; 351 | b0.msg[i] <== msg_prime_bits[i]; 352 | } 353 | 354 | /*signal output out[256];*/ 355 | /*for (var i = 0; i < 256; i ++) {*/ 356 | /*out[i] <== hasher.out[i];*/ 357 | /*}*/ 358 | component b1 = HashB(1); 359 | for (var i = 0; i < 256; i ++) { 360 | b1.b_bits[i] <== b0.out[i]; 361 | } 362 | 363 | component b2 = HashBi(2); 364 | for (var i = 0; i < 256; i ++) { 365 | b2.b0_bits[i] <== b0.out[i]; 366 | b2.bi_minus_one_bits[i] <== b1.bi_bits[i]; 367 | } 368 | 369 | component b3 = HashBi(3); 370 | for (var i = 0; i < 256; i ++) { 371 | b3.b0_bits[i] <== b0.out[i]; 372 | b3.bi_minus_one_bits[i] <== b2.bi_bits[i]; 373 | } 374 | 375 | component b2n_b1[32]; 376 | for (var i = 0; i < 32; i ++) { 377 | b2n_b1[i] = Bits2Num(8); 378 | for (var j = 0; j < 8; j ++) { 379 | b2n_b1[i].in[j] <== b1.bi_bits[i * 8 + (7 - j)]; 380 | } 381 | out[i] <== b2n_b1[i].out; 382 | } 383 | 384 | component b2n_b2[32]; 385 | for (var i = 0; i < 32; i ++) { 386 | b2n_b2[i] = Bits2Num(8); 387 | for (var j = 0; j < 8; j ++) { 388 | b2n_b2[i].in[j] <== b2.bi_bits[i * 8 + (7 - j)]; 389 | } 390 | out[32 + i] <== b2n_b2[i].out; 391 | } 392 | 393 | component b2n_b3[32]; 394 | for (var i = 0; i < 32; i ++) { 395 | b2n_b3[i] = Bits2Num(8); 396 | for (var j = 0; j < 8; j ++) { 397 | b2n_b3[i].in[j] <== b3.bi_bits[i * 8 + (7 - j)]; 398 | } 399 | out[64 + i] <== b2n_b3[i].out; 400 | } 401 | } 402 | 403 | // The first offset elements of in should be 0, followed by substring_length 404 | // elements, and the rest should be 0 405 | template ZeroSandwich(offset, length) { 406 | assert(offset < length); 407 | signal input in[length]; 408 | signal input substring_length; 409 | 410 | // Check that the first offset elements are 0 411 | component isz[offset]; 412 | for (var i = 0; i < offset; i ++) { 413 | isz[i] = IsZero(); 414 | isz[i].in <== in[i]; 415 | isz[i].out === 1; 416 | } 417 | 418 | /* 419 | length = 8 420 | offset = 4 421 | substring_length = 2 422 | in: [0, 0, 0, 0, 5, 6, 0, 0] 423 | a: [0, 0, 0, 0, 0, 0, 1, 1] <- [i >= (offset + substring_length) for i in length] 424 | b: [0, 0, 0, 0, 0, 0, 0, 0] <- [a[i] * in[i] for in in length] 425 | check that each element from offset onwards is 0 426 | */ 427 | 428 | component gte_a[length - offset]; 429 | component isz_b[length - offset]; 430 | for (var i = offset; i < length; i ++) { 431 | // TODO: save on constraints by first checking that substring_length is 432 | // less than length (using LessThan(252) outside the loop), then using 433 | // GreaterEqThan(log2(length)) inside the loop 434 | gte_a[i - offset] = GreaterEqThan(252); 435 | gte_a[i - offset].in[0] <== i; 436 | gte_a[i - offset].in[1] <== offset + substring_length; 437 | 438 | isz_b[i - offset] = IsZero(); 439 | isz_b[i - offset].in <== gte_a[i - offset].out * in[i]; 440 | isz_b[i - offset].out === 1; 441 | } 442 | } 443 | -------------------------------------------------------------------------------- /circuits/ts/__tests__/MapToCurve.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(120000) 2 | const crypto = require('crypto') 3 | const ff = require('ffjavascript') 4 | const stringifyBigInts = ff.utils.stringifyBigInts 5 | import { 6 | callGenWitness as genWitness, 7 | callGetSignalByName as getSignalByName, 8 | } from 'circom-helper' 9 | 10 | import { bigint_to_array } from '../utils' 11 | import { iso_map } from '../iso_map' 12 | import { sqrt_mod_p, sgn0 } from '../utils' 13 | import { 14 | map_to_curve, 15 | expand_msg_xmd, 16 | generate_inputs, 17 | str_to_array, 18 | } from '../generate_inputs' 19 | 20 | const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F') 21 | const u0 = BigInt('8386638881075453792406600069412283052291822895580742641789327979312054938465') 22 | const u1 = BigInt('40071583224459737250239606232440854032076176808341187421679277989916398099968') 23 | const u0_registers = bigint_to_array(64, 4, u0) 24 | const u1_registers = bigint_to_array(64, 4, u1) 25 | const A = BigInt('28734576633528757162648956269730739219262246272443394170905244663053633733939') 26 | const B = BigInt('1771') 27 | const Z = BigInt('115792089237316195423570985008687907853269984665640564039457584007908834671652') 28 | const Z_registers = bigint_to_array(64, 4, Z) 29 | const c1 = BigInt('5324262023205125242632636178842408935272934169651804884418803605709653231043') 30 | const c2 = BigInt('31579660701086235115519359547823974869073632181538335647124795638520591274090') 31 | const field = new ff.F1Field(p) 32 | 33 | describe('MapToCurve', () => { 34 | it('u ** 2', async () => { 35 | const expected_registers: bigint[] = bigint_to_array(64, 4, (u0 * u0) % p) 36 | 37 | const circuit = 'u_squared_test' 38 | const circuitInputs = stringifyBigInts({ 'in': u0_registers }) 39 | const witness = await genWitness(circuit, circuitInputs) 40 | for (let i = 0; i < 4; i ++) { 41 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 42 | expect(out).toEqual(expected_registers[i]) 43 | } 44 | }) 45 | 46 | it('Z * u^2', async () => { 47 | const u_squared = bigint_to_array(64, 4, (u0 * u0) % p) 48 | const expected_registers: bigint[] = bigint_to_array(64, 4, (Z * (u0 * u0)) % p) 49 | const circuit = 'z_mul_u_squared_test' 50 | const circuitInputs = stringifyBigInts({ u_squared }) 51 | const witness = await genWitness(circuit, circuitInputs) 52 | for (let i = 0; i < 4; i ++) { 53 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 54 | expect(out).toEqual(expected_registers[i]) 55 | } 56 | }) 57 | 58 | it('x1 = tv1 + tv2', async () => { 59 | const tv1 = (Z * u0 * u0) % p 60 | const tv2 = (tv1 * tv1) % p 61 | 62 | expect(tv1).toEqual(BigInt('20859123609890259037730945376790481235073009751836911085977936258096472725432')) 63 | expect(tv2).toEqual(BigInt('69125977817722673093369867421906949146947726600759336378042760386702683237276')) 64 | 65 | const tv1_registers = bigint_to_array(64, 4, tv1) 66 | const tv2_registers = bigint_to_array(64, 4, tv2) 67 | const expected_registers = bigint_to_array(64, 4, (tv1 + tv2) % p) 68 | 69 | const x1_registers = bigint_to_array(64, 4, BigInt('89985101427612932131100812798697430382020736352596247464020696644799155962708')) 70 | for (let i = 0; i < 4; i ++) { 71 | expect(x1_registers[i]).toEqual(expected_registers[i]) 72 | } 73 | 74 | const circuit = 'tv2_plus_tv1_test' 75 | const circuitInputs = stringifyBigInts({ 76 | a: tv1_registers, 77 | b: tv2_registers, 78 | }) 79 | const witness = await genWitness(circuit, circuitInputs) 80 | for (let i = 0; i < 4; i ++) { 81 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 82 | expect(out).toEqual(expected_registers[i]) 83 | } 84 | }) 85 | 86 | it('x1 = inv0(x1)', async () => { 87 | const x1 = BigInt('89985101427612932131100812798697430382020736352596247464020696644799155962708') 88 | const inv_x1 = BigInt('8772949712675871307975074402064985445164432129069910775576927500367828979411') 89 | const x1_registers = bigint_to_array(64, 4, x1) 90 | const inv_x1_registers = bigint_to_array(64, 4, inv_x1) 91 | const circuit = 'inv0_test' 92 | const circuitInputs = stringifyBigInts({ 93 | a: x1_registers, 94 | }) 95 | const witness = await genWitness(circuit, circuitInputs) 96 | for (let i = 0; i < 4; i ++) { 97 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 98 | expect(out).toEqual(inv_x1_registers[i]) 99 | } 100 | }) 101 | 102 | it('cmov', async () => { 103 | const circuit = 'cmov_test' 104 | const a = BigInt('89985101427612932131100812798697430382020736352596247464020696644799155962708') 105 | const b = BigInt('456') 106 | const a_array = bigint_to_array(64, 4, a) 107 | const b_array = bigint_to_array(64, 4, b) 108 | 109 | const circuitInputs = stringifyBigInts({ a: a_array, b: a_array, c: 0 }) 110 | const witness = await genWitness(circuit, circuitInputs) 111 | for (let i = 0; i < 4; i ++) { 112 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 113 | expect(out).toEqual(a_array[i]) 114 | } 115 | 116 | const circuitInputs2 = stringifyBigInts({ a: a_array, b: b_array, c: 1 }) 117 | const witness2 = await genWitness(circuit, circuitInputs2) 118 | for (let i = 0; i < 4; i ++) { 119 | const out = BigInt(await getSignalByName(circuit, witness2, 'main.out[' + i.toString() + ']')) 120 | expect(out).toEqual(b_array[i]) 121 | } 122 | }) 123 | 124 | it('sgn0()', async() => { 125 | const circuit = 'sgn0_test' 126 | const odd = BigInt('90488382598551863334512914506083217651378280855023645338709486233743148212823') 127 | const even = BigInt('90488382598551863334512914506083217651378280855023645338709486233743148212822') 128 | 129 | for (const val of [odd, even]) { 130 | const circuitInputs = stringifyBigInts({ 'in': bigint_to_array(64, 4, val) }) 131 | const witness = await genWitness(circuit, circuitInputs) 132 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out')) 133 | expect(out).toEqual(val % BigInt(2)) 134 | } 135 | }) 136 | 137 | it('XY2Selector()', async() => { 138 | const circuit = 'x_y2_selector_test' 139 | 140 | const test_suites: any[] = [ 141 | { 142 | gx1: BigInt(4), 143 | gx1_sqrt: BigInt(2), 144 | gx2: BigInt(5), 145 | gx2_sqrt: BigInt(2), 146 | x1: BigInt(123), 147 | x2: BigInt(456), 148 | }, 149 | { 150 | gx1: BigInt(5), 151 | gx1_sqrt: BigInt(2), 152 | gx2: BigInt(4), 153 | gx2_sqrt: BigInt(2), 154 | x1: BigInt(123), 155 | x2: BigInt(456), 156 | }, 157 | ] 158 | for (const suite of test_suites) { 159 | const s1 = suite.gx1_sqrt * suite.gx1_sqrt 160 | const s2 = suite.gx2_sqrt * suite.gx2_sqrt 161 | 162 | let x = 0 163 | if (s1 === suite.gx1) { x += 1 } 164 | if (s2 === suite.gx2) { x += 1 } 165 | expect(x).toEqual(1) 166 | 167 | const expected_x = s1 === suite.gx1 ? suite.x1 : suite.x2 168 | const expected_y2 = s1 === suite.gx1 ? suite.gx1 : suite.gx2 169 | const expected_x_array = bigint_to_array(64, 4, expected_x) 170 | const expected_y2_array = bigint_to_array(64, 4, expected_y2) 171 | const circuitInputs = stringifyBigInts({ 172 | gx1: bigint_to_array(64, 4, suite.gx1), 173 | gx1_sqrt: bigint_to_array(64, 4, suite.gx1_sqrt), 174 | gx2: bigint_to_array(64, 4, suite.gx2), 175 | gx2_sqrt: bigint_to_array(64, 4, suite.gx2_sqrt), 176 | x1: bigint_to_array(64, 4, suite.x1), 177 | x2: bigint_to_array(64, 4, suite.x2), 178 | }) 179 | const witness = await genWitness(circuit, circuitInputs) 180 | 181 | for (let i = 0; i < 4; i ++) { 182 | const x = BigInt(await getSignalByName(circuit, witness, 'main.x[' + i.toString() + ']')) 183 | const y2 = BigInt(await getSignalByName(circuit, witness, 'main.y2[' + i.toString() + ']')) 184 | expect(x).toEqual(expected_x_array[i]) 185 | expect(y2).toEqual(expected_y2_array[i]) 186 | } 187 | } 188 | }) 189 | 190 | it('Negate()', async() => { 191 | const circuit = 'negate_test' 192 | const a = BigInt(5) 193 | const expected_neg_a = p - BigInt(5) 194 | const expected_neg_a_array = bigint_to_array(64, 4, expected_neg_a) 195 | 196 | // Generate witness 197 | const circuitInputs = stringifyBigInts({ 198 | 'in': bigint_to_array(64, 4, a), 199 | }) 200 | const witness = await genWitness(circuit, circuitInputs) 201 | 202 | for (let i = 0; i < 4; i ++) { 203 | const out = BigInt(await getSignalByName(circuit, witness, 'main.out[' + i.toString() + ']')) 204 | expect(out).toEqual(expected_neg_a_array[i]) 205 | } 206 | }) 207 | 208 | it('MapToCurve()', async() => { 209 | const circuit = 'map_to_curve_test' 210 | 211 | // Step 1 212 | const step1_tv1 = (Z * (u0 * u0)) % p 213 | const step1_tv1_array = bigint_to_array(64, 4, step1_tv1) 214 | // Step 2 215 | const step2_tv2 = (step1_tv1 * step1_tv1) % p 216 | const step2_tv2_array = bigint_to_array(64, 4, step2_tv2) 217 | // Step 3 218 | const step3_tv1_plus_tv2 = (step1_tv1 + step2_tv2) % p 219 | const step3_tv1_plus_tv2_array = bigint_to_array(64, 4, step3_tv1_plus_tv2) 220 | // Step 4 221 | const step4_inv0_x1 = field.inv(step3_tv1_plus_tv2) 222 | const step4_inv0_x1_array = bigint_to_array(64, 4, step4_inv0_x1) 223 | // Step 6 224 | const step6_x1_plus_1 = step4_inv0_x1 + BigInt(1) 225 | const step6_x1_plus_1_array = bigint_to_array(64, 4, step6_x1_plus_1) 226 | // Step 8 check 227 | const step8_x1_mul_c1 = (step6_x1_plus_1 * c1) % p 228 | const step8_x1_mul_c1_array = bigint_to_array(64, 4, step8_x1_mul_c1) 229 | // Step 9 230 | const gx1 = (step8_x1_mul_c1 * step8_x1_mul_c1) % p 231 | const step9_gx1_array = bigint_to_array(64, 4, gx1) 232 | // Step 10 233 | const step10_gx1 = (gx1 + A) % p 234 | const step10_gx1_array = bigint_to_array(64, 4, step10_gx1) 235 | // Step 11 236 | const step11_gx1_mul_x1 = (step10_gx1 * step8_x1_mul_c1) % p 237 | const step11_gx1_mul_x1_array = bigint_to_array(64, 4, step11_gx1_mul_x1) 238 | // Step 12 239 | const step12_gx1 = (step11_gx1_mul_x1 + B) % p 240 | const step12_gx1_array = bigint_to_array(64, 4, step12_gx1) 241 | // Step 13 242 | const step13_x2 = (step1_tv1 * step8_x1_mul_c1) % p 243 | const step13_x2_array = bigint_to_array(64, 4, step13_x2) 244 | // Step 14 245 | const step14_tv2 = (step1_tv1 * step2_tv2) % p 246 | const step14_tv2_array = bigint_to_array(64, 4, step14_tv2) 247 | // Step 15 248 | const step15_gx2 = (step12_gx1 * step14_tv2) % p 249 | const step15_gx2_array = bigint_to_array(64, 4, step15_gx2) 250 | // Step 16 251 | let gx1_sqrt = field.sqrt(step12_gx1) 252 | let gx2_sqrt = field.sqrt(step15_gx2) 253 | let step16_expected_x 254 | let step16_expected_y2 255 | if (gx1_sqrt == null) { 256 | gx1_sqrt = BigInt(1) 257 | step16_expected_x = step13_x2 258 | step16_expected_y2 = step15_gx2 259 | } 260 | 261 | if (gx2_sqrt == null) { 262 | gx2_sqrt = BigInt(1) 263 | step16_expected_x = step8_x1_mul_c1 264 | step16_expected_y2 = step12_gx1 265 | } 266 | const step16_expected_x_array = bigint_to_array(64, 4, step16_expected_x) 267 | const step16_expected_y2_array = bigint_to_array(64, 4, step16_expected_y2) 268 | // Step 19 269 | const step19_sqrt_y2 = field.sqrt(step16_expected_y2) 270 | const step19_sqrt_y2_array = bigint_to_array(64, 4, step16_expected_y2) 271 | expect(step19_sqrt_y2).not.toEqual(null) 272 | 273 | const mapped = iso_map(step16_expected_x, step19_sqrt_y2, p) 274 | 275 | // Generate witness 276 | const circuitInputs = stringifyBigInts({ 277 | u: bigint_to_array(64, 4, u0), 278 | gx1_sqrt: bigint_to_array(64, 4, gx1_sqrt), 279 | gx2_sqrt: bigint_to_array(64, 4, gx2_sqrt), 280 | y_pos: bigint_to_array(64, 4, step19_sqrt_y2), 281 | x_mapped: bigint_to_array(64, 4, mapped.x), 282 | y_mapped: bigint_to_array(64, 4, mapped.y), 283 | }) 284 | const witness = await genWitness(circuit, circuitInputs) 285 | 286 | // Step 1 check 287 | for (let i = 0; i < 4; i ++) { 288 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step1_tv1.out[' + i.toString() + ']')) 289 | expect(out).toEqual(step1_tv1_array[i]) 290 | } 291 | 292 | // Step 2 check 293 | for (let i = 0; i < 4; i ++) { 294 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step2_tv2.out[' + i.toString() + ']')) 295 | expect(out).toEqual(step2_tv2_array[i]) 296 | } 297 | 298 | // Step 3 check 299 | for (let i = 0; i < 4; i ++) { 300 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step3_tv1_plus_tv2.out[' + i.toString() + ']')) 301 | expect(out).toEqual(step3_tv1_plus_tv2_array[i]) 302 | } 303 | 304 | // Step 4 check 305 | for (let i = 0; i < 4; i ++) { 306 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step4_inv0_x1.out[' + i.toString() + ']')) 307 | expect(out).toEqual(step4_inv0_x1_array[i]) 308 | } 309 | 310 | // Step 5 check 311 | const step5_e1 = BigInt(await getSignalByName(circuit, witness, 'main.step5_e1.out')) 312 | expect(step5_e1).toEqual(BigInt(0)) 313 | 314 | // Step 6 check 315 | for (let i = 0; i < 4; i ++) { 316 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step6_x1_plus_1.out[' + i.toString() + ']')) 317 | expect(out).toEqual(step6_x1_plus_1_array[i]) 318 | } 319 | 320 | // Step 7 check 321 | for (let i = 0; i < 4; i ++) { 322 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step7_cmov.out[' + i.toString() + ']')) 323 | expect(out).toEqual(step6_x1_plus_1_array[i]) 324 | } 325 | 326 | // Step 8 327 | for (let i = 0; i < 4; i ++) { 328 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step8_x1_mul_c1.out[' + i.toString() + ']')) 329 | expect(out).toEqual(step8_x1_mul_c1_array[i]) 330 | } 331 | 332 | // Step 9 check 333 | for (let i = 0; i < 4; i ++) { 334 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step9_gx1.out[' + i.toString() + ']')) 335 | expect(out).toEqual(step9_gx1_array[i]) 336 | } 337 | 338 | // Step 10 check 339 | for (let i = 0; i < 4; i ++) { 340 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step10_gx1.out[' + i.toString() + ']')) 341 | expect(out).toEqual(step10_gx1_array[i]) 342 | } 343 | 344 | // Step 11 check 345 | for (let i = 0; i < 4; i ++) { 346 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step11_gx1_mul_x1.out[' + i.toString() + ']')) 347 | expect(out).toEqual(step11_gx1_mul_x1_array[i]) 348 | } 349 | 350 | // Step 12 check 351 | for (let i = 0; i < 4; i ++) { 352 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step12_gx1.out[' + i.toString() + ']')) 353 | expect(out).toEqual(step12_gx1_array[i]) 354 | } 355 | 356 | // Step 13 check 357 | for (let i = 0; i < 4; i ++) { 358 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step13_x2.out[' + i.toString() + ']')) 359 | expect(out).toEqual(step13_x2_array[i]) 360 | } 361 | 362 | // Step 14 check 363 | for (let i = 0; i < 4; i ++) { 364 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step14_tv2.out[' + i.toString() + ']')) 365 | expect(out).toEqual(step14_tv2_array[i]) 366 | } 367 | 368 | // Step 15 check 369 | for (let i = 0; i < 4; i ++) { 370 | const out = BigInt(await getSignalByName(circuit, witness, 'main.step15_gx2.out[' + i.toString() + ']')) 371 | expect(out).toEqual(step15_gx2_array[i]) 372 | } 373 | 374 | // Steps 16-18 check 375 | for (let i = 0; i < 4; i ++) { 376 | const x = BigInt(await getSignalByName(circuit, witness, 'main.step16_x_y2_selector.x[' + i.toString() + ']')) 377 | const y2 = BigInt(await getSignalByName(circuit, witness, 'main.step16_x_y2_selector.y2[' + i.toString() + ']')) 378 | expect(x).toEqual(step16_expected_x_array[i]) 379 | expect(y2).toEqual(step16_expected_y2_array[i]) 380 | } 381 | 382 | // Step 19 check 383 | for (let i = 0; i < 4; i ++) { 384 | const step19_expected_y2 = BigInt(await getSignalByName(circuit, witness, 'main.step19_expected_y2.out[' + i.toString() + ']')) 385 | expect(step19_expected_y2).toEqual(step16_expected_y2_array[i]) 386 | } 387 | 388 | // Step 20 check 389 | const step20_e3 = BigInt(await getSignalByName(circuit, witness, 'main.step20_e3.out')) 390 | const sgn0_u = sgn0(u0) 391 | const sgn0_y = sgn0(step19_sqrt_y2) 392 | expect(step20_e3).toEqual(sgn0_u === sgn0_y ? BigInt(1) : BigInt(0)) 393 | 394 | let expected_y 395 | if (step20_e3 === BigInt(1)) { 396 | expected_y = step19_sqrt_y2 397 | } else { 398 | expected_y = p - step19_sqrt_y2 399 | } 400 | const expected_y_array = bigint_to_array(64, 4, expected_y) 401 | 402 | // Step 21 check 403 | for (let i = 0; i < 4; i ++) { 404 | const step21_x = BigInt(await getSignalByName(circuit, witness, 'main.step16_x_y2_selector.x[' + i.toString() + ']')) 405 | expect(step21_x).toEqual(step16_expected_x_array[i]) 406 | const step21_y = BigInt(await getSignalByName(circuit, witness, 'main.step21_y.out[' + i.toString() + ']')) 407 | expect(step21_y).toEqual(expected_y_array[i]) 408 | } 409 | 410 | const expected_result = map_to_curve(u0) 411 | expect(mapped.x).toEqual(expected_result.x) 412 | expect(mapped.y).toEqual(expected_result.y) 413 | }) 414 | 415 | it('map_to_curve for abcdef0123456789 u1', async () => { 416 | const circuit = 'map_to_curve_test' 417 | const msg = 'abcdef0123456789' 418 | const uniform_bytes = expand_msg_xmd(str_to_array(msg)) 419 | 420 | //const u0_bytes = uniform_bytes.slice(0, 48) 421 | //const u0 = ff.utils.beBuff2int(Buffer.from(u0_bytes)) % p 422 | 423 | const u1_bytes = uniform_bytes.slice(48) 424 | const u1 = ff.utils.beBuff2int(Buffer.from(u1_bytes)) % p 425 | 426 | const q1 = map_to_curve(u1) 427 | 428 | const inputs = generate_inputs(msg) 429 | 430 | // Generate witness 431 | const circuitInputs = stringifyBigInts({ 432 | u: bigint_to_array(64, 4, u1), 433 | gx1_sqrt: inputs.q1_gx1_sqrt, 434 | gx2_sqrt: inputs.q1_gx2_sqrt, 435 | y_pos: inputs.q1_y_pos, 436 | x_mapped: inputs.q1_x_mapped, 437 | y_mapped: inputs.q1_y_mapped, 438 | }) 439 | 440 | // x and y in E' - iso_map is not applied 441 | const expected_x_out_array = bigint_to_array(64, 4, q1.x) 442 | const expected_y_out_array = bigint_to_array(64, 4, q1.y) 443 | 444 | const witness = await genWitness(circuit, circuitInputs) 445 | for (let i = 0; i < 4; i ++) { 446 | const x_out = BigInt(await getSignalByName(circuit, witness, 'main.x[' + i.toString() + ']')) 447 | expect(x_out).toEqual(expected_x_out_array[i]) 448 | const y_out = BigInt(await getSignalByName(circuit, witness, 'main.y[' + i.toString() + ']')) 449 | expect(y_out).toEqual(expected_y_out_array[i]) 450 | } 451 | }) 452 | }) 453 | --------------------------------------------------------------------------------