Hand someone a string like 5f4dcc3b5aa765d61d8327deb882cf99 or
$argon2id$v=19$m=65536,t=3,p=4$... and the first question is always the same:
what produced this, and is it still safe?
Saltminer answers both. It takes a hash string and tells you:
- What it is — ranked algorithm candidates, each with a confidence level and a plain-English reason.
- Whether it's safe — a security verdict against current OWASP Password Storage guidance: is the algorithm modern, and are its cost parameters strong enough?
It runs fully offline — no network, no files, no telemetry. A single Rust engine powers three interfaces: a command-line tool, a desktop GUI, and a Python library.
- Identify 45+ hash formats by prefix, length, and character set — bcrypt, the Argon2 family, the Unix crypt family (SHA-512/256, MD5, yescrypt, DES), phpass (WordPress/phpBB), LDAP schemes, Django, MySQL, NetNTLMv1/v2, pwdump/NTLM, and bare hex digests (MD5, SHA-1, SHA-256, SHA-512, and more), plus a generic PHC fallback for anything unrecognised.
- Security auditing — parses PHC cost parameters and judges them against OWASP
thresholds:
secure,weak-params,deprecated, orbroken, each with a reason. - Recognises non-hashes — tells you when you've pasted a JWT or a base64 blob instead of leaving you guessing.
- Three interfaces, one engine — CLI, GUI, and a pip-installable Python package, all sharing the exact same identification logic.
- Offline and dependency-light — pure logic, no network or filesystem access.
- Tested hard — unit tests, property-based tests (
proptest), and coverage-guided fuzzing (cargo-fuzz) running in CI.
pip install saltminerimport saltminer
saltminer.identify("5f4dcc3b5aa765d61d8327deb882cf99")
# [('MD5', 'medium', '32 hex chars — most likely at this length'),
# ('NTLM', 'low', '32 hex chars — also possible at this length'), ...]
saltminer.audit("$2b$04$abcdefghijklmnopqrstuv")
# ('bcrypt', 'WeakParams', 'cost 4 is below the minimum of 10')Wheels are published for Windows and Linux (Python 3.10+).
Download the prebuilt binaries for your platform from the latest release, or build them from source (see below).
saltminer <hash> [--color <colour>] [--audit]| Flag | Description |
|---|---|
--audit, -a |
Also print the OWASP security verdict. |
--color, -c |
Output colour: red, orange, yellow, green, blue, indigo, violet. |
Examples:
saltminer 5f4dcc3b5aa765d61d8327deb882cf99
saltminer --audit '$2b$04$abcdefghijklmnopqrstuv'
saltminer --color violet --audit '$argon2id$v=19$m=1024,t=1,p=1$c2FsdA$aGFzaA'Note: always wrap a hash that starts with
$in single quotes, or your shell will try to expand$2,$1, etc. as variables and mangle the input.
Exit codes: 0 when at least one candidate is found, 1 when nothing matches
(useful in scripts).
Launch the saltminer-gui binary. Paste a hash into the field and the candidates
and audit verdict appear live, colour-coded by severity.
import saltminer
candidates = saltminer.identify("$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQNQ")
report = saltminer.audit("$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQNQ")identify() returns a list of (algorithm, confidence, reason) tuples;
audit() returns an (algorithm, verdict, detail) tuple, or None for formats
it does not rate.
identify() runs a fixed sequence of checks and returns the first that matches,
so the strongest signal always wins:
- Prefix rules — a self-describing marker like
$2b$(bcrypt) or$argon2id$is definitive → high confidence. - Special shapes — formats with an unmistakable structure but no prefix:
MySQL5 (
*+ 40 uppercase hex), NetNTLMv1/v2, and pwdump/NTLM (user:rid:lm:nt:::). - Length + charset — a bare hex string is matched by its length (32 → MD5, 40 → SHA-1, 64 → SHA-256, …), ranked by real-world prevalence: the most likely candidate gets medium confidence, the rest low.
- Not-a-hash hints — a leading
eyJ(a JWT) or base64-only characters (+,/,=) are flagged so you know what you actually pasted.
For hashes with cost parameters, audit() parses them and compares against
OWASP guidance:
| Algorithm | Requirement for a secure verdict |
|---|---|
| Argon2id | memory ≥ 19 MiB |
| bcrypt | cost factor ≥ 10 |
| PBKDF2-HMAC-SHA256 | ≥ 600,000 iterations |
MD5-crypt ($1$), Apache $apr1$ |
always deprecated |
| raw MD5 / SHA-1 / NTLM | always broken (fast, unsalted) |
Everything hard lives in one pure, I/O-free Rust library. The CLI, GUI, and Python module are thin shells over it — a rule written once is correct in all three.
flowchart LR
core["saltminer-core<br/>rules · PHC parser · OWASP audit<br/>(pure, no I/O)"]
core --> cli["saltminer-cli<br/>terminal (clap)"]
core --> gui["saltminer-gui<br/>desktop (egui)"]
core --> py["saltminer<br/>Python package (PyO3)"]
Saltminer/
├─ crates/
│ ├─ saltminer-core/ # the engine: identify() + audit(), and all tests
│ ├─ saltminer-cli/ # command-line interface (clap)
│ ├─ saltminer-gui/ # desktop app (eframe / egui)
│ └─ saltminer-py/ # Python bindings (PyO3), published to PyPI
├─ fuzz/ # cargo-fuzz target for the parser
├─ .github/workflows/ # CI + release automation
└─ docs/ # banner and screenshots
To use the Python package: Python 3.10 or newer — that's it (pip install saltminer).
To build from source:
- Rust 1.98+ (stable) — install from rustup.rs.
- just and cargo-nextest (optional, for the dev shortcuts):
cargo install just cargo-nextest. - Building the GUI on Linux additionally needs the X11/Wayland development libraries:
sudo apt install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \ libxkbcommon-dev libwayland-dev
- Building the Python wheel needs uv and
maturin (
uv tool install maturin).
git clone https://github.com/DebanganMALI/Salt-miner.git
cd Salt-miner
# core + CLI
cargo build --release
cargo run -p saltminer-cli -- 5f4dcc3b5aa765d61d8327deb882cf99
# desktop GUI
cargo run --manifest-path crates/saltminer-gui/Cargo.toml
# Python module into a local environment
cd crates/saltminer-py
uv venv --python 3.13
uv pip install maturin
uv run maturin developWith just installed you can use the shortcuts instead: just test, just lint,
just build, just run <hash>, just gui.
- Unit + property tests:
just test(orcargo nextest run). Property tests useproptestto throw thousands of random inputs at the engine and prove it never panics. - Lint:
just lintrunsclippywith warnings denied, plus a formatting check. - Fuzzing: a
cargo-fuzztarget runs against the PHC parser in CI (Linux). - CI: every push runs format, clippy, tests, and a fuzz smoke-run; tagged releases build and publish the wheels and binaries automatically.
Inspired by CarterPerez-dev/Cybersecurity-Projects'
hash-identifier, reimagined in Rust with an added OWASP security auditor.
Audit thresholds follow the OWASP Password Storage Cheat Sheet.
Released under the MIT License. © 2026 Debangan Mali.
Debangan Mali GitHub @DebanganMALI · LinkedIn · debanganmali.social@yahoo.com


