How file-to-hash extraction works
A technical primer on why password-protected files are crackable, what the *2john format does, and how Hash Extractor pulls it off in WebAssembly.
Most security tooling that touches encrypted files starts the same way: before you can crack anything, you need to extract the right bytes from the file. This post explains what those bytes are, why they exist, and how Hash Extractor turns a binary file into a string a password cracker understands — entirely in the browser, without your file leaving your machine.
Password protection does not store your password
When you set a password on a PDF, a KeePass database, or a ZIP archive, the application does not store your password — doing so would make it trivially readable by anyone with file access. Instead it stores the material needed to verify that a future candidate password is correct. That typically involves:
- A salt — random bytes generated at encryption time, unique per file.
- KDF parameters — the key derivation function and its cost settings (iteration count, memory parameters, etc.) that slow down brute-force attempts.
- A verifier — either a small block of known plaintext encrypted with the derived key, or a wrapped copy of the actual data key that can be decrypted and then checked.
Cracking means running a candidate password through the same KDF with the same salt and cost parameters, then testing whether the result decrypts the verifier correctly. The actual file data is never touched during cracking.
Extraction is the act of reading those fields out of the file container and encoding them into a compact string that a cracker like hashcat or John the Ripper can parse.
The *2john family
John the Ripper ships a collection of Python/Perl scripts — pdf2john.pl, zip2john, keepass2john, and so on — each one a parser for a specific file format. They read the relevant fields and print a normalized hash string to stdout. That string is what you feed to john or hashcat.
The strings follow a $tag$field1$field2$... convention, where $tag$ identifies the algorithm and cracker mode. A few examples:
$pdf$5*6*256*-4*1*16*...*32*...*32*...
$keepass$*2*60000*222*...*...*...*...*...*1*...*...
$zip2$*0*3*0*...*...*...*0*
The --format= flag in John and the -m mode number in hashcat both key off the hash prefix to select the right cracking engine. If you are ever unsure which prefix maps to which algorithm, you can identify the hash type from the string itself. Having a normalized string means the same cracker binary handles every supported format without needing to understand the original container.
What Hash Extractor does differently
Hash Extractor reimplements the extraction side of this pipeline as a Rust crate compiled to WebAssembly. The public surface exposed to JavaScript is two functions:
extract(filename, bytes)— given a file's raw bytes, return anOutcomeobject.list_formats()— return metadata about every supported format for the UI.
The extract call runs entirely inside a Web Worker. The file is read by the browser's FileReader API and passed as a byte slice to the WASM module; it never touches a network socket. This matters when the file is a KeePass database, a private key, or a backup of a device — types of files that users are rightly reluctant to upload to a web service.
The extraction pipeline
Internally, run(filename, data) loops over a registry of format handlers. Each handler exposes two functions:
detect(data, filename)— a cheap magic-bytes or extension check that answers "could this possibly be my format?"extract(data)— the full parse: locate the relevant fields, assemble the hash string, return anExtractedstruct or an error.
The Extracted struct carries everything the UI needs:
hash — the ready-to-crack hash string
johnFormat — the John --format= value
hashcatModes — one or more { mode, name } objects for -m
warnings — non-fatal notes (e.g. "file uses RC4-40, trivially crackable")
If no handler succeeds, the runner surfaces the most informative error: a definitive "this is a PDF but it is not encrypted" beats a generic "unrecognized format."
Supported formats
The current registry covers fourteen format handlers across five rough categories.
Documents
| Format | John --format | Extensions |
|---|---|---|
| PDF (Acrobat) | PDF | .pdf |
| Microsoft Office | office | .docx, .xlsx, .pptx, .doc, .xls, .ppt |
Office support (OOXML) is marked experimental — the parsing logic exists but is pending verification against a real password-protected file produced by Office or LibreOffice.
Archives
| Format | John --format | Extensions |
|---|---|---|
| ZIP | ZIP | .zip |
| 7-Zip | 7z | .7z |
Keys, keystores, and encrypted containers
| Format | John --format | Extensions |
|---|---|---|
| KeePass | KeePass | .kdbx, .kdb |
| SSH private key | ssh / SSH | .pem, .key, .ppk, .rsa, .dsa, .ecdsa, .ed25519 |
| PKCS#12 / PFX | pfx | .p12, .pfx |
| GPG / PGP secret key | gpg | .gpg, .pgp, .asc, .skr, .key |
| Ansible Vault | ansible | .yml, .yaml, .vault |
Apple and mobile
| Format | John --format | Extensions |
|---|---|---|
| macOS DMG | dmg | .dmg |
| macOS Keychain | keychain | .keychain, .keychain-db |
| iTunes Backup | itunes-backup | .plist |
| Android Backup | AndroidBackup | .ab |
Cryptocurrency wallets
| Format | John --format | Extensions |
|---|---|---|
| Ethereum wallet (UTC/JSON) | ethereum | .json |
What this series covers
Each post in this series will go one level deeper on a specific format: what the file structure looks like, which bytes the extractor actually reads, which KDF is involved, and what realistic cracking speeds look like with different hardware and attack modes.
The first format deep-dives will cover PDF (because it spans five major revisions with very different security models), KeePass (AES-KDF and Argon2 are fundamentally different cracking targets), and SSH private keys (the OpenSSH v1 format versus legacy PEM).
If you want to follow along hands-on, drop a file into Hash Extractor — the output includes the John --format value and the hashcat -m mode alongside the hash string itself.