mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-07-04 21:37:47 +02:00
docs: cross-host decryption guide and comment cleanup (#614)
* docs(readme): document cross-host decryption workflow * docs: drop RFC citations and what-comments
This commit is contained in:
@@ -61,7 +61,7 @@ func extractCreditCards(masterKeys masterkey.MasterKeys, path string) ([]types.C
|
||||
return cards, nil
|
||||
}
|
||||
|
||||
// extractYandexCreditCards reads the records table (not Chromium's credit_cards). AAD = guid. See RFC-012 §4.
|
||||
// extractYandexCreditCards reads the records table (not Chromium's credit_cards). AAD = guid.
|
||||
func extractYandexCreditCards(masterKeys masterkey.MasterKeys, path string) ([]types.CreditCardEntry, error) {
|
||||
dataKey, err := loadYandexDataKey(path, masterKeys.V10)
|
||||
if err != nil {
|
||||
|
||||
@@ -51,7 +51,7 @@ func extractPasswordsWithQuery(masterKeys masterkey.MasterKeys, path, query stri
|
||||
return logins, nil
|
||||
}
|
||||
|
||||
// extractYandexPasswords walks Ya Passman Data; protocol in RFC-012 §4.
|
||||
// extractYandexPasswords walks Ya Passman Data.
|
||||
// Note: URL column is origin_url — it's what the per-row AAD is computed over (not action_url).
|
||||
func extractYandexPasswords(masterKeys masterkey.MasterKeys, path string) ([]types.LoginEntry, error) {
|
||||
dataKey, err := loadYandexDataKey(path, masterKeys.V10)
|
||||
|
||||
@@ -73,10 +73,10 @@ func yandexCardAAD(guid string, keyID []byte) []byte {
|
||||
return out
|
||||
}
|
||||
|
||||
// errYandexMasterPasswordSet: caller warns + skips; RSA-OAEP unseal is deferred (RFC-012 §6).
|
||||
// errYandexMasterPasswordSet: caller warns + skips; RSA-OAEP unseal is deferred.
|
||||
var errYandexMasterPasswordSet = errors.New("yandex: profile protected by master password, skipping")
|
||||
|
||||
// loadYandexDataKey honors the master-password gate and returns the per-DB data key. See RFC-012 §4.2.
|
||||
// loadYandexDataKey honors the master-password gate and returns the per-DB data key.
|
||||
func loadYandexDataKey(dbPath string, masterKey []byte) ([]byte, error) {
|
||||
if len(masterKey) == 0 {
|
||||
return nil, fmt.Errorf("yandex: master key not available")
|
||||
|
||||
@@ -50,13 +50,11 @@ func readKey4DB(path string) (*key4DB, error) {
|
||||
|
||||
var record key4DB
|
||||
|
||||
// Read metaData table
|
||||
const metaQuery = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
|
||||
if err := db.QueryRow(metaQuery).Scan(&record.globalSalt, &record.passwordCheck); err != nil {
|
||||
return nil, fmt.Errorf("query metaData: %w", err)
|
||||
}
|
||||
|
||||
// Read nssPrivate table
|
||||
const nssQuery = `SELECT a11, a102 FROM nssPrivate`
|
||||
rows, err := db.Query(nssQuery)
|
||||
if err != nil {
|
||||
|
||||
@@ -55,7 +55,6 @@ func (p *profile) extract(categories []types.Category) *types.BrowserData {
|
||||
return data
|
||||
}
|
||||
|
||||
// count counts entries per category without decryption.
|
||||
func (p *profile) count(categories []types.Category) map[types.Category]int {
|
||||
session, err := filemanager.NewSession()
|
||||
if err != nil {
|
||||
@@ -76,7 +75,6 @@ func (p *profile) count(categories []types.Category) map[types.Category]int {
|
||||
return counts
|
||||
}
|
||||
|
||||
// acquireFiles copies source files to the session temp directory.
|
||||
func (p *profile) acquireFiles(session *filemanager.Session, categories []types.Category) map[types.Category]string {
|
||||
tempPaths := make(map[types.Category]string)
|
||||
for _, cat := range categories {
|
||||
@@ -114,7 +112,6 @@ func (p *profile) getMasterKey(session *filemanager.Session, tempPaths map[types
|
||||
return retrieveMasterKey(key4Dst, loginsPath)
|
||||
}
|
||||
|
||||
// extractCategory calls the appropriate extract function for a category.
|
||||
func (p *profile) extractCategory(data *types.BrowserData, cat types.Category, masterKey []byte, path string) {
|
||||
var err error
|
||||
switch cat {
|
||||
@@ -140,7 +137,6 @@ func (p *profile) extractCategory(data *types.BrowserData, cat types.Category, m
|
||||
}
|
||||
}
|
||||
|
||||
// countCategory calls the appropriate count function for a category.
|
||||
func (p *profile) countCategory(cat types.Category, path string) int {
|
||||
var count int
|
||||
var err error
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestDiscoverSafariProfiles_OrphanUUIDWithoutDBEntry(t *testing.T) {
|
||||
// Profile directory with a History.db exists on disk but is absent from
|
||||
// SafariTabs.db. When the DB is readable and doesn't mention it, we trust
|
||||
// the DB — the orphan stays hidden because production filters profiles
|
||||
// with no resolvable data in NewBrowsers anyway. Here we assert discovery
|
||||
// with no resolvable data in NewBrowser anyway. Here we assert discovery
|
||||
// returns only what the DB declares.
|
||||
const dbUUID = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"
|
||||
const orphanUUID = "11111111-2222-3333-4444-555555555555"
|
||||
@@ -182,7 +182,7 @@ func TestDiscoverSafariProfiles_DefaultProfileSentinelIgnored(t *testing.T) {
|
||||
func TestDiscoverSafariProfiles_EmptyProfileDirectoryFiltersOutInNewBrowsers(t *testing.T) {
|
||||
// Matches the real 4E2D8DD0 orphan on the author's Mac: a profile dir
|
||||
// listed in neither SafariTabs.db nor containing any extractable data.
|
||||
// Discovery without the DB surfaces it; NewBrowsers then drops it when
|
||||
// Discovery without the DB surfaces it; NewBrowser then drops it when
|
||||
// resolveSourcePaths yields zero matches.
|
||||
const uuid = "4E2D8DD0-A7D2-4684-939A-898B7675C700"
|
||||
library := t.TempDir()
|
||||
|
||||
@@ -107,10 +107,9 @@ func resolveSourcePaths(sources map[types.Category][]sourcePath) map[types.Categ
|
||||
// Offset from the Core Data epoch (2001-01-01 UTC) to the Unix epoch.
|
||||
const coreDataEpochOffset = 978307200
|
||||
|
||||
// maxCoreDataSeconds is the largest CFAbsoluteTime that still lands inside
|
||||
// time.Time.MarshalJSON's [1, 9999] year window. Also bounds the float →
|
||||
// int64 conversion below; Go's spec makes out-of-range conversions return
|
||||
// an implementation-dependent int64, which could silently corrupt results.
|
||||
// maxCoreDataSeconds guards against CFAbsoluteTime values that would exceed
|
||||
// time.Time.MarshalJSON's year-9999 ceiling, and bounds the float→int64
|
||||
// conversion below (Go spec: out-of-range result is implementation-dependent).
|
||||
const maxCoreDataSeconds = 252423993600
|
||||
|
||||
// coredataTimestamp converts Core Data seconds (CFAbsoluteTime) to UTC.
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestNewBrowsers(t *testing.T) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NewBrowsers — multi-profile (macOS 14+ named profiles)
|
||||
// NewBrowser — multi-profile (macOS 14+ named profiles)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNewBrowsers_MultiProfile(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user