Files
HackBrowserData/crypto/keyretriever/keyretriever.go
T
Roger 9fb5165fcb feat: add crypto/keyretriever with keychainbreaker integration (#518)
* feat: add crypto/keyretriever package for Chromium master key retrieval

* feat: complete keyretriever with gcoredump, chainbreaker, and tests

* refactor: replace internal chainbreaker with keychainbreaker v0.1.0

Replace the incomplete internal chainbreaker implementation (~1400 lines
of duplicated code) with the external keychainbreaker package, which
provides a complete, well-tested keychain parsing library.

Changes:
- Add github.com/moond4rk/keychainbreaker v0.1.0 dependency
- Update gcoredump_darwin.go to use keychainbreaker API (Open/Unlock/GenericPasswords)
- Add KeychainPasswordRetriever for password-based keychain unlocking
  with sync.Once caching across multiple browser queries
- Unify DefaultRetriever(keychainPassword string) signature across all platforms
- Delete utils/chainbreaker/ (696 lines + test + testdata)
- Delete crypto/keyretriever/chainbreaker_darwin.go (696 lines duplicate)
- Delete browser/exploit/gcoredump/ (duplicate of keyretriever version)
- Update chromium_darwin.go to use keyretriever.DecryptKeychain
- Clean up .golangci.yml lint exceptions and .gitignore entries
- Use errors.Is() instead of == for context.DeadlineExceeded check

* refactor: improve gcoredump exploit code quality and add comments
* fix: address Copilot review feedback on keyretriever
2026-04-04 01:41:01 +08:00

41 lines
1.3 KiB
Go

package keyretriever
import (
"errors"
"fmt"
)
// KeyRetriever retrieves the master encryption key for a Chromium-based browser.
// Each platform has different implementations:
// - macOS: Keychain access (security command) or gcoredump exploit
// - Windows: DPAPI decryption of Local State file
// - Linux: D-Bus Secret Service or fallback to "peanuts" password
type KeyRetriever interface {
RetrieveKey(storage, localStatePath string) ([]byte, error)
}
// ChainRetriever tries multiple retrievers in order, returning the first success.
// Used on macOS (gcoredump → password → security) and Linux (D-Bus → peanuts).
type ChainRetriever struct {
retrievers []KeyRetriever
}
// NewChain creates a ChainRetriever that tries each retriever in order.
func NewChain(retrievers ...KeyRetriever) KeyRetriever {
return &ChainRetriever{retrievers: retrievers}
}
func (c *ChainRetriever) RetrieveKey(storage, localStatePath string) ([]byte, error) {
var errs []error
for _, r := range c.retrievers {
key, err := r.RetrieveKey(storage, localStatePath)
if err == nil && len(key) > 0 {
return key, nil
}
if err != nil {
errs = append(errs, fmt.Errorf("%T: %w", r, err))
}
}
return nil, fmt.Errorf("all retrievers failed: %w", errors.Join(errs...))
}