diff --git a/test/bigint.test.ts b/test/bigint.test.ts index b1d12b5..1242f03 100644 --- a/test/bigint.test.ts +++ b/test/bigint.test.ts @@ -8,21 +8,7 @@ const F1Field = require("ffjavascript").F1Field; const Scalar = require("ffjavascript").Scalar; exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); const Fr = new F1Field(exports.p); - -function bigint_to_array(n: number, k: number, x: bigint) { - let mod: bigint = 1n; - for (var idx = 0; idx < n; idx++) { - mod = mod * 2n; - } - - let ret: bigint[] = []; - var x_temp: bigint = x; - for (var idx = 0; idx < k; idx++) { - ret.push(x_temp % mod); - x_temp = x_temp / mod; - } - return ret; -} +import { bigint_to_array } from './utils' describe("BigMod n = 2, k = 2 exhaustive", function() { this.timeout(1000 * 1000); diff --git a/test/circuits/test_secp256k1_fp_add.circom b/test/circuits/test_secp256k1_fp_add.circom new file mode 100644 index 0000000..d1cbb0a --- /dev/null +++ b/test/circuits/test_secp256k1_fp_add.circom @@ -0,0 +1,37 @@ +pragma circom 2.0.2; + +include "../../circuits/bigint.circom"; +include "../../circuits/secp256k1_func.circom"; + +template Secp256k1_Fp_Add(n, k) { + signal input a[k]; + signal input b[k]; + signal output out[k]; + + component adder = BigAdd(n, k); + for (var i = 0; i < k; i ++) { + adder.a[i] <== a[i]; + adder.b[i] <== b[i]; + } + + component mod = BigMod(n, k); + + for (var i = 0; i < k + 1; i ++) { + mod.a[i] <== adder.out[i]; + } + + for (var i = k + 1; i < k * 2; i ++) { + mod.a[i] <== 0; + } + + mod.b[0] <== 18446744069414583343; + mod.b[1] <== 18446744073709551615; + mod.b[2] <== 18446744073709551615; + mod.b[3] <== 18446744073709551615; + + for (var i = 0; i < k; i ++) { + out[i] <== mod.mod[i]; + } +} + +component main {public [a, b]} = Secp256k1_Fp_Add(64, 4); diff --git a/test/circuits/test_secp256k1_fp_mod.circom b/test/circuits/test_secp256k1_fp_mod.circom new file mode 100644 index 0000000..e0389ab --- /dev/null +++ b/test/circuits/test_secp256k1_fp_mod.circom @@ -0,0 +1,29 @@ +pragma circom 2.0.2; + +include "../../circuits/bigint.circom"; + +template Secp256k1_Fp_Mod() { + var n = 64; + var k = 4; + + signal input a[k]; + signal output out[k]; + + component m = BigMod(n, k); + + for (var i = 0; i < k; i ++) { + m.a[i] <== a[i]; + m.a[k + i] <== 0; + } + + m.b[0] <== 18446744069414583343; + m.b[1] <== 18446744073709551615; + m.b[2] <== 18446744073709551615; + m.b[3] <== 18446744073709551615; + + for (var i = 0; i < k; i ++) { + out[i] <== m.mod[i]; + } +} + +component main {public [a]} = Secp256k1_Fp_Mod(); diff --git a/test/circuits/test_secp256k1_fp_mul.circom b/test/circuits/test_secp256k1_fp_mul.circom new file mode 100644 index 0000000..07769f0 --- /dev/null +++ b/test/circuits/test_secp256k1_fp_mul.circom @@ -0,0 +1,25 @@ +pragma circom 2.0.2; + +include "../../circuits/bigint.circom"; + +template Secp256k1_Fp_Mul(n, k) { + signal input a[k]; + signal input b[k]; + signal output out[k]; + + component mul_mod_p = BigMultModP(n, k); + for (var i = 0; i < k; i ++) { + mul_mod_p.a[i] <== a[i]; + mul_mod_p.b[i] <== b[i]; + } + mul_mod_p.p[0] <== 18446744069414583343; + mul_mod_p.p[1] <== 18446744073709551615; + mul_mod_p.p[2] <== 18446744073709551615; + mul_mod_p.p[3] <== 18446744073709551615; + + for (var i = 0; i < k; i ++) { + out[i] <== mul_mod_p.out[i]; + } +} + +component main {public [a, b]} = Secp256k1_Fp_Mul(64, 4); diff --git a/test/circuits/test_secp256k1_fp_sub.circom b/test/circuits/test_secp256k1_fp_sub.circom new file mode 100644 index 0000000..3fdd54c --- /dev/null +++ b/test/circuits/test_secp256k1_fp_sub.circom @@ -0,0 +1,27 @@ +pragma circom 2.0.2; + +include "../../circuits/bigint.circom"; +include "../../circuits/secp256k1_func.circom"; + +template Secp256k1_Fp_Sub(n, k) { + signal input a[k]; + signal input b[k]; + signal output out[k]; + + component sub = BigSubModP(n, k); + for (var i = 0; i < k; i ++) { + sub.a[i] <== a[i]; + sub.b[i] <== b[i]; + } + + sub.p[0] <== 18446744069414583343; + sub.p[1] <== 18446744073709551615; + sub.p[2] <== 18446744073709551615; + sub.p[3] <== 18446744073709551615; + + for (var i = 0; i < k; i ++) { + out[i] <== sub.out[i]; + } +} + +component main {public [a, b]} = Secp256k1_Fp_Sub(64, 4); diff --git a/test/ecdsa.test.ts b/test/ecdsa.test.ts index a696b7d..f88402b 100644 --- a/test/ecdsa.test.ts +++ b/test/ecdsa.test.ts @@ -9,47 +9,7 @@ const F1Field = require("ffjavascript").F1Field; const Scalar = require("ffjavascript").Scalar; exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); const Fr = new F1Field(exports.p); - -function bigint_to_tuple(x: bigint) { - let mod: bigint = 2n ** 64n; - let ret: [bigint, bigint, bigint, bigint] = [0n, 0n, 0n, 0n]; - - var x_temp: bigint = x; - for (var idx = 0; idx < ret.length; idx++) { - ret[idx] = x_temp % mod; - x_temp = x_temp / mod; - } - return ret; -} - -function bigint_to_array(n: number, k: number, x: bigint) { - let mod: bigint = 1n; - for (var idx = 0; idx < n; idx++) { - mod = mod * 2n; - } - - let ret: bigint[] = []; - var x_temp: bigint = x; - for (var idx = 0; idx < k; idx++) { - ret.push(x_temp % mod); - x_temp = x_temp / mod; - } - return ret; -} - -// converts x = sum of a[i] * 2 ** (small_stride * i) for 0 <= 2 ** small_stride - 1 -// to: sum of a[i] * 2 ** (stride * i) -function get_strided_bigint(stride: bigint, small_stride: bigint, x: bigint) { - var ret: bigint = 0n; - var exp: bigint = 0n; - while (x > 0) { - var mod: bigint = x % (2n ** small_stride); - ret = ret + mod * (2n ** (stride * exp)); - x = x / (2n ** small_stride); - exp = exp + 1n; - } - return ret; -} +import { bigint_to_array, bigint_to_tuple, get_strided_bigint } from './utils' describe("ECDSAPrivToPub", function () { this.timeout(1000 * 1000); diff --git a/test/secp256k1.test.ts b/test/secp256k1.test.ts index 09e356e..90a5977 100644 --- a/test/secp256k1.test.ts +++ b/test/secp256k1.test.ts @@ -9,21 +9,7 @@ const F1Field = require("ffjavascript").F1Field; const Scalar = require("ffjavascript").Scalar; exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); const Fr = new F1Field(exports.p); - -function bigint_to_array(n: number, k: number, x: bigint) { - let mod: bigint = 1n; - for (var idx = 0; idx < n; idx++) { - mod = mod * 2n; - } - - let ret: bigint[] = []; - var x_temp: bigint = x; - for (var idx = 0; idx < k; idx++) { - ret.push(x_temp % mod); - x_temp = x_temp / mod; - } - return ret; -} +import { bigint_to_array } from './utils' describe("Secp256k1AddUnequal", function () { this.timeout(1000 * 1000); diff --git a/test/secp256k1_Fp.test.ts b/test/secp256k1_Fp.test.ts new file mode 100644 index 0000000..7f5589d --- /dev/null +++ b/test/secp256k1_Fp.test.ts @@ -0,0 +1,204 @@ +import path = require("path"); +import { expect, assert } from 'chai'; +const circom_tester = require('circom_tester'); +const wasm_tester = circom_tester.wasm; + +function bigint_to_array(n: number, k: number, x: bigint) { + let mod: bigint = 1n; + for (var idx = 0; idx < n; idx++) { + mod = mod * 2n; + } + + let ret: bigint[] = []; + var x_temp: bigint = x; + for (var idx = 0; idx < k; idx++) { + ret.push(x_temp % mod); + x_temp = x_temp / mod; + } + return ret; +} + +let P = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); +let n = 64; +let k = 4 +let p_arr = bigint_to_array(n, k, P); + +let rands: Array = [ + 37341064718519768n, 80197949145034304n, 52311183102518480n, 79170924519984336n, + 72040116375830584n, 96811527250814784n, 83704621808075920n, 97335777836843424n, + 36191564228607768n, 68640101432687264n, 83088292537602432n, 85829122398627552n, + 76037297289358544n, 58257037970984608n, 12372783206242798n, 62671901797711336n, + 48726329364784608n, 77742828232298384n, 12962932632943170n, 34319361389693960n, +]; + +let circuit: any + +describe("secp256k1 field", function () { + it("p should be correct", function () { + expect(p_arr[0]).to.equal(18446744069414583343n); + expect(p_arr[1]).to.equal(18446744073709551615n); + expect(p_arr[2]).to.equal(18446744073709551615n); + expect(p_arr[3]).to.equal(18446744073709551615n); + }); +}); + +describe("secp256k1 Fp modulo", function () { + // runs circom compilation + let circuit: any; + before(async function () { + circuit = await wasm_tester(path.join(__dirname, "circuits", "test_secp256k1_fp_mod.circom")); + }); + + // v, v mod P + var test_cases: Array<[bigint, bigint]> = []; + + for (let i = -5; i < 5; i ++) { + let v = P + BigInt(i); + let v_mod_p = v % P; + test_cases.push([v, v_mod_p]); + } + + var test_fp_mod = function(x: [bigint, bigint]) { + const [v, v_mod_p] = x; + const a_array = bigint_to_array(n, k, v); + const expected_mod = bigint_to_array(n, k, v_mod_p); + + it('Testing a: ' + v, async function() { + let witness = await circuit.calculateWitness({"a": a_array }); + let mod_output = witness.slice(1, 1 + k); + + for (let i = 0; i < expected_mod.length; i ++) { + expect(mod_output[i]).to.equal(expected_mod[i]); + } + }); + } + + test_cases.forEach(test_fp_mod); +}); + +describe("secp256k1 Fp addition", function () { + let circuit: any; + before(async function () { + circuit = await wasm_tester(path.join(__dirname, "circuits", "test_secp256k1_fp_add.circom")); + }); + + // a, b, (a + b) mod P + var test_cases: Array<[bigint, bigint, bigint]> = []; + + for (let rand of rands) { + const a = (P - (rand)) % P; + const b = (P + (rand * 123456n * 987654n * 92748572n)) % P; + assert((a + b) > P); + let res = (a + b) % P; + + // test cases where a+b will overflow + test_cases.push([a, b, res]); + + // test cases where a+b will not overflow + test_cases.push([rand, rand, (rand + rand) % P]); + } + + var test_fp_add = function(x: [bigint, bigint, bigint]) { + let [a, b, res] = x; + let a_array = bigint_to_array(n, k, a); + let b_array = bigint_to_array(n, k, b); + let res_array = bigint_to_array(n, k, res); + + it('Testing ' + a + ' + ' + b, async function() { + let witness = await circuit.calculateWitness({ a: a_array, b: b_array }); + let output = witness.slice(1, 1 + k); + for (let i = 0; i < res_array.length; i ++) { + expect(output[i]).to.equal(res_array[i]); + } + }); + } + + test_cases.forEach(test_fp_add); +}) + +describe("secp256k1 Fp subtraction", function () { + let circuit: any; + before(async function () { + circuit = await wasm_tester(path.join(__dirname, "circuits", "test_secp256k1_fp_sub.circom")); + }); + + // a, b, (a - b) mod P + var test_cases: Array<[bigint, bigint, bigint]> = []; + + for (let i = 0; i < rands.length; i += 2){ + // roughly half of these test cases will underflow + const rand = rands[i]; + const rand2 = rands[i + 1]; + const a = (P - (rand * 3n)) % P; + const b = (P - (rand2 * 2n)) % P; + + let a_minus_b = a - b; + if (a_minus_b < 0n) { + a_minus_b = P - (-1n * a_minus_b); + } else { + a_minus_b = a_minus_b % P; + } + assert(a_minus_b >= 0n); + + test_cases.push([a, b, a_minus_b]); + } + + var test_fp_sub = function(x: [bigint, bigint, bigint]) { + let [a, b, res] = x; + let a_array = bigint_to_array(n, k, a); + let b_array = bigint_to_array(n, k, b); + let res_array = bigint_to_array(n, k, res); + + it('Testing ' + a + ' - ' + b, async function() { + let witness = await circuit.calculateWitness({ a: a_array, b: b_array }); + let output = witness.slice(1, 1 + k); + for (let i = 0; i < res_array.length; i ++) { + expect(output[i]).to.equal(res_array[i]); + } + }); + } + + test_cases.forEach(test_fp_sub); +}) + +describe("secp256k1 Fp multiplication", function () { + let circuit: any; + before(async function () { + circuit = await wasm_tester(path.join(__dirname, "circuits", "test_secp256k1_fp_mul.circom")); + }); + + // a, b, (a * b) mod P + var test_cases: Array<[bigint, bigint, bigint]> = []; + + for (let i = 0; i < rands.length; i += 2){ + const rand = rands[i]; + const rand2 = rands[i + 1]; + const a = (P - (rand * 3n)) % P; + const b = (P - (rand2 * 2n)) % P; + + let a_mul_b = (a * b) % P; + + // test cases where a*b will probably overflow + test_cases.push([a, b, a_mul_b]); + + // test cases where a*b will probably not overflow + test_cases.push([rand, rand2, (rand * rand2) % P]); + } + + var test_fp_mul = function(x: [bigint, bigint, bigint]) { + let [a, b, res] = x; + let a_array = bigint_to_array(n, k, a); + let b_array = bigint_to_array(n, k, b); + let res_array = bigint_to_array(n, k, res); + + it('Testing ' + a + ' * ' + b, async function() { + let witness = await circuit.calculateWitness({ a: a_array, b: b_array }); + let output = witness.slice(1, 1 + k); + for (let i = 0; i < res_array.length; i ++) { + expect(output[i]).to.equal(res_array[i]); + } + }); + } + + test_cases.forEach(test_fp_mul); +}) diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 0000000..9342ae4 --- /dev/null +++ b/test/utils.ts @@ -0,0 +1,42 @@ +function bigint_to_tuple(x: bigint) { + let mod: bigint = 2n ** 64n; + let ret: [bigint, bigint, bigint, bigint] = [0n, 0n, 0n, 0n]; + + var x_temp: bigint = x; + for (var idx = 0; idx < ret.length; idx++) { + ret[idx] = x_temp % mod; + x_temp = x_temp / mod; + } + return ret; +} + +function bigint_to_array(n: number, k: number, x: bigint) { + let mod: bigint = 1n; + for (var idx = 0; idx < n; idx++) { + mod = mod * 2n; + } + + let ret: bigint[] = []; + var x_temp: bigint = x; + for (var idx = 0; idx < k; idx++) { + ret.push(x_temp % mod); + x_temp = x_temp / mod; + } + return ret; +} + +// converts x = sum of a[i] * 2 ** (small_stride * i) for 0 <= 2 ** small_stride - 1 +// to: sum of a[i] * 2 ** (stride * i) +function get_strided_bigint(stride: bigint, small_stride: bigint, x: bigint) { + var ret: bigint = 0n; + var exp: bigint = 0n; + while (x > 0) { + var mod: bigint = x % (2n ** small_stride); + ret = ret + mod * (2n ** (stride * exp)); + x = x / (2n ** small_stride); + exp = exp + 1n; + } + return ret; +} + +export { bigint_to_array, bigint_to_tuple, get_strided_bigint }