mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-05-19 18:58:03 +02:00
feat(keys): add cross-host master key export (#599)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package keyretriever
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DumpVersion = "1"
|
||||
|
||||
// Dump is the cross-host portable container for Chromium master keys. Producing it on one host lets another host skip
|
||||
// platform-native retrieval (DPAPI, ABE injection, Keychain prompt, D-Bus query) when decrypting copied profile data.
|
||||
type Dump struct {
|
||||
Version string `json:"version"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Host Host `json:"host"`
|
||||
Vaults []Vault `json:"vaults"`
|
||||
}
|
||||
|
||||
// Host OS / Arch always set; Hostname / User best-effort (empty on syscall failure).
|
||||
type Host struct {
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// Vault groups profiles sharing master keys (master keys are per-installation, not per-profile).
|
||||
type Vault struct {
|
||||
Browser string `json:"browser"`
|
||||
UserDataDir string `json:"user_data_dir"`
|
||||
Profiles []string `json:"profiles"`
|
||||
Keys MasterKeys `json:"keys"`
|
||||
}
|
||||
|
||||
// NewDump returns a Dump initialized with current host metadata and an empty Vaults slice
|
||||
func NewDump() Dump {
|
||||
return Dump{
|
||||
Version: DumpVersion,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
Host: currentHost(),
|
||||
Vaults: []Vault{},
|
||||
}
|
||||
}
|
||||
|
||||
// currentHost collects host identification; Hostname/User are best-effort (syscall failure leaves them empty + omitempty).
|
||||
func currentHost() Host {
|
||||
h := Host{OS: runtime.GOOS, Arch: runtime.GOARCH}
|
||||
if name, err := os.Hostname(); err == nil {
|
||||
h.Hostname = name
|
||||
}
|
||||
if u, err := user.Current(); err == nil {
|
||||
h.User = u.Username
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// WriteJSON writes the Dump as indented JSON to w.
|
||||
func (d Dump) WriteJSON(w io.Writer) error {
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(d); err != nil {
|
||||
return fmt.Errorf("encode dump: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadJSON parses a Dump from r.
|
||||
func ReadJSON(r io.Reader) (Dump, error) {
|
||||
var d Dump
|
||||
dec := json.NewDecoder(r)
|
||||
if err := dec.Decode(&d); err != nil {
|
||||
return Dump{}, fmt.Errorf("decode dump: %w", err)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
@@ -9,9 +9,15 @@ import (
|
||||
// (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
|
||||
V10 []byte `json:"v10,omitempty"`
|
||||
V11 []byte `json:"v11,omitempty"`
|
||||
V20 []byte `json:"v20,omitempty"`
|
||||
}
|
||||
|
||||
// HasAny reports whether at least one tier carries a usable key. Centralizes the "is this MasterKeys
|
||||
// worth keeping" check so new tiers (V21, V12, …) only need to be added here, not at every caller.
|
||||
func (k MasterKeys) HasAny() bool {
|
||||
return k.V10 != nil || k.V11 != nil || k.V20 != nil
|
||||
}
|
||||
|
||||
// Retrievers is the per-tier retriever configuration; unused slots are nil.
|
||||
|
||||
Reference in New Issue
Block a user