Files
zk-email-verify/packages/circuits/lib/base64.circom
2024-05-17 12:44:18 +04:00

129 lines
3.8 KiB
Plaintext

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(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() {
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;
}