UNPKG

@zkp2p/reclaim-witness-sdk

Version:

<div> <div> <img src="https://raw.githubusercontent.com/reclaimprotocol/.github/main/assets/banners/Attestor-Core.png" /> </div> </div>

337 lines 26.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const tls_1 = require("@reclaimprotocol/tls"); const assert_1 = __importDefault(require("assert")); const config_1 = require("../config"); const api_1 = require("../proto/api"); const toprf_1 = require("../server/handlers/toprf"); const utils_1 = require("../utils"); require("../server/utils/config-env"); const ZK_CIPHER_SUITES = [ 'TLS_CHACHA20_POLY1305_SHA256', 'TLS_AES_128_GCM_SHA256', 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', ]; const ZK_ENGINES = [ 'gnark', 'snarkjs' ]; jest.setTimeout(90000); // 90s describe('Redaction Tests', () => { it('should correctly redact blocks', async () => { const vectors = [ { input: [ 'hell', 'o world' ], output: [ 'h***', '* world' ], redactions: [ { fromIndex: 1, toIndex: 5 } ] }, { input: [ 'hell', 'o world' ], output: [ // first block is completely // redacted, so it won't be included '* world' ], redactions: [ { fromIndex: 0, toIndex: 5 } ] }, { input: [ 'hello', 'how', 'do', 'you', 'do' ], output: [ 'he**o', 'd*', 'y*u', 'do' ], redactions: [ { fromIndex: 2, toIndex: 4 }, { fromIndex: 5, toIndex: 8 }, { fromIndex: 9, toIndex: 10 }, { fromIndex: 11, toIndex: 12 } ] } ]; for (const { input, output, redactions } of vectors) { const realOutput = await (0, utils_1.getBlocksToReveal)(input.map(i => ({ plaintext: Buffer.from(i) })), () => redactions, () => { throw new Error('should not call this'); }); if (realOutput === 'all') { fail('should not return "all"'); continue; } expect(realOutput).toHaveLength(output.length); for (const [i, element] of output.entries()) { expect((0, utils_1.uint8ArrayToStr)(realOutput[i].redactedPlaintext)).toEqual(element); } } }); it('should correctly hash blocks', async () => { const nullifer = (0, tls_1.strToUint8Array)('abcdefg'); const base64Nullifier = Buffer.from(nullifer).toString('base64'); const vectors = [ { input: [ 'hell', 'o world' ], output: [ 'h' + base64Nullifier.slice(0, 3), base64Nullifier.slice(3, 4) + ' world' ], redactions: [ { fromIndex: 1, toIndex: 5, hash: 'oprf' } ] }, { input: [ 'hell', 'o world' ], output: [ base64Nullifier.slice(0, 4), base64Nullifier.slice(4, 5) + ' world' ], redactions: [ { fromIndex: 0, toIndex: 5, hash: 'oprf' } ] }, ]; for (const { input, output, redactions } of vectors) { const realOutput = await (0, utils_1.getBlocksToReveal)(input.map(i => ({ plaintext: Buffer.from(i) })), () => redactions, async () => ({ dataLocation: undefined, nullifier: nullifer, responses: [], mask: (0, tls_1.strToUint8Array)('mask'), plaintext: (0, tls_1.strToUint8Array)('abcdefg') })); if (realOutput === 'all') { fail('should not return "all"'); } expect(realOutput).toHaveLength(output.length); for (const [i, element] of output.entries()) { expect((0, utils_1.uint8ArrayToStr)(realOutput[i].redactedPlaintext)).toEqual(element); } } }); }); describe('OPRF Slicing Tests', () => { const cipherSuite = 'TLS_CHACHA20_POLY1305_SHA256'; const alg = 'CHACHA20-POLY1305'; const zkEngine = 'gnark'; const keylength = 32; it('should correctly demarcate blocks for OPRF', async () => { var _a, _b; const plaintext = `lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum`; const vectors = [ { plaintext: plaintext, redactions: [ { fromIndex: 0, toIndex: 35, hash: 'oprf' }, ] }, { plaintext: plaintext, redactions: [ { fromIndex: 128, toIndex: 138, hash: 'oprf' }, ] }, { plaintext: plaintext, redactions: [ { fromIndex: 125, toIndex: 135, hash: 'oprf' }, ] } ]; const key = Buffer.alloc(keylength, 0); key[0] = 1; key[3] = 4; const { ivLength: fixedIvLength, } = tls_1.SUPPORTED_CIPHER_SUITE_MAP[cipherSuite]; const fixedIv = Buffer.alloc(fixedIvLength, 0); fixedIv[0] = 1; fixedIv[3] = 4; const encKey = await tls_1.crypto.importKey(alg, key); for (const [i, { plaintext, redactions }] of vectors.entries()) { const plaintextArr = Buffer.from(plaintext); const { ciphertext, iv } = await (0, tls_1.encryptWrappedRecord)(plaintextArr, { key: encKey, iv: fixedIv, recordNumber: 1234, recordHeaderOpts: { type: 'WRAPPED_RECORD' }, cipherSuite, version: cipherSuite.includes('ECDHE_') ? 'TLS1_2' : 'TLS1_3', }); const packet = { type: 'ciphertext', encKey, iv, recordNumber: 1234, plaintext: plaintextArr, ciphertext, fixedIv: fixedIv, data: ciphertext }; const blocksToReveal = await (0, utils_1.getBlocksToReveal)([packet], () => redactions, performOprf); (0, assert_1.default)(blocksToReveal !== 'all'); expect(blocksToReveal).toHaveLength(1); expect(blocksToReveal[0].toprfs).toBeTruthy(); const revealsMap = new Map(); revealsMap.set(packet, { type: 'zk', redactedPlaintext: blocksToReveal[0].redactedPlaintext, toprfs: blocksToReveal[0].toprfs }); const revealedMessages = await (0, utils_1.preparePacketsForReveal)([{ sender: 'server', message: packet }], revealsMap, { logger: utils_1.logger, cipherSuite: cipherSuite, zkEngine: zkEngine, }); const proofs = (_b = (_a = revealedMessages[0].reveal) === null || _a === void 0 ? void 0 : _a.zkReveal) === null || _b === void 0 ? void 0 : _b.proofs; expect(proofs === null || proofs === void 0 ? void 0 : proofs.length).toBeTruthy(); const x = await (0, utils_1.verifyZkPacket)({ ciphertext, zkReveal: { proofs: proofs }, logger: utils_1.logger, cipherSuite, zkEngine: zkEngine, recordNumber: 1234, iv: fixedIv }); expect(x.redactedPlaintext).toEqual(blocksToReveal[0].redactedPlaintext); console.log(`done: ${i + 1}/${vectors.length}`); } }); async function performOprf(plaintext) { utils_1.logger.info({ length: plaintext.length }, 'generating OPRF...'); const oprfOperator = (0, utils_1.makeDefaultOPRFOperator)('chacha20', zkEngine, utils_1.logger); const reqData = await oprfOperator.generateOPRFRequestData(plaintext, config_1.TOPRF_DOMAIN_SEPARATOR, utils_1.logger); const res = await (0, toprf_1.toprf)({ maskedData: reqData.maskedData, engine: api_1.ZKProofEngine.ZK_ENGINE_GNARK }, { logger: utils_1.logger }); const nullifier = await oprfOperator.finaliseOPRF(res.publicKeyShare, reqData, [res]); const data = { nullifier, responses: [res], mask: reqData.mask, dataLocation: undefined, plaintext }; return data; } }); describe.each(ZK_CIPHER_SUITES)('[%s] should generate ZK proof for some ciphertext', (cipherSuite) => { describe.each(ZK_ENGINES)('[%s]', (zkEngine) => { const zkProofConcurrency = zkEngine === 'snarkjs' ? 1 : undefined; it(zkEngine + '-' + cipherSuite, async () => { const alg = cipherSuite.includes('CHACHA20') ? 'CHACHA20-POLY1305' : (cipherSuite.includes('AES_256_GCM') ? 'AES-256-GCM' : 'AES-128-GCM'); const keylength = alg === 'AES-128-GCM' ? 16 : 32; const key = Buffer.alloc(keylength, 0); key[0] = 1; key[3] = 4; const { ivLength: fixedIvLength, } = tls_1.SUPPORTED_CIPHER_SUITE_MAP[cipherSuite]; const fixedIv = Buffer.alloc(fixedIvLength, 0); fixedIv[0] = 1; fixedIv[3] = 4; const encKey = await tls_1.crypto.importKey(alg, key); const vectors = [ { plaintext: 'My cool API secret is "my name jeff". Please don\'t reveal it', redactions: [ { fromIndex: 23, toIndex: 35 } ] }, { plaintext: `lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum`, redactions: [ { fromIndex: 5, toIndex: 15 }, ] } ]; const proofGenerator = await (0, utils_1.makeZkProofGenerator)({ logger: utils_1.logger, cipherSuite, zkEngine, zkProofConcurrency, }); for (const { plaintext, redactions } of vectors) { const plaintextArr = Buffer.from(plaintext); const redactedPlaintext = (0, utils_1.redactSlices)(plaintextArr, redactions); // ensure redaction fn kinda works at least expect(redactedPlaintext).not.toEqual(plaintextArr); const { ciphertext, iv } = await (0, tls_1.encryptWrappedRecord)(plaintextArr, { key: encKey, iv: fixedIv, recordNumber: 1234, recordHeaderOpts: { type: 'WRAPPED_RECORD' }, cipherSuite, version: cipherSuite.includes('ECDHE_') ? 'TLS1_2' : 'TLS1_3', }); const packet = { type: 'ciphertext', encKey, iv, recordNumber: 1234, plaintext: plaintextArr, ciphertext, fixedIv: fixedIv, data: ciphertext }; let proofs; await proofGenerator.addPacketToProve(packet, { type: 'zk', redactedPlaintext }, p => proofs = p); await proofGenerator.generateProofs(); const x = await (0, utils_1.verifyZkPacket)({ ciphertext, zkReveal: { proofs: proofs }, logger: utils_1.logger, cipherSuite, zkEngine: zkEngine, recordNumber: 1234, iv: fixedIv }); expect(redactedPlaintext).toEqual(x.redactedPlaintext); } }); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC56ay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0cy90ZXN0LnprLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsOENBQTZIO0FBRTdILG9EQUEyQjtBQUMzQix1Q0FBbUQ7QUFDbkQsdUNBQStFO0FBQy9FLHFEQUFpRDtBQUVqRCxxQ0FTa0I7QUFDbEIsdUNBQW9DO0FBRXBDLE1BQU0sZ0JBQWdCLEdBQWtCO0lBQ3ZDLDhCQUE4QjtJQUM5Qix3QkFBd0I7SUFDeEIseUNBQXlDO0NBQ3pDLENBQUE7QUFFRCxNQUFNLFVBQVUsR0FBZTtJQUM5QixPQUFPO0lBQ1AsU0FBUztDQUNULENBQUE7QUFRRCxJQUFJLENBQUMsVUFBVSxDQUFDLEtBQU0sQ0FBQyxDQUFBLENBQUMsTUFBTTtBQUU5QixRQUFRLENBQUMsaUJBQWlCLEVBQUUsR0FBRyxFQUFFO0lBRWhDLEVBQUUsQ0FBQyxnQ0FBZ0MsRUFBRSxLQUFLLElBQUcsRUFBRTtRQUM5QyxNQUFNLE9BQU8sR0FBMEI7WUFDdEM7Z0JBQ0MsS0FBSyxFQUFFO29CQUNOLE1BQU07b0JBQ04sU0FBUztpQkFDVDtnQkFDRCxNQUFNLEVBQUU7b0JBQ1AsTUFBTTtvQkFDTixTQUFTO2lCQUNUO2dCQUNELFVBQVUsRUFBRTtvQkFDWCxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRTtpQkFDNUI7YUFDRDtZQUNEO2dCQUNDLEtBQUssRUFBRTtvQkFDTixNQUFNO29CQUNOLFNBQVM7aUJBQ1Q7Z0JBQ0QsTUFBTSxFQUFFO29CQUNQLDRCQUE0QjtvQkFDNUIsb0NBQW9DO29CQUNwQyxTQUFTO2lCQUNUO2dCQUNELFVBQVUsRUFBRTtvQkFDWCxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRTtpQkFDNUI7YUFDRDtZQUNEO2dCQUNDLEtBQUssRUFBRTtvQkFDTixPQUFPO29CQUNQLEtBQUs7b0JBQ0wsSUFB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