reran generate_input to merge twitter conflict

This commit is contained in:
Divide-By-0
2022-10-13 08:38:37 -05:00
10 changed files with 217 additions and 58 deletions

View File

@@ -17,7 +17,8 @@ template EmailVerify(max_header_bytes, max_body_bytes, n, k) {
signal input in_len_padded_bytes; // length of in email data including the padding, which will inform the sha256 block length
// Next 3 signals are only needed if we are doing in-body verification
var LEN_SHA_B64 = 44; // ceil(32/3) * 4, should be automatically calculated.
signal input precomputed_sha[32];
// This body is only the part we care about, a significant prefix of the body has been pre-hashed into precomputed_sha.
signal input in_body_padded[max_body_bytes];
signal input in_body_len_padded_bytes;
@@ -27,6 +28,7 @@ template EmailVerify(max_header_bytes, max_body_bytes, n, k) {
signal input address;
signal input address_plus_one;
var LEN_SHA_B64 = 44; // ceil(32/3) * 4, should be automatically calculated.
signal input body_hash_idx;
signal body_hash[LEN_SHA_B64][max_header_bytes];
@@ -114,10 +116,13 @@ template EmailVerify(max_header_bytes, max_body_bytes, n, k) {
}
}
component sha_body = Sha256Bytes(max_body_bytes);
component sha_body = Sha256BytesPartial(max_body_bytes);
for (var i = 0; i < max_body_bytes; i++) {
sha_body.in_padded[i] <== in_body_padded[i];
}
for (var i = 0; i < 32; i++) {
sha_body.pre_hash[i] <== precomputed_sha[i];
}
sha_body.in_len_padded_bytes <== in_body_len_padded_bytes;
component sha_b64 = Base64Decode(32);
@@ -134,7 +139,7 @@ template EmailVerify(max_header_bytes, max_body_bytes, n, k) {
}
component twitter_regex = TwitterResetRegex(max_body_bytes);
for (var i = 0; i < max_header_bytes; i++) {
for (var i = 0; i < max_body_bytes; i++) {
twitter_regex.msg[i] <== in_body_padded[i];
}
log(twitter_regex.out);
@@ -144,4 +149,4 @@ template EmailVerify(max_header_bytes, max_body_bytes, n, k) {
// In circom, all output signals of the main component are public (and cannot be made private), the input signals of the main component are private if not stated otherwise using the keyword public as above. The rest of signals are all private and cannot be made public.
// This makes modulus and reveal_packed public. Signature can optionally be made public, but is not recommended since it allows the mailserver to trace who the offender is.
component main { public [ modulus, address ] } = EmailVerify(1024, 4096, 121, 17);
component main { public [ modulus, address ] } = EmailVerify(1024, 1536, 121, 17);

View File

@@ -2,6 +2,7 @@ pragma circom 2.0.3;
include "../node_modules/circomlib/circuits/bitify.circom";
include "./sha256general.circom";
include "./sha256partial.circom";
template Sha256Bytes(max_num_bytes) {
signal input in_padded[max_num_bytes];
@@ -26,6 +27,39 @@ template Sha256Bytes(max_num_bytes) {
}
}
template Sha256BytesPartial(max_num_bytes) {
signal input in_padded[max_num_bytes];
signal input in_len_padded_bytes;
signal input pre_hash[32];
signal output out[256];
var num_bits = max_num_bytes * 8;
component sha = Sha256Partial(num_bits);
component bytes[max_num_bytes];
for (var i = 0; i < max_num_bytes; i++) {
bytes[i] = Num2Bits(8);
bytes[i].in <== in_padded[i];
for (var j = 0; j < 8; j++) {
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
}
}
sha.in_len_padded_bits <== in_len_padded_bytes * 8;
component states[32];
for (var i = 0; i < 32; i++) {
states[i] = Num2Bits(8);
states[i].in <== pre_hash[i];
for (var j = 0; j < 8; j++) {
sha.pre_state[8*i+j] <== states[i].out[7-j];
}
}
for (var i = 0; i < 256; i++) {
out[i] <== sha.out[i];
}
}
// Takes in 2^(8 * 31)-sized integers, not bytes, to save calldata. n is usually 31.
// max_num_n_bytes is the number of n-byte size inputs we have. expected to be max_num_bytes / (n + 1)
// template Sha256NBytes(max_num_n_bytes, n) {

View File

@@ -5,17 +5,6 @@ include "../node_modules/circomlib/circuits/sha256/sha256compression.circom";
include "../node_modules/circomlib/circuits/comparators.circom";
include "./sha_utils.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;
}
// 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.
template Sha256General(maxBitsPadded) {
// maxBitsPadded must be a multiple of 512, and the bit circuits in this file are limited to 15 so must be raised if the message is longer.

View File

@@ -0,0 +1,99 @@
pragma circom 2.0.3;
include "../node_modules/circomlib/circuits/sha256/constants.circom";
include "../node_modules/circomlib/circuits/sha256/sha256compression.circom";
include "../node_modules/circomlib/circuits/comparators.circom";
include "./sha_utils.circom";
// Completing the sha256 hash given a pre-computed state and additional data
template Sha256Partial(maxBitsPadded) {
// maxBitsPadded must be a multiple of 512, and the bit circuits in this file are limited to 15 so must be raised if the message is longer.
assert(maxBitsPadded % 512 == 0);
var maxBitsPaddedBits = log2_ceil(maxBitsPadded);
assert(2 ** maxBitsPaddedBits > maxBitsPadded);
// Note that maxBitsPadded = maxBits + 64
signal input paddedIn[maxBitsPadded];
signal input pre_state[256];
signal output out[256];
signal input in_len_padded_bits; // This is the padded length of the message pre-hash.
signal inBlockIndex;
var i;
var k;
var j;
var maxBlocks;
var bitsLastBlock;
maxBlocks = (maxBitsPadded\512);
var maxBlocksBits = log2_ceil(maxBlocks);
assert(2 ** maxBlocksBits > maxBlocks);
inBlockIndex <-- (in_len_padded_bits >> 9);
in_len_padded_bits === 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] <== in_len_padded_bits;
bitLengthVerifier.in[1] <== maxBitsPadded;
bitLengthVerifier.out === 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[32*0+k] <== pre_state[32*0+31-k];
sha256compression[i].hin[32*1+k] <== pre_state[32*1+31-k];
sha256compression[i].hin[32*2+k] <== pre_state[32*2+31-k];
sha256compression[i].hin[32*3+k] <== pre_state[32*3+31-k];
sha256compression[i].hin[32*4+k] <== pre_state[32*4+31-k];
sha256compression[i].hin[32*5+k] <== pre_state[32*5+31-k];
sha256compression[i].hin[32*6+k] <== pre_state[32*6+31-k];
sha256compression[i].hin[32*7+k] <== pre_state[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] = QuinSelector(maxBlocks, maxBlocksBits);
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];
// }
}

View File

@@ -4,6 +4,17 @@ include "../node_modules/circomlib/circuits/bitify.circom";
include "../node_modules/circomlib/circuits/comparators.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;
}
// 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) {

View File

@@ -12,16 +12,16 @@ template TwitterResetRegex (msg_bytes) {
in[i] <== msg[i];
}
component eq[21][num_bytes];
component eq[23][num_bytes];
component lt[12][num_bytes];
component and[27][num_bytes];
component and[29][num_bytes];
component multi_or[3][num_bytes];
signal states[num_bytes+1][21];
signal states[num_bytes+1][23];
for (var i = 0; i < num_bytes; i++) {
states[i][0] <== 1;
}
for (var i = 1; i < 21; i++) {
for (var i = 1; i < 23; i++) {
states[0][i] <== 0;
}
@@ -95,7 +95,7 @@ template TwitterResetRegex (msg_bytes) {
eq[1][i].in[0] <== in[i];
eq[1][i].in[1] <== 95;
and[7][i] = AND();
and[7][i].a <== states[i][20];
and[7][i].a <== states[i][22];
multi_or[1][i] = MultiOR(4);
multi_or[1][i].in[0] <== and[4][i].out;
multi_or[1][i].in[1] <== and[5][i].out;
@@ -108,137 +108,151 @@ template TwitterResetRegex (msg_bytes) {
states[i+1][1] <== multi_or[2][i].out;
eq[2][i] = IsEqual();
eq[2][i].in[0] <== in[i];
eq[2][i].in[1] <== 112;
eq[2][i].in[1] <== 101;
and[8][i] = AND();
and[8][i].a <== states[i][0];
and[8][i].b <== eq[2][i].out;
states[i+1][2] <== and[8][i].out;
eq[3][i] = IsEqual();
eq[3][i].in[0] <== in[i];
eq[3][i].in[1] <== 97;
eq[3][i].in[1] <== 109;
and[9][i] = AND();
and[9][i].a <== states[i][2];
and[9][i].b <== eq[3][i].out;
states[i+1][3] <== and[9][i].out;
eq[4][i] = IsEqual();
eq[4][i].in[0] <== in[i];
eq[4][i].in[1] <== 115;
eq[4][i].in[1] <== 97;
and[10][i] = AND();
and[10][i].a <== states[i][3];
and[10][i].b <== eq[4][i].out;
states[i+1][4] <== and[10][i].out;
eq[5][i] = IsEqual();
eq[5][i].in[0] <== in[i];
eq[5][i].in[1] <== 115;
eq[5][i].in[1] <== 105;
and[11][i] = AND();
and[11][i].a <== states[i][4];
and[11][i].b <== eq[5][i].out;
states[i+1][5] <== and[11][i].out;
eq[6][i] = IsEqual();
eq[6][i].in[0] <== in[i];
eq[6][i].in[1] <== 119;
eq[6][i].in[1] <== 108;
and[12][i] = AND();
and[12][i].a <== states[i][5];
and[12][i].b <== eq[6][i].out;
states[i+1][6] <== and[12][i].out;
eq[7][i] = IsEqual();
eq[7][i].in[0] <== in[i];
eq[7][i].in[1] <== 111;
eq[7][i].in[1] <== 32;
and[13][i] = AND();
and[13][i].a <== states[i][6];
and[13][i].b <== eq[7][i].out;
states[i+1][7] <== and[13][i].out;
eq[8][i] = IsEqual();
eq[8][i].in[0] <== in[i];
eq[8][i].in[1] <== 114;
eq[8][i].in[1] <== 119;
and[14][i] = AND();
and[14][i].a <== states[i][7];
and[14][i].b <== eq[8][i].out;
states[i+1][8] <== and[14][i].out;
eq[9][i] = IsEqual();
eq[9][i].in[0] <== in[i];
eq[9][i].in[1] <== 100;
eq[9][i].in[1] <== 97;
and[15][i] = AND();
and[15][i].a <== states[i][8];
and[15][i].b <== eq[9][i].out;
states[i+1][9] <== and[15][i].out;
eq[10][i] = IsEqual();
eq[10][i].in[0] <== in[i];
eq[10][i].in[1] <== 32;
eq[10][i].in[1] <== 115;
and[16][i] = AND();
and[16][i].a <== states[i][9];
and[16][i].b <== eq[10][i].out;
states[i+1][10] <== and[16][i].out;
eq[11][i] = IsEqual();
eq[11][i].in[0] <== in[i];
eq[11][i].in[1] <== 114;
eq[11][i].in[1] <== 32;
and[17][i] = AND();
and[17][i].a <== states[i][10];
and[17][i].b <== eq[11][i].out;
states[i+1][11] <== and[17][i].out;
eq[12][i] = IsEqual();
eq[12][i].in[0] <== in[i];
eq[12][i].in[1] <== 101;
eq[12][i].in[1] <== 109;
and[18][i] = AND();
and[18][i].a <== states[i][11];
and[18][i].b <== eq[12][i].out;
states[i+1][12] <== and[18][i].out;
eq[13][i] = IsEqual();
eq[13][i].in[0] <== in[i];
eq[13][i].in[1] <== 115;
eq[13][i].in[1] <== 101;
and[19][i] = AND();
and[19][i].a <== states[i][12];
and[19][i].b <== eq[13][i].out;
states[i+1][13] <== and[19][i].out;
eq[14][i] = IsEqual();
eq[14][i].in[0] <== in[i];
eq[14][i].in[1] <== 101;
eq[14][i].in[1] <== 97;
and[20][i] = AND();
and[20][i].a <== states[i][13];
and[20][i].b <== eq[14][i].out;
states[i+1][14] <== and[20][i].out;
eq[15][i] = IsEqual();
eq[15][i].in[0] <== in[i];
eq[15][i].in[1] <== 116;
eq[15][i].in[1] <== 110;
and[21][i] = AND();
and[21][i].a <== states[i][14];
and[21][i].b <== eq[15][i].out;
states[i+1][15] <== and[21][i].out;
eq[16][i] = IsEqual();
eq[16][i].in[0] <== in[i];
eq[16][i].in[1] <== 32;
eq[16][i].in[1] <== 116;
and[22][i] = AND();
and[22][i].a <== states[i][15];
and[22][i].b <== eq[16][i].out;
states[i+1][16] <== and[22][i].out;
eq[17][i] = IsEqual();
eq[17][i].in[0] <== in[i];
eq[17][i].in[1] <== 102;
eq[17][i].in[1] <== 32;
and[23][i] = AND();
and[23][i].a <== states[i][16];
and[23][i].b <== eq[17][i].out;
states[i+1][17] <== and[23][i].out;
eq[18][i] = IsEqual();
eq[18][i].in[0] <== in[i];
eq[18][i].in[1] <== 111;
eq[18][i].in[1] <== 102;
and[24][i] = AND();
and[24][i].a <== states[i][17];
and[24][i].b <== eq[18][i].out;
states[i+1][18] <== and[24][i].out;
eq[19][i] = IsEqual();
eq[19][i].in[0] <== in[i];
eq[19][i].in[1] <== 114;
eq[19][i].in[1] <== 111;
and[25][i] = AND();
and[25][i].a <== states[i][18];
and[25][i].b <== eq[19][i].out;
states[i+1][19] <== and[25][i].out;
eq[20][i] = IsEqual();
eq[20][i].in[0] <== in[i];
eq[20][i].in[1] <== 32;
eq[20][i].in[1] <== 114;
and[26][i] = AND();
and[26][i].a <== states[i][19];
and[26][i].b <== eq[20][i].out;
states[i+1][20] <== and[26][i].out;
eq[21][i] = IsEqual();
eq[21][i].in[0] <== in[i];
eq[21][i].in[1] <== 32;
and[27][i] = AND();
and[27][i].a <== states[i][20];
and[27][i].b <== eq[21][i].out;
states[i+1][21] <== and[27][i].out;
eq[22][i] = IsEqual();
eq[22][i].in[0] <== in[i];
eq[22][i].in[1] <== 64;
and[28][i] = AND();
and[28][i].a <== states[i][21];
and[28][i].b <== eq[22][i].out;
states[i+1][22] <== and[28][i].out;
}
signal final_state_sum[num_bytes+1];

View File

@@ -540,7 +540,7 @@ const word_char = '(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|
// let regex = `\r\ndkim-signature:(${key_chars}=${catch_all_without_semicolon}+; )+bh=${base_64}+; `;
let regex = `password reset for ${word_char}+`;
let regex = `email was meant for @${word_char}+`;
// console.log(regex);
// console.log(Buffer.from(regex).toString('base64'));

View File

@@ -22,8 +22,7 @@ export const AAYUSH_POSTHASH_MESSAGE_PADDED_INT =
export const CIRCOM_FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
export const MAX_HEADER_PADDED_BYTES = 1024; // NOTE: this must be the same as the first arg in the email in main args circom
export const MAX_BODY_PADDED_BYTES = 14336; // NOTE: this must be the the the body size rounded up to a multiple of 512
export const MAX_BODY_REMAINDER_PADDED_BYTES = 1536; // NOTE: this must be the same as the arg to sha the remainder number of bytes in the email in main args circom
export const MAX_BODY_PADDED_BYTES = 1536; // NOTE: this must be the same as the arg to sha the remainder number of bytes in the email in main args circom
// circom constants from main.circom / https://zkrepl.dev/?gist=30d21c7a7285b1b14f608325f172417b
// template RSAGroupSigVerify(n, k, levels) {

View File

@@ -23,9 +23,9 @@ export interface ICircuitInputs {
base_message?: string[];
in_padded?: string[];
in_body_padded?: string[];
in_body_len_padded_bytes?: string[];
in_body_len_padded_bytes?: string;
in_padded_n_bytes?: string[];
in_len_padded_bytes?: string[];
in_len_padded_bytes?: string;
in_body_hash?: string[];
precomputed_sha?: string[];
body_hash_idx?: string;
@@ -153,16 +153,24 @@ export async function getCircuitInputs(
// Sha add padding
const [messagePadded, messagePaddedLen] = await sha256Pad(prehashBytesUnpadded, MAX_HEADER_PADDED_BYTES);
const [bodyPadded, bodyPaddedLen] = await sha256Pad(bufferToUint8Array(body), MAX_BODY_PADDED_BYTES);
const calc_length = Math.floor((body.length + 63)/64) * 64;
const [bodyPadded, bodyPaddedLen] = await sha256Pad(body, Math.max(MAX_BODY_PADDED_BYTES, calc_length));
// Precompute SHA prefix
const selector = STRING_PRESELECTOR.split('').map(char => char.charCodeAt(0))
console.log("Body selector found at: ", await findSelector(bodyPadded, selector));
let shaCutoffIndex = Math.floor(((await findSelector(bodyPadded, selector)) / 512)) * 512;
const selector_loc = await findSelector(bodyPadded, selector);
console.log("Body selector found at: ", selector_loc);
let shaCutoffIndex = Math.floor(((await findSelector(bodyPadded, selector)) / 64)) * 64;
const precomputeText = bodyPadded.slice(0, shaCutoffIndex);
const bodyShaPrecompute = bytesToBigInt(stringToBytes((await partialSha(precomputeText, shaCutoffIndex)).toString())) % CIRCOM_FIELD_MODULUS;
console.log("Precomputed sha:", bodyShaPrecompute);
let bodyRemaining = bodyPadded.slice(shaCutoffIndex);
const bodyRemainingLen = (bodyPaddedLen - precomputeText.length);
assert(bodyRemainingLen < MAX_BODY_PADDED_BYTES, "Invalid slice");
while (bodyRemaining.length < MAX_BODY_PADDED_BYTES) {
bodyRemaining = mergeUInt8Arrays(bodyRemaining, int32toBytes(0))
}
assert(bodyRemaining.length === MAX_BODY_PADDED_BYTES, "Invalid slice");
const bodyShaPrecompute = await partialSha(precomputeText, shaCutoffIndex);
// Ensure SHA manual unpadded is running the correct function
const shaOut = await partialSha(messagePadded, messagePaddedLen);
@@ -173,12 +181,12 @@ export async function getCircuitInputs(
const modulus = toCircomBigIntBytes(modulusBigInt);
const signature = toCircomBigIntBytes(signatureBigInt);
const in_len_padded_bytes = await Uint8ArrayToCharArray(stringToBytes(messagePaddedLen.toString()));
const in_len_padded_bytes = messagePaddedLen.toString();
const in_padded = await Uint8ArrayToCharArray(messagePadded); // Packed into 1 byte signals
const in_body_len_padded_bytes = await Uint8ArrayToCharArray(stringToBytes(bodyPaddedLen.toString()));
const in_body_padded = await Uint8ArrayToCharArray(bodyPadded);
const in_body_len_padded_bytes = bodyRemainingLen.toString();
const in_body_padded = await Uint8ArrayToCharArray(bodyRemaining);
const base_message = toCircomBigIntBytes(postShaBigintUnpadded);
const precomputed_sha = toCircomBigIntBytes(bodyShaPrecompute);
const precomputed_sha = await Uint8ArrayToCharArray(bodyShaPrecompute);
const body_hash_idx = ((bufferToString(message)).indexOf(body_hash)).toString();
const remainder_text_body = await Uint8ArrayToCharArray(bodyPadded.slice(shaCutoffIndex)); // This is the remaining part of the sha that actually gets hashed
@@ -243,8 +251,8 @@ export async function generate_inputs(email: Buffer, eth_address: string): Promi
const pubKeyData = pki.publicKeyFromPem(pubkey.toString());
let modulus = BigInt(pubKeyData.n.toString());
let fin_result = await getCircuitInputs(sig, modulus, message, body, body_hash, eth_address, circuitType);
// console.log("Writing to file...")
// fs.writeFileSync(`./circuits/inputs/input_twitter.json`, JSON.stringify(fin_result.circuitInputs), { flag: "w" });
console.log("Writing to file...")
fs.writeFileSync(`./circuits/inputs/input_twitter.json`, JSON.stringify(fin_result.circuitInputs), { flag: "w" });
return fin_result.circuitInputs;
}
@@ -284,5 +292,5 @@ async function debug_file() {
if (typeof require !== 'undefined' && require.main === module) {
debug_file();
}
// do_generate();
do_generate();
// gen_test();

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "es2020",
"module": "esnext",
"module": "CommonJS",
"esModuleInterop": true,
"lib": [
"dom",