first chrome implementation
This commit is contained in:
283
projects/chrome/src/background-common.ts
Normal file
283
projects/chrome/src/background-common.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
/* 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<void> {
|
||||
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<string> {
|
||||
return await nip04.encrypt(
|
||||
NostrHelper.hex2bytes(privkey),
|
||||
peerPubkey,
|
||||
plaintext
|
||||
);
|
||||
};
|
||||
|
||||
export const nip04Decrypt = async function (
|
||||
privkey: string,
|
||||
peerPubkey: string,
|
||||
ciphertext: string
|
||||
): Promise<string> {
|
||||
return await nip04.decrypt(
|
||||
NostrHelper.hex2bytes(privkey),
|
||||
peerPubkey,
|
||||
ciphertext
|
||||
);
|
||||
};
|
||||
|
||||
const encryptPermission = async function (
|
||||
permission: Permission_DECRYPTED,
|
||||
iv: string,
|
||||
password: string
|
||||
): Promise<Permission_ENCRYPTED> {
|
||||
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<string> {
|
||||
return await CryptoHelper.encrypt(value, iv, password);
|
||||
};
|
||||
Reference in New Issue
Block a user