/** * TOFU Key Pinning Service * Stores seller contact keys on first use, warns on key changes */ const PINNED_KEYS_STORAGE = 'kashilo_pinned_keys' class KeyPinningService { constructor() { this.pinnedKeys = this.load() } load() { try { return JSON.parse(localStorage.getItem(PINNED_KEYS_STORAGE)) || {} } catch { return {} } } save() { localStorage.setItem(PINNED_KEYS_STORAGE, JSON.stringify(this.pinnedKeys)) } /** * Check a listing's contact key against the pinned key * @param {string} listingId * @param {string} contactPublicKey * @returns {'ok'|'new'|'changed'} * - 'ok': key matches pinned key * - 'new': first time seeing this listing, key is now pinned * - 'changed': key differs from pinned key (possible attack) */ check(listingId, contactPublicKey) { if (!listingId || !contactPublicKey) return 'new' const pinned = this.pinnedKeys[listingId] if (!pinned) { this.pin(listingId, contactPublicKey) return 'new' } if (pinned === contactPublicKey) return 'ok' return 'changed' } pin(listingId, contactPublicKey) { this.pinnedKeys[listingId] = contactPublicKey this.save() } /** * Accept a changed key (user explicitly trusts the new key) */ acceptChange(listingId, newPublicKey) { this.pinnedKeys[listingId] = newPublicKey this.save() } /** * Remove pin for a listing */ unpin(listingId) { delete this.pinnedKeys[listingId] this.save() } clear() { this.pinnedKeys = {} localStorage.removeItem(PINNED_KEYS_STORAGE) } } export const keyPinningService = new KeyPinningService()