From e3166d62a01681a4a7ffe89e8632f2d2332294e4 Mon Sep 17 00:00:00 2001 From: tsukino <87639218+0xtsukino@users.noreply.github.com> Date: Wed, 11 Dec 2024 04:50:56 -0500 Subject: [PATCH] refactor: offscreen and ws event handlers (#115) * refactor: offscreen rpc code * refactor: ws message sender --- src/components/PluginList/index.tsx | 1 - src/entries/Background/rpc.ts | 56 +-- src/entries/Background/ws.ts | 76 +--- src/entries/Offscreen/Offscreen.tsx | 560 +--------------------------- src/entries/Offscreen/rpc.ts | 484 ++++++++++++++++++++++++ 5 files changed, 543 insertions(+), 634 deletions(-) create mode 100644 src/entries/Offscreen/rpc.ts diff --git a/src/components/PluginList/index.tsx b/src/components/PluginList/index.tsx index 3c762dcb..a7f52af0 100644 --- a/src/components/PluginList/index.tsx +++ b/src/components/PluginList/index.tsx @@ -24,7 +24,6 @@ import { PluginInfoModalHeader, } from '../PluginInfo'; import { getPluginConfigByHash } from '../../entries/Background/db'; -import { OffscreenActionTypes } from '../../entries/Offscreen/types'; import { SidePanelActionTypes } from '../../entries/SidePanel/types'; import { openSidePanel } from '../../entries/utils'; diff --git a/src/entries/Background/rpc.ts b/src/entries/Background/rpc.ts index 8672078d..4c4c8166 100644 --- a/src/entries/Background/rpc.ts +++ b/src/entries/Background/rpc.ts @@ -54,24 +54,14 @@ import { subtractRanges } from '../Offscreen/utils'; import { mapSecretsToRange } from './plugins/utils'; import { pushToRedux } from '../utils'; import { - acceptPairRequest, - cancelPairRequest, connectSession, disconnectSession, getP2PState, - rejectPairRequest, - requestProofByHash, requestProof, - sendPairRequest, - cancelProofRequest, - acceptProofRequest, - rejectProofRequest, - startProofRequest, - startedVerifier, - startedProver, endProofRequest, - setupProver, onProverInstantiated, + sendMessage, + sendPairedMessage, } from './ws'; const charwise = require('charwise'); @@ -274,49 +264,65 @@ export const initRPC = () => { disconnectSession().then(sendResponse); return; case BackgroundActiontype.send_pair_request: - sendPairRequest(request.data).then(sendResponse); + sendMessage(request.data, 'pair_request').then(sendResponse); return; case BackgroundActiontype.cancel_pair_request: - cancelPairRequest(request.data).then(sendResponse); + sendMessage(request.data, 'pair_request_cancel').then(sendResponse); return; case BackgroundActiontype.accept_pair_request: - acceptPairRequest(request.data).then(sendResponse); + sendMessage(request.data, 'pair_request_accept').then(sendResponse); return; case BackgroundActiontype.reject_pair_request: - rejectPairRequest(request.data).then(sendResponse); + sendMessage(request.data, 'pair_request_reject').then(sendResponse); return; case BackgroundActiontype.cancel_proof_request: - cancelProofRequest(request.data).then(sendResponse); + sendPairedMessage('proof_request_cancel', { + pluginHash: request.data, + }).then(sendResponse); return; case BackgroundActiontype.accept_proof_request: - acceptProofRequest(request.data).then(sendResponse); + sendPairedMessage('proof_request_accept', { + plugfinHash: request.data, + }).then(sendResponse); return; case BackgroundActiontype.reject_proof_request: - rejectProofRequest(request.data).then(sendResponse); + sendPairedMessage('proof_request_reject', { + pluginHash: request.data, + }).then(sendResponse); return; case BackgroundActiontype.start_proof_request: - startProofRequest(request.data.pluginHash).then(sendResponse); + sendPairedMessage('proof_request_start', { + pluginHash: request.data.pluginHash, + }).then(sendResponse); return; case BackgroundActiontype.proof_request_end: endProofRequest(request.data).then(sendResponse); return; case BackgroundActiontype.verifier_started: - startedVerifier(request.data.pluginHash).then(sendResponse); + sendPairedMessage('verifier_started', { + pluginHash: request.data.pluginHash, + }).then(sendResponse); return; case BackgroundActiontype.prover_started: - startedProver(request.data.pluginHash).then(sendResponse); + sendPairedMessage('prover_started', { + pluginHash: request.data.pluginHash, + }).then(sendResponse); return; case BackgroundActiontype.prover_instantiated: - onProverInstantiated(request.data.pluginHash); + onProverInstantiated(); return; case BackgroundActiontype.prover_setup: - setupProver(request.data.pluginHash).then(sendResponse); + sendPairedMessage('prover_setup', { + pluginHash: request.data.pluginHash, + }).then(sendResponse); return; case BackgroundActiontype.request_p2p_proof: requestProof(request.data).then(sendResponse); return; case BackgroundActiontype.request_p2p_proof_by_hash: - requestProofByHash(request.data).then(sendResponse); + sendPairedMessage('request_proof_by_hash', { + pluginHash: request.data, + }).then(sendResponse); return; case BackgroundActiontype.get_p2p_state: getP2PState(); diff --git a/src/entries/Background/ws.ts b/src/entries/Background/ws.ts index 18fcb00c..892512f7 100644 --- a/src/entries/Background/ws.ts +++ b/src/entries/Background/ws.ts @@ -18,7 +18,7 @@ import { } from '../../reducers/p2p'; import { pushToRedux } from '../utils'; import { getPluginByHash } from './db'; -import browser, { storage } from 'webextension-polyfill'; +import browser from 'webextension-polyfill'; import { OffscreenActionTypes } from '../Offscreen/types'; import { getMaxRecv, getMaxSent, getRendezvousApi } from '../../utils/storage'; import { SidePanelActionTypes } from '../SidePanel/types'; @@ -378,7 +378,11 @@ export const disconnectSession = async () => { await socket.close(); }; -function sendMessage(target: string, method: string, params?: any) { +export async function sendMessage( + target: string, + method: string, + params?: any, +) { const { socket, clientId } = state; if (clientId === target) { @@ -409,7 +413,7 @@ function sendMessage(target: string, method: string, params?: any) { ); } -function sendPairedMessage(method: string, params?: any) { +export async function sendPairedMessage(method: string, params?: any) { const { pairing } = state; if (!pairing) { @@ -420,22 +424,6 @@ function sendPairedMessage(method: string, params?: any) { sendMessage(pairing, method, params); } -export const sendPairRequest = async (target: string) => { - sendMessage(target, 'pair_request'); -}; - -export const cancelPairRequest = async (target: string) => { - sendMessage(target, 'pair_request_cancel'); -}; - -export const acceptPairRequest = async (target: string) => { - sendMessage(target, 'pair_request_accept'); -}; - -export const rejectPairRequest = async (target: string) => { - sendMessage(target, 'pair_request_reject'); -}; - export const requestProof = async (pluginHash: string) => { const pluginHex = await getPluginByHash(pluginHash); sendPairedMessage('request_proof', { @@ -444,30 +432,6 @@ export const requestProof = async (pluginHash: string) => { }); }; -export const requestProofByHash = async (pluginHash: string) => { - sendPairedMessage('request_proof_by_hash', { - pluginHash, - }); -}; - -export const cancelProofRequest = async (pluginHash: string) => { - sendPairedMessage('proof_request_cancel', { - pluginHash, - }); -}; - -export const acceptProofRequest = async (pluginHash: string) => { - sendPairedMessage('proof_request_accept', { - pluginHash, - }); -}; - -export const startProofRequest = async (pluginHash: string) => { - sendPairedMessage('proof_request_start', { - pluginHash, - }); -}; - export const endProofRequest = async (data: { pluginHash: string; proof: VerifierOutput; @@ -490,35 +454,11 @@ export const endProofRequest = async (data: { }); }; -export const rejectProofRequest = async (pluginHash: string) => { - sendPairedMessage('proof_request_reject', { - pluginHash, - }); -}; - -export const startedVerifier = async (pluginHash: string) => { - sendPairedMessage('verifier_started', { - pluginHash, - }); -}; - -export const startedProver = async (pluginHash: string) => { - sendPairedMessage('prover_started', { - pluginHash, - }); -}; - -export const onProverInstantiated = async (pluginHash: string) => { +export const onProverInstantiated = async () => { state.isProving = true; pushToRedux(setIsProving(true)); }; -export const setupProver = async (pluginHash: string) => { - sendPairedMessage('prover_setup', { - pluginHash, - }); -}; - function bufferify(data: any): Buffer { return Buffer.from(JSON.stringify(data)); } diff --git a/src/entries/Offscreen/Offscreen.tsx b/src/entries/Offscreen/Offscreen.tsx index 44f92235..da77549f 100644 --- a/src/entries/Offscreen/Offscreen.tsx +++ b/src/entries/Offscreen/Offscreen.tsx @@ -1,380 +1,56 @@ import React, { useEffect } from 'react'; -import * as Comlink from 'comlink'; import { OffscreenActionTypes } from './types'; -import { - NotaryServer, - Prover as TProver, - Verifier as TVerifier, - Presentation as TPresentation, - Transcript, -} from 'tlsn-js'; -import { verify } from 'tlsn-js-v5'; -import { - hexToArrayBuffer, - makePlugin, - safeParseJSON, - urlify, -} from '../../utils/misc'; import { BackgroundActiontype } from '../Background/rpc'; -import browser from 'webextension-polyfill'; -import { PresentationJSON } from '../../utils/types'; -import { PresentationJSON as PresentationJSONa7 } from 'tlsn-js/build/types'; -import { Method } from 'tlsn-wasm'; -import { subtractRanges } from './utils'; -import { mapSecretsToRange } from '../Background/plugins/utils'; -import { waitForEvent } from '../utils'; -import type { ParsedTranscriptData } from 'tlsn-js/src/types'; -import { getPluginByHash } from '../Background/db'; - -const { init, Prover, Presentation, Verifier }: any = Comlink.wrap( - new Worker(new URL('./worker.ts', import.meta.url)), -); - -const provers: { [id: string]: TProver } = {}; +import { + initThreads, + onCreatePresentationRequest, + onCreateProverRequest, + onNotarizationRequest, + onProcessProveRequest, + onVerifyProof, + onVerifyProofRequest, + startP2PProver, + startP2PVerifier, +} from './rpc'; const Offscreen = () => { useEffect(() => { (async () => { - const loggingLevel = await browser.runtime.sendMessage({ - type: BackgroundActiontype.get_logging_level, - hardwareConcurrency: navigator.hardwareConcurrency, - }); - await init({ loggingLevel }); + await initThreads(); // @ts-ignore chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { switch (request.type) { case OffscreenActionTypes.notarization_request: { - const { id } = request.data; - - (async () => { - try { - const proof = await createProof(request.data); - - browser.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id, - proof, - }, - }); - - browser.runtime.sendMessage({ - type: OffscreenActionTypes.notarization_response, - data: { - id, - proof, - }, - }); - } catch (error) { - console.error(error); - browser.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id, - error, - }, - }); - - browser.runtime.sendMessage({ - type: OffscreenActionTypes.notarization_response, - data: { - id, - error, - }, - }); - } - })(); - + onNotarizationRequest(request); break; } case OffscreenActionTypes.create_prover_request: { - const { id } = request.data; - - (async () => { - try { - const prover = await createProver(request.data); - - provers[id] = prover; - - browser.runtime.sendMessage({ - type: OffscreenActionTypes.create_prover_response, - data: { - id, - transcript: await prover.transcript(), - }, - }); - } catch (error) { - console.error(error); - browser.runtime.sendMessage({ - type: OffscreenActionTypes.create_prover_response, - data: { - id, - error, - }, - }); - } - })(); + onCreateProverRequest(request); break; } case OffscreenActionTypes.create_presentation_request: { - const { id, commit } = request.data; - (async () => { - const prover = provers[id]; - - try { - if (!prover) throw new Error(`Cannot find prover ${id}.`); - - const notarizationOutputs = await prover.notarize(commit); - - const presentation = (await new Presentation({ - attestationHex: notarizationOutputs.attestation, - secretsHex: notarizationOutputs.secrets, - notaryUrl: notarizationOutputs.notaryUrl, - websocketProxyUrl: notarizationOutputs.websocketProxyUrl, - reveal: commit, - })) as TPresentation; - const presentationJSON = await presentation.json(); - - browser.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id, - proof: presentationJSON, - }, - }); - - delete provers[id]; - } catch (error) { - console.error(error); - browser.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id, - error, - }, - }); - } - })(); + onCreatePresentationRequest(request); break; } case BackgroundActiontype.process_prove_request: { - const { id } = request.data; - - (async () => { - try { - const proof = await createProof(request.data); - - browser.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id, - proof: proof, - }, - }); - } catch (error) { - console.error(error); - browser.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id, - error, - }, - }); - } - })(); - + onProcessProveRequest(request); break; } case BackgroundActiontype.verify_proof: { - (async () => { - const result = await verifyProof(request.data); - sendResponse(result); - })(); - + onVerifyProof(request, sendResponse); return true; } case BackgroundActiontype.verify_prove_request: { - (async () => { - const proof: PresentationJSON = request.data.proof; - const result: { sent: string; recv: string } = - await verifyProof(proof); - - chrome.runtime.sendMessage({ - type: BackgroundActiontype.finish_prove_request, - data: { - id: request.data.id, - verification: { - sent: result.sent, - recv: result.recv, - }, - }, - }); - })(); + onVerifyProofRequest(request); break; } case OffscreenActionTypes.start_p2p_verifier: { - (async () => { - const { pluginHash, maxSentData, maxRecvData, verifierUrl } = - request.data; - const verifier: TVerifier = await new Verifier({ - id: pluginHash, - maxSentData: maxSentData, - maxRecvData: maxRecvData, - }); - - await verifier.connect(verifierUrl); - const proverStarted = waitForEvent( - OffscreenActionTypes.prover_started, - ); - - browser.runtime.sendMessage({ - type: BackgroundActiontype.verifier_started, - data: { - pluginHash, - }, - }); - - await waitForEvent(OffscreenActionTypes.prover_setup); - - verifier.verify().then((res) => { - browser.runtime.sendMessage({ - type: BackgroundActiontype.proof_request_end, - data: { - pluginHash, - proof: res, - }, - }); - }); - - await proverStarted; - - browser.runtime.sendMessage({ - type: BackgroundActiontype.start_proof_request, - data: { - pluginHash, - }, - }); - })(); + startP2PVerifier(request); break; } case OffscreenActionTypes.start_p2p_prover: { - (async () => { - const { - pluginHash, - pluginHex, - url, - method, - headers, - body, - proverUrl, - websocketProxyUrl, - maxRecvData, - maxSentData, - secretHeaders, - getSecretResponse, - } = request.data; - - const hostname = urlify(url)?.hostname || ''; - - const prover: TProver = await new Prover({ - id: pluginHash, - serverDns: hostname, - maxSentData, - maxRecvData, - }); - - browser.runtime.sendMessage({ - type: BackgroundActiontype.prover_instantiated, - data: { - pluginHash, - }, - }); - - const proofRequestStart = waitForEvent( - OffscreenActionTypes.start_p2p_proof_request, - ); - - const proverSetup = prover.setup(proverUrl); - await new Promise((r) => setTimeout(r, 5000)); - browser.runtime.sendMessage({ - type: BackgroundActiontype.prover_setup, - data: { - pluginHash, - }, - }); - - await proverSetup; - browser.runtime.sendMessage({ - type: BackgroundActiontype.prover_started, - data: { - pluginHash, - }, - }); - await proofRequestStart; - await prover.sendRequest( - websocketProxyUrl + `?token=${hostname}`, - { - url, - method, - headers, - body, - }, - ); - - const transcript = await prover.transcript(); - - let secretResps: string[] = []; - - if (getSecretResponse) { - browser.runtime.sendMessage({ - type: BackgroundActiontype.get_secrets_from_transcript, - data: { - pluginHash, - pluginHex, - method: getSecretResponse, - transcript, - p2p: true, - }, - }); - - const msg: any = await waitForEvent( - OffscreenActionTypes.get_secrets_from_transcript_success, - ); - - secretResps = msg.data.secretResps; - } - - const commit = { - sent: subtractRanges( - transcript.ranges.sent.all, - mapSecretsToRange(secretHeaders, transcript.sent), - ), - recv: subtractRanges( - transcript.ranges.recv.all, - secretResps - .map((secret: string) => { - const index = transcript.recv.indexOf(secret); - return index > -1 - ? { - start: index, - end: index + secret.length, - } - : null; - }) - .filter((data: any) => !!data) as { - start: number; - end: number; - }[], - ), - }; - - const endRequest = waitForEvent( - OffscreenActionTypes.end_p2p_proof_request, - ); - await prover.reveal(commit); - await endRequest; - })(); + startP2PProver(request); break; } default: @@ -388,199 +64,3 @@ const Offscreen = () => { }; export default Offscreen; - -async function createProof(options: { - url: string; - notaryUrl: string; - websocketProxyUrl: string; - method?: Method; - headers?: { - [name: string]: string; - }; - body?: any; - maxSentData?: number; - maxRecvData?: number; - id: string; - secretHeaders: string[]; - secretResps: string[]; -}): Promise { - const { - url, - method = 'GET', - headers = {}, - body, - maxSentData, - maxRecvData, - notaryUrl, - websocketProxyUrl, - id, - secretHeaders = [], - secretResps = [], - } = options; - - const hostname = urlify(url)?.hostname || ''; - const notary = NotaryServer.from(notaryUrl); - const prover: TProver = await new Prover({ - id, - serverDns: hostname, - maxSentData, - maxRecvData, - }); - - await prover.setup(await notary.sessionUrl(maxSentData, maxRecvData)); - - await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, { - url, - method, - headers, - body, - }); - - const transcript = await prover.transcript(); - - const commit = { - sent: subtractRanges( - transcript.ranges.sent.all, - mapSecretsToRange(secretHeaders, transcript.sent), - ), - recv: subtractRanges( - transcript.ranges.recv.all, - mapSecretsToRange(secretResps, transcript.recv), - ), - }; - - const notarizationOutputs = await prover.notarize(commit); - - const presentation = (await new Presentation({ - attestationHex: notarizationOutputs.attestation, - secretsHex: notarizationOutputs.secrets, - notaryUrl: notarizationOutputs.notaryUrl, - websocketProxyUrl: notarizationOutputs.websocketProxyUrl, - reveal: commit, - })) as TPresentation; - - return presentation.json(); -} - -async function createProver(options: { - url: string; - notaryUrl: string; - websocketProxyUrl: string; - method?: Method; - headers?: { - [name: string]: string; - }; - body?: any; - maxSentData?: number; - maxRecvData?: number; - id: string; -}): Promise { - const { - url, - method = 'GET', - headers = {}, - body, - maxSentData, - maxRecvData, - notaryUrl, - websocketProxyUrl, - id, - } = options; - - const hostname = urlify(url)?.hostname || ''; - const notary = NotaryServer.from(notaryUrl); - const prover: TProver = await new Prover({ - id, - serverDns: hostname, - maxSentData, - maxRecvData, - }); - - await prover.setup(await notary.sessionUrl(maxSentData, maxRecvData)); - - await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, { - url, - method, - headers, - body, - }); - - return prover; -} - -function getCommitFromTranscript( - transcript: { - sent: string; - recv: string; - ranges: { recv: ParsedTranscriptData; sent: ParsedTranscriptData }; - }, - secretHeaders: string[], - secretResps: string[], -) { - const commit = { - sent: subtractRanges( - transcript.ranges.sent.all, - secretHeaders - .map((secret: string) => { - const index = transcript.sent.indexOf(secret); - return index > -1 - ? { - start: index, - end: index + secret.length, - } - : null; - }) - .filter((data: any) => !!data) as { - start: number; - end: number; - }[], - ), - recv: subtractRanges( - transcript.ranges.recv.all, - secretResps - .map((secret: string) => { - const index = transcript.recv.indexOf(secret); - return index > -1 - ? { - start: index, - end: index + secret.length, - } - : null; - }) - .filter((data: any) => !!data) as { - start: number; - end: number; - }[], - ), - }; - - return commit; -} - -async function verifyProof( - proof: PresentationJSON, -): Promise<{ sent: string; recv: string }> { - let result: { sent: string; recv: string }; - - switch (proof.version) { - case undefined: { - result = await verify(proof); - break; - } - case '0.1.0-alpha.7': { - const presentation: TPresentation = await new Presentation(proof.data); - const verifierOutput = await presentation.verify(); - const transcript = new Transcript({ - sent: verifierOutput.transcript.sent, - recv: verifierOutput.transcript.recv, - }); - result = { - sent: transcript.sent(), - recv: transcript.recv(), - }; - break; - } - } - - return result; -} diff --git a/src/entries/Offscreen/rpc.ts b/src/entries/Offscreen/rpc.ts new file mode 100644 index 00000000..36ddb80c --- /dev/null +++ b/src/entries/Offscreen/rpc.ts @@ -0,0 +1,484 @@ +import browser from 'webextension-polyfill'; +import { BackgroundActiontype } from '../Background/rpc'; +import { Method } from 'tlsn-wasm'; +import { + NotaryServer, + Presentation as TPresentation, + Prover as TProver, + Transcript, + Verifier as TVerifier, +} from 'tlsn-js'; +import { urlify } from '../../utils/misc'; +import * as Comlink from 'comlink'; +import { PresentationJSON as PresentationJSONa7 } from 'tlsn-js/build/types'; +import { subtractRanges } from './utils'; +import { mapSecretsToRange } from '../Background/plugins/utils'; +import { OffscreenActionTypes } from './types'; +import { PresentationJSON } from '../../utils/types'; +import { verify } from 'tlsn-js-v5'; +import { waitForEvent } from '../utils'; + +const { init, Prover, Presentation, Verifier }: any = Comlink.wrap( + new Worker(new URL('./worker.ts', import.meta.url)), +); + +const provers: { [id: string]: TProver } = {}; + +export const initThreads = async () => { + const loggingLevel = await browser.runtime.sendMessage({ + type: BackgroundActiontype.get_logging_level, + hardwareConcurrency: navigator.hardwareConcurrency, + }); + await init({ loggingLevel }); +}; +export const onNotarizationRequest = async (request: any) => { + const { id } = request.data; + + try { + const proof = await createProof(request.data); + + browser.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id, + proof, + }, + }); + + browser.runtime.sendMessage({ + type: OffscreenActionTypes.notarization_response, + data: { + id, + proof, + }, + }); + } catch (error) { + console.error(error); + browser.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id, + error, + }, + }); + + browser.runtime.sendMessage({ + type: OffscreenActionTypes.notarization_response, + data: { + id, + error, + }, + }); + } +}; + +export const onCreateProverRequest = async (request: any) => { + const { id } = request.data; + + try { + const prover = await createProver(request.data); + + provers[id] = prover; + + browser.runtime.sendMessage({ + type: OffscreenActionTypes.create_prover_response, + data: { + id, + transcript: await prover.transcript(), + }, + }); + } catch (error) { + console.error(error); + browser.runtime.sendMessage({ + type: OffscreenActionTypes.create_prover_response, + data: { + id, + error, + }, + }); + } +}; + +export const onCreatePresentationRequest = async (request: any) => { + const { id, commit } = request.data; + const prover = provers[id]; + + try { + if (!prover) throw new Error(`Cannot find prover ${id}.`); + + const notarizationOutputs = await prover.notarize(commit); + + const presentation = (await new Presentation({ + attestationHex: notarizationOutputs.attestation, + secretsHex: notarizationOutputs.secrets, + notaryUrl: notarizationOutputs.notaryUrl, + websocketProxyUrl: notarizationOutputs.websocketProxyUrl, + reveal: commit, + })) as TPresentation; + const presentationJSON = await presentation.json(); + + browser.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id, + proof: presentationJSON, + }, + }); + + delete provers[id]; + } catch (error) { + console.error(error); + browser.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id, + error, + }, + }); + } +}; + +export const onProcessProveRequest = async (request: any) => { + const { id } = request.data; + + try { + const proof = await createProof(request.data); + + browser.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id, + proof: proof, + }, + }); + } catch (error) { + console.error(error); + browser.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id, + error, + }, + }); + } +}; + +export const onVerifyProof = async (request: any, sendResponse: any) => { + const result = await verifyProof(request.data); + sendResponse(result); +}; + +export const onVerifyProofRequest = async (request: any) => { + const proof: PresentationJSON = request.data.proof; + const result: { sent: string; recv: string } = await verifyProof(proof); + + chrome.runtime.sendMessage({ + type: BackgroundActiontype.finish_prove_request, + data: { + id: request.data.id, + verification: { + sent: result.sent, + recv: result.recv, + }, + }, + }); +}; + +export const startP2PVerifier = async (request: any) => { + const { pluginHash, maxSentData, maxRecvData, verifierUrl } = request.data; + const verifier: TVerifier = await new Verifier({ + id: pluginHash, + maxSentData: maxSentData, + maxRecvData: maxRecvData, + }); + + await verifier.connect(verifierUrl); + const proverStarted = waitForEvent(OffscreenActionTypes.prover_started); + + browser.runtime.sendMessage({ + type: BackgroundActiontype.verifier_started, + data: { + pluginHash, + }, + }); + + await waitForEvent(OffscreenActionTypes.prover_setup); + + verifier.verify().then((res) => { + browser.runtime.sendMessage({ + type: BackgroundActiontype.proof_request_end, + data: { + pluginHash, + proof: res, + }, + }); + }); + + await proverStarted; + + browser.runtime.sendMessage({ + type: BackgroundActiontype.start_proof_request, + data: { + pluginHash, + }, + }); +}; + +export const startP2PProver = async (request: any) => { + const { + pluginHash, + pluginHex, + url, + method, + headers, + body, + proverUrl, + websocketProxyUrl, + maxRecvData, + maxSentData, + secretHeaders, + getSecretResponse, + } = request.data; + + const hostname = urlify(url)?.hostname || ''; + + const prover: TProver = await new Prover({ + id: pluginHash, + serverDns: hostname, + maxSentData, + maxRecvData, + }); + + browser.runtime.sendMessage({ + type: BackgroundActiontype.prover_instantiated, + data: { + pluginHash, + }, + }); + + const proofRequestStart = waitForEvent( + OffscreenActionTypes.start_p2p_proof_request, + ); + + const proverSetup = prover.setup(proverUrl); + await new Promise((r) => setTimeout(r, 5000)); + browser.runtime.sendMessage({ + type: BackgroundActiontype.prover_setup, + data: { + pluginHash, + }, + }); + + await proverSetup; + browser.runtime.sendMessage({ + type: BackgroundActiontype.prover_started, + data: { + pluginHash, + }, + }); + await proofRequestStart; + await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, { + url, + method, + headers, + body, + }); + + const transcript = await prover.transcript(); + + let secretResps: string[] = []; + + if (getSecretResponse) { + browser.runtime.sendMessage({ + type: BackgroundActiontype.get_secrets_from_transcript, + data: { + pluginHash, + pluginHex, + method: getSecretResponse, + transcript, + p2p: true, + }, + }); + + const msg: any = await waitForEvent( + OffscreenActionTypes.get_secrets_from_transcript_success, + ); + + secretResps = msg.data.secretResps; + } + + const commit = { + sent: subtractRanges( + transcript.ranges.sent.all, + mapSecretsToRange(secretHeaders, transcript.sent), + ), + recv: subtractRanges( + transcript.ranges.recv.all, + secretResps + .map((secret: string) => { + const index = transcript.recv.indexOf(secret); + return index > -1 + ? { + start: index, + end: index + secret.length, + } + : null; + }) + .filter((data: any) => !!data) as { + start: number; + end: number; + }[], + ), + }; + + const endRequest = waitForEvent(OffscreenActionTypes.end_p2p_proof_request); + await prover.reveal(commit); + await endRequest; +}; + +async function createProof(options: { + url: string; + notaryUrl: string; + websocketProxyUrl: string; + method?: Method; + headers?: { + [name: string]: string; + }; + body?: any; + maxSentData?: number; + maxRecvData?: number; + id: string; + secretHeaders: string[]; + secretResps: string[]; +}): Promise { + const { + url, + method = 'GET', + headers = {}, + body, + maxSentData, + maxRecvData, + notaryUrl, + websocketProxyUrl, + id, + secretHeaders = [], + secretResps = [], + } = options; + + const hostname = urlify(url)?.hostname || ''; + const notary = NotaryServer.from(notaryUrl); + const prover: TProver = await new Prover({ + id, + serverDns: hostname, + maxSentData, + maxRecvData, + }); + + await prover.setup(await notary.sessionUrl(maxSentData, maxRecvData)); + + await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, { + url, + method, + headers, + body, + }); + + const transcript = await prover.transcript(); + + const commit = { + sent: subtractRanges( + transcript.ranges.sent.all, + mapSecretsToRange(secretHeaders, transcript.sent), + ), + recv: subtractRanges( + transcript.ranges.recv.all, + mapSecretsToRange(secretResps, transcript.recv), + ), + }; + + const notarizationOutputs = await prover.notarize(commit); + + const presentation = (await new Presentation({ + attestationHex: notarizationOutputs.attestation, + secretsHex: notarizationOutputs.secrets, + notaryUrl: notarizationOutputs.notaryUrl, + websocketProxyUrl: notarizationOutputs.websocketProxyUrl, + reveal: commit, + })) as TPresentation; + + return presentation.json(); +} + +async function createProver(options: { + url: string; + notaryUrl: string; + websocketProxyUrl: string; + method?: Method; + headers?: { + [name: string]: string; + }; + body?: any; + maxSentData?: number; + maxRecvData?: number; + id: string; +}): Promise { + const { + url, + method = 'GET', + headers = {}, + body, + maxSentData, + maxRecvData, + notaryUrl, + websocketProxyUrl, + id, + } = options; + + const hostname = urlify(url)?.hostname || ''; + const notary = NotaryServer.from(notaryUrl); + const prover: TProver = await new Prover({ + id, + serverDns: hostname, + maxSentData, + maxRecvData, + }); + + await prover.setup(await notary.sessionUrl(maxSentData, maxRecvData)); + + await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, { + url, + method, + headers, + body, + }); + + return prover; +} + +async function verifyProof( + proof: PresentationJSON, +): Promise<{ sent: string; recv: string }> { + let result: { sent: string; recv: string }; + + switch (proof.version) { + case undefined: { + result = await verify(proof); + break; + } + case '0.1.0-alpha.7': { + const presentation: TPresentation = await new Presentation(proof.data); + const verifierOutput = await presentation.verify(); + const transcript = new Transcript({ + sent: verifierOutput.transcript.sent, + recv: verifierOutput.transcript.recv, + }); + result = { + sent: transcript.sent(), + recv: transcript.recv(), + }; + break; + } + } + + return result; +}