mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
use zkemail for short rsa and circom-dl for long rsa
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsaPkcs1v1_5.circom";
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsa65537Pkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[32];
|
||||
signal input modulus[32];
|
||||
signal input message[32];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
VerifyRsaPkcs1v1_5(3, 64, 32, 65537, 160)(signature, modulus, message, dummy);
|
||||
VerifyRsa65537Pkcs1v1_5(64, 32, 65537, 160)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
@@ -1,15 +1,13 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsaPkcs1v1_5.circom";
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsa3Pkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[32];
|
||||
signal input modulus[32];
|
||||
signal input message[32];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
VerifyRsaPkcs1v1_5(13, 64, 32, 3, 256)(signature, modulus, message, dummy);
|
||||
VerifyRsa3Pkcs1v1_5(64, 32, 3, 256)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
@@ -1,15 +1,14 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsaPkcs1v1_5.circom";
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsa65537Pkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[32];
|
||||
signal input modulus[32];
|
||||
signal input message[32];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
VerifyRsaPkcs1v1_5(1, 64, 32, 65537, 256)(signature, modulus, message, dummy);
|
||||
VerifyRsa65537Pkcs1v1_5(64, 32, 65537, 256)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
@@ -1,15 +1,13 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsaPkcs1v1_5.circom";
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsa65537Pkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[32];
|
||||
signal input modulus[32];
|
||||
signal input message[32];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
VerifyRsaPkcs1v1_5(14, 96, 32, 65537, 256)(signature, modulus, message, dummy);
|
||||
VerifyRsa65537Pkcs1v1_5(96, 32, 65537, 256)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
@@ -1,15 +1,13 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsaPkcs1v1_5.circom";
|
||||
include "../../../utils/circomlib/signature/rsa/verifyLargeRsaPkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[64];
|
||||
signal input modulus[64];
|
||||
signal input message[64];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
VerifyRsaPkcs1v1_5(10, 64, 64, 65537, 256)(signature, modulus, message, dummy);
|
||||
VerifyLargeRsaPkcs1v1_5(10, 64, 64, 65537, 256)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
@@ -1,15 +1,13 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/circomlib/signature/rsa/verifyRsaPkcs1v1_5.circom";
|
||||
include "../../../utils/circomlib/signature/rsa/verifyLargeRsaPkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[64];
|
||||
signal input modulus[64];
|
||||
signal input message[64];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
VerifyRsaPkcs1v1_5(15, 64, 64, 65537, 512)(signature, modulus, message, dummy);
|
||||
VerifyLargeRsaPkcs1v1_5(15, 64, 64, 65537, 512)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
|
||||
@@ -386,11 +386,12 @@ template PowerMod(CHUNK_SIZE, CHUNK_NUMBER, EXP) {
|
||||
|
||||
signal input base[CHUNK_NUMBER];
|
||||
signal input modulus[CHUNK_NUMBER];
|
||||
signal input dummy;
|
||||
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
var exp_process[256] = exp_to_bits(EXP);
|
||||
|
||||
var dummy = 0;
|
||||
|
||||
component muls[exp_process[0]];
|
||||
component resultMuls[exp_process[1] - 1];
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../bitify/bitify.circom";
|
||||
|
||||
// PKCS1v1.5 Padding Scheme
|
||||
// 0x00 || 0x01 || PS || 0x00 || OID || Hash
|
||||
// PS is a sequence of 0xFF bytes that is padded so that the data to be signed matches the length of the key.
|
||||
// OID is the object identifier for the hash function used.
|
||||
// For SHA1, the OID is 0x3021300906052b0e03021a05000414
|
||||
// For SHA256, the OID is 0x3031300d060960864801650304020105000420
|
||||
// For SHA384, the OID is 0x3041300d060960864801650304020205000430
|
||||
// For SHA512, the OID is 0x3051300d060960864801650304020305000440
|
||||
|
||||
template Pkcs1v1_5Padding(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE) {
|
||||
signal input modulus[CHUNK_NUMBER];
|
||||
signal input message[CHUNK_NUMBER];
|
||||
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
var OID_SIZE = getOIDSize(HASH_SIZE);
|
||||
|
||||
signal paddedMessageBits[CHUNK_SIZE * CHUNK_NUMBER];
|
||||
|
||||
component modulusN2B[CHUNK_NUMBER];
|
||||
component messageN2B[CHUNK_NUMBER];
|
||||
signal modulusBits[CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal messageBits[CHUNK_SIZE * CHUNK_NUMBER];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
messageN2B[i] = Num2Bits(CHUNK_SIZE);
|
||||
messageN2B[i].in <== message[i];
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
messageBits[i*CHUNK_SIZE+j] <== messageN2B[i].out[j];
|
||||
}
|
||||
modulusN2B[i] = Num2Bits(CHUNK_SIZE);
|
||||
modulusN2B[i].in <== modulus[i];
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
modulusBits[i*CHUNK_SIZE+j] <== modulusN2B[i].out[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < HASH_SIZE; i++) {
|
||||
paddedMessageBits[i] <== messageBits[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
paddedMessageBits[HASH_SIZE + OID_SIZE + i] <== 0;
|
||||
}
|
||||
|
||||
var OID = getOID(HASH_SIZE);
|
||||
for (var i = HASH_SIZE; i < HASH_SIZE + OID_SIZE; i++) {
|
||||
paddedMessageBits[i] <== (OID >> (i - HASH_SIZE)) & 1;
|
||||
}
|
||||
|
||||
component modulusZero[(CHUNK_SIZE * CHUNK_NUMBER + 7 - (HASH_SIZE + OID_SIZE)) \ 8];
|
||||
{
|
||||
var modulusPrefix = 0;
|
||||
for (var i = CHUNK_SIZE * CHUNK_NUMBER - 1; i >= (HASH_SIZE + OID_SIZE) + 8; i--) {
|
||||
if (i + 8 < CHUNK_SIZE * CHUNK_NUMBER) {
|
||||
modulusPrefix += modulusBits[i+8];
|
||||
if (i % 8 == 0) {
|
||||
var idx = (i - (HASH_SIZE + OID_SIZE)) \ 8;
|
||||
modulusZero[idx] = IsZero();
|
||||
modulusZero[idx].in <== modulusPrefix;
|
||||
paddedMessageBits[i] <== 1-modulusZero[idx].out;
|
||||
} else {
|
||||
paddedMessageBits[i] <== paddedMessageBits[i+1];
|
||||
}
|
||||
} else {
|
||||
paddedMessageBits[i] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(HASH_SIZE + OID_SIZE + 8 + 65 < CHUNK_SIZE * CHUNK_NUMBER);
|
||||
|
||||
for (var i = HASH_SIZE + OID_SIZE + 8; i < HASH_SIZE + OID_SIZE + 8 + 65; i++) {
|
||||
paddedMessageBits[i] === 1;
|
||||
}
|
||||
|
||||
component passedMessageB2N[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
passedMessageB2N[i] = Bits2Num(CHUNK_SIZE);
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
passedMessageB2N[i].in[j] <== paddedMessageBits[i*CHUNK_SIZE+j];
|
||||
}
|
||||
out[i] <== passedMessageB2N[i].out;
|
||||
}
|
||||
}
|
||||
|
||||
function getOID(HASH_SIZE) {
|
||||
if (HASH_SIZE == 160) {
|
||||
return 0x3021300906052b0e03021a05000414;
|
||||
}
|
||||
if (HASH_SIZE == 256) {
|
||||
return 0x3031300d060960864801650304020105000420;
|
||||
}
|
||||
if (HASH_SIZE == 384) {
|
||||
return 0x3041300d060960864801650304020205000430;
|
||||
}
|
||||
if (HASH_SIZE == 512) {
|
||||
return 0x3051300d060960864801650304020305000440;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getOIDSize(HASH_SIZE) {
|
||||
if (HASH_SIZE == 160) {
|
||||
return 120;
|
||||
}
|
||||
if (HASH_SIZE == 256) {
|
||||
return 152;
|
||||
}
|
||||
if (HASH_SIZE == 384) {
|
||||
return 152;
|
||||
}
|
||||
if (HASH_SIZE == 512) {
|
||||
return 152;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -5,23 +5,24 @@ include "../../bigInt/bigInt.circom";
|
||||
include "../../../passport/signatureAlgorithm.circom";
|
||||
include "../../int/arithmetic.circom";
|
||||
|
||||
// For exponent is 3, use E_BITS = 2
|
||||
// For exponent is 65537, use E_BITS = 17
|
||||
// For exponent, EXP is 3
|
||||
// For exponent, EXP is 65537
|
||||
|
||||
// For 2048bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 32
|
||||
// For 3072bits RSA, CHUNK_SIZE = 96, CHUNK_NUMBER = 32
|
||||
// For 4096bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 64
|
||||
// For 2048bits, CHUNK_SIZE = 64, CHUNK_NUMBER = 32
|
||||
// For 3072bits, CHUNK_SIZE = 96, CHUNK_NUMBER = 32
|
||||
// For 4096bits, CHUNK_SIZE = 64, CHUNK_NUMBER = 64
|
||||
|
||||
// HASH_SIZE is the size of the hash in bits
|
||||
// For SHA256, HASH_SIZE = 256
|
||||
// For SHA384, HASH_SIZE = 384
|
||||
// For SHA512, HASH_SIZE = 512
|
||||
|
||||
template VerifyRsaPkcs1v1_5(signatureAlgorithm, CHUNK_SIZE, CHUNK_NUMBER, E_BITS, HASH_SIZE) {
|
||||
template VerifyLargeRsaPkcs1v1_5(signatureAlgorithm, CHUNK_SIZE, CHUNK_NUMBER, EXP, HASH_SIZE) {
|
||||
signal input signature[CHUNK_NUMBER];
|
||||
signal input modulus[CHUNK_NUMBER];
|
||||
|
||||
signal input message[CHUNK_NUMBER];
|
||||
|
||||
signal input dummy;
|
||||
|
||||
// Range check which is came from old openpassport impl
|
||||
component signatureRangeCheck[CHUNK_NUMBER];
|
||||
component bigLessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
@@ -34,12 +35,11 @@ template VerifyRsaPkcs1v1_5(signatureAlgorithm, CHUNK_SIZE, CHUNK_NUMBER, E_BITS
|
||||
bigLessThan.out === 1;
|
||||
|
||||
// Calc Power Mod
|
||||
component bigPow = PowerMod(CHUNK_SIZE, CHUNK_NUMBER, E_BITS);
|
||||
component bigPow = PowerMod(CHUNK_SIZE, CHUNK_NUMBER, EXP);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bigPow.base[i] <== signature[i];
|
||||
bigPow.modulus[i] <== modulus[i];
|
||||
}
|
||||
bigPow.dummy <== dummy;
|
||||
|
||||
var padding[5] = getPadding(signatureAlgorithm);
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../zkemail/lib/fp.circom";
|
||||
include "./pkcs1v1_5Padding.circom";
|
||||
include "../../bitify/bitify.circom";
|
||||
|
||||
// For exponent, EXP is 3
|
||||
// For exponent, EXP is 65537
|
||||
|
||||
// For 2048bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 32
|
||||
// For 3072bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 48
|
||||
// For 4096bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 64
|
||||
|
||||
// HASH_SIZE is the size of the hash in bits
|
||||
|
||||
template VerifyRsa3Pkcs1v1_5(CHUNK_SIZE, CHUNK_NUMBER, EXP, HASH_SIZE) {
|
||||
signal input signature[CHUNK_NUMBER];
|
||||
signal input modulus[CHUNK_NUMBER];
|
||||
|
||||
signal input message[CHUNK_NUMBER];
|
||||
|
||||
// 1. Add padding to the hashed message
|
||||
component padder = Pkcs1v1_5Padding(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
padder.modulus[i] <== modulus[i];
|
||||
padder.message[i] <== message[i];
|
||||
}
|
||||
|
||||
// 2. Check that the signature is in proper form and reduced mod modulus.
|
||||
component signatureRangeCheck[CHUNK_NUMBER];
|
||||
component bigLessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
signatureRangeCheck[i] = Num2Bits(CHUNK_SIZE);
|
||||
signatureRangeCheck[i].in <== signature[i];
|
||||
bigLessThan.a[i] <== signature[i];
|
||||
bigLessThan.b[i] <== modulus[i];
|
||||
}
|
||||
bigLessThan.out === 1;
|
||||
|
||||
// 3. Compute the signature^exponent mod modulus
|
||||
component bigPow = FpPow3Mod(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bigPow.base[i] <== signature[i];
|
||||
bigPow.modulus[i] <== modulus[i];
|
||||
}
|
||||
|
||||
// 4. Check that the computed value is equal to the padded message
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bigPow.out[i] === padder.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// @title FpPow3Mod
|
||||
/// @notice Computes base^3 mod modulus
|
||||
/// @dev Does not necessarily reduce fully mod modulus (the answer could be too big by a multiple of modulus)
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input base The base to exponentiate; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @input modulus The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @output out The result of the exponentiation.
|
||||
template FpPow3Mod(n, k) {
|
||||
signal input base[k];
|
||||
signal input modulus[k];
|
||||
|
||||
signal output out[k];
|
||||
|
||||
component doublers = FpMul(n, k);
|
||||
component adder = FpMul(n, k);
|
||||
|
||||
for (var j = 0; j < k; j++) {
|
||||
adder.p[j] <== modulus[j];
|
||||
doublers.p[j] <== modulus[j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
doublers.a[j] <== base[j];
|
||||
doublers.b[j] <== base[j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
adder.a[j] <== base[j];
|
||||
adder.b[j] <== doublers.out[j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
out[j] <== adder.out[j];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../zkemail/lib/fp.circom";
|
||||
include "./pkcs1v1_5Padding.circom";
|
||||
include "../../bitify/bitify.circom";
|
||||
|
||||
// For exponent, EXP is 3
|
||||
// For exponent, EXP is 65537
|
||||
|
||||
// For 2048bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 32
|
||||
// For 3072bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 48
|
||||
// For 4096bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 64
|
||||
|
||||
// HASH_SIZE is the size of the hash in bits
|
||||
|
||||
template VerifyRsa65537Pkcs1v1_5(CHUNK_SIZE, CHUNK_NUMBER, EXP, HASH_SIZE) {
|
||||
signal input signature[CHUNK_NUMBER];
|
||||
signal input modulus[CHUNK_NUMBER];
|
||||
|
||||
signal input message[CHUNK_NUMBER];
|
||||
|
||||
// 1. Add padding to the hashed message
|
||||
component padder = Pkcs1v1_5Padding(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
padder.modulus[i] <== modulus[i];
|
||||
padder.message[i] <== message[i];
|
||||
}
|
||||
|
||||
// 2. Check that the signature is in proper form and reduced mod modulus.
|
||||
component signatureRangeCheck[CHUNK_NUMBER];
|
||||
component bigLessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
signatureRangeCheck[i] = Num2Bits(CHUNK_SIZE);
|
||||
signatureRangeCheck[i].in <== signature[i];
|
||||
bigLessThan.a[i] <== signature[i];
|
||||
bigLessThan.b[i] <== modulus[i];
|
||||
}
|
||||
bigLessThan.out === 1;
|
||||
|
||||
// 3. Compute the signature^exponent mod modulus
|
||||
component bigPow = FpPow65537Mod(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bigPow.base[i] <== signature[i];
|
||||
bigPow.modulus[i] <== modulus[i];
|
||||
}
|
||||
|
||||
// 4. Check that the computed value is equal to the padded message
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bigPow.out[i] === padder.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// @title FpPow65537Mod
|
||||
/// @notice Computes base^65537 mod modulus
|
||||
/// @dev Does not necessarily reduce fully mod modulus (the answer could be too big by a multiple of modulus)
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input base The base to exponentiate; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @input modulus The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @output out The result of the exponentiation.
|
||||
template FpPow65537Mod(n, k) {
|
||||
signal input base[k];
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
/// @title FpPow3Mod
|
||||
/// @notice Computes base^3 mod modulus
|
||||
/// @dev Does not necessarily reduce fully mod modulus (the answer could be too big by a multiple of modulus)
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input base The base to exponentiate; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @input modulus The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @output out The result of the exponentiation.
|
||||
template FpPow3Mod(n, k) {
|
||||
signal input base[k];
|
||||
signal input modulus[k];
|
||||
|
||||
signal output out[k];
|
||||
|
||||
component doublers = FpMul(n, k);
|
||||
component adder = FpMul(n, k);
|
||||
|
||||
for (var j = 0; j < k; j++) {
|
||||
adder.p[j] <== modulus[j];
|
||||
doublers.p[j] <== modulus[j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
doublers.a[j] <== base[j];
|
||||
doublers.b[j] <== base[j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
adder.a[j] <== base[j];
|
||||
adder.b[j] <== doublers.out[j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
out[j] <== adder.out[j];
|
||||
}
|
||||
}
|
||||
74
circuits/circuits/utils/circomlib/utils/compconstant.circom
Normal file
74
circuits/circuits/utils/circomlib/utils/compconstant.circom
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
||||
|
||||
circom is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "../bitify/bitify.circom";
|
||||
|
||||
// Returns 1 if in (in binary) > ct
|
||||
|
||||
template CompConstant(ct) {
|
||||
signal input in[254];
|
||||
signal output out;
|
||||
|
||||
signal parts[127];
|
||||
signal sout;
|
||||
|
||||
var clsb;
|
||||
var cmsb;
|
||||
var slsb;
|
||||
var smsb;
|
||||
|
||||
var sum=0;
|
||||
|
||||
var b = (1 << 128) -1;
|
||||
var a = 1;
|
||||
var e = 1;
|
||||
var i;
|
||||
|
||||
for (i=0;i<127; i++) {
|
||||
clsb = (ct >> (i*2)) & 1;
|
||||
cmsb = (ct >> (i*2+1)) & 1;
|
||||
slsb = in[i*2];
|
||||
smsb = in[i*2+1];
|
||||
|
||||
if ((cmsb==0)&&(clsb==0)) {
|
||||
parts[i] <== -b*smsb*slsb + b*smsb + b*slsb;
|
||||
} else if ((cmsb==0)&&(clsb==1)) {
|
||||
parts[i] <== a*smsb*slsb - a*slsb + b*smsb - a*smsb + a;
|
||||
} else if ((cmsb==1)&&(clsb==0)) {
|
||||
parts[i] <== b*smsb*slsb - a*smsb + a;
|
||||
} else {
|
||||
parts[i] <== -a*smsb*slsb + a;
|
||||
}
|
||||
|
||||
sum = sum + parts[i];
|
||||
|
||||
b = b -e;
|
||||
a = a +e;
|
||||
e = e*2;
|
||||
}
|
||||
|
||||
sout <== sum;
|
||||
|
||||
component num2bits = Num2Bits(135);
|
||||
|
||||
num2bits.in <== sout;
|
||||
|
||||
out <== num2bits.out[127];
|
||||
}
|
||||
36
circuits/circuits/utils/circomlib/utils/sign.circom
Normal file
36
circuits/circuits/utils/circomlib/utils/sign.circom
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2018 0KIMS association.
|
||||
|
||||
This file is part of circom (Zero Knowledge Circuit Compiler).
|
||||
|
||||
circom is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
circom is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "compconstant.circom";
|
||||
|
||||
template Sign() {
|
||||
signal input in[254];
|
||||
signal output sign;
|
||||
|
||||
component comp = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808);
|
||||
|
||||
var i;
|
||||
|
||||
for (i=0; i<254; i++) {
|
||||
comp.in[i] <== in[i];
|
||||
}
|
||||
|
||||
sign <== comp.out;
|
||||
}
|
||||
128
circuits/circuits/utils/zkemail/lib/base64.circom
Normal file
128
circuits/circuits/utils/zkemail/lib/base64.circom
Normal file
@@ -0,0 +1,128 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
|
||||
|
||||
/// @title Base64Decode
|
||||
/// @notice Decodes a Base64 encoded string to array of bytes.
|
||||
/// @notice Only support inputs with length = `byteLength` (no 0 padding).
|
||||
/// @notice It is known that padding char '=' can be replaed with `A` to produce the same output
|
||||
/// as Base64Lookup returns `0` for both, but a pracical attack from this is unlikely.
|
||||
/// @param byteLength Byte length of the encoded value - length of the output array.
|
||||
/// @input in Base64 encoded string; assumes elements to be valid Base64 characters.
|
||||
/// @output out Decoded array of bytes.
|
||||
template Base64Decode_ZK_EMAIL(byteLength) {
|
||||
var charLength = 4 * ((byteLength + 2) \ 3); // 4 chars encode 3 bytes
|
||||
|
||||
signal input in[charLength];
|
||||
signal output out[byteLength];
|
||||
|
||||
component bitsIn[charLength\4][4];
|
||||
component bitsOut[charLength\4][3];
|
||||
component translate[charLength\4][4];
|
||||
|
||||
var idx = 0;
|
||||
for (var i = 0; i < charLength; i += 4) {
|
||||
for (var j = 0; j < 3; j++) {
|
||||
bitsOut[i\4][j] = Bits2Num(8);
|
||||
}
|
||||
|
||||
for (var j = 0; j < 4; j++) {
|
||||
bitsIn[i\4][j] = Num2Bits(6);
|
||||
translate[i\4][j] = Base64Lookup();
|
||||
translate[i\4][j].in <== in[i+j];
|
||||
translate[i\4][j].out ==> bitsIn[i\4][j].in;
|
||||
}
|
||||
|
||||
// Do the re-packing from four 6-bit words to three 8-bit words.
|
||||
for (var j = 0; j < 6; j++) {
|
||||
bitsOut[i\4][0].in[j+2] <== bitsIn[i\4][0].out[j];
|
||||
}
|
||||
bitsOut[i\4][0].in[0] <== bitsIn[i\4][1].out[4];
|
||||
bitsOut[i\4][0].in[1] <== bitsIn[i\4][1].out[5];
|
||||
|
||||
for (var j = 0; j < 4; j++) {
|
||||
bitsOut[i\4][1].in[j+4] <== bitsIn[i\4][1].out[j];
|
||||
}
|
||||
for (var j = 0; j < 4; j++) {
|
||||
bitsOut[i\4][1].in[j] <== bitsIn[i\4][2].out[j+2];
|
||||
}
|
||||
|
||||
bitsOut[i\4][2].in[6] <== bitsIn[i\4][2].out[0];
|
||||
bitsOut[i\4][2].in[7] <== bitsIn[i\4][2].out[1];
|
||||
for (var j = 0; j < 6; j++) {
|
||||
bitsOut[i\4][2].in[j] <== bitsIn[i\4][3].out[j];
|
||||
}
|
||||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
if (idx+j < byteLength) {
|
||||
out[idx+j] <== bitsOut[i\4][j].out;
|
||||
}
|
||||
}
|
||||
idx += 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title Base64Lookup
|
||||
/// @notice http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html#vector-lookup-base
|
||||
/// @input in input character; assumes input to be valid Base64 character (though constrained implicitly).
|
||||
/// @output out output bit value.
|
||||
template Base64Lookup_ZK_EMAIL() {
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
// ['A', 'Z']
|
||||
component le_Z = LessThan(8);
|
||||
le_Z.in[0] <== in;
|
||||
le_Z.in[1] <== 90+1;
|
||||
|
||||
component ge_A = GreaterThan(8);
|
||||
ge_A.in[0] <== in;
|
||||
ge_A.in[1] <== 65-1;
|
||||
|
||||
signal range_AZ <== ge_A.out * le_Z.out;
|
||||
signal sum_AZ <== range_AZ * (in - 65);
|
||||
|
||||
// ['a', 'z']
|
||||
component le_z = LessThan(8);
|
||||
le_z.in[0] <== in;
|
||||
le_z.in[1] <== 122+1;
|
||||
|
||||
component ge_a = GreaterThan(8);
|
||||
ge_a.in[0] <== in;
|
||||
ge_a.in[1] <== 97-1;
|
||||
|
||||
signal range_az <== ge_a.out * le_z.out;
|
||||
signal sum_az <== sum_AZ + range_az * (in - 71);
|
||||
|
||||
// ['0', '9']
|
||||
component le_9 = LessThan(8);
|
||||
le_9.in[0] <== in;
|
||||
le_9.in[1] <== 57+1;
|
||||
|
||||
component ge_0 = GreaterThan(8);
|
||||
ge_0.in[0] <== in;
|
||||
ge_0.in[1] <== 48-1;
|
||||
|
||||
signal range_09 <== ge_0.out * le_9.out;
|
||||
signal sum_09 <== sum_az + range_09 * (in + 4);
|
||||
|
||||
// '+'
|
||||
component equal_plus = IsZero();
|
||||
equal_plus.in <== in - 43;
|
||||
signal sum_plus <== sum_09 + equal_plus.out * (in + 19);
|
||||
|
||||
// '/'
|
||||
component equal_slash = IsZero();
|
||||
equal_slash.in <== in - 47;
|
||||
signal sum_slash <== sum_plus + equal_slash.out * (in + 16);
|
||||
|
||||
out <== sum_slash;
|
||||
|
||||
// '='
|
||||
component equal_eqsign = IsZero();
|
||||
equal_eqsign.in <== in - 61;
|
||||
|
||||
1 === range_AZ + range_az + range_09 + equal_plus.out + equal_slash.out + equal_eqsign.out;
|
||||
}
|
||||
264
circuits/circuits/utils/zkemail/lib/bigint-func.circom
Normal file
264
circuits/circuits/utils/zkemail/lib/bigint-func.circom
Normal file
@@ -0,0 +1,264 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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[100];
|
||||
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[100] = long_scalar_mult(n, k + 1, scale, a);
|
||||
// k + 1 registers now
|
||||
var norm_b[100] = 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;
|
||||
}
|
||||
94
circuits/circuits/utils/zkemail/lib/bigint.circom
Normal file
94
circuits/circuits/utils/zkemail/lib/bigint.circom
Normal file
@@ -0,0 +1,94 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../circomlib/bitify/comparators.circom";
|
||||
include "../../circomlib/bitify/bitify.circom";
|
||||
include "../../circomlib/bitify/gates.circom";
|
||||
include "./bigint-func.circom";
|
||||
|
||||
|
||||
/// @template BigLessThan
|
||||
/// @notice Less than comparison for big integers
|
||||
/// @param n The number of bits in each chunk
|
||||
/// @param k The number of chunks
|
||||
/// @param a The first bigint; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @param b The second bigint; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @param out The output of the comparison
|
||||
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 CheckCarryToZero
|
||||
/// @notice Check that in[] as a big integer is zero
|
||||
/// @param n The number of bits in each chunk
|
||||
/// @param m
|
||||
/// @param k The number of chunks
|
||||
/// @input in The input big integer; assumes elements to be in the range -2^(m-1) to 2^(m-1)
|
||||
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;
|
||||
}
|
||||
81
circuits/circuits/utils/zkemail/lib/fp.circom
Normal file
81
circuits/circuits/utils/zkemail/lib/fp.circom
Normal file
@@ -0,0 +1,81 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../circomlib/bitify/bitify.circom";
|
||||
include "../../circomlib/bitify/comparators.circom";
|
||||
include "../../circomlib/utils/sign.circom";
|
||||
include "./bigint.circom";
|
||||
include "./bigint-func.circom";
|
||||
|
||||
|
||||
/// @title FpMul
|
||||
/// @notice Multiple two numbers in Fp
|
||||
/// @param a Input 1 to FpMul; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @param b Input 2 to FpMul; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @param p The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @output out The result of the FpMul; asserted to be less than `p`
|
||||
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[100] = 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];
|
||||
component r_p_lt_check = BigLessThan(n,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];
|
||||
|
||||
r_p_lt_check.a[i] <== r[i];
|
||||
r_p_lt_check.b[i] <== p[i];
|
||||
}
|
||||
r_p_lt_check.out === 1;
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
181
circuits/circuits/utils/zkemail/lib/rsa.circom
Normal file
181
circuits/circuits/utils/zkemail/lib/rsa.circom
Normal file
@@ -0,0 +1,181 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "./fp.circom";
|
||||
|
||||
|
||||
/// @title RSAVerifier65537
|
||||
/// @notice Verifies an RSA signature with exponent 65537.
|
||||
/// @param n Number of bits per chunk the modulus is split into. Recommended to be 121.
|
||||
/// @param k Number of chunks the modulus is split into. Recommended to be 17.
|
||||
/// @input message[k] The message that was signed; assumes to consist of `k` chunks that fit in `n` bits (also constrained implicitly).
|
||||
/// @input signature[k] The signature to verify; assumes to consist of `k` chunks that fit in `n` bits (also constrained implicitly).
|
||||
/// @input modulus[k] The modulus of the RSA key (pubkey); assumes to consist of `k` chunks that fit in `n` bits (also constrained implicitly).
|
||||
template RSAVerifier65537(n, k) {
|
||||
signal input message[k];
|
||||
signal input signature[k];
|
||||
signal input modulus[k];
|
||||
|
||||
component padder = RSAPad(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
padder.modulus[i] <== modulus[i];
|
||||
padder.message[i] <== 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.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title FpPow65537Mod
|
||||
/// @notice Computes base^65537 mod modulus
|
||||
/// @dev Does not necessarily reduce fully mod modulus (the answer could be too big by a multiple of modulus)
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input base The base to exponentiate; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @input modulus The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits
|
||||
/// @output out The result of the exponentiation.
|
||||
template FpPow65537Mod(n, k) {
|
||||
signal input base[k];
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
/// @title RSAPad
|
||||
/// @notice Pads a message for RSA signing.
|
||||
/// @param n Number of bits per chunk the modulus is split into.
|
||||
/// @param k Number of chunks the modulus is split into.
|
||||
/// @input modulus The modulus of the RSA key (pubkey).
|
||||
/// @input message The message to pad.
|
||||
/// @output out The padded message.
|
||||
template RSAPad(n, k) {
|
||||
signal input modulus[k];
|
||||
signal input message[k];
|
||||
signal output out[k];
|
||||
|
||||
// The extra 152 bits comes from 0x3031300d060960864801650304020105000420
|
||||
// This is due to padding from the RSASSA-PKCS1-v1_5 standard
|
||||
var baseLen = 408;
|
||||
var msgLen = 256;
|
||||
|
||||
signal paddedMessageBits[n*k];
|
||||
|
||||
component modulusN2B[k];
|
||||
component messageN2B[k];
|
||||
signal modulusBits[n*k];
|
||||
signal messageBits[n*k];
|
||||
for (var i = 0; i < k; i++) {
|
||||
messageN2B[i] = Num2Bits(n);
|
||||
messageN2B[i].in <== message[i];
|
||||
for (var j = 0; j < n; j++) {
|
||||
messageBits[i*n+j] <== messageN2B[i].out[j];
|
||||
}
|
||||
modulusN2B[i] = Num2Bits(n);
|
||||
modulusN2B[i].in <== modulus[i];
|
||||
for (var j = 0; j < n; j++) {
|
||||
modulusBits[i*n+j] <== modulusN2B[i].out[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = msgLen; i < n*k; i++) {
|
||||
messageBits[i] === 0;
|
||||
}
|
||||
|
||||
for (var i = 0; i < msgLen; i++) {
|
||||
paddedMessageBits[i] <== messageBits[i];
|
||||
}
|
||||
|
||||
for (var i = baseLen; i < baseLen + 8; i++) {
|
||||
paddedMessageBits[i] <== 0;
|
||||
}
|
||||
|
||||
for (var i = msgLen; i < baseLen; i++) {
|
||||
paddedMessageBits[i] <== (0x3031300d060960864801650304020105000420 >> (i - msgLen)) & 1;
|
||||
}
|
||||
|
||||
component modulusZero[(n*k + 7 - (baseLen + 8))\8];
|
||||
{
|
||||
var modulusPrefix = 0;
|
||||
for (var i = n*k - 1; i >= baseLen + 8; i--) {
|
||||
if (i+8 < n*k) {
|
||||
modulusPrefix += modulusBits[i+8];
|
||||
if (i % 8 == 0) {
|
||||
var idx = (i - (baseLen + 8)) \ 8;
|
||||
modulusZero[idx] = IsZero();
|
||||
modulusZero[idx].in <== modulusPrefix;
|
||||
paddedMessageBits[i] <== 1-modulusZero[idx].out;
|
||||
} else {
|
||||
paddedMessageBits[i] <== paddedMessageBits[i+1];
|
||||
}
|
||||
} else {
|
||||
paddedMessageBits[i] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The RFC guarantees at least 8 octets of 0xff padding.
|
||||
assert(baseLen + 8 + 65 <= n * k);
|
||||
|
||||
for (var i = baseLen + 8; i < baseLen + 8 + 65; i++) {
|
||||
paddedMessageBits[i] === 1;
|
||||
}
|
||||
|
||||
component passedMessageB2N[k];
|
||||
for (var i = 0; i < k; i++) {
|
||||
passedMessageB2N[i] = Bits2Num(n);
|
||||
for (var j = 0; j < n; j++) {
|
||||
passedMessageB2N[i].in[j] <== paddedMessageBits[i*n+j];
|
||||
}
|
||||
out[i] <== passedMessageB2N[i].out;
|
||||
}
|
||||
}
|
||||
292
circuits/circuits/utils/zkemail/lib/sha.circom
Normal file
292
circuits/circuits/utils/zkemail/lib/sha.circom
Normal file
@@ -0,0 +1,292 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/sha256/constants.circom";
|
||||
include "circomlib/circuits/sha256/sha256compression.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "./fp.circom";
|
||||
include "../utils/array.circom";
|
||||
include "../utils/functions.circom";
|
||||
|
||||
|
||||
/// @title Sha256Bytes
|
||||
/// @notice Computes the SHA256 hash of input bytes
|
||||
/// @input paddedIn Message to hash, padded as per the SHA256 specification; assumes to consist of bytes
|
||||
/// @input paddedInLength Length of the padded message; assumes to be in `ceil(log2(8 * maxByteLength))` bits
|
||||
/// @output out The 256-bit hash of the input message
|
||||
template Sha256Bytes(maxByteLength) {
|
||||
signal input paddedIn[maxByteLength];
|
||||
signal input paddedInLength;
|
||||
signal output out[256];
|
||||
|
||||
var maxBits = maxByteLength * 8;
|
||||
component sha = Sha256General(maxBits);
|
||||
|
||||
component bytes[maxByteLength];
|
||||
for (var i = 0; i < maxByteLength; i++) {
|
||||
bytes[i] = Num2Bits(8);
|
||||
bytes[i].in <== paddedIn[i];
|
||||
for (var j = 0; j < 8; j++) {
|
||||
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
|
||||
}
|
||||
}
|
||||
sha.paddedInLength <== paddedInLength * 8;
|
||||
|
||||
for (var i = 0; i < 256; i++) {
|
||||
out[i] <== sha.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title Sha256BytesPartial
|
||||
/// @notice Computes the SHA256 hash of input bytes with a precomputed state
|
||||
/// @input paddedIn Message to hash padded as per the SHA256 specification; assumes to consist of bytes
|
||||
/// @input paddedInLength Length of the padded message; assumes to be in `ceil(log2(8 * maxByteLength))` bits
|
||||
/// @input preHash The precomputed state of the hash
|
||||
/// @output out SHA hash the input message with the precomputed state
|
||||
template Sha256BytesPartial(maxByteLength) {
|
||||
assert(maxByteLength % 32 == 0);
|
||||
|
||||
signal input paddedIn[maxByteLength];
|
||||
signal input paddedInLength;
|
||||
signal input preHash[32];
|
||||
signal output out[256];
|
||||
|
||||
var maxBits = maxByteLength * 8;
|
||||
component sha = Sha256Partial(maxBits);
|
||||
|
||||
component bytes[maxByteLength];
|
||||
for (var i = 0; i < maxByteLength; i++) {
|
||||
bytes[i] = Num2Bits(8);
|
||||
bytes[i].in <== paddedIn[i];
|
||||
for (var j = 0; j < 8; j++) {
|
||||
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
|
||||
}
|
||||
}
|
||||
sha.paddedInLength <== paddedInLength * 8;
|
||||
|
||||
component states[32];
|
||||
for (var i = 0; i < 32; i++) {
|
||||
states[i] = Num2Bits(8);
|
||||
states[i].in <== preHash[i];
|
||||
for (var j = 0; j < 8; j++) {
|
||||
sha.preHash[8*i+j] <== states[i].out[7-j];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 256; i++) {
|
||||
out[i] <== sha.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title Sha256General
|
||||
/// @notice A modified version of the SHA256 circuit that allows specified length messages up to a
|
||||
/// max to all work via array indexing on the SHA256 compression circuit.
|
||||
/// @input paddedIn Message to hash padded as per the SHA256 specification; assumes to consist of bits
|
||||
/// @input paddedInLength Length of the padded message; assumes to be in `ceil(log2(maxBitLength))` bits
|
||||
/// @output out The 256-bit hash of the input message
|
||||
template Sha256General(maxBitLength) {
|
||||
// maxBitLength must be a multiple of 512
|
||||
// the bit circuits in this file are limited to 15 so must be raised if the message is longer.
|
||||
assert(maxBitLength % 512 == 0);
|
||||
|
||||
var maxBitsPaddedBits = log2Ceil(maxBitLength);
|
||||
|
||||
// Note that maxBitLength = maxBits + 64
|
||||
signal input paddedIn[maxBitLength];
|
||||
signal input paddedInLength;
|
||||
|
||||
signal output out[256];
|
||||
|
||||
signal inBlockIndex;
|
||||
|
||||
var i;
|
||||
var k;
|
||||
var j;
|
||||
var maxBlocks;
|
||||
var bitsLastBlock;
|
||||
maxBlocks = (maxBitLength\512);
|
||||
|
||||
inBlockIndex <-- (paddedInLength >> 9);
|
||||
paddedInLength === inBlockIndex * 512;
|
||||
|
||||
// These verify the unconstrained floor calculation is the uniquely correct integer that represents the floor
|
||||
// component floorVerifierUnder = LessEqThan(maxBitsPaddedBits); // todo verify the length passed in is less than nbits. note that maxBitsPaddedBits can likely be lowered or made it a fn of maxbits
|
||||
// floorVerifierUnder.in[0] <== (inBlockIndex)*512;
|
||||
// floorVerifierUnder.in[1] <== paddedInLength;
|
||||
// floorVerifierUnder.out === 1;
|
||||
|
||||
// component floorVerifierOver = GreaterThan(maxBitsPaddedBits);
|
||||
// floorVerifierOver.in[0] <== (inBlockIndex+1)*512;
|
||||
// floorVerifierOver.in[1] <== paddedInLength;
|
||||
// floorVerifierOver.out === 1;
|
||||
|
||||
// These verify we pass in a valid number of bits to the SHA256 compression circuit.
|
||||
component bitLengthVerifier = LessEqThan(maxBitsPaddedBits); // todo verify the length passed in is less than nbits. note that maxBitsPaddedBits can likely be lowered or made it a fn of maxbits
|
||||
bitLengthVerifier.in[0] <== paddedInLength;
|
||||
bitLengthVerifier.in[1] <== maxBitLength;
|
||||
bitLengthVerifier.out === 1;
|
||||
|
||||
// Note that we can no longer do padded verification efficiently inside the SHA because it requires non deterministic array indexing.
|
||||
// We can do it if we add a constraint, but since guessing a valid SHA2 preimage is hard anyways, we'll just do it outside the circuit.
|
||||
|
||||
// signal paddedIn[maxBlocks*512];
|
||||
// for (k=0; k<maxBits; k++) {
|
||||
// paddedIn[k] <== in[k];
|
||||
// }
|
||||
// paddedIn[maxBits] <== 1;
|
||||
// for (k=maxBits+1; k<maxBlocks*512-64; k++) {
|
||||
// paddedIn[k] <== 0;
|
||||
// }
|
||||
// for (k = 0; k< 64; k++) {
|
||||
// paddedIn[maxBlocks*512 - k -1] <== (maxBits >> k)&1;
|
||||
// }
|
||||
|
||||
component ha0 = H(0);
|
||||
component hb0 = H(1);
|
||||
component hc0 = H(2);
|
||||
component hd0 = H(3);
|
||||
component he0 = H(4);
|
||||
component hf0 = H(5);
|
||||
component hg0 = H(6);
|
||||
component hh0 = H(7);
|
||||
|
||||
component sha256compression[maxBlocks];
|
||||
|
||||
for (i=0; i<maxBlocks; i++) {
|
||||
sha256compression[i] = Sha256compression() ;
|
||||
|
||||
if (i==0) {
|
||||
for (k=0; k<32; k++ ) {
|
||||
sha256compression[i].hin[0*32+k] <== ha0.out[k];
|
||||
sha256compression[i].hin[1*32+k] <== hb0.out[k];
|
||||
sha256compression[i].hin[2*32+k] <== hc0.out[k];
|
||||
sha256compression[i].hin[3*32+k] <== hd0.out[k];
|
||||
sha256compression[i].hin[4*32+k] <== he0.out[k];
|
||||
sha256compression[i].hin[5*32+k] <== hf0.out[k];
|
||||
sha256compression[i].hin[6*32+k] <== hg0.out[k];
|
||||
sha256compression[i].hin[7*32+k] <== hh0.out[k];
|
||||
}
|
||||
} else {
|
||||
for (k=0; k<32; k++ ) {
|
||||
sha256compression[i].hin[32*0+k] <== sha256compression[i-1].out[32*0+31-k];
|
||||
sha256compression[i].hin[32*1+k] <== sha256compression[i-1].out[32*1+31-k];
|
||||
sha256compression[i].hin[32*2+k] <== sha256compression[i-1].out[32*2+31-k];
|
||||
sha256compression[i].hin[32*3+k] <== sha256compression[i-1].out[32*3+31-k];
|
||||
sha256compression[i].hin[32*4+k] <== sha256compression[i-1].out[32*4+31-k];
|
||||
sha256compression[i].hin[32*5+k] <== sha256compression[i-1].out[32*5+31-k];
|
||||
sha256compression[i].hin[32*6+k] <== sha256compression[i-1].out[32*6+31-k];
|
||||
sha256compression[i].hin[32*7+k] <== sha256compression[i-1].out[32*7+31-k];
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<512; k++) {
|
||||
sha256compression[i].inp[k] <== paddedIn[i*512+k];
|
||||
}
|
||||
}
|
||||
|
||||
// Select the correct compression output for the given length, instead of just the last one.
|
||||
component arraySelectors[256];
|
||||
for (k=0; k<256; k++) {
|
||||
arraySelectors[k] = ItemAtIndex(maxBlocks);
|
||||
for (j=0; j<maxBlocks; j++) {
|
||||
arraySelectors[k].in[j] <== sha256compression[j].out[k];
|
||||
}
|
||||
arraySelectors[k].index <== inBlockIndex - 1; // The index is 0 indexed and the block numbers are 1 indexed.
|
||||
out[k] <== arraySelectors[k].out;
|
||||
}
|
||||
|
||||
// for (k=0; k<256; k++) {
|
||||
// out[k] <== sha256compression[maxBlocks-1].out[k];
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/// @title Sha256Partial
|
||||
/// @notice Calculates the SHA256 hash of a message with a precomputed state
|
||||
/// @input paddedIn Message to hash padded as per the SHA256 specification; assumes to consist of bits
|
||||
/// @input paddedInLength Length of the padded message; assumes to be in `ceil(log2(maxBitLength))` bits
|
||||
/// @input preHash The precomputed state of the hash; assumes to consist of bits
|
||||
/// @output out The 256-bit hash of the input message
|
||||
template Sha256Partial(maxBitLength) {
|
||||
// maxBitLength must be a multiple of 512
|
||||
// the bit circuits in this file are limited to 15 so must be raised if the message is longer.
|
||||
assert(maxBitLength % 512 == 0);
|
||||
|
||||
var maxBitsPaddedBits = log2Ceil(maxBitLength);
|
||||
|
||||
// Note that maxBitLength = maxBits + 64
|
||||
signal input paddedIn[maxBitLength];
|
||||
signal input paddedInLength;
|
||||
signal input preHash[256];
|
||||
|
||||
signal output out[256];
|
||||
|
||||
signal inBlockIndex;
|
||||
|
||||
var i;
|
||||
var k;
|
||||
var j;
|
||||
var maxBlocks;
|
||||
var bitsLastBlock;
|
||||
maxBlocks = (maxBitLength\512);
|
||||
|
||||
inBlockIndex <-- (paddedInLength >> 9);
|
||||
paddedInLength === inBlockIndex * 512;
|
||||
|
||||
// These verify we pass in a valid number of bits to the SHA256 compression circuit.
|
||||
component bitLengthVerifier = LessEqThan(maxBitsPaddedBits); // todo verify the length passed in is less than nbits. note that maxBitsPaddedBits can likely be lowered or made it a fn of maxbits
|
||||
bitLengthVerifier.in[0] <== paddedInLength;
|
||||
bitLengthVerifier.in[1] <== maxBitLength;
|
||||
bitLengthVerifier.out === 1;
|
||||
|
||||
component sha256compression[maxBlocks];
|
||||
|
||||
for (i=0; i<maxBlocks; i++) {
|
||||
sha256compression[i] = Sha256compression() ;
|
||||
|
||||
if (i==0) {
|
||||
for (k=0; k<32; k++ ) {
|
||||
sha256compression[i].hin[32*0+k] <== preHash[32*0+31-k];
|
||||
sha256compression[i].hin[32*1+k] <== preHash[32*1+31-k];
|
||||
sha256compression[i].hin[32*2+k] <== preHash[32*2+31-k];
|
||||
sha256compression[i].hin[32*3+k] <== preHash[32*3+31-k];
|
||||
sha256compression[i].hin[32*4+k] <== preHash[32*4+31-k];
|
||||
sha256compression[i].hin[32*5+k] <== preHash[32*5+31-k];
|
||||
sha256compression[i].hin[32*6+k] <== preHash[32*6+31-k];
|
||||
sha256compression[i].hin[32*7+k] <== preHash[32*7+31-k];
|
||||
}
|
||||
} else {
|
||||
for (k=0; k<32; k++ ) {
|
||||
sha256compression[i].hin[32*0+k] <== sha256compression[i-1].out[32*0+31-k];
|
||||
sha256compression[i].hin[32*1+k] <== sha256compression[i-1].out[32*1+31-k];
|
||||
sha256compression[i].hin[32*2+k] <== sha256compression[i-1].out[32*2+31-k];
|
||||
sha256compression[i].hin[32*3+k] <== sha256compression[i-1].out[32*3+31-k];
|
||||
sha256compression[i].hin[32*4+k] <== sha256compression[i-1].out[32*4+31-k];
|
||||
sha256compression[i].hin[32*5+k] <== sha256compression[i-1].out[32*5+31-k];
|
||||
sha256compression[i].hin[32*6+k] <== sha256compression[i-1].out[32*6+31-k];
|
||||
sha256compression[i].hin[32*7+k] <== sha256compression[i-1].out[32*7+31-k];
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<512; k++) {
|
||||
sha256compression[i].inp[k] <== paddedIn[i*512+k];
|
||||
}
|
||||
}
|
||||
|
||||
// Select the correct compression output for the given length, instead of just the last one.
|
||||
component arraySelectors[256];
|
||||
for (k=0; k<256; k++) {
|
||||
arraySelectors[k] = ItemAtIndex(maxBlocks);
|
||||
for (j=0; j<maxBlocks; j++) {
|
||||
arraySelectors[k].in[j] <== sha256compression[j].out[k];
|
||||
}
|
||||
arraySelectors[k].index <== inBlockIndex - 1; // The index is 0 indexed and the block numbers are 1 indexed.
|
||||
out[k] <== arraySelectors[k].out;
|
||||
}
|
||||
|
||||
// for (k=0; k<256; k++) {
|
||||
// out[k] <== sha256compression[maxBlocks-1].out[k];
|
||||
// }
|
||||
}
|
||||
254
circuits/circuits/utils/zkemail/utils/array.circom
Normal file
254
circuits/circuits/utils/zkemail/utils/array.circom
Normal file
@@ -0,0 +1,254 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "./functions.circom";
|
||||
|
||||
|
||||
/// @title ItemAtIndex
|
||||
/// @notice Select item at given index from the input array
|
||||
/// @notice This template that the index is valid
|
||||
/// @notice This is a modified version of QuinSelector from MACI https://github.com/privacy-scaling-explorations/maci/
|
||||
/// @param maxArrayLen The number of elements in the array
|
||||
/// @input in The input array
|
||||
/// @input index The index of the element to select
|
||||
/// @output out The selected element
|
||||
template ItemAtIndex(maxArrayLen) {
|
||||
signal input in[maxArrayLen];
|
||||
signal input index;
|
||||
|
||||
signal output out;
|
||||
|
||||
component calcTotalValue = CalculateTotal(maxArrayLen);
|
||||
component calcTotalIndex = CalculateTotal(maxArrayLen);
|
||||
component eqs[maxArrayLen];
|
||||
|
||||
// For each item, check whether its index equals the input index.
|
||||
for (var i = 0; i < maxArrayLen; i ++) {
|
||||
eqs[i] = IsEqual();
|
||||
eqs[i].in[0] <== i;
|
||||
eqs[i].in[1] <== index;
|
||||
|
||||
// eqs[i].out is 1 if the index matches - so calcTotal is sum of 0s + 1 * valueAtIndex
|
||||
calcTotalValue.nums[i] <== eqs[i].out * in[i];
|
||||
|
||||
// Take the sum of all eqs[i].out and assert that it is at most 1.
|
||||
calcTotalIndex.nums[i] <== eqs[i].out;
|
||||
}
|
||||
|
||||
// Assert that the sum of eqs[i].out is 1. This is to ensure the index passed is valid.
|
||||
calcTotalIndex.sum === 1;
|
||||
|
||||
out <== calcTotalValue.sum;
|
||||
}
|
||||
|
||||
|
||||
/// @title CalculateTotal
|
||||
/// @notice Calculate the sum of an array
|
||||
/// @param n The number of elements in the array
|
||||
/// @input nums The input array; assumes elements are small enough that their sum does not overflow the field
|
||||
/// @output sum The sum of the input array
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
/// @title SelectSubArray
|
||||
/// @notice Select sub array from an array given a `startIndex` and `length`
|
||||
/// @notice This is same as `VarShiftLeft` but with elements after `length` set to zero
|
||||
/// @notice This is not used in core ZK-Email circuits at the moment
|
||||
/// @param maxArrayLen: the maximum number of bytes in the input array
|
||||
/// @param maxSubArrayLen: the maximum number of integers in the output array
|
||||
/// @input in: the input array
|
||||
/// @input startIndex: the start index of the sub array; assumes a valid index
|
||||
/// @input length: the length of the sub array; assumes to fit in `ceil(log2(maxArrayLen))` bits
|
||||
/// @output out: array of `maxSubArrayLen` size, items starting from `startIndex`, and items after `length` set to zero
|
||||
template SelectSubArray(maxArrayLen, maxSubArrayLen) {
|
||||
assert(maxSubArrayLen < maxArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input startIndex;
|
||||
signal input length;
|
||||
|
||||
signal output out[maxSubArrayLen];
|
||||
|
||||
component shifter = VarShiftLeft(maxArrayLen, maxSubArrayLen);
|
||||
shifter.in <== in;
|
||||
shifter.shift <== startIndex;
|
||||
|
||||
// Set value after length to zero
|
||||
component gts[maxSubArrayLen];
|
||||
for (var i = 0; i < maxSubArrayLen; i++) {
|
||||
gts[i] = GreaterThan(log2Ceil(maxSubArrayLen));
|
||||
gts[i].in[0] <== length;
|
||||
gts[i].in[1] <== i;
|
||||
|
||||
out[i] <== gts[i].out * shifter.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title VarShiftLeft
|
||||
/// @notice Shift input array by `shift` indices to the left
|
||||
/// @notice Output array length can be reduced by setting `maxOutArrayLen`
|
||||
/// @notice Based on https://demo.hedgedoc.org/s/Le0R3xUhB
|
||||
/// @param maxArrayLen The maximum length of the input array
|
||||
/// @param maxOutArrayLen The maximum length of the output array
|
||||
/// @input in The input array
|
||||
/// @input shift The number of indices to shift the array to the left
|
||||
/// @output out hifted subarray
|
||||
template VarShiftLeft(maxArrayLen, maxOutArrayLen) {
|
||||
assert(maxOutArrayLen <= maxArrayLen);
|
||||
|
||||
var bitLength = log2Ceil(maxArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input shift;
|
||||
|
||||
signal output out[maxOutArrayLen];
|
||||
|
||||
component n2b = Num2Bits(bitLength);
|
||||
n2b.in <== shift;
|
||||
|
||||
signal tmp[bitLength][maxArrayLen];
|
||||
for (var j = 0; j < bitLength; j++) {
|
||||
for (var i = 0; i < maxArrayLen; i++) {
|
||||
var offset = (i + (1 << j)) % maxArrayLen;
|
||||
// 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
|
||||
for (var i = 0; i < maxOutArrayLen; i++) {
|
||||
out[i] <== tmp[bitLength - 1][i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title AssertZeroPadding
|
||||
/// @notice Assert that the input array is zero-padded from the given `startIndex`
|
||||
/// @param maxArrayLen The maximum number of elements in the input array
|
||||
/// @input in The input array;
|
||||
/// @input startIndex The index from which the elements should be 0; assumes `startIndex - 1` to fit in `ceil(log2(maxArrayLen))` bits
|
||||
template AssertZeroPadding(maxArrayLen) {
|
||||
var bitLength = log2Ceil(maxArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input startIndex;
|
||||
|
||||
component lessThans[maxArrayLen];
|
||||
|
||||
for (var i = 0; i < maxArrayLen; i++) {
|
||||
lessThans[i] = LessThan(bitLength);
|
||||
lessThans[i].in[0] <== startIndex - 1;
|
||||
lessThans[i].in[1] <== i;
|
||||
|
||||
lessThans[i].out * in[i] === 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// @title Slice
|
||||
/// @notice Extract a fixed portion of an array
|
||||
/// @dev Unlike SelectSubArray, Slice uses compile-time known indices and doesn't pad the output
|
||||
/// @dev Slice is more efficient for fixed ranges, while SelectSubArray offers runtime flexibility
|
||||
/// @param n The length of the input array
|
||||
/// @param start The starting index of the slice (inclusive)
|
||||
/// @param end The ending index of the slice (exclusive)
|
||||
/// @input in The input array of length n
|
||||
/// @output out The sliced array of length (end - start)
|
||||
template Slice(n, start, end) {
|
||||
assert(n >= end);
|
||||
assert(start >= 0);
|
||||
assert(end >= start);
|
||||
|
||||
signal input in[n];
|
||||
signal output out[end - start];
|
||||
|
||||
for (var i = start; i < end; i++) {
|
||||
out[i - start] <== in[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// @title CheckSubstringMatch
|
||||
/// @notice Check if a substring matches the input array
|
||||
/// @param maxSubstringLen The maximum length of the substring
|
||||
/// @input input The portion of the input array to check
|
||||
/// @input substring The substring pattern to match
|
||||
/// @output isMatch 1 if the substring matches, 0 otherwise
|
||||
template CheckSubstringMatch(maxSubstringLen) {
|
||||
signal input in[maxSubstringLen];
|
||||
signal input substring[maxSubstringLen];
|
||||
signal output isMatch;
|
||||
|
||||
// Ensure the first element of the pattern is non-zero
|
||||
signal firstElementNonZero;
|
||||
firstElementNonZero <== IsZero()(substring[0]);
|
||||
firstElementNonZero === 0;
|
||||
|
||||
signal matchAccumulator[maxSubstringLen + 1];
|
||||
signal difference[maxSubstringLen];
|
||||
signal isZeroDifference[maxSubstringLen];
|
||||
|
||||
matchAccumulator[0] <== 1;
|
||||
|
||||
for (var i = 0; i < maxSubstringLen; i++) {
|
||||
difference[i] <== (in[i] - substring[i]) * substring[i];
|
||||
isZeroDifference[i] <== IsZero()(difference[i]);
|
||||
matchAccumulator[i + 1] <== matchAccumulator[i] * isZeroDifference[i];
|
||||
}
|
||||
|
||||
isMatch <== matchAccumulator[maxSubstringLen];
|
||||
}
|
||||
|
||||
/// @title CountSubstringOccurrences
|
||||
/// @notice Count the number of times a substring occurs in the input array
|
||||
/// @param maxLen The maximum length of the input array
|
||||
/// @param maxSubstringLen The maximum length of the substring
|
||||
/// @input in The input array to search in
|
||||
/// @input substring The substring to search for
|
||||
/// @output count The number of occurrences of the substring in the input
|
||||
template CountSubstringOccurrences(maxLen, maxSubstringLen) {
|
||||
assert(maxLen >= maxSubstringLen);
|
||||
|
||||
signal input in[maxLen];
|
||||
signal input substring[maxSubstringLen];
|
||||
signal output count;
|
||||
|
||||
// Check for matches at each possible starting position
|
||||
component matches[maxLen];
|
||||
for (var i = 0; i < maxLen; i++) {
|
||||
matches[i] = CheckSubstringMatch(maxSubstringLen);
|
||||
for (var j = 0; j < maxSubstringLen; j++) {
|
||||
if (i + j < maxLen) {
|
||||
matches[i].in[j] <== in[i + j];
|
||||
} else {
|
||||
matches[i].in[j] <== 0;
|
||||
}
|
||||
}
|
||||
matches[i].substring <== substring;
|
||||
}
|
||||
|
||||
// Sum up all matches to get the total count
|
||||
component summer = CalculateTotal(maxLen);
|
||||
for (var i = 0; i < maxLen; i++) {
|
||||
summer.nums[i] <== matches[i].isMatch;
|
||||
}
|
||||
|
||||
count <== summer.sum;
|
||||
}
|
||||
185
circuits/circuits/utils/zkemail/utils/bytes.circom
Normal file
185
circuits/circuits/utils/zkemail/utils/bytes.circom
Normal file
@@ -0,0 +1,185 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "./array.circom";
|
||||
include "./constants.circom";
|
||||
include "./functions.circom";
|
||||
|
||||
|
||||
function computeIntChunkLength(byteLength) {
|
||||
var packSize = MAX_BYTES_IN_FIELD();
|
||||
|
||||
var remain = byteLength % packSize;
|
||||
var numChunks = (byteLength - remain) / packSize;
|
||||
if (remain > 0) {
|
||||
numChunks += 1;
|
||||
}
|
||||
|
||||
return numChunks;
|
||||
}
|
||||
|
||||
|
||||
/// @title PackBytes
|
||||
/// @notice Pack an array of bytes to numbers that fit in the field
|
||||
/// @param maxBytes the maximum number of bytes in the input array
|
||||
/// @input in the input byte array; assumes elements to be bytes
|
||||
/// @output out the output integer array
|
||||
template PackBytes(maxBytes) {
|
||||
var packSize = MAX_BYTES_IN_FIELD();
|
||||
var maxInts = computeIntChunkLength(maxBytes);
|
||||
|
||||
signal input in[maxBytes];
|
||||
signal output out[maxInts];
|
||||
|
||||
signal intSums[maxInts][packSize];
|
||||
|
||||
for (var i = 0; i < maxInts; i++) {
|
||||
for(var j=0; j < packSize; j++) {
|
||||
var idx = packSize * i + j;
|
||||
|
||||
// Copy the previous value if we are out of bounds - we take last item as final result
|
||||
if(idx >= maxBytes) {
|
||||
intSums[i][j] <== intSums[i][j-1];
|
||||
}
|
||||
// First item of each chunk is the byte itself
|
||||
else if (j == 0){
|
||||
intSums[i][j] <== in[idx];
|
||||
}
|
||||
// Every other item is 256^j * byte
|
||||
else {
|
||||
intSums[i][j] <== intSums[i][j-1] + (1 << (8*j)) * in[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last item of each chunk is the final sum
|
||||
for (var i = 0; i < maxInts; i++) {
|
||||
out[i] <== intSums[i][packSize-1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title PackByteSubArray
|
||||
/// @notice Select sub array from the input array and pack it to numbers that fit in the field
|
||||
/// @notice This is not used in ZK-Email circuits anywhere
|
||||
/// @param maxArrayLen the maximum number of elements in the input array
|
||||
/// @param maxSubArrayLen the maximum number of elements in the sub array
|
||||
/// @input in the input byte array; assumes elements to be bytes
|
||||
/// @input startIndex the start index of the sub array; assumes to be a valid index
|
||||
/// @input length the length of the sub array; assumes to fit in `ceil(log2(maxSubArrayLen))` bits
|
||||
/// @output out the output integer array
|
||||
template PackByteSubArray(maxArrayLen, maxSubArrayLen) {
|
||||
assert(maxSubArrayLen < maxArrayLen);
|
||||
var chunkLength = computeIntChunkLength(maxSubArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input startIndex;
|
||||
signal input length;
|
||||
|
||||
signal output out[chunkLength];
|
||||
|
||||
component SelectSubArray = SelectSubArray(maxArrayLen, maxSubArrayLen);
|
||||
SelectSubArray.in <== in;
|
||||
SelectSubArray.startIndex <== startIndex;
|
||||
SelectSubArray.length <== length;
|
||||
|
||||
component packer = PackBytes(maxSubArrayLen);
|
||||
packer.in <== SelectSubArray.out;
|
||||
|
||||
out <== packer.out;
|
||||
}
|
||||
|
||||
|
||||
/// @title DigitBytesToInt
|
||||
/// @notice Converts a byte array representing digits to an integer
|
||||
/// @notice Assumes the output number fits in the field
|
||||
/// @param n The number of bytes in the input array
|
||||
/// @input in The input byte array; assumes elements are between 48 and 57 (ASCII numbers)
|
||||
/// @output out The output integer; assumes to fit in the field
|
||||
template DigitBytesToInt(n) {
|
||||
signal input in[n];
|
||||
|
||||
signal output out;
|
||||
|
||||
signal sums[n+1];
|
||||
sums[0] <== 0;
|
||||
|
||||
for(var i = 0; i < n; i++) {
|
||||
sums[i + 1] <== 10 * sums[i] + (in[i] - 48);
|
||||
}
|
||||
|
||||
out <== sums[n];
|
||||
}
|
||||
|
||||
|
||||
// NOTE: this circuit is unaudited and should not be used in production
|
||||
/// @title SplitBytesToWords
|
||||
/// @notice split an array of bytes into an array of words
|
||||
/// @notice useful for casting a message or modulus before RSA verification
|
||||
/// @param l: number of bytes in the input array
|
||||
/// @param n: number of bits in a word
|
||||
/// @param k: number of words
|
||||
/// @input in: array of bytes
|
||||
/// @output out: array of words
|
||||
template SplitBytesToWords (l,n,k) {
|
||||
signal input in[l];
|
||||
signal output out[k];
|
||||
|
||||
component num2bits[l];
|
||||
for (var i = 0 ; i < l ; i++){
|
||||
num2bits[i] = Num2Bits(8);
|
||||
num2bits[i].in <== in[i];
|
||||
}
|
||||
component bits2num[k];
|
||||
for (var i = 0 ; i < k ; i++){
|
||||
bits2num[i] = Bits2Num(n);
|
||||
for(var j = 0 ; j < n ; j++){
|
||||
if(i*n + j >= 8 * l){
|
||||
bits2num[i].in[j] <== 0;
|
||||
}
|
||||
else{
|
||||
bits2num[i].in[j] <== num2bits[l - (( i * n + j) \ 8) - 1].out[ ((i * n + j) % 8)];
|
||||
}
|
||||
}
|
||||
}
|
||||
for( var i = 0 ; i< k ; i++){
|
||||
out[i] <== bits2num[i].out;
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that a given input is binary.
|
||||
//
|
||||
// Inputs:
|
||||
// - in: an input signal, expected to be 0 or 1.
|
||||
template AssertBit() {
|
||||
signal input in;
|
||||
in * (in - 1) === 0;
|
||||
}
|
||||
|
||||
// The ByteMask template masks an input array using a binary mask array.
|
||||
// Each element in the input array is multiplied by the corresponding element in the mask array.
|
||||
// The mask array is validated to ensure all elements are binary (0 or 1).
|
||||
//
|
||||
// Parameters:
|
||||
// - maxLength: The maximum length of the input and mask arrays.
|
||||
//
|
||||
// Inputs:
|
||||
// - body: An array of signals representing the body to be masked.
|
||||
// - mask: An array of signals representing the binary mask.
|
||||
//
|
||||
// Outputs:
|
||||
// - out: An array of signals representing the masked input.
|
||||
template ByteMask(maxLength) {
|
||||
signal input in[maxLength];
|
||||
signal input mask[maxLength];
|
||||
signal output out[maxLength];
|
||||
|
||||
component bit_check[maxLength];
|
||||
|
||||
for (var i = 0; i < maxLength; i++) {
|
||||
bit_check[i] = AssertBit();
|
||||
bit_check[i].in <== mask[i];
|
||||
out[i] <== in[i] * mask[i];
|
||||
}
|
||||
}
|
||||
15
circuits/circuits/utils/zkemail/utils/constants.circom
Normal file
15
circuits/circuits/utils/zkemail/utils/constants.circom
Normal file
@@ -0,0 +1,15 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
|
||||
function EMAIL_ADDR_MAX_BYTES() {
|
||||
return 256;
|
||||
}
|
||||
|
||||
function DOMAIN_MAX_BYTES() {
|
||||
return 255;
|
||||
}
|
||||
|
||||
// Field support maximum of ~253 bit
|
||||
function MAX_BYTES_IN_FIELD() {
|
||||
return 31;
|
||||
}
|
||||
17
circuits/circuits/utils/zkemail/utils/functions.circom
Normal file
17
circuits/circuits/utils/zkemail/utils/functions.circom
Normal file
@@ -0,0 +1,17 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
/// @function log2Ceil
|
||||
/// @notice Calculate log2 of a number and round it up
|
||||
/// @param a The input value
|
||||
/// @return The result of the log2Ceil
|
||||
function log2Ceil(a) {
|
||||
var n = a - 1;
|
||||
var r = 0;
|
||||
|
||||
while (n > 0) {
|
||||
r++;
|
||||
n \= 2;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -20,7 +20,6 @@ describe('VerifyRsaPkcs1v1_5 Circuit Test', function () {
|
||||
it(`should verify RSA signature using the circuit for ${algorithm}`, async function () {
|
||||
// Generate inputs using the utility function
|
||||
const { signature, modulus, message } = generateMockRsaPkcs1v1_5Inputs(algorithm);
|
||||
let dummy = 0;
|
||||
|
||||
// Run circuit with inputs
|
||||
const circuit = await wasmTester(
|
||||
@@ -37,7 +36,6 @@ describe('VerifyRsaPkcs1v1_5 Circuit Test', function () {
|
||||
signature,
|
||||
modulus,
|
||||
message,
|
||||
dummy,
|
||||
});
|
||||
|
||||
// Check constraints
|
||||
|
||||
Reference in New Issue
Block a user