Files
Shadowbroker/frontend/src/mesh/meshSas.ts
T
anoracleofra-code 668ce16dc7 v0.9.6: InfoNet hashchain, Wormhole gate encryption, mesh reputation, 16 community contributors
Gate messages now propagate via the Infonet hashchain as encrypted blobs — every node syncs them
through normal chain sync while only Gate members with MLS keys can decrypt. Added mesh reputation
system, peer push workers, voluntary Wormhole opt-in for node participation, fork recovery,
killwormhole scripts, obfuscated terminology, and hardened the self-updater to protect encryption
keys and chain state during updates.

New features: Shodan search, train tracking, Sentinel Hub imagery, 8 new intelligence layers,
CCTV expansion to 11,000+ cameras across 6 countries, Mesh Terminal CLI, prediction markets,
desktop-shell scaffold, and comprehensive mesh test suite (215 frontend + backend tests passing).

Community contributors: @wa1id, @AlborzNazari, @adust09, @Xpirix, @imqdcr, @csysp, @suranyami,
@chr0n1x, @johan-martensson, @singularfailure, @smithbh, @OrfeoTerkuci, @deuza, @tm-const,
@Elhard1, @ttulttul
2026-03-26 05:58:04 -06:00

111 lines
2.1 KiB
TypeScript

import { deriveSharedSecret, getNodeIdentity } from '@/mesh/meshIdentity';
import { hmacSha256 } from '@/mesh/meshDeadDrop';
import { deriveWormholeSasPhrase, isWormholeReady } from '@/mesh/wormholeIdentityClient';
const SAS_WORDS = [
'able',
'acid',
'aged',
'also',
'area',
'army',
'atom',
'aunt',
'away',
'baby',
'back',
'bake',
'ball',
'band',
'bank',
'base',
'bean',
'bear',
'belt',
'bird',
'book',
'boom',
'boot',
'boss',
'bowl',
'burn',
'cafe',
'calm',
'camp',
'card',
'cash',
'cell',
'chat',
'city',
'clay',
'cloud',
'coin',
'cool',
'crew',
'data',
'dawn',
'desk',
'dome',
'door',
'dust',
'eagle',
'east',
'easy',
'echo',
'edge',
'envy',
'fair',
'farm',
'fast',
'file',
'flag',
'foam',
'fold',
'food',
'game',
'gate',
'gear',
'glow',
'gold',
];
function sasContext(peerId: string): string | null {
const identity = getNodeIdentity();
if (!identity) return null;
const ids = [identity.nodeId, peerId].sort().join('|');
return ids;
}
function bytesToWords(bytes: Uint8Array, count: number): string[] {
const out: string[] = [];
let acc = 0;
let accBits = 0;
for (const b of bytes) {
acc = (acc << 8) | b;
accBits += 8;
while (accBits >= 6 && out.length < count) {
const idx = (acc >> (accBits - 6)) & 0x3f;
out.push(SAS_WORDS[idx]);
accBits -= 6;
}
if (out.length >= count) break;
}
return out;
}
export async function deriveSasPhrase(peerId: string, peerDhPub: string, words: number = 8): Promise<string> {
if (await isWormholeReady()) {
const result = await deriveWormholeSasPhrase(peerId, peerDhPub, words).catch(() => null);
if (result?.ok && result.phrase) {
return String(result.phrase || '');
}
}
const ctx = sasContext(peerId);
if (!ctx) return '';
const secret = await deriveSharedSecret(peerDhPub);
const digest = await hmacSha256(secret, `sb_sas|v1|${ctx}`);
const bytes = new Uint8Array(digest);
const phrase = bytesToWords(bytes, words);
return phrase.join(' ');
}