From 7dac98c42942cbbed6e6fe2b0efbb7b88320a0fe Mon Sep 17 00:00:00 2001 From: tsukino <0xtsukino@gmail.com> Date: Tue, 29 Oct 2024 21:29:25 +0800 Subject: [PATCH] wip --- src/entries/Background/rpc.ts | 151 ++++++++++++++++++++----- src/entries/Offscreen/Offscreen.tsx | 165 ++++++++++++++++++++-------- src/entries/Offscreen/types.ts | 4 + src/entries/Offscreen/utils.ts | 39 +++++++ src/utils/misc.ts | 32 ++++-- 5 files changed, 310 insertions(+), 81 deletions(-) create mode 100644 src/entries/Offscreen/utils.ts diff --git a/src/entries/Background/rpc.ts b/src/entries/Background/rpc.ts index ea95fd1..574c179 100644 --- a/src/entries/Background/rpc.ts +++ b/src/entries/Background/rpc.ts @@ -46,6 +46,7 @@ import { deferredPromise } from '../../utils/promise'; import { minimatch } from 'minimatch'; import { OffscreenActionTypes } from '../Offscreen/types'; import { SidePanelActionTypes } from '../SidePanel/types'; +import { subtractRanges } from '../Offscreen/utils'; const charwise = require('charwise'); @@ -382,8 +383,9 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) { method, headers, body, - secretHeaders, - secretResps, + secretHeaders = [], + // secretResps, + getSecretResponse, notaryUrl: _notaryUrl, websocketProxyUrl: _websocketProxyUrl, maxSentData: _maxSentData, @@ -394,33 +396,130 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) { const maxSentData = _maxSentData || (await getMaxSent()); const maxRecvData = _maxRecvData || (await getMaxRecv()); - const { id } = await addNotaryRequest(now, { - url, - method, - headers, - body, - notaryUrl, - websocketProxyUrl, - maxRecvData, - maxSentData, - secretHeaders, - secretResps, - }); + let secretResps: string[] = []; + + // const { id } = await addNotaryRequest(now, { + // url, + // method, + // headers, + // body, + // notaryUrl, + // websocketProxyUrl, + // maxRecvData, + // maxSentData, + // secretHeaders, + // secretResps, + // }); + // + // await setNotaryRequestStatus(id, 'pending'); + // + // await browser.runtime.sendMessage({ + // type: BackgroundActiontype.push_action, + // data: { + // tabId: 'background', + // }, + // action: addRequestHistory(await getNotaryRequest(id)), + // }); + + const onProverResponse = async (request: any) => { + const { data, type } = request; + + if (type !== OffscreenActionTypes.create_prover_response) { + return; + } - await setNotaryRequestStatus(id, 'pending'); + if (data.error) { + console.error(data.error); + return; + } - await browser.runtime.sendMessage({ - type: BackgroundActiontype.push_action, - data: { - tabId: 'background', - }, - action: addRequestHistory(await getNotaryRequest(id)), - }); + if (data.id !== now) { + return; + } - await browser.runtime.sendMessage({ - type: BackgroundActiontype.process_prove_request, + if (getSecretResponse) { + console.log('getting secret response'); + const { + recv, + ranges: { + recv: { + body: { start }, + all: { end }, + }, + }, + } = data.transcript; + secretResps = await getSecretResponse(recv.slice(start, end)); + } + + const commit = { + sent: subtractRanges( + data.transcript.ranges.sent.all, + secretHeaders + .map((secret: string) => { + const index = data.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( + data.transcript.ranges.recv.all, + secretResps + .map((secret: string) => { + const index = data.transcript.recv.indexOf(secret); + return index > -1 + ? { + start: index, + end: index + secret.length, + } + : null; + }) + .filter((data: any) => !!data) as { start: number; end: number }[], + ), + }; + + browser.runtime.sendMessage({ + type: OffscreenActionTypes.create_presentation_request, + data: { + id: now, + commit, + }, + }); + + browser.runtime.onMessage.removeListener(onProverResponse); + }; + + const onPresentationResponse = async (request: any) => { + const { data, type } = request; + + if (type !== OffscreenActionTypes.create_presentation_response) { + return; + } + + if (data.error) { + console.error(data.error); + return; + } + + if (data.id !== now) { + return; + } + + console.log(request); + browser.runtime.onMessage.removeListener(onPresentationResponse); + }; + + browser.runtime.onMessage.addListener(onProverResponse); + browser.runtime.onMessage.addListener(onPresentationResponse); + + browser.runtime.sendMessage({ + type: OffscreenActionTypes.create_prover_request, data: { - id, + id: now, url, method, headers, @@ -429,8 +528,6 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) { websocketProxyUrl, maxRecvData, maxSentData, - secretHeaders, - secretResps, }, }); } diff --git a/src/entries/Offscreen/Offscreen.tsx b/src/entries/Offscreen/Offscreen.tsx index 5841d90..020fb16 100644 --- a/src/entries/Offscreen/Offscreen.tsx +++ b/src/entries/Offscreen/Offscreen.tsx @@ -14,12 +14,15 @@ 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-js/wasm/pkg'; +import { Commit, Method } from 'tlsn-wasm'; +import { subtractRanges } from './utils'; const { init, Prover, Presentation }: any = Comlink.wrap( new Worker(new URL('./worker.ts', import.meta.url)), ); +const provers: { [id: string]: TProver } = {}; + const Offscreen = () => { useEffect(() => { (async () => { @@ -74,6 +77,76 @@ const Offscreen = () => { 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, + }, + }); + } + })(); + 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: OffscreenActionTypes.create_presentation_response, + data: { + id, + presentation: presentationJSON, + }, + }); + + delete provers[id]; + } catch (error) { + console.error(error); + browser.runtime.sendMessage({ + type: OffscreenActionTypes.create_presentation_response, + data: { + id, + error, + }, + }); + } + })(); + break; + } case BackgroundActiontype.process_prove_request: { const { id } = request.data; @@ -141,46 +214,6 @@ const Offscreen = () => { export default Offscreen; -function subtractRanges( - ranges: { start: number; end: number }, - negatives: { start: number; end: number }[], -): { start: number; end: number }[] { - const returnVal: { start: number; end: number }[] = [ranges]; - - negatives - .sort((a, b) => (a.start < b.start ? -1 : 1)) - .forEach(({ start, end }) => { - const last = returnVal.pop()!; - - if (start < last.start || end > last.end) { - console.error('invalid ranges'); - return; - } - - if (start === last.start && end === last.end) { - return; - } - - if (start === last.start && end < last.end) { - returnVal.push({ start: end, end: last.end }); - return; - } - - if (start > last.start && end < last.end) { - returnVal.push({ start: last.start, end: start }); - returnVal.push({ start: end, end: last.end }); - return; - } - - if (start > last.start && end === last.end) { - returnVal.push({ start: last.start, end: start }); - return; - } - }); - - return returnVal; -} - async function createProof(options: { url: string; notaryUrl: string; @@ -270,8 +303,54 @@ async function createProof(options: { websocketProxyUrl: notarizationOutputs.websocketProxyUrl, reveal: commit, })) as TPresentation; - const presentationJSON = await presentation.json(); - return presentationJSON; + + 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( diff --git a/src/entries/Offscreen/types.ts b/src/entries/Offscreen/types.ts index 8873309..2bd1607 100644 --- a/src/entries/Offscreen/types.ts +++ b/src/entries/Offscreen/types.ts @@ -1,4 +1,8 @@ export enum OffscreenActionTypes { notarization_request = 'offscreen/notarization_request', notarization_response = 'offscreen/notarization_response', + create_prover_request = 'offscreen/create_prover_request', + create_prover_response = 'offscreen/create_prover_response', + create_presentation_request = 'offscreen/create_presentation_request', + create_presentation_response = 'offscreen/create_presentation_response', } diff --git a/src/entries/Offscreen/utils.ts b/src/entries/Offscreen/utils.ts new file mode 100644 index 0000000..d5fe894 --- /dev/null +++ b/src/entries/Offscreen/utils.ts @@ -0,0 +1,39 @@ +export function subtractRanges( + ranges: { start: number; end: number }, + negatives: { start: number; end: number }[], +): { start: number; end: number }[] { + const returnVal: { start: number; end: number }[] = [ranges]; + + negatives + .sort((a, b) => (a.start < b.start ? -1 : 1)) + .forEach(({ start, end }) => { + const last = returnVal.pop()!; + + if (start < last.start || end > last.end) { + console.error('invalid ranges'); + return; + } + + if (start === last.start && end === last.end) { + return; + } + + if (start === last.start && end < last.end) { + returnVal.push({ start: end, end: last.end }); + return; + } + + if (start > last.start && end < last.end) { + returnVal.push({ start: last.start, end: start }); + returnVal.push({ start: end, end: last.end }); + return; + } + + if (start > last.start && end === last.end) { + returnVal.push({ start: last.start, end: start }); + return; + } + }); + + return returnVal; +} diff --git a/src/utils/misc.ts b/src/utils/misc.ts index d8adb17..4609151 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -207,24 +207,34 @@ export const makePlugin = async ( body: reqBody, } = params; let secretResps; - const resp = await fetch(url, { - method, - headers, - body: reqBody, - }); - const body = await extractBodyFromResponse(resp); - if (getSecretResponse) { - const out = await plugin.call(getSecretResponse, body); - secretResps = JSON.parse(out.string()); - } + // const resp = await fetch(url, { + // method, + // headers, + // body: reqBody, + // }); + // const body = await extractBodyFromResponse(resp); + // console.log('pre', body); + // + // if (getSecretResponse) { + // const out = await plugin.call(getSecretResponse, body); + // secretResps = JSON.parse(out.string()); + // console.log({ secretResps }); + // } handleExecPluginProver({ type: BackgroundActiontype.execute_plugin_prover, data: { ...params, body: reqBody, - secretResps, + getSecretResponse: async (body: string) => { + return new Promise((resolve) => { + setTimeout(async () => { + const out = await plugin.call(getSecretResponse, body); + resolve(JSON.parse(out.string())); + }, 0); + }); + }, now, }, });