Skip to content

Commit e24d698

Browse files
authored
Merge pull request #8 from zkp-application/add_mont_exp
feat(mont exponent): add mont exp alg
2 parents 60b1fe1 + 6f6427a commit e24d698

File tree

10 files changed

+366
-88
lines changed

10 files changed

+366
-88
lines changed

circuits/mont_exp.circom

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
include "./montgomery.circom"
2+
// r = 2 ^ (n * w). n is the modulus word count
3+
// p_a = a * r mod modulus
4+
// p_A = r mod modulus
5+
// output A = (a ^ e) mod(p)
6+
template mont_exp(w, nb) {
7+
signal input p_a[nb];
8+
signal input p_A[nb];
9+
10+
signal input exp;
11+
signal input m0inv;
12+
signal input modulus[nb];
13+
14+
15+
signal output out[nb];
16+
17+
exp === 65537;
18+
19+
component muls[19];
20+
for (var i = 0; i< 19; i++) {
21+
muls[i] = mont_cios(w, nb);
22+
23+
muls[i].m0inv <-- m0inv;
24+
25+
for (var j = 0; j < nb; j++) {
26+
muls[i].modulus[j] <-- modulus[j];
27+
}
28+
29+
if (i == 0) {
30+
for (var j = 0; j < nb; j++) {
31+
muls[i].x[j] <-- p_A[j];
32+
muls[i].y[j] <-- p_A[j];
33+
}
34+
} else if (i == 1 || i == 18) {
35+
for (var j = 0; j < nb; j++) {
36+
muls[i].x[j] <-- muls[i - 1].out[j];
37+
muls[i].y[j] <-- p_a[j];
38+
}
39+
} else {
40+
for (var j = 0; j < nb; j++) {
41+
muls[i].x[j] <-- muls[i - 1].out[j];
42+
muls[i].y[j] <-- muls[i - 1].out[j];
43+
}
44+
}
45+
}
46+
47+
// A <- MontPr(p_A, 1);
48+
component result_mul = mont_cios(w, nb);
49+
50+
result_mul.m0inv <-- m0inv;
51+
52+
for (var j = 0; j < nb; j++) {
53+
result_mul.modulus[j] <-- modulus[j];
54+
result_mul.x[j] <-- muls[18].out[j];
55+
56+
if (j == 0) {
57+
result_mul.y[j] <-- 1;
58+
}else {
59+
result_mul.y[j] <-- 0;
60+
}
61+
}
62+
63+
64+
for (var i = 0; i< nb; i++) {
65+
out[i] <-- result_mul.out[i];
66+
}
67+
}

circuits/montgomery.circom

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ template mont_mul(w, nb) {
1111

1212
signal output out [nb];
1313

14-
component montPr = mont_pr_cios(w, nb);
14+
component montPr = mont_cios(w, nb);
1515

1616
for (var i = 0; i < nb; i++) {
1717
montPr.x[i] <-- pre_compute_a[i];
@@ -29,7 +29,7 @@ template mont_mul(w, nb) {
2929
// Montgomery modular multiplication. CIOS alg
3030
// Souce paper from https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf
3131
// (x * y) mod modulus
32-
template mont_pr_cios(w, nb) {
32+
template mont_cios(w, nb) {
3333
signal input x[nb];
3434
signal input y[nb];
3535
signal input modulus[nb];
@@ -92,7 +92,6 @@ template mont_pr_cios(w, nb) {
9292
}
9393

9494
// a > modulus ? a - modulus : a;
95-
//
9695
template normalize(w, nb) {
9796
signal input a[nb];
9897
signal input modulus[nb];
@@ -123,7 +122,6 @@ template normalize(w, nb) {
123122
}
124123
} else {
125124
// out = a - m;
126-
127125
for (var i = 0; i< nb; i++) {
128126
temp = a[i];
129127

test/circuits/mont_exp.circom

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include "../../circuits/mont_exp.circom"
2+
3+
component main = mont_exp(64, 32);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
include "../../circuits/montgomery.circom"
22

3-
component main = mont_pr_cios(64, 4);
3+
component main = mont_cios(64, 32);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include "../../circuits/montgomery.circom"
2+
3+
component main = mont_cios(64, 4);

test/mont_exp.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const path = require("path");
2+
3+
const bigInt = require("big-integer");
4+
const Scalar = require("ffjavascript").Scalar;
5+
const tester = require("circom").tester;
6+
7+
const { splitToArray } = require("./util.js");
8+
9+
10+
describe("montgomery exponent 64bits/4words", function () {
11+
this.timeout(100000);
12+
13+
let circuit;
14+
before(async () => {
15+
circuit = await tester(path.join(__dirname, "circuits", "mont_exp.circom"));
16+
});
17+
18+
it("64bits/4words. and mod = 52435875175126190479447740508185965837690552500527637822603658699938581184513", async () => {
19+
20+
const modulus = bigInt("24226501697440012621102249466312043787685293040734225606346036389705515508545746221669035424138747582133889500686654172873671086178893587422987328751464627501601101326475761646014534358699943642495332701081302954020983110372109611581202820849485662540890985814355975252780310958088652613376767040069489530039075302709233494829280591680666351811024913107949144932224439129715181798714328219977771472462901856297952813239115577652450722815852332547886777292613005505949100406231716599634852632308325816916535875123863510650526931916871614411907700873376659841257216885666098127478325534982891697988739616416855214839339");
21+
22+
const m0inv = "4736324870124914557";
23+
24+
const sign = bigInt("27166015521685750287064830171899789431519297967327068200526003963687696216659347317736779094212876326032375924944649760206771585778103092909024744594654706678288864890801000499430246054971129440518072676833029702477408973737931913964693831642228421821166326489172152903376352031367604507095742732994611253344812562891520292463788291973539285729019102238815435155266782647328690908245946607690372534644849495733662205697837732960032720813567898672483741410294744324300408404611458008868294953357660121510817012895745326996024006347446775298357303082471522757091056219893320485806442481065207020262668955919408138704593");
25+
26+
// p_A = (2 ** (64 * 32)) % modulus
27+
const p_A = bigInt("8090504373870994679612627222357908172758809628981258425784309037819139630322144671528165987384165881554828460235243846620448472971597333672100823634983655619029776040825234445735663391689708464300724937302764614255809108270510144580635273488990507929690660037680329790107264933452413195230785359054440855482839030680434847591404383105898217683831262927377177125853366529615844393994132094172487120401275260427990790479346279705147628336449313356516920320620281725278025278479103610090304469418605506553142667456793187284519065351481384226307824293012777494286385249767131477166992943622060450204816237194204381391317");
28+
29+
// p_a = (a * r) % modulus
30+
const p_a = bigInt("19072154410336807139418696976260212968540675922546765752736826145996869655756622548873347455947248021050921538311171789715523470247724251557601058125103625700941761034646698858071310182565269768981168519584711995302991904285228697391419713388819104994438114806716920075484924844137359380134009667510391269169076279760491579238026910153675786149676670940528964362751766341740256137008228081948558082219709238817789460567174381895573690274674295098414261384354408417638073989695885913898217149419056478104428576834037267113663842145482274989462614945861102288293269483256693676536058880107741774817376564564751474668870");
31+
32+
var testCases = [{
33+
description: "calc powerMod",
34+
input: {
35+
// 1844674407370955161600
36+
p_a: splitToArray(p_a, 64, 32),
37+
p_A: splitToArray(p_A, 64, 32),
38+
39+
exp: 65537,
40+
modulus: splitToArray(modulus, 64, 32),
41+
m0inv: m0inv,
42+
},
43+
output: { out: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] },
44+
}];
45+
46+
47+
for (var i = 0; i < testCases.length; i++) {
48+
const witness = await circuit.calculateWitness(testCases[i].input, true);
49+
50+
await circuit.assertOut(witness, testCases[i].output);
51+
}
52+
});
53+
});
54+
55+

test/montgomery_reduction.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe("montgomery reduction 64bits/4words", function () {
1212

1313
let circuit;
1414
before(async () => {
15-
circuit = await tester(path.join(__dirname, "circuits", "montgomery.circom"));
15+
circuit = await tester(path.join(__dirname, "circuits", "montgomery_64_4.circom"));
1616
});
1717

1818
it("64bits/4words. and mod = 52435875175126190479447740508185965837690552500527637822603658699938581184513", async () => {
@@ -61,4 +61,3 @@ describe("montgomery reduction 64bits/4words", function () {
6161
}
6262
});
6363
});
64-

test/montgomery_reduction_64_32.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const path = require("path");
2+
3+
const bigInt = require("big-integer");
4+
const Scalar = require("ffjavascript").Scalar;
5+
const tester = require("circom").tester;
6+
7+
const { splitToArray } = require("./util.js");
8+
9+
10+
describe("montgomery reduction 64bits/32words", function () {
11+
this.timeout(100000);
12+
13+
let circuit;
14+
before(async () => {
15+
circuit = await tester(path.join(__dirname, "circuits", "montgomery_64_32.circom"));
16+
});
17+
18+
it("64bits/4words. and mod = 52435875175126190479447740508185965837690552500527637822603658699938581184513", async () => {
19+
20+
const modulus = bigInt("27333278531038650284292446400685983964543820405055158402397263907659995327446166369388984969315774410223081038389734916442552953312548988147687296936649645550823280957757266695625382122565413076484125874545818286099364801140117875853249691189224238587206753225612046406534868213180954324992542640955526040556053150097561640564120642863954208763490114707326811013163227280580130702236406906684353048490731840275232065153721031968704703853746667518350717957685569289022049487955447803273805415754478723962939325870164033644600353029240991739641247820015852898600430315191986948597672794286676575642204004244219381500407");
21+
22+
const m0inv = "12890617734997456953";
23+
24+
const sign = bigInt("27166015521685750287064830171899789431519297967327068200526003963687696216659347317736779094212876326032375924944649760206771585778103092909024744594654706678288864890801000499430246054971129440518072676833029702477408973737931913964693831642228421821166326489172152903376352031367604507095742732994611253344812562891520292463788291973539285729019102238815435155266782647328690908245946607690372534644849495733662205697837732960032720813567898672483741410294744324300408404611458008868294953357660121510817012895745326996024006347446775298357303082471522757091056219893320485806442481065207020262668955919408138704593");
25+
26+
// p_A = (2 ** (64 * 32)) % modulus
27+
const p_A = bigInt("8090504373870994679612627222357908172758809628981258425784309037819139630322144671528165987384165881554828460235243846620448472971597333672100823634983655619029776040825234445735663391689708464300724937302764614255809108270510144580635273488990507929690660037680329790107264933452413195230785359054440855482839030680434847591404383105898217683831262927377177125853366529615844393994132094172487120401275260427990790479346279705147628336449313356516920320620281725278025278479103610090304469418605506553142667456793187284519065351481384226307824293012777494286385249767131477166992943622060450204816237194204381391317");
28+
29+
const r = bigInt("32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656");
30+
// p_a = (a * r) % modulus
31+
const p_a = sign.multiply(r).mod(modulus);
32+
33+
34+
const result = sign.multiply(sign).mod(modulus);
35+
36+
// console.log("result", splitToArray(result, 64, 32));
37+
38+
var testCases = [{
39+
description: "one word",
40+
input: {
41+
x: splitToArray(p_a, 64, 32),
42+
y: splitToArray(sign, 64, 32),
43+
modulus: splitToArray(modulus, 64, 32),
44+
m0inv: m0inv,
45+
},
46+
output: {
47+
out: [
48+
"6726338490320059920",
49+
"10912188918279059842",
50+
"9480684547274909370",
51+
"17957855819450938343",
52+
"14472935173278472155",
53+
"7079099867353219214",
54+
"16449430926296343172",
55+
"5104880238183543706",
56+
"10665479437761628207",
57+
"5855523454381758411",
58+
"10461162232393809384",
59+
"8506503615129027292",
60+
"713958493375598634",
61+
"6870643563889906329",
62+
"6496643502438963194",
63+
"3333526337816518494",
64+
"7059814241034669637",
65+
"11046528448239496056",
66+
"8821061847720768545",
67+
"8804814027843795770",
68+
"9550990298891609521",
69+
"4546832636732460115",
70+
"11005916837189288046",
71+
"6747247992486122322",
72+
"8062076261952398748",
73+
"11274417876012308159",
74+
"12082695046704314515",
75+
"9977902230884319566",
76+
"8117196317861485583",
77+
"6870310691445046553",
78+
"15178250919948156997",
79+
"13075022197409122935"]
80+
},
81+
}]
82+
83+
84+
for (var i = 0; i < testCases.length; i++) {
85+
const witness = await circuit.calculateWitness(testCases[i].input, true);
86+
87+
await circuit.assertOut(witness, testCases[i].output);
88+
}
89+
});
90+
});

test/montgomery_reduction_64_4.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const path = require("path");
2+
3+
const bigInt = require("big-integer");
4+
const Scalar = require("ffjavascript").Scalar;
5+
const tester = require("circom").tester;
6+
7+
const { splitToArray } = require("./util.js");
8+
9+
10+
describe("montgomery reduction 64bits/4words", function () {
11+
this.timeout(100000);
12+
13+
let circuit;
14+
before(async () => {
15+
circuit = await tester(path.join(__dirname, "circuits", "montgomery_64_4.circom"));
16+
});
17+
18+
it("64bits/4words. and mod = 52435875175126190479447740508185965837690552500527637822603658699938581184513", async () => {
19+
20+
const modulus = splitToArray(bigInt("52435875175126190479447740508185965837690552500527637822603658699938581184513"), 64, 4);
21+
const m0inv = "18446744069414584319";
22+
23+
24+
var testCases = [{
25+
description: "one word",
26+
input: {
27+
x: splitToArray(bigInt("1844674407370955161600"), 64, 4),
28+
y: splitToArray(bigInt("100"), 64, 4),
29+
// 52435875175126190479447740508185965837690552500527637822603658699938581184513
30+
modulus: modulus,
31+
m0inv: m0inv,
32+
},
33+
output: { out: [0, 0, 10000, 0] },
34+
}, {
35+
description: "two words",
36+
input: {
37+
x: splitToArray(bigInt("184467440737095516161561654"), 64, 4),
38+
y: splitToArray(bigInt("184467440737095516161561654"), 64, 4),
39+
// 52435875175126190479447740508185965837690552500527637822603658699938581184513
40+
modulus: modulus,
41+
m0inv: m0inv,
42+
},
43+
output: { out: [2438763215716, 31233080000000, 100000000000000, 0] },
44+
}, {
45+
description: "Four words",
46+
input: {
47+
x: ['9366702579560100036', '3153357315881433552', '1550162754830709279', '3363521531482743643'],
48+
y: splitToArray(bigInt("12389174593798789739243342131821798678432796312321"), 64, 4),
49+
// 52435875175126190479447740508185965837690552500527637822603658699938581184513
50+
modulus: modulus,
51+
m0inv: m0inv,
52+
},
53+
output: { out: ['11877590625878374308', '91176932193565308', '14825753355501141940', '4643043795916575606'] },
54+
}
55+
]
56+
57+
58+
for (var i = 0; i < testCases.length; i++) {
59+
const witness = await circuit.calculateWitness(testCases[i].input, true);
60+
await circuit.assertOut(witness, testCases[i].output);
61+
}
62+
});
63+
});

0 commit comments

Comments
 (0)