Files
Shade/examples/02-prekey-server/main.ts

79 lines
3.3 KiB
TypeScript
Raw Permalink Normal View History

import { ShadeSessionManager } from '../../packages/shade-core/src/index.js';
import { SubtleCryptoProvider, MemoryStorage } from '../../packages/shade-crypto-web/src/index.js';
import { createPrekeyServer, MemoryPrekeyStore } from '../../packages/shade-server/src/index.js';
import { ShadeFetchTransport } from '../../packages/shade-transport/src/index.js';
const crypto = new SubtleCryptoProvider();
async function main() {
console.log('=== Shade Prekey Server Demo ===\n');
// Start the prekey server
const store = new MemoryPrekeyStore();
const server = createPrekeyServer({ crypto, store });
const port = 19850;
const handle = Bun.serve({ port, fetch: server.fetch });
console.log(`Prekey server listening on http://localhost:${port}\n`);
try {
// ─── Bob registers ────────────────────────────────────
const bobMgr = new ShadeSessionManager(crypto, new MemoryStorage());
await bobMgr.initialize();
const bobIdentity = await new MemoryStorage().getIdentityKeyPair();
// Note: in real usage, get the storage instance you passed in. Simplified here:
const bobStorageRef = new MemoryStorage();
const bobMgr2 = new ShadeSessionManager(crypto, bobStorageRef);
await bobMgr2.initialize();
const bobKp = await bobStorageRef.getIdentityKeyPair();
const bobTransport = new ShadeFetchTransport({
baseUrl: `http://localhost:${port}`,
crypto,
signingPrivateKey: bobKp!.signingPrivateKey,
});
const bobOTPKs = await bobMgr2.generateOneTimePreKeys(10);
const bobBundle = await bobMgr2.createPreKeyBundle();
await bobTransport.register('bob', bobMgr2.getPublicIdentity(), bobBundle.signedPreKey, bobOTPKs);
console.log('Bob registered with prekey server (10 one-time prekeys uploaded)');
console.log('Bob fingerprint:', await bobMgr2.getIdentityFingerprint());
console.log();
// ─── Alice fetches Bob's bundle anonymously ───────────
const aliceMgr = new ShadeSessionManager(crypto, new MemoryStorage());
await aliceMgr.initialize();
console.log('Alice fingerprint:', await aliceMgr.getIdentityFingerprint());
// Alice doesn't need a signing key to fetch
const aliceTransport = new ShadeFetchTransport({
baseUrl: `http://localhost:${port}`,
crypto,
});
const fetchedBundle = await aliceTransport.fetchBundle('bob');
console.log('\nAlice fetched Bob\'s bundle');
console.log('Remaining one-time prekeys:', await aliceTransport.getKeyCount('bob'));
await aliceMgr.initSessionFromBundle('bob', fetchedBundle);
// ─── Encrypted conversation ───────────────────────────
console.log('\n=== Encrypted conversation ===');
const env1 = await aliceMgr.encrypt('bob', 'Hello via prekey server!');
console.log('→ Alice sends encrypted message');
const plain1 = await bobMgr2.decrypt('alice', env1);
console.log(`← Bob decrypts: "${plain1}"`);
const env2 = await bobMgr2.encrypt('alice', 'Got your message!');
console.log('\n→ Bob replies (DH ratchet step)');
const plain2 = await aliceMgr.decrypt('bob', env2);
console.log(`← Alice decrypts: "${plain2}"`);
console.log('\n=== Done ===');
} finally {
handle.stop();
}
}
main().catch(console.error);