ilusm.dev

hash

Hash identification, password cracking, rainbow tables, salted hashes, PBKDF2, and timing-safe comparison.

Load with: use hash

What this module does

hash is built around cracking and identifying password hashes. Given a hash string it can tell you what algorithm produced it, then crack it via brute-force (up to 2-character candidates from a charset) or a dictionary attack. It also builds rainbow chains, generates salted hashes, wraps PBKDF2, and provides a timing-safe comparison to prevent timing attacks when verifying hashes.

Hashing delegates to cry for MD5 and SHA-256. SHA-1 and SHA-512 call host native functions (__hash_sha1, __hash_sha512). The hash.* aliases provide a clean public API over the raw function names.

Quick example

use hash

# Identify a hash by its format
prn(hashi("5f4dcc3b5aa765d61d8327deb882cf99"))  # "md5"
prn(hashi("$2b$10$..."))                         # "bcrypt"

# Dictionary crack with the built-in common password list
result = hashd("5f4dcc3b5aa765d61d8327deb882cf99", hashw())
prn(result.found)     # tru
prn(result.password)  # "password"

# Timing-safe comparison
ok = hasht(submitted_hash, stored_hash)

Functions

Hash identification

hashi(hash_str)

Identifies the algorithm that produced a hash string by inspecting its length and prefix. Returns a string label:

  • 32 hex chars → "md5"
  • 40 hex chars → "sha1"
  • 64 hex chars → "sha256"
  • 128 hex chars → "sha512"
  • Prefix $2a$ / $2b$ / $2y$"bcrypt"
  • Prefix $6$"sha512crypt"
  • Prefix $5$"sha256crypt"
  • Prefix $1$"md5crypt"
  • Prefix $apr1$"apr1"
  • Prefix sha1$"sha1django"
  • Prefix pbkdf2_sha256$"pbkdf2_sha256"
  • Prefix scrypt$"scrypt"
  • Prefix argon2"argon2"
  • Anything else → "unknown"

Hash algorithms

hashm(s) / hash.md5(s)

MD5 digest of s. Delegates to cry.crymd.

hash1(s) / hash.sha1(s)

SHA-1 digest of s. Calls the host native __hash_sha1.

hash2(s) / hash.hashs(s)

SHA-256 digest of s. Delegates to cry.s25.

hash5(s) / hash.sha512(s)

SHA-512 digest of s. Calls the host native __hash_sha512.

hashx(w, t)

Dispatches to the right hash function based on a type label from hashi. Supports "md5", "sha1", "sha256". Falls back to SHA-256 for anything else.

hash.hashm(d, k)

HMAC - computes cry.cryhm(k, d). k is the key, d is the data.

hash.hashr(s)

Non-cryptographic deterministic integer from a string - uses a polynomial rolling hash (x = x * 31 + ord(c) mod 1000000007). Good for bucketing, not security.

Cracking

hashb(hash_str, charset, max_len)

Brute-force crack. Generates all 1- and 2-character candidates from charset using hashg, hashes each one with the algorithm identified by hashi, and returns the first match. Returns {found: tru, password, attempts} on success, or {found: fls, attempts} if nothing matched. Note: currently generates up to 2-character candidates only.

hashg(charset, max_len)

Generates the candidate list for brute-force: all single characters from charset, then all 2-character combinations. The max_len parameter is accepted but the current implementation generates 1- and 2-character strings only.

hashd(hash_str, wordlist)

Dictionary attack. Iterates through wordlist, hashes each word with the algorithm identified by hashi, and returns the first match. Returns {found: tru, password, attempts} or {found: fls, attempts}.

hashmx(hashes, wordlist)

Cracks multiple hashes at once from a single wordlist pass. For each word, checks it against all remaining uncracked hashes. Stops early if all hashes are cracked. Returns {cracked: [{hash, password}], uncracked: [...]}.

hashw()

Returns a built-in list of 20 common passwords (e.g. "123456", "password", "qwerty") for quick dictionary attacks.

Rainbow tables

hashrr(hash_str, chain_pos, charset)

Reduction function for rainbow tables. Converts a hash back to a plaintext candidate by using 8 pairs of hex characters from the hash, adding the chain position chain_pos, and indexing into charset. Returns an 8-character plaintext string.

hashrc(start_plaintext, chain_len, charset)

Walks a rainbow chain starting from start_plaintext for chain_len steps. Each step: hash the current value with MD5, then reduce with hashrr. Returns {start, end, length} where end is the MD5 of the final plaintext.

Salted hashes and key derivation

hashsa(password, salt, algo)

Produces a salted hash by concatenating password + salt and hashing with algo. Supports "md5", "sha256", "sha512". Errors on unknown algorithms.

hashp(password, salt, iterations)

PBKDF2-SHA256 key derivation. Produces a 32-byte derived key. Delegates to cry.crypb.

Comparison

hasht(a, b) / hash.hashc(a, b)

Timing-safe string comparison. Delegates to cry.cryct. Use this instead of == when comparing hashes to prevent timing oracle attacks.

Notes

  • hashi identifies by length first, then prefix - a 32-char string with no $ will always be classified as "md5" even if the second length-32 branch is unreachable in practice.
  • Brute-force (hashb) only generates 1 and 2-character candidates - it is not suitable for real passwords of any length.
  • Rainbow chains use MD5 for the hash step. The reduction function reads 8 pairs of hex chars, so it requires hashes of at least 16 characters.
  • Always use hasht / hash.hashc for hash verification - never ==.
  • hash.hashb (blake2) is defined but throws "hash.blake2: not implemented".