busyTimeout(5000); $st = $db->prepare('SELECT id, username, password_hash, salt, hash_algo FROM users WHERE username=:u'); $st->bindValue(':u', $u, SQLITE3_TEXT); $row = $st->execute()->fetchArray(SQLITE3_ASSOC); if (!$row) { echo "User '$u' not found.\n"; exit; } echo "=== Stored in vault.db ===\n"; echo "id : " . $row['id'] . "\n"; echo "username : " . $row['username'] . "\n"; echo "hash_algo : " . ($row['hash_algo'] ?? '(null)') . "\n"; echo "salt : " . $row['salt'] . "\n"; echo "salt len : " . strlen($row['salt']) . "\n"; echo "stored hash : " . $row['password_hash'] . "\n"; echo "stored len : " . strlen($row['password_hash']) . "\n\n"; $algo = $row['hash_algo'] ?? 'pbkdf2'; if ($algo === 'pbkdf2') { $computed = hash_pbkdf2('sha256', $p, $row['salt'], 100000); echo "=== PHP hash_pbkdf2('sha256', '$p', salt, 100000) ===\n"; echo "computed : $computed\n"; echo "computed len : " . strlen($computed) . "\n\n"; $match = hash_equals($row['password_hash'], $computed); echo "Match : " . ($match ? 'YES ✓ (PHP would let this user in)' : 'NO ✗ (Delphi & PHP disagree on PBKDF2)') . "\n"; if (!$match) { echo "\nFirst chars side by side:\n"; echo "Stored : " . substr($row['password_hash'], 0, 32) . "\n"; echo "Computed : " . substr($computed, 0, 32) . "\n"; } } else { echo "Account uses BCRYPT, not PBKDF2. Cannot diagnose PBKDF2 mismatch here.\n"; } $db->close();