72 lines
2.6 KiB
TypeScript
72 lines
2.6 KiB
TypeScript
|
|
import { ShadeSessionManager } from '../../packages/shade-core/src/index.js';
|
||
|
|
import { SubtleCryptoProvider, MemoryStorage } from '../../packages/shade-crypto-web/src/index.js';
|
||
|
|
import { ShadeWebSocket } from '../../packages/shade-transport/src/index.js';
|
||
|
|
|
||
|
|
const crypto = new SubtleCryptoProvider();
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
console.log('=== Shade WebSocket Tunnel ===\n');
|
||
|
|
|
||
|
|
// Set up Alice and Bob
|
||
|
|
const alice = new ShadeSessionManager(crypto, new MemoryStorage());
|
||
|
|
const bob = new ShadeSessionManager(crypto, new MemoryStorage());
|
||
|
|
await alice.initialize();
|
||
|
|
await bob.initialize();
|
||
|
|
|
||
|
|
// Establish session (skipping prekey server for brevity)
|
||
|
|
const otpks = await bob.generateOneTimePreKeys(5);
|
||
|
|
const bundle = await bob.createPreKeyBundle();
|
||
|
|
bundle.oneTimePreKey = { keyId: otpks[0].keyId, publicKey: otpks[0].keyPair.publicKey };
|
||
|
|
await alice.initSessionFromBundle('bob', bundle);
|
||
|
|
|
||
|
|
// Pair of WebSockets connected to each other
|
||
|
|
// Use Bun's built-in WebSocket server + client for the demo
|
||
|
|
const port = 19851;
|
||
|
|
const messages: string[] = [];
|
||
|
|
|
||
|
|
const server = Bun.serve({
|
||
|
|
port,
|
||
|
|
fetch(req, srv) {
|
||
|
|
if (srv.upgrade(req)) return;
|
||
|
|
return new Response('upgrade failed', { status: 500 });
|
||
|
|
},
|
||
|
|
websocket: {
|
||
|
|
async message(ws, message) {
|
||
|
|
// Server side decrypts (acts as Bob)
|
||
|
|
const bytes = message instanceof Uint8Array ? message : new Uint8Array(Buffer.from(message as string));
|
||
|
|
// Need to manually use ShadeWebSocket or do raw decoding
|
||
|
|
console.log(`[Bob received ${bytes.length} encrypted bytes]`);
|
||
|
|
// Forward to Bob's session
|
||
|
|
const { decodeEnvelope } = await import('../../packages/shade-proto/src/index.js');
|
||
|
|
const envelope = decodeEnvelope(bytes);
|
||
|
|
const plain = await bob.decrypt('alice', envelope);
|
||
|
|
messages.push(plain);
|
||
|
|
console.log(`[Bob decrypted]: "${plain}"`);
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
// Alice opens a WebSocket and uses ShadeWebSocket wrapper
|
||
|
|
const aliceWs = new WebSocket(`ws://localhost:${port}/`);
|
||
|
|
await new Promise((r) => aliceWs.addEventListener('open', r));
|
||
|
|
|
||
|
|
const aliceShade = new ShadeWebSocket(aliceWs, alice, 'bob');
|
||
|
|
|
||
|
|
console.log('Alice → Bob: "Hello over WebSocket"');
|
||
|
|
await aliceShade.send('Hello over WebSocket');
|
||
|
|
|
||
|
|
console.log('Alice → Bob: "Second message"');
|
||
|
|
await aliceShade.send('Second message');
|
||
|
|
|
||
|
|
// Wait for messages to arrive
|
||
|
|
await new Promise((r) => setTimeout(r, 200));
|
||
|
|
|
||
|
|
console.log('\nMessages Bob received:', messages);
|
||
|
|
console.log('\n=== Done ===');
|
||
|
|
|
||
|
|
aliceShade.close();
|
||
|
|
server.stop();
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch(console.error);
|