diff --git a/holo-key-manager-extension/build-scripts/fixBackgroundScriptForSigning.cjs b/holo-key-manager-extension/build-scripts/fixBackgroundScriptForSigning.cjs new file mode 100644 index 0000000..6dbd975 --- /dev/null +++ b/holo-key-manager-extension/build-scripts/fixBackgroundScriptForSigning.cjs @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +// replaceForFirefox.cjs +const fs = require('fs'); +const path = require('path'); +const backgroundScriptPath = path.resolve(__dirname, '../build/scripts/background.js'); + +fs.readFile(backgroundScriptPath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading the background script file:', err); + return; + } + + let modifiedData = data.replace(/global\./g, 'self.'); + + modifiedData = modifiedData.replace( + /var normalize = bufferUtil\.normalize;/g, + `var normalize = function (buffer) { + if (Buffer.isBuffer(buffer)) return buffer; + return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength); +};` + ); + + fs.writeFile(backgroundScriptPath, modifiedData, 'utf8', (err) => { + if (err) { + console.error('Error writing the modified background script file:', err); + return; + } + + console.log('Background script file modified successfully.'); + }); +}); diff --git a/holo-key-manager-extension/package.json b/holo-key-manager-extension/package.json index f3c1e85..a36dedc 100644 --- a/holo-key-manager-extension/package.json +++ b/holo-key-manager-extension/package.json @@ -3,13 +3,15 @@ "version": "0.0.3", "private": true, "scripts": { - "build": "pnpm build:vite && pnpm build:script && pnpm build:removeInline", - "buildDev": "pnpm build:vite && pnpm build:script && pnpm build:removeInline && pnpm build:buildForFirefoxDev", + "buildCommon": "pnpm build:vite && pnpm build:script && pnpm build:fixBackgroundScriptForSigning && pnpm build:removeInline", + "build": "pnpm buildCommon", + "buildDev": "pnpm buildCommon && pnpm build:buildForFirefoxDev", "build:vite": "vite build", "build:script": "cd scripts && pnpm build && cd ..", "build:removeInline": "node build-scripts/removeInlineScript.cjs", "build:replaceForFirefox": "node build-scripts/replaceForFirefox.cjs", "build:buildForFirefoxDev": "node build-scripts/devFirefox.cjs", + "build:fixBackgroundScriptForSigning": "node build-scripts/fixBackgroundScriptForSigning.cjs", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" diff --git a/holo-key-manager-extension/scripts/background.ts b/holo-key-manager-extension/scripts/background.ts index 6593a83..e783ce6 100644 --- a/holo-key-manager-extension/scripts/background.ts +++ b/holo-key-manager-extension/scripts/background.ts @@ -8,6 +8,7 @@ import { SENDER_EXTENSION, SIGN_IN, SIGN_MESSAGE, + SIGN_MESSAGE_SUCCESS, SIGN_OUT, SIGN_OUT_SUCCESS, SIGN_UP, @@ -22,6 +23,12 @@ import { type WindowProperties } from '@shared/types'; +import { signMessageLogic } from './helpers'; + +// Important: This script is modified by a +// post-build script (build-scripts/fixBackgroundScriptForSigning.cjs) +// which may alter some of its intended behavior. + let windowId: number | undefined; type SendResponse = (response?: Message) => void; @@ -149,12 +156,14 @@ const processMessage = async (message: Message, sendResponse: SendResponse) => { }) : sendResponseWithSender({ action: NO_KEY_FOR_HAPP }); case SIGN_MESSAGE: - return (await isAuthenticated(parsedMessage.data.payload.happId)) - ? createOrUpdateDataResponseWindow(sendResponseWithSender, { - action: parsedMessage.data.action, - payload: parsedMessage.data.payload - }) - : sendResponseWithSender({ action: NOT_AUTHENTICATED }); + if (await isAuthenticated(parsedMessage.data.payload.happId)) { + const signedMessage = await signMessageLogic(parsedMessage.data.payload); + return sendResponseWithSender({ + action: SIGN_MESSAGE_SUCCESS, + payload: signedMessage + }); + } + return sendResponseWithSender({ action: NOT_AUTHENTICATED }); case SIGN_OUT: signOut(parsedMessage.data.payload.happId); return sendResponseWithSender({ action: SIGN_OUT_SUCCESS }); diff --git a/holo-key-manager-extension/scripts/helpers.ts b/holo-key-manager-extension/scripts/helpers.ts new file mode 100644 index 0000000..c794264 --- /dev/null +++ b/holo-key-manager-extension/scripts/helpers.ts @@ -0,0 +1,61 @@ +import { AUTHENTICATED_APPS_LIST, SESSION } from '@shared/const'; +import { base64ToUint8Array, uint8ArrayToBase64 } from '@shared/helpers'; +import { getSessionKey, storageService } from '@shared/services'; +import { + AuthenticatedAppsListSchema, + type MessageToSign, + SuccessMessageSignedSchema +} from '@shared/types'; +// @ts-expect-error no types for hcSeedBundle +import * as hcSeedBundle from 'hcSeedBundle'; + +export const signMessageLogic = async ({ message, happId }: MessageToSign) => { + const authenticatedAppsListData = await storageService.getWithoutCallback({ + key: AUTHENTICATED_APPS_LIST, + area: SESSION + }); + + const parsedAuthenticatedAppsListData = + AuthenticatedAppsListSchema.safeParse(authenticatedAppsListData); + + if (!parsedAuthenticatedAppsListData.success) { + throw new Error('Failed to parse authenticated apps list data'); + } + + const index = parsedAuthenticatedAppsListData.data[happId]; + const sessionKey = await getSessionKey(); + + if (!sessionKey.success) { + throw new Error('Session data not found'); + } + + await hcSeedBundle.seedBundleReady; + + const cipherList = hcSeedBundle.UnlockedSeedBundle.fromLocked( + base64ToUint8Array(sessionKey.data) + ); + + if (!(cipherList[0] instanceof hcSeedBundle.LockedSeedCipherPwHash)) { + throw new Error('Expecting PwHash'); + } + + const pw = new TextEncoder().encode(SESSION); + const keyUnlocked = cipherList[0].unlock(hcSeedBundle.parseSecret(pw)); + + const appKey = keyUnlocked.derive(index); + + const signedMessage = appKey.sign(message); + + keyUnlocked.zero(); + appKey.zero(); + + const validatedSchema = SuccessMessageSignedSchema.safeParse({ + signature: uint8ArrayToBase64(signedMessage) + }); + + if (!validatedSchema.success) { + throw new Error('Invalid message'); + } + + return validatedSchema.data; +}; diff --git a/holo-key-manager-extension/scripts/package.json b/holo-key-manager-extension/scripts/package.json index 7e8e0d9..78c6a88 100644 --- a/holo-key-manager-extension/scripts/package.json +++ b/holo-key-manager-extension/scripts/package.json @@ -5,5 +5,13 @@ "type": "module", "scripts": { "build": "rollup -c" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-inject": "^5.0.5", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^5.0.5", + "buffer": "^6.0.3", + "rollup-plugin-node-polyfills": "^0.2.1" } } diff --git a/holo-key-manager-extension/scripts/rollup.config.js b/holo-key-manager-extension/scripts/rollup.config.js index 2188eb5..fbbb756 100644 --- a/holo-key-manager-extension/scripts/rollup.config.js +++ b/holo-key-manager-extension/scripts/rollup.config.js @@ -1,4 +1,8 @@ -import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import inject from '@rollup/plugin-inject'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import replace from '@rollup/plugin-replace'; +import nodePolyfills from 'rollup-plugin-node-polyfills'; import tscAlias from 'rollup-plugin-tsc-alias'; import typescript from 'rollup-plugin-typescript2'; @@ -8,7 +12,20 @@ const createConfig = (input, file) => ({ file, format: 'esm' }, - plugins: [tscAlias(), typescript(), resolve()] + plugins: [ + tscAlias(), + nodePolyfills(), + nodeResolve({ preferBuiltins: true }), + commonjs(), + inject({ + Buffer: ['buffer', 'Buffer'] + }), + replace({ + preventAssignment: true, + global: 'self' + }), + typescript() + ] }); export default [ diff --git a/holo-key-manager-extension/scripts/tsconfig.json b/holo-key-manager-extension/scripts/tsconfig.json index fb03610..31e0b3a 100644 --- a/holo-key-manager-extension/scripts/tsconfig.json +++ b/holo-key-manager-extension/scripts/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "outDir": "../build" }, - "include": ["background.ts", "content.ts"] + "include": ["background.ts", "content.ts", "helpers.ts"] } diff --git a/holo-key-manager-extension/src/lib/helpers/queries.ts b/holo-key-manager-extension/src/lib/helpers/queries.ts index 5855abc..96296d3 100644 --- a/holo-key-manager-extension/src/lib/helpers/queries.ts +++ b/holo-key-manager-extension/src/lib/helpers/queries.ts @@ -7,19 +7,16 @@ import { BACKGROUND_SCRIPT_RECEIVED_DATA, LOCAL, PASSWORD, - SESSION, - SESSION_STORAGE_KEY + SESSION } from '$shared/const'; import { parseMessageSchema, uint8ArrayToBase64 } from '$shared/helpers'; -import { sendMessage, storageService } from '$shared/services'; +import { getSessionKey, sendMessage, storageService } from '$shared/services'; import { AppsListSchema, AuthenticatedAppsListSchema, HashSaltSchema, type Message, - PubKeySchema, - SessionStateSchema, - SuccessMessageSignedSchema + PubKeySchema } from '$shared/types'; export const handleSuccess = (queryClient: QueryClient, queryKey: string[]) => () => @@ -33,14 +30,6 @@ export const getPassword = async () => { return HashSaltSchema.safeParse(data); }; -export const getSessionKey = async () => { - const data = await storageService.getWithoutCallback({ - key: SESSION_STORAGE_KEY, - area: SESSION - }); - return SessionStateSchema.safeParse(data); -}; - export const fetchAndParseAppsList = async () => { const appsListData = await storageService.getWithoutCallback({ key: APPS_LIST, @@ -103,29 +92,3 @@ export const deriveSignPubKey = async (newIndex: number) => { return validatedSchema.data; }; - -export const signMessage = async (message: string, index: number) => { - const sessionKey = await getSessionKey(); - - if (!sessionKey.success) { - throw new Error('Session data not found'); - } - - const keyUnlocked = await unlockKey(sessionKey.data, SESSION); - const appKey = keyUnlocked.derive(index); - - const signedMessage = appKey.sign(message); - - keyUnlocked.zero(); - appKey.zero(); - - const validatedSchema = SuccessMessageSignedSchema.safeParse({ - signature: uint8ArrayToBase64(signedMessage) - }); - - if (!validatedSchema.success) { - throw new Error('Invalid message'); - } - - return validatedSchema.data; -}; diff --git a/holo-key-manager-extension/src/lib/queries/applicationQueries.ts b/holo-key-manager-extension/src/lib/queries/applicationQueries.ts index 9048059..9f33ca1 100644 --- a/holo-key-manager-extension/src/lib/queries/applicationQueries.ts +++ b/holo-key-manager-extension/src/lib/queries/applicationQueries.ts @@ -5,8 +5,7 @@ import { fetchAndParseAppsList, fetchAuthenticatedAppsList, handleSuccess, - sendMessageAndHandleResponse, - signMessage + sendMessageAndHandleResponse } from '$helpers'; import { APPLICATION_KEYS, @@ -17,7 +16,6 @@ import { SENDER_EXTENSION, SESSION, SIGN_IN_SUCCESS, - SIGN_MESSAGE_SUCCESS, SIGN_UP_SUCCESS } from '$shared/const'; import { storageService } from '$shared/services'; @@ -121,26 +119,6 @@ export function createSignInWithKeyMutation(queryClient: QueryClient) { }); } -export function createMessageMutation() { - return createMutation({ - mutationFn: async (signMessageData: { happId: string; message: string }) => { - const { happId, message } = signMessageData; - - const currentAuthenticatedAppsList = await fetchAuthenticatedAppsList(happId); - - const index = currentAuthenticatedAppsList[happId]; - - const signedMessage = await signMessage(message, index); - - await sendMessageAndHandleResponse({ - sender: SENDER_EXTENSION, - action: SIGN_MESSAGE_SUCCESS, - payload: signedMessage - }); - } - }); -} - export function createSignedInApplicationKeysIndexQuery() { return (happId: string) => { return createQuery({ diff --git a/holo-key-manager-extension/src/lib/queries/extensionQueries.ts b/holo-key-manager-extension/src/lib/queries/extensionQueries.ts index 6b62433..c2c1897 100644 --- a/holo-key-manager-extension/src/lib/queries/extensionQueries.ts +++ b/holo-key-manager-extension/src/lib/queries/extensionQueries.ts @@ -1,9 +1,9 @@ import { createMutation, createQuery, QueryClient } from '@tanstack/svelte-query'; -import { getSessionKey, handleSuccess } from '$helpers'; +import { handleSuccess } from '$helpers'; import { unlockKey } from '$services'; import { DEVICE_KEY, LOCAL, PASSWORD, SESSION_DATA_KEY, SETUP_KEY } from '$shared/const'; -import { isSetupComplete, storageService } from '$shared/services'; +import { getSessionKey, isSetupComplete, storageService } from '$shared/services'; import { EncryptedDeviceKeySchema, HashSaltSchema } from '$shared/types'; import { deviceKeyContentStore, passphraseStore } from '$stores'; diff --git a/holo-key-manager-extension/src/lib/queries/index.ts b/holo-key-manager-extension/src/lib/queries/index.ts index f9be748..ef64b65 100644 --- a/holo-key-manager-extension/src/lib/queries/index.ts +++ b/holo-key-manager-extension/src/lib/queries/index.ts @@ -3,7 +3,6 @@ import { useQueryClient } from '@tanstack/svelte-query'; import { createApplicationKeyMutation, createApplicationKeysQuery, - createMessageMutation, createSignInWithKeyMutation } from './applicationQueries'; import { @@ -31,7 +30,6 @@ export function appQueries() { const setupPasswordQuery = createSetupPasswordQuery(); const applicationKeysQueryFunction = createApplicationKeysQuery(); const signInMutation = createSignInMutation(queryClient); - const messageMutation = createMessageMutation(); const storeDeviceKey = createStoreDeviceKey(queryClient); const recoverDeviceKeyMutation = createRecoverDeviceKeyMutation(); const applicationKeyMutation = createApplicationKeyMutation(queryClient); @@ -49,7 +47,6 @@ export function appQueries() { recoverDeviceKeyMutation, passwordAndStoreDeviceKeyMutation, applicationKeysQueryFunction, - signInWithKeyMutation, - messageMutation + signInWithKeyMutation }; } diff --git a/holo-key-manager-extension/src/lib/services/index.ts b/holo-key-manager-extension/src/lib/services/index.ts index bfd61b7..3fff6c8 100644 --- a/holo-key-manager-extension/src/lib/services/index.ts +++ b/holo-key-manager-extension/src/lib/services/index.ts @@ -1 +1 @@ -export * from './generate-keys'; +export * from './manage-keys'; diff --git a/holo-key-manager-extension/src/lib/services/generate-keys.ts b/holo-key-manager-extension/src/lib/services/manage-keys.ts similarity index 100% rename from holo-key-manager-extension/src/lib/services/generate-keys.ts rename to holo-key-manager-extension/src/lib/services/manage-keys.ts diff --git a/holo-key-manager-extension/src/lib/stores/keys-store.ts b/holo-key-manager-extension/src/lib/stores/keys-store.ts index 69c6b59..bd0e0fb 100644 --- a/holo-key-manager-extension/src/lib/stores/keys-store.ts +++ b/holo-key-manager-extension/src/lib/stores/keys-store.ts @@ -1,9 +1,8 @@ import { writable } from 'svelte/store'; +import { generateKeys } from '$services'; import type { KeysState } from '$types'; -import { generateKeys } from '../services/generate-keys'; - const initKeysStore = () => { const initialState: KeysState = { keys: { diff --git a/holo-key-manager-extension/src/routes/+page.svelte b/holo-key-manager-extension/src/routes/+page.svelte index d90a055..48df6f9 100644 --- a/holo-key-manager-extension/src/routes/+page.svelte +++ b/holo-key-manager-extension/src/routes/+page.svelte @@ -19,7 +19,7 @@ onMount(async () => { if (isChromePermissionsSafe()) { const permissions = await chrome.permissions.getAll(); - permissionGranted = permissions.origins?.includes('*://localhost/*') ?? false; + permissionGranted = permissions.origins?.includes('') ?? false; } }); diff --git a/holo-key-manager-extension/src/routes/webapp-extension/setup/+page.svelte b/holo-key-manager-extension/src/routes/webapp-extension/setup/+page.svelte index fa12161..8639309 100644 --- a/holo-key-manager-extension/src/routes/webapp-extension/setup/+page.svelte +++ b/holo-key-manager-extension/src/routes/webapp-extension/setup/+page.svelte @@ -4,7 +4,7 @@ import { goto } from '$app/navigation'; import { ActionPage } from '$components'; import { extractDetailsFromUrl } from '$helpers'; - import { SIGN_IN, SIGN_MESSAGE } from '$shared/const'; + import { SIGN_IN } from '$shared/const'; const submitFormData = () => { goto(`create-key?${new URLSearchParams(window.location.search)}`); @@ -14,9 +14,6 @@ if ($extractDetailsFromUrl.action === SIGN_IN) { goto(`select-key-to-login?${new URLSearchParams(window.location.search)}`); } - if ($extractDetailsFromUrl.action === SIGN_MESSAGE) { - goto(`sign-message?${new URLSearchParams(window.location.search)}`); - } }); diff --git a/holo-key-manager-extension/src/routes/webapp-extension/sign-message/+page.svelte b/holo-key-manager-extension/src/routes/webapp-extension/sign-message/+page.svelte deleted file mode 100644 index 87425a3..0000000 --- a/holo-key-manager-extension/src/routes/webapp-extension/sign-message/+page.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - -
- -
-

Sign message

-
-
-
-
diff --git a/holo-key-manager-extension/static/manifest.json b/holo-key-manager-extension/static/manifest.json index 1911870..1cf6882 100644 --- a/holo-key-manager-extension/static/manifest.json +++ b/holo-key-manager-extension/static/manifest.json @@ -1,7 +1,7 @@ { "name": "Holo key manager", "description": "A browser extension to manage holo keys", - "version": "0.0.47", + "version": "0.0.48", "manifest_version": 3, "action": { "default_title": "Holo key manager", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2b1b91..0abff9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,11 +128,7 @@ importers: specifier: ^5.2.4 version: 5.2.4 - holo-key-manager-js-client: - dependencies: - dotenv: - specifier: ^16.4.5 - version: 16.4.5 + holo-key-manager-js-client: {} shared: dependencies: @@ -1364,11 +1360,6 @@ packages: esutils: 2.0.3 dev: true - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: false - /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fd6497c..0cba016 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: - 'holo-key-manager-js-client' - 'holo-key-manager-extension' + - 'holo-key-manager-extension-scripts' - 'shared' diff --git a/shared/services/storage.ts b/shared/services/storage.ts index 6d79fa4..9081348 100644 --- a/shared/services/storage.ts +++ b/shared/services/storage.ts @@ -1,4 +1,11 @@ -import { APPS_LIST, AUTHENTICATED_APPS_LIST, DEVICE_KEY, LOCAL, SESSION } from '../const'; +import { + APPS_LIST, + AUTHENTICATED_APPS_LIST, + DEVICE_KEY, + LOCAL, + SESSION, + SESSION_STORAGE_KEY +} from '../const'; import { isChromeStorageSafe } from '../helpers'; import { AppsListSchema, @@ -6,6 +13,7 @@ import { AuthenticatedAppsListSchema, type ChangesType, EncryptedDeviceKeySchema, + SessionStateSchema, type StorageService } from '../types'; @@ -97,3 +105,11 @@ export const signOut = async (happId: string) => { }); } }; + +export const getSessionKey = async () => { + const data = await storageService.getWithoutCallback({ + key: SESSION_STORAGE_KEY, + area: SESSION + }); + return SessionStateSchema.safeParse(data); +}; diff --git a/shared/types/message.ts b/shared/types/message.ts index e8b9556..14b590e 100644 --- a/shared/types/message.ts +++ b/shared/types/message.ts @@ -35,6 +35,8 @@ export const MessageToSignSchema = HappIdSchema.extend({ message: z.string() }); +export type MessageToSign = z.infer; + export const PubKeySchema = z.object({ pubKey: z.string() });