diff --git a/.gitignore b/.gitignore index 46c2bd8..ccdd4eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ target/ .superpowers/ .worktrees/ +extension/node_modules/ +extension/dist/ +extension/wasm/ diff --git a/extension/manifest.json b/extension/manifest.json new file mode 100644 index 0000000..deafd15 --- /dev/null +++ b/extension/manifest.json @@ -0,0 +1,28 @@ +{ + "manifest_version": 3, + "name": "idfoto", + "version": "0.1.0", + "description": "Two-factor encrypted password manager", + "permissions": ["storage", "activeTab", "clipboardWrite"], + "host_permissions": [""], + "background": { + "service_worker": "service-worker.js", + "type": "module" + }, + "action": { + "default_popup": "popup.html", + "default_icon": { + "16": "icons/icon-16.png", + "48": "icons/icon-48.png", + "128": "icons/icon-128.png" + } + }, + "content_scripts": [{ + "matches": [""], + "js": ["content.js"], + "run_at": "document_idle" + }], + "content_security_policy": { + "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'" + } +} diff --git a/extension/package.json b/extension/package.json new file mode 100644 index 0000000..96eca1d --- /dev/null +++ b/extension/package.json @@ -0,0 +1,19 @@ +{ + "name": "idfoto-extension", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "webpack --mode production", + "dev": "webpack --mode development --watch", + "build:wasm": "wasm-pack build ../crates/idfoto-wasm --target web --out-dir ../../extension/wasm", + "build:all": "npm run build:wasm && npm run build" + }, + "devDependencies": { + "@types/chrome": "^0.1.40", + "copy-webpack-plugin": "^12.0", + "ts-loader": "^9.5", + "typescript": "^5.4", + "webpack": "^5.90", + "webpack-cli": "^5.1" + } +} diff --git a/extension/src/popup/index.html b/extension/src/popup/index.html new file mode 100644 index 0000000..bae145c --- /dev/null +++ b/extension/src/popup/index.html @@ -0,0 +1,13 @@ + + + + + + + idfoto + + +
+ + + diff --git a/extension/src/wasm.d.ts b/extension/src/wasm.d.ts new file mode 100644 index 0000000..0ea4504 --- /dev/null +++ b/extension/src/wasm.d.ts @@ -0,0 +1,24 @@ +/// Type declarations for the idfoto WASM module produced by wasm-pack. + +// Ambient module declarations for the WASM glue code. +// The module specifier must exactly match what's used in import statements. + +declare module 'idfoto-wasm' { + export default function init(input?: string | URL): Promise; + export function derive_master_key( + passphrase: string, + image_secret: Uint8Array, + salt: Uint8Array, + params_json: string, + ): Uint8Array; + export function encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array; + export function decrypt(ciphertext: Uint8Array, key: Uint8Array): Uint8Array; + export function extract_image_secret(jpeg_bytes: Uint8Array): Uint8Array; + export function encrypt_entry(entry_json: string, key: Uint8Array): Uint8Array; + export function decrypt_entry(ciphertext: Uint8Array, key: Uint8Array): string; + export function encrypt_manifest(manifest_json: string, key: Uint8Array): Uint8Array; + export function decrypt_manifest(ciphertext: Uint8Array, key: Uint8Array): string; + export function generate_totp(secret_base32: string, timestamp_secs: bigint): string; + export function generate_password(length: number): string; + export function generate_entry_id(): string; +} diff --git a/extension/tsconfig.json b/extension/tsconfig.json new file mode 100644 index 0000000..0f83f24 --- /dev/null +++ b/extension/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "outDir": "./dist", + "rootDir": "./src", + "sourceMap": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "paths": { + "idfoto-wasm": ["./wasm/idfoto_wasm.js"] + }, + "baseUrl": "." + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "wasm"] +} diff --git a/extension/webpack.config.js b/extension/webpack.config.js new file mode 100644 index 0000000..9fefa24 --- /dev/null +++ b/extension/webpack.config.js @@ -0,0 +1,34 @@ +const path = require('path'); +const CopyPlugin = require('copy-webpack-plugin'); + +module.exports = { + entry: { + 'service-worker': './src/service-worker/index.ts', + popup: './src/popup/popup.ts', + content: './src/content/detector.ts', + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: '[name].js', + clean: true, + }, + resolve: { + extensions: ['.ts', '.js'], + }, + module: { + rules: [{ test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ }], + }, + plugins: [ + new CopyPlugin({ + patterns: [ + { from: 'manifest.json', to: '.' }, + { from: 'src/popup/index.html', to: 'popup.html' }, + { from: 'src/popup/styles.css', to: 'styles.css' }, + { from: 'icons', to: 'icons' }, + { from: 'wasm/idfoto_wasm_bg.wasm', to: '.' }, + { from: 'wasm/idfoto_wasm.js', to: '.' }, + ], + }), + ], + experiments: { asyncWebAssembly: true }, +};