Compare commits

...

3 Commits

Author SHA1 Message Date
tsukino
d47cf0d8ea feat: integrate with alpha.7 (#102) 2024-10-04 05:37:41 -04:00
Tanner
25f35d0051 feat: moving requestbuilder.tsx from utils to it's own component (#92) 2024-08-28 04:10:28 -04:00
tsukino
5ccdd9b06a feat: add default plugins to extension bundle (#93) 2024-08-28 04:05:56 -04:00
21 changed files with 3294 additions and 3128 deletions

6180
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "tlsn-extension",
"version": "0.1.0.6",
"version": "0.1.0.700",
"license": "MIT",
"repository": {
"type": "git",
@@ -38,8 +38,8 @@
"redux-logger": "^3.0.6",
"redux-thunk": "^2.4.2",
"tailwindcss": "^3.3.3",
"tlsn-js": "0.1.0-alpha.6.2",
"tlsn-jsV5.3": "npm:tlsn-js@0.1.0-alpha.5.3"
"tlsn-js": "0.1.0-alpha.7.1",
"tlsn-js-v5": "npm:tlsn-js@0.1.0-alpha.5.4"
},
"devDependencies": {
"@babel/core": "^7.20.12",

View File

@@ -1 +0,0 @@
You can find example plugins at https://github.com/tlsnotary/tlsn-plugin-boilerplate/tree/main/examples

24
pnpm-lock.yaml generated
View File

@@ -72,11 +72,11 @@ dependencies:
specifier: ^3.3.3
version: 3.4.3
tlsn-js:
specifier: 0.1.0-alpha.6.2
version: 0.1.0-alpha.6.2
tlsn-jsV5.3:
specifier: npm:tlsn-js@0.1.0-alpha.5.3
version: /tlsn-js@0.1.0-alpha.5.3
specifier: 0.1.0-alpha.7.1
version: 0.1.0-alpha.7.1
tlsn-js-v5:
specifier: npm:tlsn-js@0.1.0-alpha.5.4
version: /tlsn-js@0.1.0-alpha.5.4
devDependencies:
'@babel/core':
@@ -7974,16 +7974,22 @@ packages:
resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
dev: true
/tlsn-js@0.1.0-alpha.5.3:
resolution: {integrity: sha512-eTSCQ6MaH8mN2oCfLsQDe/mdfTlIq74MbKVesV6M01C2SRE0FJcVlTWMsnT3L+wOpvOlvsiBeCWV7T9m/YMzew==}
/tlsn-js@0.1.0-alpha.5.4:
resolution: {integrity: sha512-qbqaDjApXarohn/XMJrxMsNHYTCzy+pw0Fc8gtPPN17PLE+1DwwLTXAAhnxYqYQyo3+Hmy+ODd4C02+KCwRWmg==}
engines: {node: '>= 16.20.2'}
dependencies:
comlink: 4.4.1
dev: false
/tlsn-js@0.1.0-alpha.6.2:
resolution: {integrity: sha512-6PANWQEFw48VFNfDgA017zcNRae2OutzNmE5Xcc/h6lwkZZ8MBZIFAFfz/WHZJ3fcFJumaKrG80gpomP6blZqw==}
/tlsn-js@0.1.0-alpha.7.1:
resolution: {integrity: sha512-EWdRp1VQBfdre8jehJgmDjtDvt01ZL1JWbcscctnFTLIIwMYS7IBxU07UYG0NMZFXeTE8PlrUDEVwEl1+vla+g==}
engines: {node: '>= 16.20.2'}
dependencies:
tlsn-wasm: 0.1.0-alpha.7.2
dev: false
/tlsn-wasm@0.1.0-alpha.7.2:
resolution: {integrity: sha512-NzrDfOxmFtMHDb4lmMsx6RaS6F+IVXEHxK0zow8jpnx+NryuJ+qnp4380Lq0uj61w/Yuq+yzOhzFe6Bpeo59dA==}
dev: false
/tmp@0.0.33:

Binary file not shown.

Binary file not shown.

View File

@@ -24,6 +24,7 @@ import {
} from '../../utils/plugins';
import { ErrorModal } from '../ErrorModal';
import classNames from 'classnames';
import DefaultPluginIcon from '../../assets/img/default-plugin-icon.png';
export default function PluginUploadInfo(): ReactElement {
const [error, showError] = useState('');
@@ -122,7 +123,7 @@ export function PluginInfoModal(props: {
<ModalHeader className="w-full p-2 border-gray-200 text-gray-500">
{header || (
<div className="flex flex-row items-end justify-start gap-2">
<img className="h-5" src={logo} alt="logo" />
<img className="h-5" src={logo || DefaultPluginIcon} alt="logo" />
<span className="font-semibold">{`Installing ${pluginContent.title}`}</span>
</div>
)}
@@ -132,7 +133,7 @@ export function PluginInfoModal(props: {
<>
<img
className="w-12 h-12"
src={pluginContent.icon}
src={pluginContent.icon || DefaultPluginIcon}
alt="Plugin Icon"
/>
<span className="text-3xl text-center">

View File

@@ -169,7 +169,7 @@ export function Plugin(props: {
<PluginInfoModalContent className="flex flex-col items-center cursor-default">
<img
className="w-12 h-12 mb-2"
src={config.icon}
src={config.icon || DefaultPluginIcon}
alt="Plugin Icon"
/>
<span className="text-3xl text-blue-600 font-semibold">

View File

@@ -28,6 +28,12 @@ const cookiesDb = db.sublevel<string, boolean>('cookies', {
const headersDb = db.sublevel<string, boolean>('headers', {
valueEncoding: 'json',
});
const appDb = db.sublevel<string, any>('app', {
valueEncoding: 'json',
});
enum AppDatabaseKey {
DefaultPluginsInstalled = 'DefaultPluginsInstalled',
}
export async function addNotaryRequest(
now = Date.now(),
@@ -372,3 +378,19 @@ export async function getHeadersByHost(host: string) {
}
return ret;
}
async function getDefaultPluginsInstalled(): Promise<boolean> {
return appDb.get(AppDatabaseKey.DefaultPluginsInstalled).catch(() => false);
}
export async function setDefaultPluginsInstalled(installed = false) {
return mutex.runExclusive(async () => {
await appDb.put(AppDatabaseKey.DefaultPluginsInstalled, installed);
});
}
export async function getAppState() {
return {
defaultPluginsInstalled: await getDefaultPluginsInstalled(),
};
}

View File

@@ -1,6 +1,8 @@
import { onBeforeRequest, onResponseStarted, onSendHeaders } from './handlers';
import { deleteCacheByTabId } from './cache';
import browser from 'webextension-polyfill';
import { getAppState, setDefaultPluginsInstalled } from './db';
import { installPlugin } from './plugins/utils';
(async () => {
browser.webRequest.onSendHeaders.addListener(
@@ -31,6 +33,19 @@ import browser from 'webextension-polyfill';
deleteCacheByTabId(tabId);
});
const { defaultPluginsInstalled } = await getAppState();
if (!defaultPluginsInstalled) {
try {
const twitterProfileUrl = browser.runtime.getURL('twitter_profile.wasm');
const discordDmUrl = browser.runtime.getURL('discord_dm.wasm');
await installPlugin(twitterProfileUrl);
await installPlugin(discordDmUrl);
} finally {
await setDefaultPluginsInstalled(true);
}
}
const { initRPC } = await import('./rpc');
await createOffscreenDocument();
initRPC();

View File

@@ -0,0 +1,29 @@
import { addPlugin, addPluginConfig, addPluginMetadata } from '../db';
import { getPluginConfig } from '../../../utils/misc';
export async function installPlugin(
urlOrBuffer: ArrayBuffer | string,
origin = '',
filePath = '',
metadata: {[key: string]: string} = {},
) {
let arrayBuffer;
if (typeof urlOrBuffer === 'string') {
const resp = await fetch(urlOrBuffer);
arrayBuffer = await resp.arrayBuffer();
} else {
arrayBuffer = urlOrBuffer;
}
const config = await getPluginConfig(arrayBuffer);
const hex = Buffer.from(arrayBuffer).toString('hex');
const hash = await addPlugin(hex);
await addPluginConfig(hash!, config);
await addPluginMetadata(hash!, {
...metadata,
origin,
filePath,
});
return hash;
}

View File

@@ -24,6 +24,8 @@ import {
getPlugins,
getCookiesByHost,
getHeadersByHost,
getAppState,
setDefaultPluginsInstalled,
} from './db';
import { addOnePlugin, removeOnePlugin } from '../../reducers/plugins';
import {
@@ -85,6 +87,8 @@ export enum BackgroundActiontype {
run_plugin_request = 'run_plugin_request',
run_plugin_response = 'run_plugin_response',
get_logging_level = 'get_logging_level',
get_app_state = 'get_app_state',
set_default_plugins_installed = 'set_default_plugins_installed',
}
export type BackgroundAction = {
@@ -192,6 +196,12 @@ export const initRPC = () => {
case BackgroundActiontype.get_logging_level:
getLoggingFilter().then(sendResponse);
return true;
case BackgroundActiontype.get_app_state:
getAppState().then(sendResponse);
return true;
case BackgroundActiontype.set_default_plugins_installed:
setDefaultPluginsInstalled(request.data).then(sendResponse);
return true;
default:
break;
}

View File

@@ -1,7 +1,7 @@
import { ContentScriptTypes, RPCClient } from './rpc';
import { RequestHistory } from '../Background/rpc';
import { PluginConfig, PluginMetadata } from '../../utils/misc';
import { Proof } from '../../utils/types';
import { PresentationJSON } from '../../utils/types';
const client = new RPCClient();
@@ -27,7 +27,7 @@ class TLSN {
return resp || [];
}
async getProof(id: string): Promise<Proof | null> {
async getProof(id: string): Promise<PresentationJSON | null> {
const resp = await client.call(ContentScriptTypes.get_proof, {
id,
});
@@ -52,7 +52,7 @@ class TLSN {
[k: string]: string;
};
},
): Promise<Proof> {
): Promise<PresentationJSON> {
const resp = await client.call(ContentScriptTypes.notarize, {
url,
method: requestOptions?.method,

View File

@@ -3,19 +3,20 @@ import * as Comlink from 'comlink';
import { OffscreenActionTypes } from './types';
import {
NotaryServer,
Prover as _Prover,
NotarizedSession as _NotarizedSession,
TlsProof as _TlsProof,
Prover as TProver,
Presentation as TPresentation,
Transcript,
} from 'tlsn-js';
import { verify } from 'tlsn-jsV5.3';
import { verify } from 'tlsn-js-v5';
import { urlify } from '../../utils/misc';
import { BackgroundActiontype } from '../Background/rpc';
import browser from 'webextension-polyfill';
import { Proof, ProofV1 } from '../../utils/types';
import { PresentationJSON } from '../../utils/types';
import { PresentationJSON as PresentationJSONa7 } from 'tlsn-js/build/types';
import { Method } from 'tlsn-js/wasm/pkg';
const { init, Prover, NotarizedSession, TlsProof }: any = Comlink.wrap(
const { init, Prover, Presentation }: any = Comlink.wrap(
new Worker(new URL('./worker.ts', import.meta.url)),
);
@@ -111,7 +112,7 @@ const Offscreen = () => {
}
case BackgroundActiontype.verify_prove_request: {
(async () => {
const proof: Proof = request.data.proof;
const proof: PresentationJSON = request.data.proof;
const result: { sent: string; recv: string } =
await verifyProof(proof);
@@ -194,7 +195,7 @@ async function createProof(options: {
id: string;
secretHeaders: string[];
secretResps: string[];
}): Promise<ProofV1> {
}): Promise<PresentationJSONa7> {
const {
url,
method = 'GET',
@@ -211,7 +212,7 @@ async function createProof(options: {
const hostname = urlify(url)?.hostname || '';
const notary = NotaryServer.from(notaryUrl);
const prover: _Prover = await new Prover({
const prover: TProver = await new Prover({
id,
serverDns: hostname,
maxSentData,
@@ -260,24 +261,21 @@ async function createProof(options: {
),
};
const session: _NotarizedSession = await new NotarizedSession(
await prover.notarize(commit),
);
const notarizationOutputs = await prover.notarize(commit);
const proofHex = await session.proof(commit);
const proof: ProofV1 = {
version: '1.0',
meta: {
notaryUrl,
websocketProxyUrl,
},
data: proofHex,
};
return proof;
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();
return presentationJSON;
}
async function verifyProof(
proof: Proof,
proof: PresentationJSON,
): Promise<{ sent: string; recv: string }> {
let result: { sent: string; recv: string };
@@ -286,12 +284,17 @@ async function verifyProof(
result = await verify(proof);
break;
}
case '1.0': {
const tlsProof: _TlsProof = await new TlsProof(proof.data);
result = await tlsProof.verify({
typ: 'P256',
key: await NotaryServer.from(proof.meta.notaryUrl).publicKey(),
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;
}
}

View File

@@ -1,9 +1,8 @@
import * as Comlink from 'comlink';
import init, { Prover, NotarizedSession, TlsProof } from 'tlsn-js';
import init, { Prover, Presentation } from 'tlsn-js';
Comlink.expose({
init,
Prover,
NotarizedSession,
TlsProof,
Presentation,
});

View File

@@ -28,7 +28,7 @@
],
"web_accessible_resources": [
{
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "content.bundle.js"],
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "content.bundle.js", "discord_dm.wasm", "twitter_profile.wasm"],
"matches": ["http://*/*", "https://*/*", "<all_urls>"]
}
],

View File

@@ -24,7 +24,7 @@ import {
InputBody,
FormBodyTable,
parseResponse,
} from '../../utils/requestbuilder';
} from '../../components/RequestBuilder';
enum TabType {
Params = 'Params',

View File

@@ -26,7 +26,7 @@ export async function getMaxRecv() {
}
export async function getNotaryApi() {
return await get(NOTARY_API_LS_KEY, 'https://notary.pse.dev/v0.1.0-alpha.6');
return await get(NOTARY_API_LS_KEY, 'https://notary.pse.dev/v0.1.0-alpha.7');
}
export async function getProxyApi() {

View File

@@ -1,18 +1,10 @@
export type Proof = ProofV0 | ProofV1;
import { PresentationJSON as PresentationJSONa7 } from 'tlsn-js/build/types';
export type ProofV0 = {
export type PresentationJSON = PresentationJSONa5 | PresentationJSONa7;
export type PresentationJSONa5 = {
version?: undefined;
session: any;
substrings: any;
notaryUrl: string;
};
export type ProofV1 = {
version: '1.0';
data: string;
meta: {
notaryUrl: string;
websocketProxyUrl: string;
pluginUrl?: string;
};
};

View File

@@ -40,7 +40,8 @@ var options = {
mode: process.env.NODE_ENV || "development",
ignoreWarnings: [
/Circular dependency between chunks with runtime/,
/ResizeObserver loop completed with undelivered notifications/
/ResizeObserver loop completed with undelivered notifications/,
/Should not import the named export/,
],
entry: {
@@ -82,6 +83,9 @@ var options = {
loader: "sass-loader",
options: {
sourceMap: true,
sassOptions: {
silenceDeprecations: ["legacy-js-api"],
}
},
},
],
@@ -199,31 +203,21 @@ var options = {
}),
new CopyWebpackPlugin({
patterns: [
// {
// from: "node_modules/tlsn-js/build/7.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
// {
// from: "node_modules/tlsn-js/build/250.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
// {
// from: "node_modules/tlsn-js/build/278.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
// {
// from: "node_modules/tlsn-js/build/349.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
{
from: "node_modules/tlsn-js/build",
to: path.join(__dirname, "build"),
force: true,
},
{
from: "src/assets/plugins/discord_dm.wasm",
to: path.join(__dirname, "build"),
force: true,
},
{
from: "src/assets/plugins/twitter_profile.wasm",
to: path.join(__dirname, "build"),
force: true,
},
],
}),
new HtmlWebpackPlugin({