Skip to content

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.

Published on 5 min read

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 an Outcome object.
  • 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:

  1. detect(data, filename) — a cheap magic-bytes or extension check that answers "could this possibly be my format?"
  2. extract(data) — the full parse: locate the relevant fields, assemble the hash string, return an Extracted struct 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

FormatJohn --formatExtensions
PDF (Acrobat)PDF.pdf
Microsoft Officeoffice.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

FormatJohn --formatExtensions
ZIPZIP.zip
7-Zip7z.7z

Keys, keystores, and encrypted containers

FormatJohn --formatExtensions
KeePassKeePass.kdbx, .kdb
SSH private keyssh / SSH.pem, .key, .ppk, .rsa, .dsa, .ecdsa, .ed25519
PKCS#12 / PFXpfx.p12, .pfx
GPG / PGP secret keygpg.gpg, .pgp, .asc, .skr, .key
Ansible Vaultansible.yml, .yaml, .vault

Apple and mobile

FormatJohn --formatExtensions
macOS DMGdmg.dmg
macOS Keychainkeychain.keychain, .keychain-db
iTunes Backupitunes-backup.plist
Android BackupAndroidBackup.ab

Cryptocurrency wallets

FormatJohn --formatExtensions
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.

Related articles

A technical walkthrough of how Hash Extractor parses KDB and KDBX 3.x files to produce a crackable $keepass$ hash for hashcat and John the Ripper.
A precise look at how the $pdf$ hash is built from an encrypted PDF's /Encrypt dictionary, and how to crack it with hashcat or John the Ripper.
Why 7z is the trickiest archive to crack: LZMA-compressed headers, AES coder property encoding, and the full $7z$ hash format explained.