Skip to content

Commit b25867e

Browse files
committed
Add ECDSA circom circuits with PrivToPub template
1 parent f056775 commit b25867e

File tree

2 files changed

+73976
-0
lines changed

2 files changed

+73976
-0
lines changed

circuits/ecdsa/ecdsa.circom

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
pragma circom 2.0.2;
2+
3+
include "../../node_modules/circomlib/circuits/comparators.circom";
4+
include "../../node_modules/circomlib/circuits/multiplexer.circom";
5+
6+
include "../bigint/bigint.circom";
7+
include "../secp256k1/secp256k1.circom";
8+
include "../bigint/bigint_func.circom";
9+
include "ecdsa_func.circom";
10+
include "../secp256k1/secp256k1_func.circom";
11+
12+
// keys are encoded as (x, y) pairs with each coordinate being
13+
// encoded with k registers of n bits each
14+
template ECDSAPrivToPub(n, k) {
15+
var stride = 8;
16+
signal input privkey[k];
17+
signal output pubkey[2][k];
18+
19+
component n2b[k];
20+
for (var i = 0; i < k; i++) {
21+
n2b[i] = Num2Bits(n);
22+
n2b[i].in <== privkey[i];
23+
}
24+
25+
var num_strides = div_ceil(n * k, stride);
26+
// power[i][j] contains: [j * (1 << stride * i) * G] for 1 <= j < (1 << stride)
27+
var powers[num_strides][2 ** stride][2][k];
28+
powers = get_g_pow_stride8_table(n, k);
29+
30+
// contains a dummy point G * 2 ** 255 to stand in when we are adding 0
31+
// this point is sometimes an input into AddUnequal, so it must be guaranteed
32+
// to never equal any possible partial sum that we might get
33+
var dummyHolder[2][100] = get_dummy_point(n, k);
34+
var dummy[2][k];
35+
for (var i = 0; i < k; i++) dummy[0][i] = dummyHolder[0][i];
36+
for (var i = 0; i < k; i++) dummy[1][i] = dummyHolder[1][i];
37+
38+
// selector[i] contains a value in [0, ..., 2**i - 1]
39+
component selectors[num_strides];
40+
for (var i = 0; i < num_strides; i++) {
41+
selectors[i] = Bits2Num(stride);
42+
for (var j = 0; j < stride; j++) {
43+
var bit_idx1 = (i * stride + j) \ n;
44+
var bit_idx2 = (i * stride + j) % n;
45+
if (bit_idx1 < k) {
46+
selectors[i].in[j] <== n2b[bit_idx1].out[bit_idx2];
47+
} else {
48+
selectors[i].in[j] <== 0;
49+
}
50+
}
51+
}
52+
53+
// multiplexers[i][l].out will be the coordinates of:
54+
// selectors[i].out * (2 ** (i * stride)) * G if selectors[i].out is non-zero
55+
// (2 ** 255) * G if selectors[i].out is zero
56+
component multiplexers[num_strides][2];
57+
// select from k-register outputs using a 2 ** stride bit selector
58+
for (var i = 0; i < num_strides; i++) {
59+
for (var l = 0; l < 2; l++) {
60+
multiplexers[i][l] = Multiplexer(k, (1 << stride));
61+
multiplexers[i][l].sel <== selectors[i].out;
62+
for (var idx = 0; idx < k; idx++) {
63+
multiplexers[i][l].inp[0][idx] <== dummy[l][idx];
64+
for (var j = 1; j < (1 << stride); j++) {
65+
multiplexers[i][l].inp[j][idx] <== powers[i][j][l][idx];
66+
}
67+
}
68+
}
69+
}
70+
71+
component iszero[num_strides];
72+
for (var i = 0; i < num_strides; i++) {
73+
iszero[i] = IsZero();
74+
iszero[i].in <== selectors[i].out;
75+
}
76+
77+
// has_prev_nonzero[i] = 1 if at least one of the selections in privkey up to stride i is non-zero
78+
component has_prev_nonzero[num_strides];
79+
has_prev_nonzero[0] = OR();
80+
has_prev_nonzero[0].a <== 0;
81+
has_prev_nonzero[0].b <== 1 - iszero[0].out;
82+
for (var i = 1; i < num_strides; i++) {
83+
has_prev_nonzero[i] = OR();
84+
has_prev_nonzero[i].a <== has_prev_nonzero[i - 1].out;
85+
has_prev_nonzero[i].b <== 1 - iszero[i].out;
86+
}
87+
88+
signal partial[num_strides][2][k];
89+
for (var idx = 0; idx < k; idx++) {
90+
for (var l = 0; l < 2; l++) {
91+
partial[0][l][idx] <== multiplexers[0][l].out[idx];
92+
}
93+
}
94+
95+
component adders[num_strides - 1];
96+
signal intermed1[num_strides - 1][2][k];
97+
signal intermed2[num_strides - 1][2][k];
98+
for (var i = 1; i < num_strides; i++) {
99+
adders[i - 1] = Secp256k1AddUnequal();
100+
for (var idx = 0; idx < k; idx++) {
101+
for (var l = 0; l < 2; l++) {
102+
adders[i - 1].a[l][idx] <== partial[i - 1][l][idx];
103+
adders[i - 1].b[l][idx] <== multiplexers[i][l].out[idx];
104+
}
105+
}
106+
107+
// partial[i] = has_prev_nonzero[i - 1] * ((1 - iszero[i]) * adders[i - 1].out + iszero[i] * partial[i - 1][0][idx])
108+
// + (1 - has_prev_nonzero[i - 1]) * (1 - iszero[i]) * multiplexers[i]
109+
for (var idx = 0; idx < k; idx++) {
110+
for (var l = 0; l < 2; l++) {
111+
intermed1[i - 1][l][idx] <== iszero[i].out * (partial[i - 1][l][idx] - adders[i - 1].out[l][idx]) + adders[i - 1].out[l][idx];
112+
intermed2[i - 1][l][idx] <== multiplexers[i][l].out[idx] - iszero[i].out * multiplexers[i][l].out[idx];
113+
partial[i][l][idx] <== has_prev_nonzero[i - 1].out * (intermed1[i - 1][l][idx] - intermed2[i - 1][l][idx]) + intermed2[i - 1][l][idx];
114+
}
115+
}
116+
}
117+
118+
for (var i = 0; i < k; i++) {
119+
for (var l = 0; l < 2; l++) {
120+
pubkey[l][i] <== partial[num_strides - 1][l][i];
121+
}
122+
}
123+
}
124+
125+
// r, s, msghash, and pubkey have coordinates
126+
// encoded with k registers of n bits each
127+
// signature is (r, s)
128+
// Does not check that pubkey is valid
129+
template ECDSAVerifyNoPubkeyCheck(n, k) {
130+
assert(k >= 2);
131+
assert(k <= 100);
132+
133+
signal input r[k];
134+
signal input s[k];
135+
signal input msghash[k];
136+
signal input pubkey[2][k];
137+
138+
signal output result;
139+
140+
var p[100] = get_secp256k1_prime(n, k);
141+
var order[100] = get_secp256k1_order(n, k);
142+
143+
// compute multiplicative inverse of s mod n
144+
var sinv_comp[100] = mod_inv(n, k, s, order);
145+
signal sinv[k];
146+
component sinv_range_checks[k];
147+
for (var idx = 0; idx < k; idx++) {
148+
sinv[idx] <-- sinv_comp[idx];
149+
sinv_range_checks[idx] = Num2Bits(n);
150+
sinv_range_checks[idx].in <== sinv[idx];
151+
}
152+
component sinv_check = BigMultModP(n, k);
153+
for (var idx = 0; idx < k; idx++) {
154+
sinv_check.a[idx] <== sinv[idx];
155+
sinv_check.b[idx] <== s[idx];
156+
sinv_check.p[idx] <== order[idx];
157+
}
158+
for (var idx = 0; idx < k; idx++) {
159+
if (idx > 0) {
160+
sinv_check.out[idx] === 0;
161+
}
162+
if (idx == 0) {
163+
sinv_check.out[idx] === 1;
164+
}
165+
}
166+
167+
// compute (h * sinv) mod n
168+
component g_coeff = BigMultModP(n, k);
169+
for (var idx = 0; idx < k; idx++) {
170+
g_coeff.a[idx] <== sinv[idx];
171+
g_coeff.b[idx] <== msghash[idx];
172+
g_coeff.p[idx] <== order[idx];
173+
}
174+
175+
// compute (h * sinv) * G
176+
component g_mult = ECDSAPrivToPub(n, k);
177+
for (var idx = 0; idx < k; idx++) {
178+
g_mult.privkey[idx] <== g_coeff.out[idx];
179+
}
180+
181+
// compute (r * sinv) mod n
182+
component pubkey_coeff = BigMultModP(n, k);
183+
for (var idx = 0; idx < k; idx++) {
184+
pubkey_coeff.a[idx] <== sinv[idx];
185+
pubkey_coeff.b[idx] <== r[idx];
186+
pubkey_coeff.p[idx] <== order[idx];
187+
}
188+
189+
// compute (r * sinv) * pubkey
190+
component pubkey_mult = Secp256k1ScalarMult(n, k);
191+
for (var idx = 0; idx < k; idx++) {
192+
pubkey_mult.scalar[idx] <== pubkey_coeff.out[idx];
193+
pubkey_mult.point[0][idx] <== pubkey[0][idx];
194+
pubkey_mult.point[1][idx] <== pubkey[1][idx];
195+
}
196+
197+
// compute (h * sinv) * G + (r * sinv) * pubkey
198+
component sum_res = Secp256k1AddUnequal();
199+
for (var idx = 0; idx < k; idx++) {
200+
sum_res.a[0][idx] <== g_mult.pubkey[0][idx];
201+
sum_res.a[1][idx] <== g_mult.pubkey[1][idx];
202+
sum_res.b[0][idx] <== pubkey_mult.out[0][idx];
203+
sum_res.b[1][idx] <== pubkey_mult.out[1][idx];
204+
}
205+
206+
// compare sum_res.x with r
207+
component compare[k];
208+
signal num_equal[k - 1];
209+
for (var idx = 0; idx < k; idx++) {
210+
compare[idx] = IsEqual();
211+
compare[idx].in[0] <== r[idx];
212+
compare[idx].in[1] <== sum_res.out[0][idx];
213+
214+
if (idx > 0) {
215+
if (idx == 1) {
216+
num_equal[idx - 1] <== compare[0].out + compare[1].out;
217+
} else {
218+
num_equal[idx - 1] <== num_equal[idx - 2] + compare[idx].out;
219+
}
220+
}
221+
}
222+
component res_comp = IsEqual();
223+
res_comp.in[0] <== k;
224+
res_comp.in[1] <== num_equal[k - 2];
225+
result <== res_comp.out;
226+
}
227+
228+
// TODO: implement ECDSA extended verify
229+
// r, s, and msghash have coordinates
230+
// encoded with k registers of n bits each
231+
// v is a single bit
232+
// extended signature is (r, s, v)
233+
template ECDSAExtendedVerify(n, k) {
234+
signal input r[k];
235+
signal input s[k];
236+
signal input v;
237+
signal input msghash[k];
238+
239+
signal output result;
240+
}

0 commit comments

Comments
 (0)