update circuit imports to @zk-email

This commit is contained in:
0xturboblitz
2024-01-15 22:10:40 +01:00
parent 5d6d1e2bfc
commit 57cac9a3ee
14 changed files with 54 additions and 1658 deletions

View File

@@ -1,561 +0,0 @@
pragma circom 2.1.5;
include "../../node_modules/circomlib/circuits/comparators.circom";
include "../../node_modules/circomlib/circuits/bitify.circom";
include "../../node_modules/circomlib/circuits/gates.circom";
include "./bigint_func.circom";
// addition mod 2**n with carry bit
template ModSum(n) {
assert(n <= 252);
signal input a;
signal input b;
signal output sum;
signal output carry;
component n2b = Num2Bits(n + 1);
n2b.in <== a + b;
carry <== n2b.out[n];
sum <== a + b - carry * (1 << n);
}
// a - b
template ModSub(n) {
assert(n <= 252);
signal input a;
signal input b;
signal output out;
signal output borrow;
component lt = LessThan(n);
lt.in[0] <== a;
lt.in[1] <== b;
borrow <== lt.out;
out <== borrow * (1 << n) + a - b;
}
// a - b - c
// assume a - b - c + 2**n >= 0
template ModSubThree(n) {
assert(n + 2 <= 253);
signal input a;
signal input b;
signal input c;
assert(a - b - c + (1 << n) >= 0);
signal output out;
signal output borrow;
signal b_plus_c;
b_plus_c <== b + c;
component lt = LessThan(n + 1);
lt.in[0] <== a;
lt.in[1] <== b_plus_c;
borrow <== lt.out;
out <== borrow * (1 << n) + a - b_plus_c;
}
template ModSumThree(n) {
assert(n + 2 <= 253);
signal input a;
signal input b;
signal input c;
signal output sum;
signal output carry;
component n2b = Num2Bits(n + 2);
n2b.in <== a + b + c;
carry <== n2b.out[n] + 2 * n2b.out[n + 1];
sum <== a + b + c - carry * (1 << n);
}
template ModSumFour(n) {
assert(n + 2 <= 253);
signal input a;
signal input b;
signal input c;
signal input d;
signal output sum;
signal output carry;
component n2b = Num2Bits(n + 2);
n2b.in <== a + b + c + d;
carry <== n2b.out[n] + 2 * n2b.out[n + 1];
sum <== a + b + c + d - carry * (1 << n);
}
// product mod 2**n with carry
template ModProd(n) {
assert(n <= 126);
signal input a;
signal input b;
signal output prod;
signal output carry;
component n2b = Num2Bits(2 * n);
n2b.in <== a * b;
component b2n1 = Bits2Num(n);
component b2n2 = Bits2Num(n);
var i;
for (i = 0; i < n; i++) {
b2n1.in[i] <== n2b.out[i];
b2n2.in[i] <== n2b.out[i + n];
}
prod <== b2n1.out;
carry <== b2n2.out;
}
// split a n + m bit input into two outputs
template Split(n, m) {
assert(n <= 126);
signal input in;
signal output small;
signal output big;
small <-- in % (1 << n);
big <-- in \ (1 << n);
component n2b_small = Num2Bits(n);
n2b_small.in <== small;
component n2b_big = Num2Bits(m);
n2b_big.in <== big;
in === small + big * (1 << n);
}
// split a n + m + k bit input into three outputs
template SplitThree(n, m, k) {
assert(n <= 126);
signal input in;
signal output small;
signal output medium;
signal output big;
small <-- in % (1 << n);
medium <-- (in \ (1 << n)) % (1 << m);
big <-- in \ (1 << n + m);
component n2b_small = Num2Bits(n);
n2b_small.in <== small;
component n2b_medium = Num2Bits(m);
n2b_medium.in <== medium;
component n2b_big = Num2Bits(k);
n2b_big.in <== big;
in === small + medium * (1 << n) + big * (1 << n + m);
}
// a[i], b[i] in 0... 2**n-1
// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
template BigAdd(n, k) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k + 1];
component unit0 = ModSum(n);
unit0.a <== a[0];
unit0.b <== b[0];
out[0] <== unit0.sum;
component unit[k - 1];
for (var i = 1; i < k; i++) {
unit[i - 1] = ModSumThree(n);
unit[i - 1].a <== a[i];
unit[i - 1].b <== b[i];
if (i == 1) {
unit[i - 1].c <== unit0.carry;
} else {
unit[i - 1].c <== unit[i - 2].carry;
}
out[i] <== unit[i - 1].sum;
}
out[k] <== unit[k - 2].carry;
}
// a and b have n-bit registers
// a has ka registers, each with NONNEGATIVE ma-bit values (ma can be > n)
// b has kb registers, each with NONNEGATIVE mb-bit values (mb can be > n)
// out has ka + kb - 1 registers, each with (ma + mb + ceil(log(max(ka, kb))))-bit values
template BigMultNoCarry(n, ma, mb, ka, kb) {
assert(ma + mb <= 253);
signal input a[ka];
signal input b[kb];
signal output out[ka + kb - 1];
var prod_val[ka + kb - 1];
for (var i = 0; i < ka + kb - 1; i++) {
prod_val[i] = 0;
}
for (var i = 0; i < ka; i++) {
for (var j = 0; j < kb; j++) {
prod_val[i + j] += a[i] * b[j];
}
}
for (var i = 0; i < ka + kb - 1; i++) {
out[i] <-- prod_val[i];
}
var a_poly[ka + kb - 1];
var b_poly[ka + kb - 1];
var out_poly[ka + kb - 1];
for (var i = 0; i < ka + kb - 1; i++) {
out_poly[i] = 0;
a_poly[i] = 0;
b_poly[i] = 0;
for (var j = 0; j < ka + kb - 1; j++) {
out_poly[i] = out_poly[i] + out[j] * (i ** j);
}
for (var j = 0; j < ka; j++) {
a_poly[i] = a_poly[i] + a[j] * (i ** j);
}
for (var j = 0; j < kb; j++) {
b_poly[i] = b_poly[i] + b[j] * (i ** j);
}
}
for (var i = 0; i < ka + kb - 1; i++) {
out_poly[i] === a_poly[i] * b_poly[i];
}
}
// in[i] contains longs
// out[i] contains shorts
template LongToShortNoEndCarry(n, k) {
assert(n <= 126);
signal input in[k];
signal output out[k+1];
var split[k][3];
for (var i = 0; i < k; i++) {
split[i] = SplitThreeFn(in[i], n, n, n);
}
var carry[k];
carry[0] = 0;
out[0] <-- split[0][0];
if (k == 1) {
out[1] <-- split[0][1];
}
if (k > 1) {
var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n);
out[1] <-- sumAndCarry[0];
carry[1] = sumAndCarry[1];
}
if (k == 2) {
out[2] <-- split[1][1] + split[0][2] + carry[1];
}
if (k > 2) {
for (var i = 2; i < k; i++) {
var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n);
out[i] <-- sumAndCarry[0];
carry[i] = sumAndCarry[1];
}
out[k] <-- split[k-1][1] + split[k-2][2] + carry[k-1];
}
component outRangeChecks[k+1];
for (var i = 0; i < k+1; i++) {
outRangeChecks[i] = Num2Bits(n);
outRangeChecks[i].in <== out[i];
}
signal runningCarry[k];
component runningCarryRangeChecks[k];
runningCarry[0] <-- (in[0] - out[0]) / (1 << n);
runningCarryRangeChecks[0] = Num2Bits(n + log_ceil(k));
runningCarryRangeChecks[0].in <== runningCarry[0];
runningCarry[0] * (1 << n) === in[0] - out[0];
for (var i = 1; i < k; i++) {
runningCarry[i] <-- (in[i] - out[i] + runningCarry[i-1]) / (1 << n);
runningCarryRangeChecks[i] = Num2Bits(n + log_ceil(k));
runningCarryRangeChecks[i].in <== runningCarry[i];
runningCarry[i] * (1 << n) === in[i] - out[i] + runningCarry[i-1];
}
runningCarry[k-1] === out[k];
}
template BigMult(n, k) {
signal input a[k];
signal input b[k];
signal output out[2 * k];
component mult = BigMultNoCarry(n, n, n, k, k);
for (var i = 0; i < k; i++) {
mult.a[i] <== a[i];
mult.b[i] <== b[i];
}
// no carry is possible in the highest order register
component longshort = LongToShortNoEndCarry(n, 2 * k - 1);
for (var i = 0; i < 2 * k - 1; i++) {
longshort.in[i] <== mult.out[i];
}
for (var i = 0; i < 2 * k; i++) {
out[i] <== longshort.out[i];
}
}
template BigLessThan(n, k){
signal input a[k];
signal input b[k];
signal output out;
component lt[k];
component eq[k];
for (var i = 0; i < k; i++) {
lt[i] = LessThan(n);
lt[i].in[0] <== a[i];
lt[i].in[1] <== b[i];
eq[i] = IsEqual();
eq[i].in[0] <== a[i];
eq[i].in[1] <== b[i];
}
// ors[i] holds (lt[k - 1] || (eq[k - 1] && lt[k - 2]) .. || (eq[k - 1] && .. && lt[i]))
// ands[i] holds (eq[k - 1] && .. && lt[i])
// eq_ands[i] holds (eq[k - 1] && .. && eq[i])
component ors[k - 1];
component ands[k - 1];
component eq_ands[k - 1];
for (var i = k - 2; i >= 0; i--) {
ands[i] = AND();
eq_ands[i] = AND();
ors[i] = OR();
if (i == k - 2) {
ands[i].a <== eq[k - 1].out;
ands[i].b <== lt[k - 2].out;
eq_ands[i].a <== eq[k - 1].out;
eq_ands[i].b <== eq[k - 2].out;
ors[i].a <== lt[k - 1].out;
ors[i].b <== ands[i].out;
} else {
ands[i].a <== eq_ands[i + 1].out;
ands[i].b <== lt[i].out;
eq_ands[i].a <== eq_ands[i + 1].out;
eq_ands[i].b <== eq[i].out;
ors[i].a <== ors[i + 1].out;
ors[i].b <== ands[i].out;
}
}
out <== ors[0].out;
}
template BigIsEqual(k){
signal input in[2][k];
signal output out;
component isEqual[k+1];
var sum = 0;
for(var i = 0; i < k; i++){
isEqual[i] = IsEqual();
isEqual[i].in[0] <== in[0][i];
isEqual[i].in[1] <== in[1][i];
sum = sum + isEqual[i].out;
}
isEqual[k] = IsEqual();
isEqual[k].in[0] <== sum;
isEqual[k].in[1] <== k;
out <== isEqual[k].out;
}
// leading register of b should be non-zero
template BigMod(n, k) {
assert(n <= 126);
signal input a[2 * k];
signal input b[k];
signal output div[k + 1];
signal output mod[k];
var longdiv[2][100] = long_div(n, k, k, a, b);
for (var i = 0; i < k; i++) {
div[i] <-- longdiv[0][i];
mod[i] <-- longdiv[1][i];
}
div[k] <-- longdiv[0][k];
component range_checks[k + 1];
for (var i = 0; i <= k; i++) {
range_checks[i] = Num2Bits(n);
range_checks[i].in <== div[i];
}
component mul = BigMult(n, k + 1);
for (var i = 0; i < k; i++) {
mul.a[i] <== div[i];
mul.b[i] <== b[i];
}
mul.a[k] <== div[k];
mul.b[k] <== 0;
component add = BigAdd(n, 2 * k + 2);
for (var i = 0; i < 2 * k; i++) {
add.a[i] <== mul.out[i];
if (i < k) {
add.b[i] <== mod[i];
} else {
add.b[i] <== 0;
}
}
add.a[2 * k] <== mul.out[2 * k];
add.a[2 * k + 1] <== mul.out[2 * k + 1];
add.b[2 * k] <== 0;
add.b[2 * k + 1] <== 0;
for (var i = 0; i < 2 * k; i++) {
add.out[i] === a[i];
}
add.out[2 * k] === 0;
add.out[2 * k + 1] === 0;
component lt = BigLessThan(n, k);
for (var i = 0; i < k; i++) {
lt.a[i] <== mod[i];
lt.b[i] <== b[i];
}
lt.out === 1;
}
// a[i], b[i] in 0... 2**n-1
// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
// assume a >= b
template BigSub(n, k) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal output out[k];
signal output underflow;
component unit0 = ModSub(n);
unit0.a <== a[0];
unit0.b <== b[0];
out[0] <== unit0.out;
component unit[k - 1];
for (var i = 1; i < k; i++) {
unit[i - 1] = ModSubThree(n);
unit[i - 1].a <== a[i];
unit[i - 1].b <== b[i];
if (i == 1) {
unit[i - 1].c <== unit0.borrow;
} else {
unit[i - 1].c <== unit[i - 2].borrow;
}
out[i] <== unit[i - 1].out;
}
underflow <== unit[k - 2].borrow;
}
// calculates (a - b) % p, where a, b < p
// note: does not assume a >= b
template BigSubModP(n, k){
assert(n <= 252);
signal input a[k];
signal input b[k];
signal input p[k];
signal output out[k];
component sub = BigSub(n, k);
for (var i = 0; i < k; i++){
sub.a[i] <== a[i];
sub.b[i] <== b[i];
}
signal flag;
flag <== sub.underflow;
component add = BigAdd(n, k);
for (var i = 0; i < k; i++){
add.a[i] <== sub.out[i];
add.b[i] <== flag * p[i];
}
for (var i = 0; i < k; i++){
out[i] <== add.out[i];
}
}
template BigMultModP(n, k) {
assert(n <= 252);
signal input a[k];
signal input b[k];
signal input p[k];
signal output out[k];
component big_mult = BigMult(n, k);
for (var i = 0; i < k; i++) {
big_mult.a[i] <== a[i];
big_mult.b[i] <== b[i];
}
component big_mod = BigMod(n, k);
for (var i = 0; i < 2 * k; i++) {
big_mod.a[i] <== big_mult.out[i];
}
for (var i = 0; i < k; i++) {
big_mod.b[i] <== p[i];
}
for (var i = 0; i < k; i++) {
out[i] <== big_mod.mod[i];
}
}
template BigModInv(n, k) {
assert(n <= 252);
signal input in[k];
signal input p[k];
signal output out[k];
// length k
var inv[100] = mod_inv(n, k, in, p);
for (var i = 0; i < k; i++) {
out[i] <-- inv[i];
}
component range_checks[k];
for (var i = 0; i < k; i++) {
range_checks[i] = Num2Bits(n);
range_checks[i].in <== out[i];
}
component mult = BigMult(n, k);
for (var i = 0; i < k; i++) {
mult.a[i] <== in[i];
mult.b[i] <== out[i];
}
component mod = BigMod(n, k);
for (var i = 0; i < 2 * k; i++) {
mod.a[i] <== mult.out[i];
}
for (var i = 0; i < k; i++) {
mod.b[i] <== p[i];
}
mod.mod[0] === 1;
for (var i = 1; i < k; i++) {
mod.mod[i] === 0;
}
}
// in[i] contains values in the range -2^(m-1) to 2^(m-1)
// constrain that in[] as a big integer is zero
// each limbs is n bits
template CheckCarryToZero(n, m, k) {
assert(k >= 2);
var EPSILON = 3;
assert(m + EPSILON <= 253);
signal input in[k];
signal carry[k];
component carryRangeChecks[k];
for (var i = 0; i < k-1; i++){
carryRangeChecks[i] = Num2Bits(m + EPSILON - n);
if( i == 0 ){
carry[i] <-- in[i] / (1<<n);
in[i] === carry[i] * (1<<n);
}
else{
carry[i] <-- (in[i]+carry[i-1]) / (1<<n);
in[i] + carry[i-1] === carry[i] * (1<<n);
}
// checking carry is in the range of - 2^(m-n-1+eps), 2^(m+-n-1+eps)
carryRangeChecks[i].in <== carry[i] + ( 1<< (m + EPSILON - n - 1));
}
in[k-1] + carry[k-2] === 0;
}

View File

@@ -1,442 +0,0 @@
pragma circom 2.1.5;
function isNegative(x) {
// half babyjubjub field size
return x > 10944121435919637611123202872628637544274182200208017171849102093287904247808 ? 1 : 0;
}
function div_ceil(m, n) {
var ret = 0;
if (m % n == 0) {
ret = m \ n;
} else {
ret = m \ n + 1;
}
return ret;
}
function log_ceil(n) {
var n_temp = n;
for (var i = 0; i < 254; i++) {
if (n_temp == 0) {
return i;
}
n_temp = n_temp \ 2;
}
return 254;
}
function SplitFn(in, n, m) {
return [in % (1 << n), (in \ (1 << n)) % (1 << m)];
}
function SplitThreeFn(in, n, m, k) {
return [in % (1 << n), (in \ (1 << n)) % (1 << m), (in \ (1 << n + m)) % (1 << k)];
}
// m bits per overflowed register (values are potentially negative)
// n bits per properly-sized register
// in has k registers
// out has k + ceil(m/n) - 1 + 1 registers. highest-order potentially negative,
// all others are positive
// - 1 since the last register is included in the last ceil(m/n) array
// + 1 since the carries from previous registers could push you over
function getProperRepresentation(m, n, k, in) {
var ceilMN = div_ceil(m, n);
var out[100]; // should be out[k + ceilMN]
assert(k + ceilMN < 100);
for (var i = 0; i < k; i++) {
out[i] = in[i];
}
for (var i = k; i < 100; i++) {
out[i] = 0;
}
assert(n <= m);
for (var i = 0; i+1 < k + ceilMN; i++) {
assert((1 << m) >= out[i] && out[i] >= -(1 << m));
var shifted_val = out[i] + (1 << m);
assert(0 <= shifted_val && shifted_val <= (1 << (m+1)));
out[i] = shifted_val & ((1 << n) - 1);
out[i+1] += (shifted_val >> n) - (1 << (m - n));
}
return out;
}
// Evaluate polynomial a at point x
function poly_eval(len, a, x) {
var v = 0;
for (var i = 0; i < len; i++) {
v += a[i] * (x ** i);
}
return v;
}
// Interpolate a degree len-1 polynomial given its evaluations at 0..len-1
function poly_interp(len, v) {
assert(len <= 200);
var out[200];
for (var i = 0; i < len; i++) {
out[i] = 0;
}
// Product_{i=0..len-1} (x-i)
var full_poly[201];
full_poly[0] = 1;
for (var i = 0; i < len; i++) {
full_poly[i+1] = 0;
for (var j = i; j >= 0; j--) {
full_poly[j+1] += full_poly[j];
full_poly[j] *= -i;
}
}
for (var i = 0; i < len; i++) {
var cur_v = 1;
for (var j = 0; j < len; j++) {
if (i == j) {
// do nothing
} else {
cur_v *= i-j;
}
}
cur_v = v[i] / cur_v;
var cur_rem = full_poly[len];
for (var j = len-1; j >= 0; j--) {
out[j] += cur_v * cur_rem;
cur_rem = full_poly[j] + i * cur_rem;
}
assert(cur_rem == 0);
}
return out;
}
// 1 if true, 0 if false
function long_gt(n, k, a, b) {
for (var i = k - 1; i >= 0; i--) {
if (a[i] > b[i]) {
return 1;
}
if (a[i] < b[i]) {
return 0;
}
}
return 0;
}
// n bits per register
// a has k registers
// b has k registers
// a >= b
function long_sub(n, k, a, b) {
var diff[100];
var borrow[100];
for (var i = 0; i < k; i++) {
if (i == 0) {
if (a[i] >= b[i]) {
diff[i] = a[i] - b[i];
borrow[i] = 0;
} else {
diff[i] = a[i] - b[i] + (1 << n);
borrow[i] = 1;
}
} else {
if (a[i] >= b[i] + borrow[i - 1]) {
diff[i] = a[i] - b[i] - borrow[i - 1];
borrow[i] = 0;
} else {
diff[i] = (1 << n) + a[i] - b[i] - borrow[i - 1];
borrow[i] = 1;
}
}
}
return diff;
}
// a is a n-bit scalar
// b has k registers
function long_scalar_mult(n, k, a, b) {
var out[100];
for (var i = 0; i < 100; i++) {
out[i] = 0;
}
for (var i = 0; i < k; i++) {
var temp = out[i] + (a * b[i]);
out[i] = temp % (1 << n);
out[i + 1] = out[i + 1] + temp \ (1 << n);
}
return out;
}
// n bits per register
// a has k + m registers
// b has k registers
// out[0] has length m + 1 -- quotient
// out[1] has length k -- remainder
// implements algorithm of https://people.eecs.berkeley.edu/~fateman/282/F%20Wright%20notes/week4.pdf
function long_div(n, k, m, a, b){
var out[2][100];
m += k;
while (b[k-1] == 0) {
out[1][k] = 0;
k--;
assert(k > 0);
}
m -= k;
var remainder[200];
for (var i = 0; i < m + k; i++) {
remainder[i] = a[i];
}
var mult[200];
var dividend[200];
for (var i = m; i >= 0; i--) {
if (i == m) {
dividend[k] = 0;
for (var j = k - 1; j >= 0; j--) {
dividend[j] = remainder[j + m];
}
} else {
for (var j = k; j >= 0; j--) {
dividend[j] = remainder[j + i];
}
}
out[0][i] = short_div(n, k, dividend, b);
var mult_shift[100] = long_scalar_mult(n, k, out[0][i], b);
var subtrahend[200];
for (var j = 0; j < m + k; j++) {
subtrahend[j] = 0;
}
for (var j = 0; j <= k; j++) {
if (i + j < m + k) {
subtrahend[i + j] = mult_shift[j];
}
}
remainder = long_sub(n, m + k, remainder, subtrahend);
}
for (var i = 0; i < k; i++) {
out[1][i] = remainder[i];
}
out[1][k] = 0;
return out;
}
// n bits per register
// a has k + 1 registers
// b has k registers
// assumes leading digit of b is at least 2 ** (n - 1)
// 0 <= a < (2**n) * b
function short_div_norm(n, k, a, b) {
var qhat = (a[k] * (1 << n) + a[k - 1]) \ b[k - 1];
if (qhat > (1 << n) - 1) {
qhat = (1 << n) - 1;
}
var mult[100] = long_scalar_mult(n, k, qhat, b);
if (long_gt(n, k + 1, mult, a) == 1) {
mult = long_sub(n, k + 1, mult, b);
if (long_gt(n, k + 1, mult, a) == 1) {
return qhat - 2;
} else {
return qhat - 1;
}
} else {
return qhat;
}
}
// n bits per register
// a has k + 1 registers
// b has k registers
// assumes leading digit of b is non-zero
// 0 <= a < (2**n) * b
function short_div(n, k, a, b) {
var scale = (1 << n) \ (1 + b[k - 1]);
// k + 2 registers now
var norm_a[200] = long_scalar_mult(n, k + 1, scale, a);
// k + 1 registers now
var norm_b[200] = long_scalar_mult(n, k, scale, b);
var ret;
if (norm_b[k] != 0) {
ret = short_div_norm(n, k + 1, norm_a, norm_b);
} else {
ret = short_div_norm(n, k, norm_a, norm_b);
}
return ret;
}
// n bits per register
// a and b both have k registers
// out[0] has length 2 * k
// adapted from BigMulShortLong and LongToShortNoEndCarry2 witness computation
function prod(n, k, a, b) {
// first compute the intermediate values. taken from BigMulShortLong
var prod_val[100]; // length is 2 * k - 1
for (var i = 0; i < 2 * k - 1; i++) {
prod_val[i] = 0;
if (i < k) {
for (var a_idx = 0; a_idx <= i; a_idx++) {
prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx];
}
} else {
for (var a_idx = i - k + 1; a_idx < k; a_idx++) {
prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx];
}
}
}
// now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2
var out[100]; // length is 2 * k
var split[100][3]; // first dimension has length 2 * k - 1
for (var i = 0; i < 2 * k - 1; i++) {
split[i] = SplitThreeFn(prod_val[i], n, n, n);
}
var carry[100]; // length is 2 * k - 1
carry[0] = 0;
out[0] = split[0][0];
if (2 * k - 1 > 1) {
var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n);
out[1] = sumAndCarry[0];
carry[1] = sumAndCarry[1];
}
if (2 * k - 1 > 2) {
for (var i = 2; i < 2 * k - 1; i++) {
var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n);
out[i] = sumAndCarry[0];
carry[i] = sumAndCarry[1];
}
out[2 * k - 1] = split[2*k-2][1] + split[2*k-3][2] + carry[2*k-2];
}
return out;
}
// n bits per register
// a has k registers
// p has k registers
// e has k registers
// k * n <= 500
// p is a prime
// computes a^e mod p
function mod_exp(n, k, a, p, e) {
var eBits[500]; // length is k * n
for (var i = 0; i < k; i++) {
for (var j = 0; j < n; j++) {
eBits[j + n * i] = (e[i] >> j) & 1;
}
}
var out[100]; // length is k
for (var i = 0; i < 100; i++) {
out[i] = 0;
}
out[0] = 1;
// repeated squaring
for (var i = k * n - 1; i >= 0; i--) {
// multiply by a if bit is 0
if (eBits[i] == 1) {
var temp[200]; // length 2 * k
temp = prod(n, k, out, a);
var temp2[2][100];
temp2 = long_div(n, k, k, temp, p);
out = temp2[1];
}
// square, unless we're at the end
if (i > 0) {
var temp[200]; // length 2 * k
temp = prod(n, k, out, out);
var temp2[2][100];
temp2 = long_div(n, k, k, temp, p);
out = temp2[1];
}
}
return out;
}
// n bits per register
// a has k registers
// p has k registers
// k * n <= 500
// p is a prime
// if a == 0 mod p, returns 0
// else computes inv = a^(p-2) mod p
function mod_inv(n, k, a, p) {
var isZero = 1;
for (var i = 0; i < k; i++) {
if (a[i] != 0) {
isZero = 0;
}
}
if (isZero == 1) {
var ret[100];
for (var i = 0; i < k; i++) {
ret[i] = 0;
}
return ret;
}
var pCopy[100];
for (var i = 0; i < 100; i++) {
if (i < k) {
pCopy[i] = p[i];
} else {
pCopy[i] = 0;
}
}
var two[100];
for (var i = 0; i < 100; i++) {
two[i] = 0;
}
two[0] = 2;
var pMinusTwo[100];
pMinusTwo = long_sub(n, k, pCopy, two); // length k
var out[100];
out = mod_exp(n, k, a, pCopy, pMinusTwo);
return out;
}
// a, b and out are all n bits k registers
function long_sub_mod_p(n, k, a, b, p){
var gt = long_gt(n, k, a, b);
var tmp[100];
if(gt){
tmp = long_sub(n, k, a, b);
}
else{
tmp = long_sub(n, k, b, a);
}
var out[2][100];
for(var i = k;i < 2 * k; i++){
tmp[i] = 0;
}
out = long_div(n, k, k, tmp, p);
if(gt==0){
tmp = long_sub(n, k, p, out[1]);
}
return tmp;
}
// a, b, p and out are all n bits k registers
function prod_mod_p(n, k, a, b, p){
var tmp[100];
var result[2][100];
tmp = prod(n, k, a, b);
result = long_div(n, k, k, tmp, p);
return result[1];
}

View File

@@ -1,175 +0,0 @@
pragma circom 2.1.2;
include "./utils.circom";
// circuits in this folder copied from zk-email, credits to them
// A set of utils for shifting and packing signal arrays
// Performs extraction of reveal signals and packed signals
// From https://github.com/iden3/circomlib/blob/master/circuits/multiplexer.circom
function log2(a) {
if (a == 0) {
return 0;
}
var n = 1;
var r = 1;
while (n<a) {
r++;
n *= 2;
}
return r;
}
// Pack size is # of chunks i.e. number of char signals that fit into a signal (default 7 but can be 30)
template PackBytes(max_in_signals, max_out_signals, pack_size) {
assert(max_out_signals == ((max_in_signals - 1) \ pack_size + 1)); // Packing constant is wrong
signal input in[max_in_signals];
signal output out[max_out_signals];
component packer[max_out_signals];
for (var i = 0; i < max_out_signals; i++) {
packer[i] = Bytes2Packed(pack_size);
for (var j = 0; j < pack_size; j++) {
var reveal_idx = i * pack_size + j;
if (reveal_idx < max_in_signals) {
packer[i].in[j] <== in[i * pack_size + j];
} else {
packer[i].in[j] <== 0;
}
}
out[i] <== packer[i].out;
}
}
// Shift the input left by variable size of bytes.
// From https://demo.hedgedoc.org/s/Le0R3xUhB
// Note that if len_bits < max_substr * C, C around 1, then
// it's more efficient to use Sampriti's O(nk) solution instead
template VarShiftLeft(in_array_len, out_array_len) {
var len_bits = log2(in_array_len);
assert(in_array_len <= (1 << len_bits));
signal input in[in_array_len]; // x
signal input shift; // k
signal output out[out_array_len]; // y
component n2b = Num2Bits(len_bits);
n2b.in <== shift;
signal tmp[len_bits][in_array_len];
for (var j = 0; j < len_bits; j++) {
for (var i = 0; i < in_array_len; i++) {
var offset = (i + (1 << j)) % in_array_len;
// Shift left by 2^j indices if bit is 1
if (j == 0) {
tmp[j][i] <== n2b.out[j] * (in[offset] - in[i]) + in[i];
} else {
tmp[j][i] <== n2b.out[j] * (tmp[j-1][offset] - tmp[j-1][i]) + tmp[j-1][i];
}
}
}
// Return last row
// TODO: Assert the rest of the values are 0
for (var i = 0; i < out_array_len; i++) {
out[i] <== tmp[len_bits - 1][i];
}
}
// Shift the input left by variable size of bytes.
// Its input and output are the same as those of VarShiftLeft.
// However, it assumes the input is the masked bytes and checks that shift is the first index of the non-masked bytes.
template VarShiftMaskedStr(in_array_len, out_array_len) {
signal input in[in_array_len]; // x
signal input shift; // k
signal output out[out_array_len] <== VarShiftLeft(in_array_len, out_array_len)(in, shift);
signal is_target_idx[in_array_len];
signal prev_byte[in_array_len];
signal is_this_zero[in_array_len];
signal is_prev_zero[in_array_len];
for(var i = 0; i < in_array_len; i++) {
is_target_idx[i] <== IsEqual()([i, shift]);
is_this_zero[i] <== IsZero()(in[i]);
is_target_idx[i] * is_this_zero[i] === 0;
if(i == 0) {
is_prev_zero[i] <== 1;
} else {
is_prev_zero[i] <== IsZero()(in[i-1]);
}
is_target_idx[i] * (1 - is_prev_zero[i]) === 0;
}
}
// From https://demo.hedgedoc.org/s/Le0R3xUhB -- unused
template ClearSubarrayAfterEndIndex(n, nBits) {
signal input in[n]; // x
signal input end; // k
signal output out[n]; // y
component lt[n];
for (var i = 0; i < n; i++) {
lt[i] = LessThan(nBits);
lt[i].in[0] <== i;
lt[i].in[1] <== end;
// y[i] = (i < k) * x[i]
out[i] <== lt[i].out * in[i];
}
}
// Lengths here are in signals, even though the final output array is 1/7 the size of max_substr_len
// TODO: Maybe a better architectural decision to avoid mistakes is to require both values and assert their equality
template ShiftAndPack(in_array_len, max_substr_len, pack_size) {
var max_substr_len_packed = ((max_substr_len - 1) \ pack_size + 1);
component shifter = VarShiftLeft(in_array_len, max_substr_len);
component packer = PackBytes(max_substr_len, max_substr_len_packed, pack_size);
signal input in[in_array_len];
signal input shift;
signal output out[max_substr_len_packed];
for (var i = 0; i < in_array_len; i++) {
shifter.in[i] <== in[i];
}
shifter.shift <== shift;
// Note that this technically doesn't constrain the rest øf the bits after the max_substr_len to be 0/unmatched/unrevealed
// Because of the constraints on signed inputs, it seems this should be OK security wise
// But still, TODO unconstrained assert to double check they are 0
for (var i = 0; i < max_substr_len; i++) {
packer.in[i] <== shifter.out[i];
}
for (var i = 0; i < max_substr_len_packed; i++) {
out[i] <== packer.out[i];
}
}
// Shift the input left by variable size of bytes and pack the shifted bytes into fields under pack_size.
// Its input and output are the same as those of ShiftAndPack.
// However, it assumes the input is the masked bytes and checks that shift is the first index of the non-masked bytes.
template ShiftAndPackMaskedStr(in_array_len, max_substr_len, pack_size) {
var max_substr_len_packed = ((max_substr_len - 1) \ pack_size + 1);
component shifter = VarShiftMaskedStr(in_array_len, max_substr_len);
component packer = PackBytes(max_substr_len, max_substr_len_packed, pack_size);
signal input in[in_array_len];
signal input shift;
signal output out[max_substr_len_packed];
for (var i = 0; i < in_array_len; i++) {
shifter.in[i] <== in[i];
}
shifter.shift <== shift;
for (var i = 0; i < max_substr_len; i++) {
packer.in[i] <== shifter.out[i];
}
for (var i = 0; i < max_substr_len_packed; i++) {
out[i] <== packer.out[i];
}
}

View File

@@ -1,145 +0,0 @@
pragma circom 2.1.5;
include "../../node_modules/circomlib/circuits/bitify.circom";
include "../../node_modules/circomlib/circuits/comparators.circom";
include "../../node_modules/circomlib/circuits/sign.circom";
include "./bigint.circom";
include "./bigint_func.circom";
// These functions operate over values in Z/Zp for some integer p (typically,
// but not necessarily prime). Values are stored as standard bignums with k
// chunks of n bits, but intermediate values often have "overflow" bits inside
// various chunks.
//
// These Fp functions will always correctly generate witnesses mod p, but they
// do not *check* that values are normalized to < p; they only check that
// values are correct mod p. This is to save the comparison circuit.
// They *will* always check for intended results mod p (soundness), but it may
// not have a unique intermediate signal.
//
// Conversely, some templates may not be satisfiable if the input witnesses are
// not < p. This does not break completeness, as honest provers will always
// generate witnesses which are canonical (between 0 and p).
// a * b = r mod p
// a * b - p * q - r for some q
template FpMul(n, k) {
assert(n + n + log_ceil(k) + 2 <= 252);
signal input a[k];
signal input b[k];
signal input p[k];
signal output out[k];
signal v_ab[2*k-1];
for (var x = 0; x < 2*k-1; x++) {
var v_a = poly_eval(k, a, x);
var v_b = poly_eval(k, b, x);
v_ab[x] <== v_a * v_b;
}
var ab[200] = poly_interp(2*k-1, v_ab);
// ab_proper has length 2*k
var ab_proper[200] = getProperRepresentation(n + n + log_ceil(k), n, 2*k-1, ab);
var long_div_out[2][100] = long_div(n, k, k, ab_proper, p);
// Since we're only computing a*b, we know that q < p will suffice, so we
// know it fits into k chunks and can do size n range checks.
signal q[k];
component q_range_check[k];
signal r[k];
component r_range_check[k];
for (var i = 0; i < k; i++) {
q[i] <-- long_div_out[0][i];
q_range_check[i] = Num2Bits(n);
q_range_check[i].in <== q[i];
r[i] <-- long_div_out[1][i];
r_range_check[i] = Num2Bits(n);
r_range_check[i].in <== r[i];
}
signal v_pq_r[2*k-1];
for (var x = 0; x < 2*k-1; x++) {
var v_p = poly_eval(k, p, x);
var v_q = poly_eval(k, q, x);
var v_r = poly_eval(k, r, x);
v_pq_r[x] <== v_p * v_q + v_r;
}
signal v_t[2*k-1];
for (var x = 0; x < 2*k-1; x++) {
v_t[x] <== v_ab[x] - v_pq_r[x];
}
var t[200] = poly_interp(2*k-1, v_t);
component tCheck = CheckCarryToZero(n, n + n + log_ceil(k) + 2, 2*k-1);
for (var i = 0; i < 2*k-1; i++) {
tCheck.in[i] <== t[i];
}
for (var i = 0; i < k; i++) {
out[i] <== r[i];
}
}
// Lifted from https://sourcegraph.com/github.com/darkforest-eth/circuits/-/blob/range_proof/circuit.circom
// NB: RangeProof is inclusive.
// input: field element, whose abs is claimed to be less than max_abs_value
// output: none
// we also want something like 4 * (abs(in) + max_abs_value) < 2 ** bits
// and bits << 256
// NB: RangeProof is inclusive.
// input: field element, whose abs is claimed to be <= than max_abs_value
// output: none
// also checks that both max and abs(in) are expressible in `bits` bits
template RangeProof(bits) {
signal input in;
signal input max_abs_value;
/* check that both max and abs(in) are expressible in `bits` bits */
component n2b1 = Num2Bits(bits+1);
n2b1.in <== in + (1 << bits);
component n2b2 = Num2Bits(bits);
n2b2.in <== max_abs_value;
/* check that in + max is between 0 and 2*max */
component lowerBound = LessThan(bits+1);
component upperBound = LessThan(bits+1);
lowerBound.in[0] <== max_abs_value + in;
lowerBound.in[1] <== 0;
lowerBound.out === 0;
upperBound.in[0] <== 2 * max_abs_value;
upperBound.in[1] <== max_abs_value + in;
upperBound.out === 0;
}
// input: n field elements, whose abs are claimed to be less than max_abs_value
// output: none
template MultiRangeProof(n, bits) {
signal input in[n];
signal input max_abs_value;
component rangeProofs[n];
for (var i = 0; i < n; i++) {
rangeProofs[i] = RangeProof(bits);
rangeProofs[i].in <== in[i];
rangeProofs[i].max_abs_value <== max_abs_value;
}
}
template IsNegative(){
signal input in;
signal output out;
component n2b = Num2Bits(254);
component sign = Sign();
in ==> n2b.in;
for (var i = 0; i<254; i++) {
n2b.out[i] ==> sign.in[i];
}
sign.sign ==> out;
}

View File

@@ -1,149 +0,0 @@
pragma circom 2.1.5;
include "../../node_modules/circomlib/circuits/bitify.circom";
include "../../node_modules/circomlib/circuits/comparators.circom";
include "../../node_modules/circomlib/circuits/mimcsponge.circom";
include "./fp.circom";
// returns ceil(log2(a+1))
function log2_ceil(a) {
var n = a+1;
var r = 0;
while (n>0) {
r++;
n \= 2;
}
return r;
}
// returns ceil(log2(a+1))
function count_packed(n, chunks) {
return (n - 1) \ chunks + 1;
}
// Lifted from MACI https://github.com/privacy-scaling-explorations/maci/blob/v1/circuits/circom/trees/incrementalQuinTree.circom#L29
// Bits is ceil(log2 choices)
template QuinSelector(choices, bits) {
signal input in[choices];
signal input index;
signal output out;
// Ensure that index < choices
component lessThan = LessThan(bits);
lessThan.in[0] <== index;
lessThan.in[1] <== choices;
lessThan.out === 1;
component calcTotal = CalculateTotal(choices);
component eqs[choices];
// For each item, check whether its index equals the input index.
for (var i = 0; i < choices; i ++) {
eqs[i] = IsEqual();
eqs[i].in[0] <== i;
eqs[i].in[1] <== index;
// eqs[i].out is 1 if the index matches. As such, at most one input to
// calcTotal is not 0.
calcTotal.nums[i] <== eqs[i].out * in[i];
}
// Returns 0 + 0 + ... + item
out <== calcTotal.sum;
}
template CalculateTotal(n) {
signal input nums[n];
signal output sum;
signal sums[n];
sums[0] <== nums[0];
for (var i=1; i < n; i++) {
sums[i] <== sums[i - 1] + nums[i];
}
sum <== sums[n - 1];
}
// Written by us
// n bytes per signal, n = 31 usually
template Packed2Bytes(n){
signal input in; // < 2 ^ (8 * 31)
signal output out[n]; // each out is < 64
// Rangecheck in and out?
// Constrain bits
component nbytes = Num2Bits(8 * n);
nbytes.in <== in;
component bytes[n];
for (var k = 0; k < n; k++){
// Witness gen out
out[k] <-- (in >> (k * 8)) % 256;
// Constrain bits to match
bytes[k] = Num2Bits(8);
bytes[k].in <== out[k];
for (var j = 0; j < 8; j++) {
nbytes.out[k * 8 + j] === bytes[k].out[j];
}
}
}
// n bytes per signal, n = 31 usually (i.e. 31 8-bit values being packed into 248 bits)
// when calling this, you must constrain each 'in' value yourself to be < 256
// TODO: Rangecheck in and out?
template Bytes2Packed(n){
signal input in[n]; // each in value is < 256 (i.e. 2^8)
signal pow2[n+1]; // [k] is 2^k
signal in_prefix_sum[n+1]; // each [k] is in[0] + 2^8 in[1]... 2^{8k-8} in[k-1]. cont.
// [0] is 0. [1] is in[0]. [n+1] is out.
signal output out; // < 2 ^ (8 * 31)
// Rangecheck in and out?
// Witness gen out
in_prefix_sum[0] <-- 0;
for (var k = 0; k < n; k++){
in_prefix_sum[k+1] <-- in_prefix_sum[k] + in[k] * (2 ** (k * 8));
}
out <-- in_prefix_sum[n];
// Constrain out bits
component nbytes = Num2Bits(8 * n);
nbytes.in <== out; // I think this auto-rangechecks out to be < 8*n bits.
component bytes[n];
for (var k = 0; k < n; k++){
bytes[k] = Num2Bits(8);
bytes[k].in <== in[k];
for (var j = 0; j < 8; j++) {
nbytes.out[k * 8 + j] === bytes[k].out[j];
}
}
}
// salt_is_message_id_from, custom_anon_from_hashed_salt = MakeAnonEmailSalt(max_email_from_len, max_message_id_len)(email_from, custom_message_id_from, shifted_message_id)
template MakeAnonEmailSalt(email_len, blinder_len) {
signal input email[email_len];
signal input custom_message_id[blinder_len]; // previous message id, used to source past account
signal input original_message_id[blinder_len]; // previous message id, used to source past account
signal intermediate_is_message_id_from[blinder_len + 1];
signal isEq[blinder_len];
signal output blinder_matches;
signal output anon_salt;
component hasher = MiMCSponge(email_len + blinder_len, 220, 1);
hasher.k <== 123;
for (var i = 0; i < email_len; i++) {
hasher.ins[i] <== email[i];
}
intermediate_is_message_id_from[0] <== 1;
for (var i = 0; i < blinder_len; i++) {
hasher.ins[i + email_len] <== custom_message_id[i];
isEq[i] <== IsEqual()([custom_message_id[i], original_message_id[i]]);
intermediate_is_message_id_from[i + 1] <== isEq[i] * intermediate_is_message_id_from[i];
}
blinder_matches <== intermediate_is_message_id_from[blinder_len];
anon_salt <== hasher.outs[0];
}

View File

@@ -1,8 +1,8 @@
pragma circom 2.1.5;
include "./rsa/rsa.circom";
include "@zk-email/circuits/helpers/rsa.circom";
include "@zk-email/circuits/helpers/extract.circom";
include "./sha256Bytes.circom";
include "../node_modules/circomlib/circuits/sha256/sha256.circom";
template PassportVerifier(n, k) {
signal input mrz[93]; // formatted mrz (5 + 88) chars

View File

@@ -1,7 +1,7 @@
pragma circom 2.1.5;
include "../node_modules/circomlib/circuits/poseidon.circom";
include "./helpers/extract.circom";
include "circomlib/circuits/poseidon.circom";
include "@zk-email/circuits/helpers/extract.circom";
include "./passport_verifier.circom";
template ProofOfPassport(n, k) {

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 zk-email
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,156 +0,0 @@
pragma circom 2.1.5;
include "../helpers/fp.circom";
// Computes base^65537 mod modulus
// Does not necessarily reduce fully mod modulus (the answer could be
// too big by a multiple of modulus)
template FpPow65537Mod(n, k) {
signal input base[k];
// Exponent is hardcoded at 65537
signal input modulus[k];
signal output out[k];
component doublers[16];
component adder = FpMul(n, k);
for (var i = 0; i < 16; i++) {
doublers[i] = FpMul(n, k);
}
for (var j = 0; j < k; j++) {
adder.p[j] <== modulus[j];
for (var i = 0; i < 16; i++) {
doublers[i].p[j] <== modulus[j];
}
}
for (var j = 0; j < k; j++) {
doublers[0].a[j] <== base[j];
doublers[0].b[j] <== base[j];
}
for (var i = 0; i + 1 < 16; i++) {
for (var j = 0; j < k; j++) {
doublers[i + 1].a[j] <== doublers[i].out[j];
doublers[i + 1].b[j] <== doublers[i].out[j];
}
}
for (var j = 0; j < k; j++) {
adder.a[j] <== base[j];
adder.b[j] <== doublers[15].out[j];
}
for (var j = 0; j < k; j++) {
out[j] <== adder.out[j];
}
}
template RSAPad(n, k) {
signal input modulus[k];
signal input base_message[k];
signal output padded_message[k];
var base_len = 408;
var msg_len = 256;
signal padded_message_bits[n*k];
component modulus_n2b[k];
component base_message_n2b[k];
signal modulus_bits[n*k];
signal base_message_bits[n*k];
for (var i = 0; i < k; i++) {
base_message_n2b[i] = Num2Bits(n);
base_message_n2b[i].in <== base_message[i];
for (var j = 0; j < n; j++) {
base_message_bits[i*n+j] <== base_message_n2b[i].out[j];
}
modulus_n2b[i] = Num2Bits(n);
modulus_n2b[i].in <== modulus[i];
for (var j = 0; j < n; j++) {
modulus_bits[i*n+j] <== modulus_n2b[i].out[j];
}
}
for (var i = msg_len; i < n*k; i++) {
base_message_bits[i] === 0;
}
for (var i = 0; i < msg_len; i++) {
padded_message_bits[i] <== base_message_bits[i];
}
for (var i = base_len; i < base_len + 8; i++) {
padded_message_bits[i] <== 0;
}
for (var i = msg_len; i < base_len; i++) {
padded_message_bits[i] <== (0x3031300d060960864801650304020105000420 >> (i - msg_len)) & 1;
}
component modulus_zero[(n*k + 7 - (base_len + 8))\8];
{
var modulus_prefix = 0;
for (var i = n*k - 1; i >= base_len + 8; i--) {
if (i+8 < n*k) {
modulus_prefix += modulus_bits[i+8];
if (i % 8 == 0) {
var idx = (i - (base_len + 8)) / 8;
modulus_zero[idx] = IsZero();
modulus_zero[idx].in <== modulus_prefix;
padded_message_bits[i] <== 1-modulus_zero[idx].out;
} else {
padded_message_bits[i] <== padded_message_bits[i+1];
}
} else {
padded_message_bits[i] <== 0;
}
}
}
// The RFC guarantees at least 8 octets of 0xff padding.
assert(base_len + 8 + 65 <= n*k);
for (var i = base_len + 8; i < base_len + 8 + 65; i++) {
padded_message_bits[i] === 1;
}
component padded_message_b2n[k];
for (var i = 0; i < k; i++) {
padded_message_b2n[i] = Bits2Num(n);
for (var j = 0; j < n; j++) {
padded_message_b2n[i].in[j] <== padded_message_bits[i*n+j];
}
padded_message[i] <== padded_message_b2n[i].out;
}
}
template RSAVerify65537(n, k) {
signal input signature[k];
signal input modulus[k];
signal input base_message[k];
component padder = RSAPad(n, k);
for (var i = 0; i < k; i++) {
padder.modulus[i] <== modulus[i];
padder.base_message[i] <== base_message[i];
}
// Check that the signature is in proper form and reduced mod modulus.
component signatureRangeCheck[k];
component bigLessThan = BigLessThan(n, k);
for (var i = 0; i < k; i++) {
signatureRangeCheck[i] = Num2Bits(n);
signatureRangeCheck[i].in <== signature[i];
bigLessThan.a[i] <== signature[i];
bigLessThan.b[i] <== modulus[i];
}
bigLessThan.out === 1;
component bigPow = FpPow65537Mod(n, k);
for (var i = 0; i < k; i++) {
bigPow.base[i] <== signature[i];
bigPow.modulus[i] <== modulus[i];
}
// By construction of the padding, the padded message is necessarily
// smaller than the modulus. Thus, we don't have to check that bigPow is fully reduced.
for (var i = 0; i < k; i++) {
bigPow.out[i] === padder.padded_message[i];
}
}

View File

@@ -1,8 +1,7 @@
pragma circom 2.1.5;
include "../node_modules/circomlib/circuits/bitify.circom";
include "../node_modules/circomlib/circuits/sha256/sha256.circom";
include "../node_modules/circomlib/circuits/bitify.circom";
include "circomlib/circuits/bitify.circom";
include "circomlib/circuits/sha256/sha256.circom";
template Sha256Bytes(max_num_bytes) {
signal input in_padded[max_num_bytes];

View File

@@ -10,6 +10,7 @@
"@types/chai-as-promised": "^7.1.6",
"@types/node": "^20.6.3",
"@types/node-forge": "^1.3.5",
"@zk-email/circuits": "^3.2.2",
"chai-as-promised": "^7.1.1",
"circomlib": "^2.0.5",
"js-sha256": "^0.10.1",

View File

@@ -20,7 +20,7 @@ fi
cd ..
echo "compiling circuit"
circom circuits/proof_of_passport.circom --r1cs --wasm --output build
circom circuits/proof_of_passport.circom -l node_modules --r1cs --wasm --output build
mkdir -p ../app/ark-circom-passport/passport/
cp build/proof_of_passport.r1cs ../app/ark-circom-passport/passport/

View File

@@ -49,6 +49,21 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.3.tgz#5b763b321cd3b80f6b8dde7a37e1a77ff9358dd9"
integrity sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==
"@zk-email/circuits@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@zk-email/circuits/-/circuits-3.2.2.tgz#019d10e644ba1a3ad73bd8d73a66f53149c530dd"
integrity sha512-3R2y64IeNq7cnfJabAlmQflhGlNAhcR4vq9n55fAIbgpYQ8P2d4k5Aa52HK8mqaVDCnNjLlAWMni557TUqQuGg==
dependencies:
"@zk-email/zk-regex-circom" "^1.1.1"
"@zk-email/zk-regex-circom@^1.1.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@zk-email/zk-regex-circom/-/zk-regex-circom-1.2.1.tgz#ea1adc832e8d855c9568abc438567a524a0ac962"
integrity sha512-+AoH5PdKZxatTWfwYWGkpuQ0xGaax6FwBHvdYXugkPDdgtJQYyxhItQtM79bo0djCmuwEK2X1rnYlFHBDClQeA==
dependencies:
commander "^11.0.0"
snarkjs "^0.7.0"
ansi-colors@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@@ -256,6 +271,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
commander@^11.0.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906"
integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -368,6 +388,15 @@ ffjavascript@0.2.60, ffjavascript@^0.2.48:
wasmcurves "0.2.2"
web-worker "^1.2.0"
ffjavascript@0.2.63:
version "0.2.63"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.63.tgz#0c1216a1f123dc9181df69e144473704d2f115eb"
integrity sha512-dBgdsfGks58b66JnUZeZpGxdMIDQ4QsD3VYlRJyFVrKQHb2kJy4R2gufx5oetrTxXPT+aEjg0dOvOLg1N0on4A==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.2"
web-worker "1.2.0"
filelist@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
@@ -773,6 +802,22 @@ serialize-javascript@6.0.0:
dependencies:
randombytes "^2.1.0"
snarkjs@^0.7.0:
version "0.7.3"
resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.7.3.tgz#7f703d05b810235255f2d0a70d8a9b8b3ea916e5"
integrity sha512-cDLpWqdqEJSCQNc+cXYX1XTKdUZBtYEisuOsgmXf/HUsN5WmGN+FO7HfCS+cMQT1Nzbm1a9gAEpKH6KRtDtS1Q==
dependencies:
"@iden3/binfileutils" "0.0.11"
bfj "^7.0.2"
blake2b-wasm "^2.4.0"
circom_runtime "0.1.24"
ejs "^3.1.6"
fastfile "0.0.20"
ffjavascript "0.2.63"
js-sha3 "^0.8.0"
logplease "^1.2.15"
r1csfile "0.0.47"
snarkjs@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.7.1.tgz#c96ecaf4db8c2eb44d60b17ee02f37ed39c821bb"
@@ -928,7 +973,7 @@ wasmcurves@0.2.2:
dependencies:
wasmbuilder "0.0.16"
web-worker@^1.2.0:
web-worker@1.2.0, web-worker@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==