o1js-email-verify
Version:
Implemented using [o1js](https://github.com/o1-labs/o1js), this project is a reimplementation of [zk-email](https://github.com/zkemail/zk-email-verify), leveraging the Mina proving system [Kimchi](https://o1-labs.github.io/proof-systems/specs/kimchi.html#
128 lines (126 loc) • 4.92 kB
JavaScript
import { Bytes, Field, ZkProgram } from 'o1js';
class Bytes1024 extends Bytes(1024) {
}
class Bytes1536 extends Bytes(1536) {
}
class Bytes32 extends Bytes(32) {
}
import { pkcs1v15Pad } from './utils.js';
const BHRegexProgram1024 = ZkProgram({
name: 'body-hash-regex',
methods: {
sha256: {
privateInputs: [Field],
async method(bodyHashIndex) {
// Reveal the body hash from the email headers using regex
// const { out, reveal } = bodyHashRegex(input.bytes);
// out.assertEquals(1);
// Select the body hash bytes subarray
// const headerBodyHash = Bytes.from(
// selectSubarray(reveal[0], bodyHashIndex, 44)
// );
// let a = selectSubarray(input.toFields(), bodyHashIndex, 44);
let rnd = Bytes(32).random();
const paddedHash = pkcs1v15Pad(rnd, Math.ceil(1024 / 8)); // PKCS#1 v1.5 encode the hash
// Provable.assertEqual(rnd, rnd);
},
},
},
});
console.log('Body Hash Regex (1024-byte) rnd rows:', (await BHRegexProgram1024.analyzeMethods()).sha256.summary());
/*
bodyHashRegex - 1024:
--> { 'Total rows': 85943, Generic: 75703, EndoMulScalar: 10240 }
bodyHashRegex + subarray - 1024:
--> { 'Total rows': 142884, Generic: 132644, EndoMulScalar: 10240 }
bodyHashRegex - 1536:
--> { 'Total rows': 129717, Generic: 114357, EndoMulScalar: 15360 }
bodyHashRegex + subarray - 1536:
--> { 'Total rows': 231202, Generic: 215842, EndoMulScalar: 15360 }
*/
import { assert, UInt8 } from 'o1js';
import { dynamicSHA256 } from 'dynamic-sha256';
function selectBytesSubarray(input, startIndex, subarrayLength) {
let inputBytes = input.map((f) => UInt8.Unsafe.fromField(f));
const maxArrayLen = input.length;
assert(subarrayLength <= maxArrayLen, 'Subarray length exceeds input array length!');
// Assert startIndex is not zero
startIndex.assertNotEquals(0, 'Subarray start index must be greater than zero!');
const bitLength = Math.ceil(Math.log2(maxArrayLen));
const shiftBits = startIndex.toBits(bitLength);
let tmp = Array.from({ length: bitLength }, () => Array.from({ length: maxArrayLen }, () => UInt8.from(0)));
let a = UInt8.from(0);
for (let j = 0; j < bitLength; j++) {
for (let i = 0; i < maxArrayLen; i++) {
let offset = (i + (1 << j)) % maxArrayLen;
// Shift left by 2^j indices if bit is 1
if (j === 0) {
tmp[j][i] = UInt8.Unsafe.fromField(shiftBits[j].toField())
.mul(inputBytes[offset].sub(inputBytes[i]))
.add(inputBytes[i]);
}
else {
tmp[j][i] = UInt8.Unsafe.fromField(shiftBits[j].toField())
.mul(tmp[j - 1][offset].sub(tmp[j - 1][i]))
.add(tmp[j - 1][i]);
}
}
}
// console.log('shifted array length: ', tmp[bitLength - 1].length);
// Return last row
let subarray = [];
for (let i = 0; i < subarrayLength; i++) {
const selectedByte = tmp[bitLength - 1][i];
// In the context of zk-regex, matched data consists of non-null bytes, while unmatched data consists of null bytes
// Assert that the subarray data doesn't contain a 0 (null) byte
selectedByte.value.assertNotEquals(0, 'Selected subarray bytes should not contain null bytes!');
subarray.push(selectedByte);
}
// console.log('\noriginal array length: ', input.length);
// console.log('subarray out length: ', subarray.length);
return subarray;
}
// -------------------------------------
const SHA256Dynamic1024Program = ZkProgram({
name: 'sha256',
publicOutput: Bytes32.provable,
methods: {
sha256: {
privateInputs: [Bytes1024.provable, Field],
async method(preimage, digestIndex) {
return dynamicSHA256(preimage, digestIndex);
},
},
},
});
const SHA256Dynamic1536Program = ZkProgram({
name: 'sha256',
publicOutput: Bytes32.provable,
methods: {
sha256: {
privateInputs: [Bytes1536.provable, Field],
async method(preimage, digestIndex) {
return dynamicSHA256(preimage, digestIndex);
},
},
},
});
console.log('\nDynamic SHA256 (1024-byte) rows:', (await SHA256Dynamic1024Program.analyzeMethods()).sha256.summary());
console.log('Dynamic SHA256 (1536-byte) rows:', (await SHA256Dynamic1536Program.analyzeMethods()).sha256.summary());
/*
Dynamic SHA256 (1024-byte) rows: {
'Total rows': 92675,
Generic: 31203,
EndoMulScalar: 27704,
Xor16: 22512,
Zero: 11256
}
Dynamic SHA256 (1536-byte) rows: {
'Total rows': 138883,
Generic: 46691,
EndoMulScalar: 41528,
Xor16: 33776,
Zero: 16888
}
*/
//# sourceMappingURL=trash.js.map