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