diff --git a/README.md b/README.md index 2a28bb0..f85f318 100644 --- a/README.md +++ b/README.md @@ -48,22 +48,18 @@ separate output directories. src/ background/ — service worker / background script index.js — extension lifecycle, message routing - wallet.js — wallet management (create, import, derive) + wallet.js — wallet management (create, import, derive via ethers.js) provider.js — EIP-1193 JSON-RPC provider implementation - transaction.js — transaction construction and signing popup/ — popup UI (the main wallet interface) index.html index.js - components/ — UI components (account list, send form, etc.) styles/ — CSS (Tailwind) content/ — content script injected into web pages index.js — injects the provider into page context inpage.js — the window.ethereum provider object shared/ — shared utilities - crypto.js — BIP-39 mnemonic, HD key derivation, signing - storage.js — encrypted storage abstraction + vault.js — encrypted storage via libsodium constants.js — chain IDs, default RPC endpoints, ERC-20 ABI - rpc.js — JSON-RPC client for Ethereum nodes manifest/ chrome.json — Manifest V3 for Chrome firefox.json — Manifest V2/V3 for Firefox @@ -197,32 +193,75 @@ What the extension does NOT do: The user's RPC endpoint is the single point of external communication. Users who want maximum privacy can point it at their own Ethereum node. +### Dependencies + +AutistMask uses two runtime libraries. All cryptographic operations are +delegated to these libraries — see the Crypto Policy section below. + +| Package | Version | License | Purpose | +| ------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ethers` | 6.16.0 | MIT | All Ethereum operations: BIP-39 mnemonic generation/validation, BIP-32/BIP-44 HD key derivation (`m/44'/60'/0'/0/n`), secp256k1 signing, transaction construction, ERC-20 contract interaction, JSON-RPC communication, address derivation (keccak256). | +| `libsodium-wrappers-sumo` | 0.8.2 | ISC | Password-based encryption of secrets at rest: Argon2id key derivation (`crypto_pwhash`), authenticated encryption (`crypto_secretbox` / XSalsa20-Poly1305). | + +Dev dependencies (not shipped in extension): + +| Package | Version | License | Purpose | +| ------------------ | ------- | ------- | --------------- | +| `tailwindcss` | 4.2.1 | MIT | CSS compilation | +| `@tailwindcss/cli` | 4.2.1 | MIT | Tailwind CLI | +| `jest` | 30.2.0 | MIT | Test runner | +| `prettier` | 3.8.1 | MIT | Code formatter | + +### Crypto Policy + +**No raw crypto primitives in application code.** If the strings `aes`, `sha`, +`pbkdf`, `hmac`, `encrypt`, `decrypt`, `hash`, `cipher`, `digest`, `sign` +(case-insensitive) appear in our own source code (outside of `node_modules/`), +it is almost certainly a bug. All cryptographic operations must go through +`ethers` or `libsodium-wrappers-sumo`. This policy exists because: + +- Rolling your own crypto is the single most common source of security + vulnerabilities in wallet software. +- Both libraries are widely audited and battle-tested. +- Keeping crypto out of application code makes security review tractable: + reviewers only need to verify that we call the libraries correctly, not that + we implemented crypto correctly. + +Exceptions require explicit authorization in a code comment referencing this +policy. + ### Key Decisions - **No framework**: The popup UI is vanilla JS and HTML. The extension is small enough that a framework adds unnecessary complexity and attack surface. - **Encrypted storage**: Recovery phrases and private keys are encrypted at rest - in the extension's local storage. The encryption scheme: - - The user's password is run through PBKDF2-SHA256 (600,000 iterations) with - a random salt to derive a 256-bit encryption key. - - The encryption key + a random IV encrypt the secret material using - AES-256-GCM. - - Stored blob: `{ salt, iv, ciphertext, authTag }`. + in the extension's local storage using libsodium. The encryption scheme: + - The user's password is run through Argon2id (`crypto_pwhash`) to derive a + 256-bit encryption key. Argon2id is memory-hard, making GPU/ASIC brute + force attacks expensive. + - The derived key encrypts the secret material using XSalsa20-Poly1305 + (`crypto_secretbox`), which provides authenticated encryption (the + ciphertext cannot be tampered with without detection). + - Stored blob: `{ salt, nonce, ciphertext }` (the auth tag is part of the + `crypto_secretbox` output). - **The password is NOT used in address derivation.** It exists solely to protect the recovery phrase / private key on disk. Anyone with the recovery phrase can restore the wallet on any device without this password. This matches MetaMask's behavior. -- **BIP-39 / BIP-44**: Standard mnemonic generation and HD key derivation - (`m/44'/60'/0'/0/n`) for Ethereum address compatibility. The BIP-39 passphrase - is always empty (matching MetaMask and most wallet software). The user's - password is completely separate and has no effect on which addresses are - generated. +- **BIP-39 / BIP-44 via ethers.js**: Mnemonic generation, validation, and HD key + derivation (`m/44'/60'/0'/0/n`) are handled entirely by ethers.js. The BIP-39 + passphrase is always empty (matching MetaMask and most wallet software). The + user's password is completely separate and has no effect on which addresses + are generated. +- **ethers.js for everything Ethereum**: Transaction construction, signing, gas + estimation, RPC communication, ERC-20 contract calls, and address derivation + are all handled by ethers.js. This means zero hand-rolled Ethereum logic. - **EIP-1193 provider**: The content script injects a `window.ethereum` object that implements the EIP-1193 provider interface, enabling web3 site connectivity. -- **Minimal RPC**: The extension communicates with Ethereum nodes via JSON-RPC. - The default endpoint is configurable. No Infura dependency — users can point - it at any Ethereum JSON-RPC endpoint. +- **Minimal RPC**: The extension communicates with Ethereum nodes via JSON-RPC + through ethers.js's `JsonRpcProvider`. The default endpoint is configurable. + No Infura dependency — users can point it at any Ethereum JSON-RPC endpoint. ### Supported Functionality diff --git a/package.json b/package.json index ba677ea..ec311d3 100644 --- a/package.json +++ b/package.json @@ -18,5 +18,8 @@ "prettier": "^3.8.1", "tailwindcss": "^4.2.1" }, - "dependencies": {} + "dependencies": { + "ethers": "^6.16.0", + "libsodium-wrappers-sumo": "^0.8.2" + } } diff --git a/yarn.lock b/yarn.lock index 99e8bf5..d1d018f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": version "7.29.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" @@ -592,6 +597,18 @@ "@emnapi/runtime" "^1.7.1" "@tybys/wasm-util" "^0.10.1" +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@parcel/watcher-android-arm64@2.5.6": version "2.5.6" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz#5f32e0dba356f4ac9a11068d2a5c134ca3ba6564" @@ -887,6 +904,13 @@ dependencies: undici-types "~7.18.0" +"@types/node@22.7.5": + version "22.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" + "@types/stack-utils@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -1006,6 +1030,11 @@ resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777" integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== +aes-js@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== + ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -1350,6 +1379,19 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +ethers@^6.16.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.16.0.tgz#fff9b4f05d7a359c774ad6e91085a800f7fccf65" + integrity sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "22.7.5" + aes-js "4.0.0-beta.5" + tslib "2.7.0" + ws "8.17.1" + execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -2004,6 +2046,18 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +libsodium-sumo@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.8.2.tgz#6aee0ce5d5f850341327a2990531a71319d86c62" + integrity sha512-uMgnjphJ717jLN+jFG1HUgNrK/gOVVfaO1DGZ1Ig/fKLKLVhvaH/sM1I1v784JFvmkJDaczDpi7xSYC4Jvdo1Q== + +libsodium-wrappers-sumo@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.8.2.tgz#3bc6ef80070d531622ccd41a317b61d32a78a7f2" + integrity sha512-wd1xAY++Kr6VMikSaa4EPRAHJmFvNlGWiiwU3Jh3GR1zRYF3/I3vy/wYsr4k3LVsNzwb9sqfEQ4LdVQ6zEebyQ== + dependencies: + libsodium-sumo "^0.8.0" + lightningcss-android-arm64@1.31.1: version "1.31.1" resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz#609ff48332adff452a8157a7c2842fd692a8eac4" @@ -2543,6 +2597,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tslib@^2.4.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" @@ -2558,6 +2617,11 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + undici-types@~7.18.0: version "7.18.2" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" @@ -2661,6 +2725,11 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" +ws@8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"