moonD4rk/HackBrowserData · error

unlock keychain

Error message

unlock keychain: %w

What it means

The keychain opened successfully but kc.Unlock(keychainbreaker.WithPassword(password)) failed; the error is wrapped as "unlock keychain: %w". Unlocking the login keychain requires the correct user login password.

Solutions

  1. Confirm the Password field matches the current macOS login password of the keychain owner
  2. Test the password manually: unlock the keychain in Keychain Access or via `security unlock-keychain`
  3. If the keychain password differs from the login password, supply the keychain-specific password
  4. Ensure the keychain is not locked: `security show-keychain-info login.keychain-db`

Example fix

// before
r := &masterkey.KeychainPasswordRetriever{Password: "password123"}
// after
pw := os.Getenv("USER_LOGIN_PASSWORD")
if pw == "" {
	return nil, errors.New("USER_LOGIN_PASSWORD not set")
}
r := &masterkey.KeychainPasswordRetriever{Password: pw}
Defensive patterns

Strategy: validation

Validate before calling

if r.Password == "" {
	return errors.New("keychain password required")
}
// optional pre-check
cmd := exec.Command("security", "unlock-keychain", "-p", r.Password)
if err := cmd.Run(); err != nil {
	return fmt.Errorf("password rejected: %w", err)
}

Type guard

func (r *masterkey.KeychainPasswordRetriever) Configured() bool {
	return r.Password != ""
}

Try / catch

key, err := r.RetrieveKey(hints)
if err != nil && strings.HasPrefix(err.Error(), "unlock keychain:") {
	return nil, errors.New("wrong login password for keychain")
}

Prevention

When it happens

Trigger: KeychainPasswordRetriever.RetrieveKey with a Password that is empty-check-passed but wrong, or when the keychain is locked and cannot be unlocked with the supplied password.

Common situations: User supplied the login password of a different account, the password was changed recently and the keychain kept the old one, or automation runs headless where the UI unlock prompt can't be answered.

Related errors


AI-assisted analysis of moonD4rk/HackBrowserData@0503d04d7a (2026-09-06). Data as JSON: /api/errors/fdb57050b423736e. Report an issue: GitHub.

Appendix: source

Thrown at masterkey/retriever_darwin.go:64

		log.Debugf("gcoredump: %v", r.err)
		return nil, nil //nolint:nilerr // intentional silent fallthrough
	}

	key, err := findStorageKey(r.records, hints.KeychainLabel)
	if err != nil {
		log.Debugf("gcoredump: %v", err)
		return nil, nil //nolint:nilerr // intentional silent fallthrough
	}
	return key, nil
}

func loadKeychainRecords(password string) ([]keychainbreaker.GenericPassword, error) {
	kc, err := keychainbreaker.Open()
	if err != nil {
		return nil, fmt.Errorf("open keychain: %w", err)
	}
	if err := kc.Unlock(keychainbreaker.WithPassword(password)); err != nil {
		return nil, fmt.Errorf("unlock keychain: %w", err)
	}
	return kc.GenericPasswords()
}

func findStorageKey(records []keychainbreaker.GenericPassword, storage string) ([]byte, error) {
	for _, rec := range records {
		if rec.Account == storage {
			return darwinParams.deriveKey(rec.Password), nil
		}
	}
	return nil, fmt.Errorf("%q: %w", storage, errStorageNotFound)
}

// KeychainPasswordRetriever unlocks login.keychain-db with the macOS login password (no root).
// Records are cached once and reused across browsers.
type KeychainPasswordRetriever struct {
	Password string

View on GitHub (pinned to 0503d04d7a)