/* eslint-disable @typescript-eslint/no-explicit-any */ import { BrowserSessionData, BrowserSyncData, BrowserSyncFlow, CryptoHelper, GootiMetaData, Identity_DECRYPTED, Nip07Method, Nip07MethodPolicy, NostrHelper, Permission_DECRYPTED, Permission_ENCRYPTED, } from '@common'; import { ChromeMetaHandler } from './app/common/data/chrome-meta-handler'; import { Event, EventTemplate, finalizeEvent, nip04 } from 'nostr-tools'; export const debug = function (message: any) { const dateString = new Date().toISOString(); console.log(`[Gooti - ${dateString}]: ${JSON.stringify(message)}`); }; export type PromptResponse = | 'reject' | 'reject-once' | 'approve' | 'approve-once'; export interface PromptResponseMessage { id: string; response: PromptResponse; } export interface BackgroundRequestMessage { method: Nip07Method; params: any; host: string; } export const getBrowserSessionData = async function (): Promise< BrowserSessionData | undefined > { const browserSessionData = await chrome.storage.session.get(null); if (Object.keys(browserSessionData).length === 0) { return undefined; } return browserSessionData as BrowserSessionData; }; export const getBrowserSyncData = async function (): Promise< BrowserSyncData | undefined > { const gootiMetaHandler = new ChromeMetaHandler(); const gootiMetaData = (await gootiMetaHandler.loadFullData()) as GootiMetaData; let browserSyncData: BrowserSyncData | undefined; if (gootiMetaData.syncFlow === BrowserSyncFlow.NO_SYNC) { browserSyncData = (await chrome.storage.local.get(null)) as BrowserSyncData; } else if (gootiMetaData.syncFlow === BrowserSyncFlow.BROWSER_SYNC) { browserSyncData = (await chrome.storage.sync.get(null)) as BrowserSyncData; } return browserSyncData; }; export const savePermissionsToBrowserSyncStorage = async function ( permissions: Permission_ENCRYPTED[] ): Promise { const gootiMetaHandler = new ChromeMetaHandler(); const gootiMetaData = (await gootiMetaHandler.loadFullData()) as GootiMetaData; if (gootiMetaData.syncFlow === BrowserSyncFlow.NO_SYNC) { await chrome.storage.local.set({ permissions }); } else if (gootiMetaData.syncFlow === BrowserSyncFlow.BROWSER_SYNC) { await chrome.storage.sync.set({ permissions }); } }; export const checkPermissions = function ( browserSessionData: BrowserSessionData, identity: Identity_DECRYPTED, host: string, method: Nip07Method, params: any ): boolean | undefined { const permissions = browserSessionData.permissions.filter( (x) => x.identityId === identity.id && x.host === host && x.method === method ); if (permissions.length === 0) { return undefined; } if (method === 'getPublicKey') { // No evaluation of params required. return permissions.every((x) => x.methodPolicy === 'allow'); } if (method === 'getRelays') { // No evaluation of params required. return permissions.every((x) => x.methodPolicy === 'allow'); } if (method === 'signEvent') { // Evaluate params. const eventTemplate = params as EventTemplate; if ( permissions.find( (x) => x.methodPolicy === 'allow' && typeof x.kind === 'undefined' ) ) { return true; } if ( permissions.some( (x) => x.methodPolicy === 'allow' && x.kind === eventTemplate.kind ) ) { return true; } if ( permissions.some( (x) => x.methodPolicy === 'deny' && x.kind === eventTemplate.kind ) ) { return false; } return undefined; } if (method === 'nip04.encrypt') { // No evaluation of params required. return permissions.every((x) => x.methodPolicy === 'allow'); } if (method === 'nip04.decrypt') { // No evaluation of params required. return permissions.every((x) => x.methodPolicy === 'allow'); } return undefined; }; export const storePermission = async function ( browserSessionData: BrowserSessionData, identity: Identity_DECRYPTED, host: string, method: Nip07Method, methodPolicy: Nip07MethodPolicy, kind?: number ) { const browserSyncData = await getBrowserSyncData(); if (!browserSyncData) { throw new Error(`Could not retrieve sync data`); } const permission: Permission_DECRYPTED = { id: crypto.randomUUID(), identityId: identity.id, host, method, methodPolicy, kind, }; // Store session data await chrome.storage.session.set({ permissions: [...browserSessionData.permissions, permission], }); // Encrypt permission to store in sync storage (depending on sync flow). const encryptedPermission = await encryptPermission( permission, browserSessionData.iv, browserSessionData.vaultPassword as string ); await savePermissionsToBrowserSyncStorage([ ...browserSyncData.permissions, encryptedPermission, ]); }; export const getPosition = async function (width: number, height: number) { let left = 0; let top = 0; try { const lastFocused = await chrome.windows.getLastFocused(); if ( lastFocused && lastFocused.top !== undefined && lastFocused.left !== undefined && lastFocused.width !== undefined && lastFocused.height !== undefined ) { // Position window in the center of the lastFocused window top = Math.round(lastFocused.top + (lastFocused.height - height) / 2); left = Math.round(lastFocused.left + (lastFocused.width - width) / 2); } else { console.error('Last focused window properties are undefined.'); } } catch (error) { console.error('Error getting window position:', error); } return { top, left, }; }; export const signEvent = function ( eventTemplate: EventTemplate, privkey: string ): Event { return finalizeEvent(eventTemplate, NostrHelper.hex2bytes(privkey)); }; export const nip04Encrypt = async function ( privkey: string, peerPubkey: string, plaintext: string ): Promise { return await nip04.encrypt( NostrHelper.hex2bytes(privkey), peerPubkey, plaintext ); }; export const nip04Decrypt = async function ( privkey: string, peerPubkey: string, ciphertext: string ): Promise { return await nip04.decrypt( NostrHelper.hex2bytes(privkey), peerPubkey, ciphertext ); }; const encryptPermission = async function ( permission: Permission_DECRYPTED, iv: string, password: string ): Promise { const encryptedPermission: Permission_ENCRYPTED = { id: await encrypt(permission.id, iv, password), identityId: await encrypt(permission.identityId, iv, password), host: await encrypt(permission.host, iv, password), method: await encrypt(permission.method, iv, password), methodPolicy: await encrypt(permission.methodPolicy, iv, password), }; if (typeof permission.kind !== 'undefined') { encryptedPermission.kind = await encrypt( permission.kind.toString(), iv, password ); } return encryptedPermission; }; const encrypt = async function ( value: string, iv: string, password: string ): Promise { return await CryptoHelper.encrypt(value, iv, password); };