# Hash Identifier ## What this is A small Python program that looks at a string of weird-looking characters and tells you what kind of cryptographic hash it probably is. You give it something like this: ``` 5f4dcc3b5aa765d61d8327deb882cf99 ``` and it tells you "that's an MD5 hash" with a reason for why it thinks so. That's the whole job. It does not crack the hash. It does not turn the hash back into a password. It just answers the question "what flavor of hash is this?" — which is the question you have to answer *first* before any other tool will help you. ## Why anyone needs this The first thing that happens when a real attack succeeds is that the attacker walks out with a database dump full of password hashes. The hashes look like nonsense, but they are not random — every hash carries clues about how it was made. Once you know the algorithm (MD5, SHA-256, bcrypt, Argon2, whatever) you can hand the hash to a cracking tool like [hashcat](https://hashcat.net) or [John the Ripper](https://www.openwall.com/john/) and start trying to recover the original password. But here's the thing: hashcat needs you to tell it the algorithm. It has [over 400 hash modes](https://hashcat.net/wiki/doku.php?id=example_hashes), each with a different number. Mode 0 is MD5. Mode 100 is SHA-1. Mode 3200 is bcrypt. If you pick the wrong mode, hashcat will sit there forever and find nothing. So before cracking, you identify. That's this tool. **Real-world moments where you'd reach for this:** - A pentester finds a dump file on a compromised server full of strings like `$2b$12$EixZaYVK1...` and needs to know what to feed hashcat. - A CTF challenge hands you a hash and zero hints about what algorithm made it. - You're reading a breach writeup and want to understand whether the leaked passwords were stored as fast unsalted MD5 (a disaster) or slow salted bcrypt (much better). - The [2012 LinkedIn breach](https://en.wikipedia.org/wiki/2012_LinkedIn_hack) leaked 6.5 million unsalted SHA-1 hashes. The first thing any researcher had to do before doing *anything* was confirm "yes, these are SHA-1." Forty-character hex strings. Easy. The tool you're about to read would have told them that in milliseconds. ## What you will learn **Security ideas:** - What a cryptographic hash actually is (a function that turns any input into a fixed-length jumble that you can't reverse). - The three signals every hash leaks about itself: its **prefix**, its **length**, and its **character set**. - Why modern password hashes (`$2b$...`, `$argon2id$...`) *announce themselves* on purpose, and why old fast hashes (MD5, SHA-1) don't. - The difference between a fast hash (made for speed, terrible for passwords) and a slow hash (made on purpose to resist cracking). - Why you can never recover the password from a hash, only *guess* the password and check if its hash matches. **Python ideas (assuming this is your first time):** - How to read a Python file from top to bottom and understand what it's doing. - What `import` does and where the standard library ends and third-party packages begin. - Functions, type hints (`str`, `int`, `list[str]`), and what `-> bool` means after a function signature. - `@dataclass` — a shortcut for making little record-like objects. - `frozenset`, `dict`, `list`, `tuple` — the core Python containers and when to pick which. - How a command-line tool actually starts running (the `if __name__ == "__main__"` line at the bottom). - How a test file works and why every function in the main code has tests next to it. **Tools you'll touch:** - [`uv`](https://github.com/astral-sh/uv) — the modern Python package manager. Like `pip` but ~100× faster. - [`just`](https://github.com/casey/just) — a command runner. Instead of memorizing long commands, you type `just test` or `just run`. - [`rich`](https://github.com/Textualize/rich) — the library that prints the pretty colored table at the end. - [`pytest`](https://pytest.org) — Python's test runner. - [`ruff`](https://github.com/astral-sh/ruff) + [`mypy`](https://mypy-lang.org) + [`pylint`](https://pylint.org) — the linters that yell at you if your code is wrong, slow, or sloppy. ## What you need before starting **Knowledge you should have:** - You've used a terminal at least once (you know what `cd` and `ls` do). - You vaguely know that "a hash" is a one-way function. If not, [01-CONCEPTS.md](./01-CONCEPTS.md) will get you there in 10 minutes. - You can read code, or at least you're willing to. We will explain every Python feature as we hit it. **Knowledge you do NOT need:** - Any prior Python experience. The whole point of the **foundations** tier is that you start here. - Any prior cybersecurity experience. - Any math beyond "counting." There is no math in this project. Cryptography uses math under the hood, but identifying a hash by its shape doesn't. **Software you need installed:** - Python 3.14 or newer. - The `uv` tool (the install script will get this for you if you don't have it). - The `just` tool (also handled by the install script). - A terminal. Any terminal. On Mac it's Terminal.app or iTerm2; on Linux it's whatever your distro shipped; on Windows it's WSL2 + Ubuntu (we strongly recommend WSL2 instead of native Windows). You do *not* need an IDE — a text editor is fine. We recommend [VS Code](https://code.visualstudio.com) with the Python extension, but `nano`, `vim`, `helix`, or whatever you already use will work. ## Quick start From inside `PROJECTS/foundations/hash-identifier/`: ```bash ./install.sh ``` That script will install `uv` and `just` if missing, create a virtual environment (an isolated Python sandbox just for this project), install all the dependencies, and verify the tests pass. It prints what it's doing as it goes — read the output, don't just close the terminal. Then try the tool: ```bash just run -- 5f4dcc3b5aa765d61d8327deb882cf99 ``` You should see a colored table identifying that string as MD5 (with NTLM, MD4, and RIPEMD-128 as less-likely alternatives — all four produce 32 hex characters, so length alone can't separate them). Try a few more: ```bash # bcrypt — modern password hash, announces itself with the $2b$ prefix just run -- '$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQNQy.uK4Of2T7G' # SHA-256 — 64 hex characters just run -- e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # A JWT (this is NOT a hash, but the tool will say so politely) just run -- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U # Total garbage — the tool will say "no idea" rather than guess just run -- helloworld ``` **Note on quoting:** when a hash starts with `$`, you must wrap it in single quotes (`'$2b$...'`). Without quotes, your shell will try to expand `$2` as a shell variable and chop the hash up. This is a shell thing, not a Python thing — every Unix shell does it. ## Project layout ``` hash-identifier/ ├── hash_identifier.py the whole tool — one file, ~680 lines ├── test_hash_identifier.py tests for every behavior the tool claims to have ├── install.sh one-shot setup script ├── justfile shortcuts for run / test / lint / format ├── pyproject.toml project config: dependencies, linter rules, etc. ├── README.md short pointer to this learn/ folder ├── learn/ you are here │ ├── 00-OVERVIEW.md quick start (this file) │ ├── 01-CONCEPTS.md what hashes are and how identification works │ ├── 02-ARCHITECTURE.md how the code is structured, with diagrams │ ├── 03-IMPLEMENTATION.md line-by-line walkthrough of the code │ └── 04-CHALLENGES.md extension ideas if you want to go further └── assets/ images, screenshots ``` One file of code is on purpose. The foundations tier is meant to be readable in one sitting. The intermediate and advanced tiers split into many files; foundations does not. ## Where to go next 1. **[01-CONCEPTS.md](./01-CONCEPTS.md)** — understand *what* a hash is, *why* identification is the first move, and *how* prefix/length/charset clues actually work. Read this even if you think you know it; the framing matters. 2. **[02-ARCHITECTURE.md](./02-ARCHITECTURE.md)** — see the six-step pipeline the tool uses to make a decision, drawn out as a flow diagram. 3. **[03-IMPLEMENTATION.md](./03-IMPLEMENTATION.md)** — read `hash_identifier.py` with us, line by line. Every Python feature gets explained when it first appears. 4. **[04-CHALLENGES.md](./04-CHALLENGES.md)** — extensions you can try on your own once you've absorbed the rest. ## Common problems **"command not found: just"** The install script should set this up, but if it didn't: `curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash`. Then close and reopen your terminal so it sees the new tool. **"command not found: uv"** Same idea: `curl -LsSf https://astral.sh/uv/install.sh | sh`, then reopen your terminal. **`just run -- $2b$12$...` chops the hash up** You forgot the single quotes around the hash. Re-run with `just run -- '$2b$12$...'`. **"ModuleNotFoundError: No module named 'rich'"** You ran `python hash_identifier.py` directly instead of `just run`. The `just run` recipe uses the virtual environment that has `rich` installed. Either use `just run`, or activate the venv first: `source .venv/bin/activate`, *then* `python hash_identifier.py `. **Tests fail right after install** Tests should pass on a fresh `./install.sh`. If they don't, you probably have an older Python (run `python --version`; you need 3.14+). On Ubuntu, install it via [`deadsnakes`](https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa); on Mac use [Homebrew](https://brew.sh).