mirror of
https://github.com/zkemail/zk-email-verify.git
synced 2026-01-09 13:38:03 -05:00
210 lines
6.5 KiB
Plaintext
210 lines
6.5 KiB
Plaintext
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];
|
|
}
|
|
}
|
|
|
|
/// @title PackBits
|
|
/// @notice Packs an array of bits into field elements of specified bit size
|
|
/// @dev Assumes input bits are in big-endian order and maintains BE in output
|
|
/// @param numBits Total number of input bits
|
|
/// @param bitsPerElement Number of bits to pack into each element
|
|
/// @input in Array of bits in big-endian order
|
|
/// @output out Array of packed field elements in big-endian order
|
|
template PackBits(numBits, bitsPerElement) {
|
|
signal input in[numBits];
|
|
var numElements = (numBits + bitsPerElement - 1) \ bitsPerElement;
|
|
signal output out[numElements];
|
|
|
|
for (var i = 0; i < numElements; i++) {
|
|
var sum = 0;
|
|
for (var j = 0; j < bitsPerElement; j++) {
|
|
var idx = i * bitsPerElement + j;
|
|
if (idx < numBits) {
|
|
// Maintain BE order by using (bitsPerElement - 1 - j) for bit position
|
|
sum += in[idx] * (1 << (bitsPerElement - 1 - j));
|
|
}
|
|
}
|
|
out[i] <== sum;
|
|
}
|
|
} |