Project Overview
SHA-1 Password Cracker — Browser Demo
A small, safe demo that demonstrates a corrected and optimized SHA‑1 password cracking approach using a top-passwords list and optional salts. The demo runs entirely in the browser using the Web Crypto API and exposes the same algorithmic improvements implemented in the Python version:
- Unsalted: precompute a hash → password map for constant-time lookups.
- Salted: iterate salts outer loop and passwords inner loop with early exit to avoid building the full cross-product in memory.
- Friendly UI to load custom
top-10000-passwords.txtandknown-salts.txt, or use the built-in sample lists for quick testing.
Bug Fix or Performance Improvement
Problem resolved
- Inconsistent encoding and repeated hashing caused incorrect comparisons and wasted CPU cycles.
- A naive salted implementation that materialized all salt×password combinations used excessive memory and was slow.
What I fixed / optimized
- Correctness: Always encode strings as UTF‑8 bytes before hashing so the digest matches server-side/other-language implementations.
-
Performance (unsalted): Build a single
hash -> passwordmap once and use O(1) lookups for each query. - Performance (salted): Avoid building the full cross-product. Iterate salts outer loop and passwords inner loop, compute salted hashes on the fly, and return immediately on match.
-
Responsiveness: Batch hashing and
awaityields to keep the UI responsive during heavy work.
Code
Files produced
-
index.html— UI and structure -
style.css— styling and layout -
script.js— core logic, hashing, caching, and UI wiring
Key implementation excerpts
- Unsalted map builder (JS) — precomputes SHA‑1 hex → password:
async function buildUnsaltedMap(passwords, progressCallback) {
const map = Object.create(null);
const batch = 256;
for (let i = 0; i < passwords.length; i += batch) {
const slice = passwords.slice(i, i + batch);
const promises = slice.map(p => sha1HexFromString(p));
const hashes = await Promise.all(promises);
for (let j = 0; j < hashes.length; j++) map[hashes[j]] = slice[j];
if (progressCallback) progressCallback(Math.round(((i + slice.length) / passwords.length) * 100));
await new Promise(r => setTimeout(r, 0));
}
return map;
}
- Salted search with early exit (JS):
async function saltedSearch(target, passwords, salts, progressCallback) {
for (let s = 0; s < salts.length; s++) {
const saltBytes = new TextEncoder().encode(salts[s]);
for (let i = 0; i < passwords.length; i += 128) {
const slice = passwords.slice(i, i + 128);
const results = await Promise.all(slice.map(async pwd => {
const pwdBytes = new TextEncoder().encode(pwd);
const aBuf = new Uint8Array(saltBytes.length + pwdBytes.length);
aBuf.set(saltBytes, 0); aBuf.set(pwdBytes, saltBytes.length);
if (await sha1HexFromBytes(aBuf) === target) return pwd;
const bBuf = new Uint8Array(pwdBytes.length + saltBytes.length);
bBuf.set(pwdBytes, 0); bBuf.set(saltBytes, pwdBytes.length);
if (await sha1HexFromBytes(bBuf) === target) return pwd;
return null;
}));
for (const r of results) if (r) return r;
await new Promise(r => setTimeout(r, 0));
}
}
return null;
}
If you want the full files, they are the three artifacts created for this submission: index.html, style.css, and script.js (the full contents were generated and are ready to drop into a static site).
My Improvements (technical approach)
Deterministic encoding
Always useTextEncoder/ UTF‑8 before hashing. This prevents mismatches between environments and languages.-
Memory vs. speed tradeoffs
- For unsalted cracking, a single
hash -> passwordmap is the fastest approach and uses O(N) memory (N = number of passwords). This is ideal for repeated lookups. - For salted cracking, building a full salt×password map is O(S×N) memory and unnecessary. Iterating salts outer loop and passwords inner loop keeps memory usage low and allows early exit on match.
- For unsalted cracking, a single
-
UI responsiveness
- Hashing is batched and
awaityields are used so the browser event loop can update the UI and remain responsive. - Progress updates are reported during both map building and salted searches.
- Hashing is batched and
-
Robust file handling
- Uploaded files are parsed into trimmed, non-empty lines.
- The demo falls back to small sample lists if no files are provided.
-
Caching
- The unsalted map and loaded lists are cached in memory for the session to speed repeated queries. A “Clear cache” button is provided.
Best Use of Sentry
This submission is primarily a focused bug fix and optimization demo; it does not include a production backend. If you were to integrate this into a larger application, here’s how Sentry would be used to improve reliability and observability:
- Error Monitoring: Capture exceptions from the client (e.g., file parsing errors, Web Crypto failures) and group by stack trace to prioritize fixes.
- Session Replay: Record user sessions where errors occur to reproduce UI states that led to failures (file formats, large lists, or slow devices).
- Performance Monitoring / Tracing: Instrument the heavy operations (map building, salted search) to measure CPU time and identify slow devices or pathological inputs.
- Logs: Attach structured logs for long-running operations (progress percentages, batch durations) to correlate with traces and errors.
Sample Sentry integration snippet (conceptual, client-side):
Sentry.init({ dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0' });
Sentry.captureMessage('Started unsalted map build', { level: 'info', extra: { passwordCount: N } });
Best Use of Google AI
This project did not require generative AI to implement the core fix. However, Google AI tools could be used to:
- Automated test generation: Generate edge-case test inputs for salted/unsalted combinations.
- Performance analysis: Use AI to analyze profiling traces and recommend batching sizes or concurrency strategies for different device classes.
- Documentation: Produce clear, accessible explanations and interactive tutorials for students learning about hashing and salts.
Closing notes
- Ethics & safety: This demo is educational. It demonstrates why weak password choices and unsalted hashes are insecure. Do not use this tool for unauthorized access or malicious activity.
-
Next steps: If you’d like, I can:
- Provide a small PR-style diff for the Python implementation.
- Add caching persistence (e.g., IndexedDB) for the unsalted map.
- Add a downloadable report of the cracking attempt (local-only, client-side).
Thanks for running the Summer Bug Smash — this submission focuses on a compact, high-impact correctness and performance improvement with a clear, testable demo.
Top comments (1)
Hackers can strip computer users for there passwords all over the world this I hope will be an important milestone for developers to take precautionary steps to stay ahead of the game.