Skip to content

Commit f669b53

Browse files
authored
Merge pull request #366 from csfloat/feature/tlsn-alpha14
Upgrades TLSN to Alpha 14
2 parents 1773ad6 + f972fcf commit f669b53

13 files changed

Lines changed: 454 additions & 3873 deletions

File tree

package-lock.json

Lines changed: 150 additions & 3797 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,6 @@
7070
"dependencies": {
7171
"buffer": "^6.0.3",
7272
"comlink": "^4.4.2",
73-
"tlsn-js": "0.1.0-alpha.12.0"
73+
"@csfloat/tlsn-wasm": "0.1.0-alpha.14"
7474
}
7575
}

src/environment.dev.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export const environment = {
22
csfloat_base_api_url: 'http://localhost:8080/api',
33
notary: {
4-
tlsn: 'https://notary.csfloat.com/tlsn/',
5-
ws: 'wss://notary.csfloat.com/ws/',
4+
tlsn: 'https://notary.csfloat.com',
5+
ws: 'wss://notary.csfloat.com/proxy',
66
loggingLevel: 'Error',
77
},
88
};

src/environment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export const environment = {
22
csfloat_base_api_url: 'https://csfloat.com/api',
33
notary: {
4-
tlsn: 'https://notary.csfloat.com/tlsn/',
5-
ws: 'wss://notary.csfloat.com/ws/',
4+
tlsn: 'https://notary.csfloat.com',
5+
ws: 'wss://notary.csfloat.com/proxy',
66
loggingLevel: 'Warn',
77
},
88
};

src/lib/bridge/handlers/notary_prove.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@ import {SendToOffscreen} from '../../../offscreen/client';
44
import {HasPermissions} from './has_permissions';
55
import {NotaryProveRequest} from '../../notary/types';
66
import {getAccessToken} from '../../alarms/access_token';
7-
import {PresentationJSON} from 'tlsn-js/build/types';
87
import {
98
OffscreenRequestType,
109
TLSNProveOffscreenRequest,
1110
TLSNProveOffscreenResponse,
11+
VerificationResults,
1212
} from '../../../offscreen/handlers/types';
1313
import {MaxConcurrency} from '../wrappers/cached';
1414

15-
export interface NotaryProveResponse {
16-
presentation: PresentationJSON;
17-
}
15+
export interface NotaryProveResponse extends VerificationResults {}
1816

1917
export const NotaryProve = MaxConcurrency(
2018
new SimpleHandler<NotaryProveRequest, NotaryProveResponse>(
@@ -31,7 +29,7 @@ export const NotaryProve = MaxConcurrency(
3129
throw new Error('must have api.steampowered.com permissions in order to prove API requests');
3230
}
3331

34-
const access_token = await getAccessToken(request.expected_steam_id);
32+
const access_token = await getAccessToken(request.meta?.expected_steam_id);
3533

3634
const response = await SendToOffscreen<TLSNProveOffscreenRequest, TLSNProveOffscreenResponse>(
3735
OffscreenRequestType.TLSN_PROVE,
@@ -42,7 +40,7 @@ export const NotaryProve = MaxConcurrency(
4240
);
4341

4442
return {
45-
presentation: response.presentation,
43+
payload: response.payload,
4644
};
4745
}
4846
),

src/lib/notary/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ interface ProveRequestPayloads {
4343
export type NotaryProveRequest = {
4444
[T in ProofType]: {
4545
type: T;
46-
expected_steam_id?: string;
46+
meta?: {
47+
expected_steam_id?: string;
48+
notary_token?: string;
49+
};
4750
} & ProveRequestPayloads[T];
4851
}[ProofType];

src/lib/notary/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export function getSteamRequestURL(request: NotaryProveRequest, access_token: Ac
3333
// Separate the 'type' property from the actual URL parameters
3434
const {type, ...params} = request;
3535
const baseUrl = PROOF_BASE_URLS[type];
36+
37+
// internal field
38+
delete params.meta;
39+
3640
const queryString = buildQueryString(Object.assign(params, {access_token: access_token.token}));
3741
return `${baseUrl}${queryString}`;
3842
}

src/offscreen/handlers/notary_prove.ts

Lines changed: 113 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ import {
77
import {getSteamRequestURL} from '../../lib/notary/utils';
88
import * as Comlink from 'comlink';
99
import {environment} from '../../environment';
10-
import {
11-
Prover as TProver,
12-
Presentation as TPresentation,
13-
Commit,
14-
NotaryServer,
15-
mapStringToRange,
16-
subtractRanges,
17-
} from 'tlsn-js';
18-
const {init, Prover, Presentation}: any = Comlink.wrap(new Worker(new URL('../worker.ts', import.meta.url)));
10+
import type {LoggingLevel, Method, Prover as TProver, Reveal} from '@csfloat/tlsn-wasm';
11+
import {NotarySessionClient} from './notary_session_client';
12+
import {Remote} from 'comlink';
13+
14+
const {init, Prover}: any = Comlink.wrap(new Worker(new URL('../worker.ts', import.meta.url)));
1915

2016
export async function initThreads() {
2117
await init({
22-
loggingLevel: environment.notary.loggingLevel,
18+
loggingLevel: environment.notary.loggingLevel as LoggingLevel,
2319
hardwareConcurrency: navigator.hardwareConcurrency,
20+
crateFilters: [
21+
{name: 'yamux', level: 'Info'},
22+
{name: 'uid_mux', level: 'Info'},
23+
],
2424
});
2525
}
2626

@@ -49,52 +49,65 @@ export const TLSNProveOffscreenHandler = new ClosableOffscreenHandler<
4949

5050
const maxRecvData = await calculateResponseSize(serverURL, 'GET', headers);
5151

52-
const notary = NotaryServer.from(environment.notary.tlsn);
53-
54-
const prover = (await new Prover({
55-
serverDns: 'api.steampowered.com',
56-
maxRecvData,
57-
maxSentData,
58-
})) as TProver;
59-
60-
await prover.setup(await notary.sessionUrl());
61-
62-
await prover.sendRequest(environment.notary.ws, {
63-
url: serverURL,
64-
method: 'GET',
65-
headers: {
66-
'Accept-Encoding': 'gzip',
67-
},
68-
});
69-
70-
const transcript = await prover.transcript();
71-
const {sent, recv} = transcript;
72-
73-
const commit: Commit = {
74-
sent: subtractRanges(
75-
{start: 0, end: sent.length},
76-
mapStringToRange([request.access_token.token], Buffer.from(sent).toString('utf-8'))
77-
),
78-
recv: [
79-
// No secrets in response body
80-
{start: 0, end: recv.length},
81-
],
82-
};
83-
const notarizationOutputs = await prover.notarize(commit);
84-
85-
const presentation = (await new Presentation({
86-
attestationHex: notarizationOutputs.attestation,
87-
secretsHex: notarizationOutputs.secrets,
88-
notaryUrl: notarizationOutputs.notaryUrl,
89-
websocketProxyUrl: notarizationOutputs.websocketProxyUrl,
90-
reveal: {...commit, server_identity: false},
91-
})) as TPresentation;
92-
93-
const presentationJSON = await presentation.json();
94-
95-
return {
96-
presentation: presentationJSON,
97-
};
52+
const maybeNotaryToken = request.notary_request.meta?.notary_token;
53+
54+
const session = await NotarySessionClient.create(maxRecvData, maxSentData, maybeNotaryToken);
55+
56+
try {
57+
// Create and setup prover
58+
const prover = (await new Prover({
59+
server_name: 'api.steampowered.com',
60+
max_recv_data: maxRecvData,
61+
max_sent_data: maxSentData,
62+
network: 'Latency',
63+
defer_decryption_from_start: true,
64+
})) as Remote<TProver>;
65+
66+
let verifierUrl = `${environment.notary.tlsn}/verifier?sessionId=${session.getID()}`;
67+
if (maybeNotaryToken) {
68+
verifierUrl += `&token=${maybeNotaryToken}`;
69+
}
70+
71+
await prover.setup(verifierUrl);
72+
73+
// Convert headers to Map<string, number[]> for WASM
74+
const headerMap = new Map<string, number[]>();
75+
for (const [key, value] of Object.entries(headers)) {
76+
headerMap.set(key, Buffer.from(value).toJSON().data);
77+
}
78+
79+
let wsUrl = `${environment.notary.ws}`;
80+
if (maybeNotaryToken) {
81+
wsUrl += `?token=${maybeNotaryToken}`;
82+
}
83+
84+
// Send HTTP request via proxy
85+
await prover.send_request(wsUrl, {
86+
uri: serverURL,
87+
method: 'GET' as Method,
88+
headers: headerMap,
89+
body: undefined,
90+
});
91+
92+
const {sent, recv} = await prover.transcript();
93+
const sentStr = Buffer.from(sent).toString('utf-8');
94+
95+
// Compute reveal ranges (hide the access token)
96+
const secretRanges = mapStringToRange([request.access_token.token], sentStr);
97+
const sentRanges = subtractRanges({start: 0, end: sent.length}, secretRanges);
98+
const recvRanges = [{start: 0, end: recv.length}];
99+
100+
// Set up listener before calling reveal
101+
const completedPromise = session.finalizeResults();
102+
103+
// Reveal to verifier
104+
const reveal: Reveal = {sent: sentRanges, recv: recvRanges, server_identity: true};
105+
await prover.reveal(reveal);
106+
107+
return await completedPromise;
108+
} finally {
109+
session.close();
110+
}
98111
},
99112
() => {
100113
// Require the offscreen to be re-initialized after every 5 prove requests
@@ -179,3 +192,47 @@ async function calculateResponseSize(
179192

180193
return headersSize + bodySize;
181194
}
195+
196+
/**
197+
* Computes ranges that hide specified strings from the transcript
198+
*/
199+
function subtractRanges(
200+
fullRange: {start: number; end: number},
201+
secretRanges: {start: number; end: number}[]
202+
): {start: number; end: number}[] {
203+
const sorted = [...secretRanges].sort((a, b) => a.start - b.start);
204+
const result: {start: number; end: number}[] = [];
205+
let currentPos = fullRange.start;
206+
207+
for (const secret of sorted) {
208+
if (secret.start > currentPos) {
209+
result.push({start: currentPos, end: secret.start});
210+
}
211+
currentPos = Math.max(currentPos, secret.end);
212+
}
213+
214+
if (currentPos < fullRange.end) {
215+
result.push({start: currentPos, end: fullRange.end});
216+
}
217+
218+
return result;
219+
}
220+
221+
/**
222+
* Maps strings to their byte ranges in the transcript
223+
*/
224+
function mapStringToRange(secrets: string[], transcript: string): {start: number; end: number}[] {
225+
const ranges: {start: number; end: number}[] = [];
226+
for (const secret of secrets) {
227+
if (!secret) {
228+
continue;
229+
}
230+
231+
let pos = 0;
232+
while ((pos = transcript.indexOf(secret, pos)) !== -1) {
233+
ranges.push({start: pos, end: pos + secret.length});
234+
pos += secret.length;
235+
}
236+
}
237+
return ranges;
238+
}

0 commit comments

Comments
 (0)