moonD4rk/HackBrowserData · error

tried %d candidates, none unlocked keychain

Error message

tried %d candidates, none unlocked keychain

What it means

After extracting N candidate 24-byte keys from the securityd core, each is tried against the login keychain via keychainbreaker. This error reports that every candidate failed to unlock (or produced zero records), i.e. the true master key was not among the candidates.

Source

Thrown at masterkey/gcoredump_darwin.go:123

	for _, candidate := range candidates {
		kc, err := keychainbreaker.Open(keychainbreaker.WithBytes(keychainBuf))
		if err != nil {
			continue
		}
		if err := kc.Unlock(keychainbreaker.WithKey(candidate)); err != nil {
			continue
		}

		records, err := kc.GenericPasswords()
		if err != nil {
			continue
		}
		if len(records) > 0 {
			return records, nil
		}
	}

	return nil, fmt.Errorf("tried %d candidates, none unlocked keychain", len(candidates))
}

// scanMasterKeyCandidates scans the core dump for 24-byte master key candidates.
//
// securityd stores the master key in a MALLOC_SMALL region with the layout:
//
//	[0x18 (8 bytes)] [pointer to key data (8 bytes)]
//
// 0x18 = 24 is the key length. The pointer references a 24-byte buffer
// within the same region containing the raw master key.
func scanMasterKeyCandidates(corePath string, regions []addressRange) ([]string, error) {
	cmf, err := macho.Open(corePath)
	if err != nil {
		return nil, fmt.Errorf("failed to open core dump: %w", err)
	}
	defer cmf.Close()

	var candidates []string

View on GitHub (pinned to 0503d04d7a)

Solutions

  1. Unlock the keychain and perform a keychain operation first so the master key is materialized in securityd memory, then re-run.
  2. Review scanMasterKeyCandidates' heuristic (0x18 length marker + intra-region pointer) against the actual securityd memory layout on your macOS build; adjust if needed.
  3. Increase coverage: scan other heap regions (MALLOC_LARGE, default zone) if MALLOC_SMALL misses the key on this version.
  4. Verify the keychain file being opened matches the securityd instance's user (same HOME) — a mismatched keychain will never unlock.
  5. Check the count in the message: if it is 0, the real problem is upstream (error 55 — no candidates at all).
Defensive patterns

Strategy: fallback

Validate before calling

// ensure keychain is unlocked first
err := exec.Command("security", "unlock-keychain", "-p", password, "login.keychain").Run()
if err != nil {
    return fmt.Errorf("keychain could not be unlocked; memory candidates will not work")
}

Try / catch

records, err := masterkey.DecryptKeychainRecords()
if err != nil && strings.Contains(err.Error(), "none unlocked keychain") {
    // fall back to explicit user-provided keychain password path
}

Prevention

When it happens

Trigger: Calling DecryptKeychainRecords when all scanned candidates fail kc.Unlock or return zero generic passwords — the memory-scan heuristic matched decoy 0x18-length patterns but not the real master key, or the real key was not resident in the dumped MALLOC_SMALL regions.

Common situations: macOS versions where securityd stores the key with a different layout so the true key is missed; keychain locked/never unlocked so key not in memory; keychain protected by additional security measures; scan timing (dump taken before keychain use).

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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