A small, educational command-line tool that demonstrates password hashing with the Unix crypt(3).
Find a file
2025-10-22 14:47:55 -05:00
.githooks chore(hooks): refactor changelog flow; add .env sourcing + SKIP_CHANGELOG; ignore .env 2025-10-22 05:44:30 -05:00
include add cmp_*.c, update Makefile, add *.o to .gitignore, added include/rc_macros.h 2025-10-13 07:10:13 -05:00
src chore(release): v2.6.0 2025-10-19 05:32:27 -05:00
tests refactor(test): adjust to use new wordlist file + add new tests 2025-10-19 03:24:43 -05:00
.gitignore chore(gitignore): update .gitignore to ignore VSCode configuration directory 2025-10-22 07:28:22 -05:00
.markdownlint-cli2.jsonc chore(changelog): KC overlay w/ Deprecated & Removed; whitespace-stable; MD lint config: 2025-10-21 00:10:04 -05:00
CHANGELOG.md fix(changelog): adjust body template **again** to only provide a single 'compare' HEAD link in 'unreleased' instead of every release section 2025-10-22 14:47:55 -05:00
cliff.toml fix(changelog): adjust body template **again** to only provide a single 'compare' HEAD link in 'unreleased' instead of every release section 2025-10-22 14:47:55 -05:00
CONTRIBUTING.md docs: update documentation to add CONTRIBUTING doc; detail changelog generation in CONTRIBUTING 2025-10-22 02:48:30 -05:00
LICENSE add LICENSE using PolyForm Noncommercial 2025-10-06 00:18:22 -05:00
Makefile build(makefile): quiet compiler output 2025-10-19 03:27:10 -05:00
README.md docs: update documentation to add CONTRIBUTING doc; detail changelog generation in CONTRIBUTING 2025-10-22 02:48:30 -05:00
TESTING.md docs: update documentation to add CONTRIBUTING doc; detail changelog generation in CONTRIBUTING 2025-10-22 02:48:30 -05:00

RexCrypt

RexCrypt is a small, educational command-line tool that demonstrates password hashing with the Unix crypt(3) API (via libxcrypt/glibc). It's designed as a teaching aid: clear CLI, heavily commented source, and practical notes about algorithms, salts, and parsing pitfalls.

Why this exists

  • Show how to pick a hashing algorithm via salt prefix.
  • Show how to generate salts with crypt_gensalt(3) and hash with crypt_r(3).
  • Teach robust CLI parsing (e.g. handling errno, ERANGE/EINVAL, and junk suffixes).
  • Illustrate practical tradeoffs (defaults vs explicit parameters, portability, and debug tracing).

⚠️ This is a teaching tool. Dont copy-paste it into production without a proper security review.


Disclaimer

This project is an educational example, not a production-ready password system. It demonstrates how the historical crypt(3) interface works and how to reason about salts, algorithms, and CLI parsing. The presence of legacy algorithms (MD5, SHA-1, NT, SunMD5, BSDi) is for completeness and interoperability only -- not an endorsement.

No warranty is provided. Use at your own risk. If you adapt any of this code, perform a full security review and prefer modern, memory-hard KDFs and vetted libraries appropriate to your platform and threat model.


Features

  • Multiple algorithms (availability depends on your libxcrypt build):
    • 0 → library default/best
    • 1 MD5 ($1$)
    • 2 NT ($3$, legacy)
    • 3 BSDi (_)
    • 4 SunMD5 ($md5, legacy)
    • 5 SHA-1 ($sha1, legacy)
    • 6 SHA-256 ($5$)
    • 7 SHA-512 ($6$)
    • 8 bcrypt ($2b$)
    • 9 scrypt ($7$)
    • a GOST-yescrypt ($gy$)
    • b yescrypt ($y$)
  • Thread-safe hashing using crypt_r(3).
  • Clear CLI with getopt(3) and defensive parsing.
  • Built-in help (-?) providing concise usage() with options, algorithm table, and examples (prints to stderr for easy scripting).
  • TTY secret entry via getpass(3) when -k is omitted.
  • Non-TTY handling: if stdin is not a TTY and no -k is provided, RexCrypt exits with an error.
  • Passphrase length cap: input is bound by CRYPT_MAX_PASSPHRASE_SIZE (commonly 512 bytes) to match the library's struct crypt_data limit.
  • Hash verification provided by -V option flag, allowing verification that a given key (via -k) produces a known hash; outputs "OK"/"FAIL" with appropriate exit code, accordingly.
  • List algorithms via -L, outputting the name of the algorithm & its availability
  • Secure memory wipe: demonstrates memory security through explicit_bzero() or volatile memset, depending on availability.
  • Readable DEBUG traces (compile-time flag) for step-through demos.
  • Companion tool: rexcrack -- multithreaded dictionary-attack demo using crypt_r(3). Validates hashes, supports legacy DES/bigcrypt for teaching, prints the winning password on stdout; verbose mode logs to stderr. Exit codes 0 (match), 2 (not found), EX_* for errors.
  • Makefile workflow with common targets (make, make test, make clean, make distclean) and simple knobs (CC, CFLAGS).
  • TAP v14 tests via make test: quick checks for prefixes, stdin path (-k -), and non-TTY handling.

Install & Build

Dependencies

  • A C compiler (clang or gcc)
  • libxcrypt / libcrypt headers and library

On Debian/Ubuntu:

sudo apt install clang libcrypt-dev

Build

Using the provided Makefile:

git clone https://repo.feedthetrolls.net/fosster/rexcrypt.git
cd rexcrypt
make

Outputs ./bin/rexcrypt and ./bin/rexcrack.

Housekeeping:

make clean      # removes compiled binaries
make distclean  # removes all generated files/directories

Tip: If you prefer a manual compile, a common line is:

# rexcrypt
clang -Wall -Wextra -W -O2 -Iinclude -o bin/rexcrypt src/rexcrypt.c -lcrypt
# rexcrack (pthreads)
clang -Wall -Wextra -W -O2 -Iinclude -o bin/rexcrack src/rexcrack.c -lcrypt -pthread

Usage

rexcrypt [-c <count>] [-r <resalt>] [-a <alg>] [-k <key>|-] [-s <salt>] [-V <hash>]

Options

  • -c <cpu_time>: Set the library-defined cost/rounds/time knob.
  • -r <resalt>: Regenerate the salt N extra times after first generation (default 0).
  • -a <algorithm>: Select algorithm (see table above). Accepts 0..9 and letters a/b.
  • -k <key>: Provide the secret inline. If omitted, RexCrypt prompts via getpass().
  • -k -: Read the secret from stdin (for scripting; echo is not disabled).
  • -s <salt>: Provide a deterministic salt to generate a consistent hash.
  • -V <hash>: Specify a known hash to verify a given key (via -k).

Examples

# Best available default algorithm; prompt for password
./bin/rexcrypt

# List algorithms supported & their availability
./bin/rexcrypt -L

# SHA-512 with a time/rounds knob of 5000 (value semantics depend on algorithm; check `man 5 crypt`)
./bin/rexcrypt -a 7 -c 5000 -k "correct horse battery staple"

# yescrypt (letter form)
./bin/rexcrypt -a b -k "hunter2"

# scrypt, resalt twice -- 3 total
./bin/rexcrypt -a 9 -r 2 -k "super secret"

# read secret from stdin (newline stripped)
printf 'supers3cret\n' | ./bin/rexcrypt -k -

# verify a specified hash matches a given key
./bin/rexcrypt -k "Joshua" -V '$5$jbfeceBV2R3cStO9$tt.VJazWc0bKk3x7fNqeCXfmy43FT3/DZUbdIjYuHE0'

Exit codes

  • 0 on success (hash printed to stdout)
  • Non-zero on invalid arguments or library failures

Notes on algorithm support

  • Not all prefixes are supported on all systems. If an unsupported prefix is requested, RexCrypt will fall back to the library default/best (detected at runtime).
  • -L (no argument) can be used to walk through the table of algorithms, probing each algorithm for support on the system, and listing its availability status.

RexCrack -- dictionary-attack demo (educational)

Try candidates from a wordlist against a specified hash using the same "hash-as-setting' verification shown in RexCrypt. Miltithreaded; one struct crypt_data per thread.

RexCrack Usage

rexcrack -h <hash> -w <wordlist> [-t <threads>] [-v]
  • -h <hash>: specify hash (exact string as produced by rexcrypt)
  • -w <wordlist>: newline-separated candidates
  • -t <threads>: number of threads (default: number of CPUs; min 1; soft cap 64)
  • -v: verbose (prints attempts/progress to stderr; stdout stays clean for the winner)

RexCrack Exit codes

  • 0 -> match found (prints the password on stdout)
  • 2 -> not found
  • errors -> EX_* from <sysexits.h> (e.g. EX_NOINPUT for bad file, EX_DATAERR for invalid hash)

RexCrack Notes

  • Hash validation uses anchored regexes (and crypt_checksalt() when available). Legacy DES/bigcrypt are intentionally supported for teaching.
  • Wordlists with overlong and blank lines are handled correctly (overlong lines are drained).

Debug & Testing

make debug    # build with debug symbols & DEBUG flag enabled
make test     # build & run TAP v14 test suite

Example Testing Output:

>>> Running tests
TAP version 14
1..4
ok 1 - sha512 (-a 7) prefix correct
ok 2 - yescrypt (-a b) prefix correct
ok 3 - stdin (-k -) path works
ok 4 - non-tty without -k fails as expected
ok 5 - algorithm defaults correctly
ok 6 - invalid algorithm fails correctly
ok 7 - negative cpu_time fails correctly
ok 8 - bcrypt hash matches regex pattern
ok 9 - scrypt hash length correct
ok 10 - md5crypt hash match
ok 11 - verify OK -- match against specified hash
ok 12 - verify OK -- mismatch fails as expected

Tests treat rexcrypt as a black-box binary and cover:

  • prefix checks for representative algorithms
  • -k - (stdin) path
  • non-TTY without -k (expected error path)
  • algorithm defaults
  • improper argument failures
  • hash matching

The TAP suite also exercises rexcrack:

  • match -> 0, not found -> 2
  • bad wordlist file -> EX_NOINPUT
  • invalid hash -> EX_DATAERR
  • stdout/stderr discipline (winnder only on stdout; verbose logs on stderr)
  • overlong line handling
  • legacy DES acceptance (educational)

See TESTING for more details on testing RexCrypt, including designing new tests.


Security & Pedagogy Notes

  • getpass(3) is legacy and may be unavailable on some platforms; it's fine for teaching. See also readpassphrase(3) from BSD(libbsd-dev on Debian).
  • The hashed output is sensitive -- treat it like a credential.
  • The code includes commentary about parsing pitfalls (strtol, errno, and input validation) and pointers about memory hygiene.
  • Method-specific limits apply; RexCrypt caps input to CRYPT_MAX_PASSPHRASE_SIZE to match the library.
  • Though steps are taken for memory security, the methods are known not to be fool-proof, though their practice is recommended. See also explicit_bzero(3) for additional notes on the security implications & caveats of its use.

Development

This repository uses Conventional Comits, local Git hooks, and auto-generated changelogs (via git-cliff). If you'd like to contribute or run the tooling locally, see CONTRIBUTING.


License

This project is licensed under the PolyForm Noncommercial License 1.0.0. See the LICENSE file for more details.


Contributing

Issues and PRs that improve clarity, portability, or pedagogy are welcome -- especially notes that help students understand why choices were made (not just what they are).