You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
|`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`. |
|`browser/chromium/source.go`|`creditCardExtractor` wrapper (parallel to `passwordExtractor` / `extensionExtractor`); `yandexExtractors` map registers Password and CreditCard overrides. |
131
132
|`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]`. |
132
133
|`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
135
136
136
137
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.
137
138
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/`
139
140
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.
141
149
142
150
## 6. Non-goals and deferred work
143
151
@@ -151,13 +159,13 @@ All decryption math is covered by pure-Go tests that synthesize Yandex DB files
151
159
152
160
| File | What it validates |
153
161
|---|---|
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. |
|`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. |
158
166
|`browser/chromium/chromium_test.go`|`TestExtractorsForKind` asserts `yandexExtractors` carries both `Password` and `CreditCard` entries. |
159
167
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.
0 commit comments