1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# Architecture
## Runtime Topology
Four execution contexts:
### 1. Background Page (control plane)
- Entry: `background/background.html` → `background/background.js` (2.9MB minified)
- Also loads: `background/health-check.js` (responds to `health-check-request`)
- Central broker for all policy decisions, vault operations, native messaging, sync, telemetry
- Initializes: database, core interface (WASM), feature flags (Unleash), native app connection, XAM backend, context menus, Watchtower data
- Event subscriptions handle account changes, lock/unlock, session state transitions
- Exports `b5xHandlers` for b5 web app integration and `initializeFinishedPromise` for startup gating
### 2. Content Script Bootstrap (every page, every frame)
- Entry: `inline/inject-content-scripts.js` at `document_start`, `all_frames: true`, `<all_urls>`
- Guards against double-injection (`injectJsHasStarted` property)
- Dynamically imports two modules:
- `/inline/injected.js` (368KB) — always loaded (page managers, autofill, inline menu, frame management)
- `/inline/injected/heuristics.js` — conditionally loaded when `login-detection-is-enabled` returns true from background
- Import retry logic: 3 attempts with 25ms/50ms delays between retries
- Error reporting via `report-error` message to background
- Initializes a `LogReporter` (logger) that forwards all content-script logs to background via `new-tab-log-event`
### 3. Specialized Content Scripts (host-specific)
Declared in manifest, loaded on matching hosts:
| Script | Hosts | Timing | Purpose |
|--------|-------|--------|---------|
| `webauthn.js` + `webauthn-listeners.js` | `https://*/*`, `http://localhost/*` | `document_start` | WebAuthn/passkey mediation (see [webauthn-analysis.md](webauthn-analysis.md)) |
| `b5.js` | `*.1password.com/ca/eu`, `*.b5dev.*`, `*.b5test.*`, `*.b5local.*`, `*.b5staging.*`, `*.b5rev.*` | `document_idle` | 1Password web app integration, SSO completion, session init |
| `kolide.js` | `app.kolide.com/ca/eu`, `auth.kolide.com/ca/eu` | `document_start` | Kolide device trust / EPM integration |
| `secure-remote-autofill-start-pairing.js` | `www.director.ai/?*`, `www.director.ai/` | `document_end` | Remote autofill pairing initiation |
| `secure-remote-autofill-complete-pairing.js` | `www.director.ai/complete-1password-pairing*` | `document_end` | Remote autofill pairing completion |
| `autofill.js` | `autofill.me/*` | `document_start` | Test/demo autofill site |
### 4. Extension UI Surfaces
- `app/app.html` + `app/app.js` (682KB) — main extension window/panel
- `popup/index.html` + `popup/index.js` (399KB) + `popup/set-popup-width.js` — browser action popup
- `launcher/apps.html` + `launcher/apps.js` — app launcher
- `inline/menu/menu.html` — inline autofill suggestion menu (web-accessible)
- `inline/modal/modal.html` — modal dialogs for Privacy.com, email alias, Brex (web-accessible)
- `inline/notification/notification.html` — save/update notifications (web-accessible)
- `inline/universal-sign-on/universal-sign-on.html` — USO banner (web-accessible)
- `inline/tutorial/tutorial.html` — onboarding tutorial
- `devtools/devtools.html` + `devtools/panels.html` — DevTools logging panel
## Chunk System
356 chunk files under `chunks/`. Two categories:
- **Code chunks**: `chunk-{HASH}.js` — shared logic modules (largest: `chunk-OJR52IF5.js` at 1.9MB, `chunk-MSIWLBOQ.js` at 804KB, `chunk-22IBMJDR.js` at 138KB)
- **Icon/asset chunks**: named by icon (e.g., `icon_creditcard_color_32-HASH.js`, `sso_login_okta_32-HASH.js`)
Semantic chunk name patterns observed: `account-family`, `account-team`, `developer_watchtower`, `browserbase_logo`, `browseruse-icon`, `anchor-browser-icon`, `browser-polyfill`, `import_guide_pen`.
## WASM Modules
Seven WebAssembly modules in `assets/wasm/` (total ~30MB), all compiled from Rust via `wasm-bindgen`:
| Module | Size | Purpose (confirmed) |
|--------|------|---------------------|
| `op_wasm_b5x_bg` | 15.6MB | **Core client** — vault encrypt/decrypt, SRP auth, key derivation, item management, page analysis, password generation, TOTP, Watchtower, SSO. This is the same Rust core used by desktop/mobile apps. |
| `op_wasm_xam_bg` | 11.2MB | XAM (cross-app management) — device trust, endpoint management, Kolide/Trelica integration backend |
| `confidential_computing_bg` | 1.8MB | Confidential computing attestation primitives |
| `b5_trustlog_bg` | 1.3MB | Cryptographic audit log — signs/verifies trust log entries for group/user/domain changes (`wasmtrustlogclient_*` exports) |
| `b5_trust-verifier_bg` | 1.0MB | Trust chain verification for account/device authorization |
| `b5_mycelium_bg` | 319KB | P2P relay protocol for remote autofill (director.ai) — Noise-protocol-like handshake (`wasmpairingsession*`, `wasmsetuptransportsession*` exports) |
| `b5_hpke_bg` | 82KB | Hybrid Public Key Encryption (RFC 9180) — used for item sharing, secure transport, sealed encryption (`wasmsealed_*` exports) |
The background.js initialization calls `rA.init(e)` ("initializeCoreInterface") which loads the main WASM module. CSP allows `wasm-unsafe-eval` for this purpose.
### WASM Core Interface (`rA.*`)
The JS-facing core interface routes nearly all sensitive operations through WASM. Confirmed methods:
| Category | Methods |
|----------|---------|
| **Fill/Autofill** | `fill`, `fillItem`, `startFillSession`, `fillSessionEvent`, `fillSessionStatus`, `nextFill`, `clearFillSession`, `fieldValueByIdentifier` |
| **Save** | `createSaveObject`, `createItemFromSaveRequest`, `mergeSaveObjectWithItem`, `saveManagerCreate`, `saveManagerConfigure`, `saveManagerAction`, `saveManagerStatus`, `saveManagerMatchingItems`, `autoSaveMatchingItems`, `saveUrlsFromSaveRequest` |
| **Password/Key Generation** | `generatePassword`, `generateSuggestedPassword`, `passwordGeneratorViewModel`, `createSshKeyItem` |
| **TOTP** | `generateOneTimePasswordFromUrl`, `refreshTotp` |
| **WebAuthn** | `webAuthnLogin`, `webAuthnRegister`, `webAuthnRPValidate` |
| **Page Analysis** | `analyzePage`, `autosubmitDetectElements`, `inferBestTitle` |
| **Email Alias** | `generateEmailAlias`, `enableEmailAliasSession`, `startEmailAliasSession`, `endEmailAliasSession`, `getEmailAliasSessionStatus`, `getEmailAliasAccountName`, `getEmailAliasState`, `updateEmailAlias` |
| **Privacy/Brex** | `createPrivacyCard`, `validatePrivacyCardParams`, `getPrivacyFundingAccounts`, `createBrexVendorCardForUser`, `getAllCardsForBrexUser`, `getCurrentBrexUser` |
| **Watchtower** | `compareWatchTowerDiff`, `handleWatchtowerAction`, `newCompromisedWebsiteItems` |
| **Auth/SSO** | `signInWithEvent`, `signInWithProviderConfig`, `signInWithUrlToProvider`, `enrollTrustedDevice` |
| **Item Management** | `getItemDetails`, `editItemFilter`, `getLargeTypeWithClientFormattedString`, `getLargeTypeWithFieldIdentifier`, `itemToReference` |
| **URL/Domain** | `nakedDomainForUrl`, `nakedDomainForUrls`, `getRichIconForUrl`, `generateRichIconBackgroundColor`, `getAppleChangePasswordUrl`, `serviceIntegrationForUrl` |
| **Account/UI** | `getAccountIcon`, `getVaultIcon`, `getEffectivePolicies`, `parseAccountType`, `parseVaultType`, `concealCreditCardNumber`, `getCreditCardType`, `generateSharableItemLink`, `parseMarkdown`, `setLocale`, `getSignInUrl`, `getSignInUrlVersion`, `getOauthAccessToken` |
| **Lifecycle** | `init`, `ensureWasmIsInitialized`, `onLock` |
| **Crypto** | `buildPermissionBundle`, `lessSafeOpenNaCl`, `unOpaqueFilePosition` |
### Architectural Role of WASM
**WASM is a portability layer, not a security boundary.** Key findings:
1. **The Rust core is shared across all 1Password clients** (desktop, mobile, browser, CLI). WASM is how it runs in the browser. This ensures crypto correctness — one Rust implementation vs. hand-rolled JS.
2. **Key material crosses the WASM↔JS boundary.** The Master Unlock Key (MUK) exists in the JS heap as an exportable JWK on the account handler object. SRP-X values are cached in JS. Decrypted item secrets (passwords, OTPs) are returned from WASM to JS for fill operations.
3. **JS WebCrypto is used minimally** — only 4 `crypto.subtle.*` calls in all of background.js (2 `digest`, 1 `sign`, 1 `importKey`, 1 `decrypt`). The WASM core handles virtually all crypto: SRP, PBKDF2, HKDF, AES-GCM, AES-CBC, RSA-OAEP, ECDSA, HMAC, NaCl.
4. **A compromised background page has full access** to all key material and decrypted secrets. WASM linear memory is readable from JS in the same origin.
See [key-hierarchy.md](key-hierarchy.md) for the full key derivation and authentication model.
## Permission Profile
### Always granted
`<all_urls>`, `alarms`, `clipboardWrite`, `contextMenus`, `downloads`, `idle`, `management`, `nativeMessaging`, `notifications`, `privacy`, `scripting`, `storage`, `tabs`, `webNavigation`, `webRequest`, `webRequestBlocking`, `declarativeNetRequestWithHostAccess`
### Optional
`bookmarks`
### Web-Accessible Resources
Source maps (`*.js.map`), fonts, images, and critically: `inline/injected.js`, `inline/injected/heuristics.js`, `inline/injected/styles/inline-tooltip.css`, and all inline UI HTML files (menu, notification, modal, universal-sign-on). These can be loaded/detected by any web page.
## Feature Flags
Unleash-based feature flag system with two tiers:
- **Pre-registration flags**: evaluated before any account is signed in (e.g., `b5x-pre-auth-tracing`)
- **Account-gated feature trials**: per-account feature flags from server
Background broadcasts `unleash-features-changed` events to all listeners when flags update. Content scripts query individual flags (e.g., `login-detection-is-enabled`).
## Initialization Sequence
From `background.js` main init function (reconstructed):
1. Initialize storage
2. Initialize feature flags cache
3. Check for terminated DB, set icon
4. Get browser language
5. Initialize Sentry
6. Init build info
7. Initialize core interface (WASM load)
8. Set locale
9. Initialize database (IndexedDB)
10. Get device info, sync pre-registration feature flags
11. Start performance observer (if tracing flag set)
12. Load all accounts, set up account handlers
13. Initialize crypto
14. Subscribe to events: session changes, account updates, lock/unlock
15. Load Watchtower data
16. Initialize native app connection
17. Initialize unlock-with-context cache
18. Enable insiders (if appropriate)
19. Initialize XAM backend
20. Initialize App Launcher
21. Migrate storage (if needed)
22. Initialize context menus
23. Initialize notifications
## External Extension Communication
3 references to `chrome.runtime.onMessageExternal` — the extension accepts messages from other extensions (likely 1Password desktop app or enterprise connectors). No `externally_connectable` manifest key found, so Firefox's default policy applies.
## Build/Signing
- Build channel: `stable`
- Signed by Mozilla AMO Production Signing Service
- COSE + RSA signatures in `META-INF/`
- Sentry debug IDs embedded in every JS file for crash correlation
|