refactor(browser): simplify credential storage config (#593)

This commit is contained in:
Roger
2026-05-14 16:29:35 +08:00
committed by GitHub
parent 5d67d3c303
commit ecf8ba0585
22 changed files with 286 additions and 243 deletions
+9 -30
View File
@@ -5,47 +5,26 @@ import (
"fmt"
)
// MasterKeys bundles the per-cipher-version Chromium master keys used to decrypt data from a
// single profile. decryptValue dispatches on the ciphertext's version prefix and picks the
// matching key; a missing (nil) key for a tier means "that cipher version cannot be decrypted",
// but the other tiers remain usable — a Chrome 127+ profile upgraded from pre-127 carries mixed
// v10+v20 ciphertexts, and Linux profiles may carry mixed v10+v11 for analogous reasons.
//
// - V10: Chrome 80+ key with "v10" cipher prefix.
// - Windows: os_crypt.encrypted_key decrypted by user-level DPAPI (AES-GCM ciphertexts).
// - macOS: derived from Keychain via PBKDF2(1003, SHA-1) (AES-CBC ciphertexts).
// - Linux: derived from "peanuts" hardcoded password (Chromium's kV10Key, AES-CBC).
// - V11: Chrome Linux key with "v11" cipher prefix, derived from D-Bus Secret Service
// (KWallet / GNOME Keyring) via PBKDF2. Nil on Windows and macOS (v11 prefix not used there).
// - V20: Chrome 127+ Windows key with "v20" cipher prefix, retrieved via reflective injection
// into the browser's elevation service. Nil on non-Windows platforms.
// MasterKeys holds per-cipher-version Chromium master keys. A profile may carry mixed prefixes
// (Chrome 127+ on Windows mixes v10+v20; Linux can mix v10+v11), so each tier must be populated
// independently for lossless decryption. A nil tier means that cipher version cannot be decrypted.
type MasterKeys struct {
V10 []byte
V11 []byte
V20 []byte
}
// Retrievers is the per-tier retriever configuration passed to NewMasterKeys. Each slot runs
// independently — failure or absence of one tier does not affect others. Platform injectors set
// only the slots that apply to their platform and leave the rest nil (e.g. Linux populates
// V10+V11, leaves V20 nil).
// Retrievers is the per-tier retriever configuration; unused slots are nil.
type Retrievers struct {
V10 KeyRetriever
V11 KeyRetriever
V20 KeyRetriever
}
// NewMasterKeys fetches every configured tier in r independently and returns the assembled
// MasterKeys together with any per-tier errors joined into one. Nil retrievers and retrievers
// returning (nil, nil) (the "not applicable" signal — e.g. ABERetriever on a non-ABE fork)
// contribute nil keys silently; only non-nil errors propagate.
//
// The returned error, when non-nil, is an errors.Join of per-tier failures formatted as
// "<tier>: <err>" (e.g. "v10: dpapi decrypt: ..."). Callers are expected to log it at whatever
// severity fits their context — this function itself never logs, leaving logging policy to its
// callers. Other pieces of the keyretriever package (e.g. ChainRetriever) may still log on their
// own failures; the "no-logging" guarantee is scoped to NewMasterKeys.
func NewMasterKeys(r Retrievers, storage, localStatePath string) (MasterKeys, error) {
// NewMasterKeys fetches each non-nil tier in r and returns the assembled MasterKeys with per-tier
// errors joined. A retriever returning (nil, nil) signals "not applicable" and contributes no key
// silently. This function never logs; the caller decides severity.
func NewMasterKeys(r Retrievers, hints Hints) (MasterKeys, error) {
var keys MasterKeys
var errs []error
@@ -61,7 +40,7 @@ func NewMasterKeys(r Retrievers, storage, localStatePath string) (MasterKeys, er
if t.r == nil {
continue
}
k, err := t.r.RetrieveKey(storage, localStatePath)
k, err := t.r.RetrieveKey(hints)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", t.name, err))
continue