import { ShadeSessionManager } from '../../packages/shade-core/src/index.js'; import { SubtleCryptoProvider, MemoryStorage } from '../../packages/shade-crypto-web/src/index.js'; const crypto = new SubtleCryptoProvider(); async function main() { console.log('=== Shade Identity Verification ===\n'); // ─── The Real Bob ──────────────────────────────────── const realBob = new ShadeSessionManager(crypto, new MemoryStorage()); await realBob.initialize(); console.log('Real Bob fingerprint:'); console.log(' Full: ', await realBob.getIdentityFingerprint()); console.log(' Short: ', await realBob.getShortFingerprint()); console.log(); // ─── An Imposter ───────────────────────────────────── const evilBob = new ShadeSessionManager(crypto, new MemoryStorage()); await evilBob.initialize(); console.log('Evil Bob (imposter) fingerprint:'); console.log(' Full: ', await evilBob.getIdentityFingerprint()); console.log(' Short: ', await evilBob.getShortFingerprint()); console.log(); console.log('━━━ The fingerprints are completely different ━━━'); console.log('In a real scenario, Alice would call Real Bob on the phone,'); console.log('read her safety number, and Real Bob would read his back.'); console.log('If the numbers match, Alice can trust that her session'); console.log('is talking to Real Bob and not Evil Bob.\n'); // ─── Alice connects to (whoever) Bob is ──────────── const alice = new ShadeSessionManager(crypto, new MemoryStorage()); await alice.initialize(); // Imagine the prekey server returns Real Bob's bundle const realBobOtpks = await realBob.generateOneTimePreKeys(5); const realBobBundle = await realBob.createPreKeyBundle(); realBobBundle.oneTimePreKey = { keyId: realBobOtpks[0].keyId, publicKey: realBobOtpks[0].keyPair.publicKey, }; await alice.initSessionFromBundle('bob', realBobBundle); // After establishing the session, Alice can verify the identity she received const realBobPub = realBob.getPublicIdentity(); const evilBobPub = evilBob.getPublicIdentity(); console.log('Alice verifies who she actually connected to:'); console.log(' Is it Real Bob? ', await alice.verifyRemoteIdentity('bob', realBobPub.dhKey)); console.log(' Is it Evil Bob? ', await alice.verifyRemoteIdentity('bob', evilBobPub.dhKey)); console.log(); // ─── Identity Change Scenario ───────────────────────── console.log('━━━ Bob legitimately rotates his identity ━━━'); const newBundle = await realBob.rotateIdentity(); console.log('New Bob fingerprint:', await realBob.getIdentityFingerprint()); console.log(); console.log('Alice fetches the new bundle and verifies it matches'); console.log('expected new identity (after out-of-band confirmation).'); // Alice would compare fingerprints again, then accept the change: await alice.acceptIdentityChange('bob', newBundle.identityDHKey); console.log('Alice accepted the new identity.'); console.log(); console.log('=== Done ==='); } main().catch(console.error);