Documentation
¶
Overview ¶
Package keychainbreaker parses and decrypts macOS Keychain files (login.keychain-db).
The library implements Apple's CSSM-based keychain binary format parser with dynamic schema discovery, and supports decrypting stored credentials using Triple-DES CBC encryption.
Quick Start ¶
Open the default system keychain and unlock with password:
kc, err := keychainbreaker.Open()
if err != nil {
log.Fatal(err)
}
if err := kc.Unlock(keychainbreaker.WithPassword("your-macos-login-password")); err != nil {
log.Fatal(err)
}
passwords, err := kc.GenericPasswords()
if err != nil {
log.Fatal(err)
}
for _, p := range passwords {
fmt.Printf("Service: %s, Account: %s, Password: %s\n", p.Service, p.Account, p.Password)
}
Open Options ¶
Three ways to open a keychain:
- Open() -- default system login keychain
- Open(WithFile) -- specific file path
- Open(WithBytes) -- in-memory buffer
Unlock Methods ¶
Two unlock methods are supported:
- WithPassword: derives master key from password via PBKDF2-HMAC-SHA1
- WithKey: uses a hex-encoded 24-byte master key directly (from memory forensics)
Package keychainbreaker parses and decrypts macOS Keychain files.
Index ¶
- Variables
- type Certificate
- type GenericPassword
- type InternetPassword
- type Keychain
- func (kc *Keychain) Certificates() ([]Certificate, error)
- func (kc *Keychain) GenericPasswords() ([]GenericPassword, error)
- func (kc *Keychain) InternetPasswords() ([]InternetPassword, error)
- func (kc *Keychain) PasswordHash() (string, error)
- func (kc *Keychain) PrivateKeys() ([]PrivateKey, error)
- func (kc *Keychain) TryUnlock(opts ...UnlockOption) error
- func (kc *Keychain) Unlock(opt UnlockOption) error
- func (kc *Keychain) Unlocked() bool
- type Logger
- type OpenOption
- type PrivateKey
- type UnlockOption
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidSignature is returned when the file is not a valid keychain. ErrInvalidSignature = errors.New("keychainbreaker: invalid keychain signature") // ErrParseFailed is returned when the keychain file structure cannot be parsed. ErrParseFailed = errors.New("keychainbreaker: parse failed") )
Errors returned by Open.
var ( // ErrLocked is returned when the keychain has not been unlocked yet. ErrLocked = errors.New("keychainbreaker: keychain is locked") )
Errors returned by record extraction methods.
var ( // ErrWrongKey is returned when the provided key or password is incorrect. ErrWrongKey = errors.New("keychainbreaker: wrong key or password") )
Errors returned by Unlock.
Functions ¶
This section is empty.
Types ¶
type Certificate ¶
type Certificate struct {
Data []byte `json:"-"`
DataHex string `json:"data_hex,omitempty"`
DataBase64 string `json:"data_base64,omitempty"`
Type uint32 `json:"type,omitempty"`
Encoding uint32 `json:"encoding,omitempty"`
PrintName string `json:"print_name,omitempty"`
Subject []byte `json:"-"`
SubjectHex string `json:"subject_hex,omitempty"`
Issuer []byte `json:"-"`
IssuerHex string `json:"issuer_hex,omitempty"`
Serial []byte `json:"-"`
SerialHex string `json:"serial_hex,omitempty"`
}
Certificate represents an X.509 certificate record.
type GenericPassword ¶
type GenericPassword struct {
Service string `json:"service,omitempty"`
Account string `json:"account,omitempty"`
Password []byte `json:"-"`
PlainPassword string `json:"password,omitempty"` //nolint:gosec // intentional credential export
HexPassword string `json:"hex_password,omitempty"`
Base64Password string `json:"base64_password,omitempty"`
Description string `json:"description,omitempty"`
Comment string `json:"comment,omitempty"`
Creator string `json:"creator,omitempty"`
Type string `json:"type,omitempty"`
PrintName string `json:"print_name,omitempty"`
Alias string `json:"alias,omitempty"`
Created time.Time `json:"created_at,omitempty"`
Modified time.Time `json:"modified_at,omitempty"`
}
GenericPassword represents a generic password record.
type InternetPassword ¶
type InternetPassword struct {
Server string `json:"server,omitempty"`
Account string `json:"account,omitempty"`
Password []byte `json:"-"`
PlainPassword string `json:"password,omitempty"` //nolint:gosec // intentional credential export
HexPassword string `json:"hex_password,omitempty"`
Base64Password string `json:"base64_password,omitempty"`
SecurityDomain string `json:"security_domain,omitempty"`
Protocol string `json:"protocol,omitempty"`
AuthType string `json:"auth_type,omitempty"`
Port uint32 `json:"port,omitempty"`
Path string `json:"path,omitempty"`
Description string `json:"description,omitempty"`
Comment string `json:"comment,omitempty"`
Creator string `json:"creator,omitempty"`
Type string `json:"type,omitempty"`
PrintName string `json:"print_name,omitempty"`
Alias string `json:"alias,omitempty"`
Created time.Time `json:"created_at,omitempty"`
Modified time.Time `json:"modified_at,omitempty"`
}
InternetPassword represents an internet password record.
type Keychain ¶
type Keychain struct {
// contains filtered or unexported fields
}
Keychain represents a parsed macOS keychain file.
func Open ¶
func Open(opts ...OpenOption) (*Keychain, error)
Open parses a keychain file and returns a locked Keychain. Call Unlock before extracting records.
With no options, it opens the default macOS login keychain (~/Library/Keychains/login.keychain-db):
kc, err := keychainbreaker.Open()
With a specific file path:
kc, err := keychainbreaker.Open(keychainbreaker.WithFile("/path/to/keychain"))
From an in-memory buffer:
kc, err := keychainbreaker.Open(keychainbreaker.WithBytes(buf))
func (*Keychain) Certificates ¶
func (kc *Keychain) Certificates() ([]Certificate, error)
Certificates returns all X.509 certificate records. Returns ErrLocked if neither Unlock nor TryUnlock has been called. Certificates are not encrypted, so they are always fully available regardless of whether decryption succeeded.
func (*Keychain) GenericPasswords ¶
func (kc *Keychain) GenericPasswords() ([]GenericPassword, error)
GenericPasswords returns all generic password records. Returns ErrLocked if neither Unlock nor TryUnlock has been called. When TryUnlock is used and decryption fails, metadata fields are returned but Password will be nil.
func (*Keychain) InternetPasswords ¶
func (kc *Keychain) InternetPasswords() ([]InternetPassword, error)
InternetPasswords returns all internet password records. Returns ErrLocked if neither Unlock nor TryUnlock has been called. When TryUnlock is used and decryption fails, metadata fields are returned but Password will be nil.
func (*Keychain) PasswordHash ¶
PasswordHash exports the keychain password hash for offline cracking. Format: $keychain$*<salt_hex>*<iv_hex>*<ciphertext_hex> Compatible with hashcat mode 23100 and John the Ripper. Does not require Unlock.
func (*Keychain) PrivateKeys ¶
func (kc *Keychain) PrivateKeys() ([]PrivateKey, error)
PrivateKeys returns all private key records. Returns ErrLocked if neither Unlock nor TryUnlock has been called. When TryUnlock is used and decryption fails, metadata fields are returned but Name and Data will be empty.
func (*Keychain) TryUnlock ¶ added in v0.2.0
func (kc *Keychain) TryUnlock(opts ...UnlockOption) error
TryUnlock attempts to decrypt the keychain, but does not block record extraction on failure. If the credential is wrong or missing, extraction methods still return record metadata (service, account, timestamps, etc.) with encrypted fields (passwords, private key data) set to nil.
TryUnlock returns any unlock error (e.g. ErrWrongKey) for informational purposes, but the caller can safely ignore it and proceed with extraction.
func (*Keychain) Unlock ¶
func (kc *Keychain) Unlock(opt UnlockOption) error
Unlock decrypts the keychain using the provided credential. After a successful Unlock, record extraction methods become available. If Unlock fails, extraction methods return ErrLocked. Use TryUnlock instead if you want to extract metadata even when the password is wrong or unavailable.
type Logger ¶ added in v0.2.5
type Logger interface {
Debug(msg string, keysAndValues ...any)
Info(msg string, keysAndValues ...any)
Warn(msg string, keysAndValues ...any)
Error(msg string, keysAndValues ...any)
}
Logger is the interface for diagnostic logging within keychainbreaker. Implement this interface to receive verbose diagnostic messages from the library during parsing and unlock operations.
The keysAndValues arguments are key-value pairs (e.g., "saltLen", 20).
The default logger is silent (no-op). Use WithLogger to inject a custom implementation.
type OpenOption ¶
type OpenOption func(*openConfig)
OpenOption configures how to open a keychain.
func WithBytes ¶
func WithBytes(buf []byte) OpenOption
WithBytes provides keychain data from an in-memory buffer.
func WithLogger ¶ added in v0.2.5
func WithLogger(l Logger) OpenOption
WithLogger sets a custom logger for diagnostic output. By default, the library is silent (no-op logger).
type PrivateKey ¶
type PrivateKey struct {
Name string `json:"name,omitempty"`
Data []byte `json:"-"`
DataHex string `json:"data_hex,omitempty"`
DataBase64 string `json:"data_base64,omitempty"`
PrintName string `json:"print_name,omitempty"`
Label string `json:"label,omitempty"`
KeyClass uint32 `json:"key_class,omitempty"`
KeyType uint32 `json:"key_type,omitempty"`
KeySize uint32 `json:"key_size,omitempty"`
}
PrivateKey represents a private key record.
type UnlockOption ¶
type UnlockOption func(*unlockConfig)
UnlockOption configures how to unlock the keychain.
func WithKey ¶
func WithKey(hexKey string) UnlockOption
WithKey unlocks using a raw hex-encoded 24-byte master key.
func WithPassword ¶
func WithPassword(password string) UnlockOption
WithPassword unlocks using the keychain password. The master key is derived via PBKDF2-HMAC-SHA1 with the salt from the keychain file. An empty password is treated as a valid unlock attempt, not as "no password provided".