Skip to content

Commit 073d7eb

Browse files
committed
docs(rfc-012): sync code layout and test strategy with final refactor
Align the RFC with the code that actually shipped: - §5 Code layout: drop stale references to AESGCMDecryptWithAAD / YandexLoginAAD / YandexCardAAD / YandexSignature (unexported or moved); add the new crypto.AESGCMDecryptBlob generic primitive; note that extract_creditcard_yandex.go was merged back into extract_creditcard.go; mention the local yandexLoginAAD / yandexCardAAD helpers that now live next to their extractors. - §5.2: replace the "why AESGCMDecryptWithAAD is new" rationale with the final split — generic AES-GCM + AAD primitive in crypto, Yandex-specific AAD construction in chromium. - §7 Test strategy: update the file/test inventory, point at the merged extract_creditcard_test.go, and soften the regression baseline wording to "0 non-ASCII" instead of a stale 574-cookie number.
1 parent 7e7ccf1 commit 073d7eb

1 file changed

Lines changed: 20 additions & 12 deletions

File tree

rfcs/012-yandex-decryption.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,12 @@ Both data keys still ultimately derive from the same level-1 Chromium master key
122122

123123
| Path | Role |
124124
|---|---|
125-
| `crypto/yandex.go` | Pure-Go primitives: `DecryptYandexIntermediateKey`, `AESGCMDecryptWithAAD`, `YandexLoginAAD`, `YandexCardAAD`, `YandexSignature`. Cross-platform, unit-testable on any host. |
126-
| `crypto/yandex_test.go` | Round-trip tests using synthesized blobs (no Yandex install required). |
127-
| `browser/chromium/yandex_key.go` | `loadYandexDataKey(dbPath, masterKey)` — opens the DB, checks `active_keys`, reads `meta.local_encryptor_data`, returns dataKey or `errYandexMasterPasswordSet`. |
128-
| `browser/chromium/extract_password.go` | `extractYandexPasswords` — queries `origin_url, username_element, username_value, password_element, password_value, signon_realm, date_created`; computes per-row AAD; decrypts. |
129-
| `browser/chromium/extract_creditcard_yandex.go` | `extractYandexCreditCards` + `countYandexCreditCards` — queries `records`; decrypts `private_data` with guid-AAD; parses both JSON blobs. |
125+
| `crypto/crypto.go` | New generic primitive `AESGCMDecryptBlob(key, blob, aad)` — splits `[12B nonce][ct+tag]` and runs AES-GCM. Not Yandex-specific; any protocol with this wire format can use it. |
126+
| `crypto/yandex.go` | Only exports `DecryptYandexIntermediateKey`. Holds the Yandex-specific protobuf signature strip + blob-length invariants as private constants. |
127+
| `crypto/yandex_test.go` | Round-trip + error-path tests for `DecryptYandexIntermediateKey` and `AESGCMDecryptBlob`. |
128+
| `browser/chromium/yandex_key.go` | `loadYandexDataKey(dbPath, masterKey)` — opens the DB, checks `active_keys.sealed_key`, reads `meta.local_encryptor_data`, returns dataKey or `errYandexMasterPasswordSet`. |
129+
| `browser/chromium/extract_password.go` | `extractYandexPasswords` + local `yandexLoginAAD` helper. Queries `origin_url, username_element, username_value, password_element, password_value, signon_realm, date_created`, computes SHA1 AAD locally, calls `crypto.AESGCMDecryptBlob`. |
130+
| `browser/chromium/extract_creditcard.go` | Merged file — Chromium `extractCreditCards` + Yandex `extractYandexCreditCards` + `countYandexCreditCards` + local `yandexCardAAD` helper + JSON struct types `yandexPublicData` / `yandexPrivateData`. |
130131
| `browser/chromium/source.go` | `creditCardExtractor` wrapper (parallel to `passwordExtractor` / `extensionExtractor`); `yandexExtractors` map registers Password and CreditCard overrides. |
131132
| `browser/chromium/chromium.go` | `countCategory` routes `CreditCard` to `countYandexCreditCards` when `cfg.Kind == ChromiumYandex` (table name differs). The extract side already dispatches through `b.extractors[cat]`. |
132133
| `types/models.go` | `CreditCardEntry` gains `CVC` and `Comment` string fields. Chromium leaves them empty. |
@@ -135,9 +136,16 @@ Both data keys still ultimately derive from the same level-1 Chromium master key
135136

136137
The keyretriever tier (V10/V11/V20) is keyed on *cipher-version prefix* — the extract side dispatches on bytes `"v10"` / `"v11"` / `"v20"`. Yandex password rows carry no such prefix; they are raw `nonce\|ct+tag`. Injecting Yandex's intermediate-key step into `keyretriever.MasterKeys` would overload the tier abstraction (which models "pick the key for this prefix"), so the intermediate key is recovered inside the Yandex extractor using the Chromium V10 key as input. The keyretriever layer is untouched.
137138

138-
### 5.2 Why `AESGCMDecryptWithAAD` is a new function rather than an extension of `AESGCMDecrypt`
139+
### 5.2 Why AAD construction lives in `browser/chromium/`, not in `crypto/`
139140

140-
`crypto.AESGCMDecrypt` is called from the v10 Chromium path with an implicit `aad = nil` semantics and is covered by the Chromium regression suite. Changing its signature or threading an AAD parameter through would ripple through every extractor. A dedicated `AESGCMDecryptWithAAD` keeps the Chromium call sites byte-identical and confines the new behavior to Yandex.
141+
`crypto` exposes cryptographic primitives (AES, GCM, 3DES, DPAPI, PBKDF2, etc.) — things that transform bytes under a key. AAD construction for Yandex (`SHA1(origin_url ‖ \x00 ‖ …)` for passwords, raw `guid` for cards) is not cryptography; it is Yandex's per-row identification rule that happens to be bound to GCM's authentication tag. Placing it in `crypto` would leak Yandex protocol knowledge into a package that otherwise knows nothing about browsers.
142+
143+
The final split:
144+
145+
- `crypto.AESGCMDecryptBlob(key, blob, aad)` — generic AES-GCM with a caller-supplied AAD. Exported once, used by any current or future protocol that wants per-row AAD.
146+
- `chromium.yandexLoginAAD` / `chromium.yandexCardAAD` — private helpers next to the extractor that calls them. Protocol knowledge stays with the protocol consumer.
147+
148+
This also keeps the `crypto` public surface small (3 extra exports: `DecryptYandexIntermediateKey`, `AESGCMDecryptBlob`, and the existing `AESGCMDecrypt`) rather than ballooning into a per-browser API.
141149

142150
## 6. Non-goals and deferred work
143151

@@ -151,13 +159,13 @@ All decryption math is covered by pure-Go tests that synthesize Yandex DB files
151159

152160
| File | What it validates |
153161
|---|---|
154-
| `crypto/yandex_test.go` | `DecryptYandexIntermediateKey` round-trip, missing marker, truncated blob, bad signature, trailing data ignored; `AESGCMDecryptWithAAD` round-trip + bad AAD + bad nonce length; `YandexLoginAAD` / `YandexCardAAD` output shape with/without keyID. |
155-
| `browser/chromium/yandex_testutil_test.go` | `setupYandexPasswordDB` / `setupYandexCreditCardDB` — seal a dataKey into `meta.local_encryptor_data`, insert logins/records with matching AAD. |
156-
| `browser/chromium/extract_password_test.go` | `TestExtractYandexPasswords` end-to-end; master-password skip path; wrong master key surfaces as error. |
157-
| `browser/chromium/extract_creditcard_yandex_test.go` | Round-trip on 2-card fixture verifying Number/CVC/Comment/NickName/ExpMonth/ExpYear mapping; count on 3-row table; wrong master key surfaces as error. |
162+
| `crypto/yandex_test.go` | `DecryptYandexIntermediateKey` round-trip, missing marker, truncated blob, bad signature, trailing data ignored. `AESGCMDecryptBlob`round-trip, mismatched AAD fails GCM, blob shorter than nonce size surfaces as `errShortCiphertext`. |
163+
| `browser/chromium/yandex_testutil_test.go` | `setupYandexPasswordDB` / `setupYandexCreditCardDB` — seal a dataKey into `meta.local_encryptor_data`, insert logins/records with matching AAD. Uses the same `yandexLoginAAD` / `yandexCardAAD` helpers as production so fixture and extractor stay in lock-step. |
164+
| `browser/chromium/extract_password_test.go` | `TestExtractYandexPasswords` end-to-end (2 real logins round-tripped); master-password skip path; wrong master key surfaces as error. `TestYandexLoginAAD_*` covers the SHA1 shape with / without keyID. |
165+
| `browser/chromium/extract_creditcard_test.go` | Merged file — Chromium tests for `credit_cards` plus Yandex tests: round-trip on 2-card fixture verifying Number/CVC/Comment/NickName/ExpMonth/ExpYear mapping; count on 3-row `records` table; wrong master key surfaces as error. `TestYandexCardAAD` covers guid bytes / guid+keyID. |
158166
| `browser/chromium/chromium_test.go` | `TestExtractorsForKind` asserts `yandexExtractors` carries both `Password` and `CreditCard` entries. |
159167

160-
Windows-host validation (out-of-tree, per `CLAUDE.local.md`): `make build-windows` → deploy to sandbox → `hbd.exe -v -b yandex` → verify non-empty `password.json` / `creditcard.json` and no regression in the 574-cookie 13-browser full-sweep baseline.
168+
Windows-host validation (out-of-tree, per `CLAUDE.local.md`): `make build-windows` → deploy to sandbox → `hbd.exe -v -b yandex` → verify non-empty `password.json` / `creditcard.json` and no regression on full-sweep baseline. Most recent run: 703 cookies across 13 browsers, 0 non-ASCII — "no regression" measured as `0 non-ASCII` rather than exact cookie count, since the sandbox naturally accumulates new cookies over time.
161169

162170
## 8. Rollout
163171

0 commit comments

Comments
 (0)