Forj128 is an educational 128-bit hash function implemented from scratch in C. It is published on npm as forj128 and on PyPI as forj128, and it is designed for experimentation, teaching, and non-security-critical fingerprinting rather than password protection or cryptographic signing.
Warning: This project is for learning and experimentation. It is not cryptographically audited and should not be used to protect real secrets.
Forj128 demonstrates a Merkle-Damgård style construction in a compact, readable implementation. It processes data in 512-bit blocks, derives runtime constants, uses a generated S-box, and produces a 16-byte digest that can be rendered as a 32-character lowercase hex string.
- Lightweight and dependency-free at the core C level
- Easy to inspect and adapt for teaching purposes
- Supports C, Python, and Node.js entry points
- Includes a small avalanche test to observe diffusion behavior
Prerequisites:
- GCC or Clang
- Make
- The standard C math library (
libm)
git clone https://github.com/Terminay/forj128.git
cd forj128
makeThis produces:
forj128_clifor hashing text from the command lineavalanche_testfor a simple diffusion benchmarklibforj128.sofor native integration
pip install forj128Or from a local checkout:
cd python
pip install .npm install forj128For a local build from source:
cd node
npm install
npm run buildOn Windows, native Node builds may require Visual Studio Build Tools with a compatible C/C++ toolchain such as ClangCL or MSVC.
./forj128_cli "hello world"#include "forj128.h"
uint8_t digest[FORJ128_DIGEST_BYTES];
char hex[33];
forj128((const uint8_t *)"hello world", 11, digest);
forj128_to_hex(digest, hex);
printf("%s\n", hex);from forj128 import hash, hash_hex
print(hash(b"hello world").hex())
print(hash_hex(b"hello world"))const forj128 = require('forj128');
console.log(forj128.hashHex('hello world'));Forj128 is best suited for:
- Learning how hash functions are structured
- Teaching Merkle-Damgård style design and avalanche behavior
- Building small fingerprints for non-security-critical caches or deduplication checks
- Experimenting with custom round functions and state layout
It is not a good fit for:
- Password storage
- Digital signatures
- Certificate or token validation
- Any security-sensitive verification flow
Run the C regression tests:
make testRun the avalanche benchmark:
./avalanche_testExample output:
Avalanche test over 2752 single-bit flips:
Average bits flipped: 64.01 (50.0%)
Ideal: 64.00 bits (50.0%)
| Aspect | Implementation | Notes |
|---|---|---|
| Construction | Merkle-Damgård style | Similar in spirit to classic hash designs |
| Digest size | 128 bits | 16 bytes |
| Block size | 512 bits | Standard block size for the design |
| IV | Runtime-derived fractional roots | Avoids hardcoded magic constants |
| Round constants | Runtime-derived from primes | Generated at startup |
| S-box | Seeded and shuffled | Introduces a custom permutation |
| Finalization | Cross-XOR and rotation | Adds a simple final mixing step |
- Not peer-reviewed or cryptographically audited
- No formal proof of collision resistance
- Not suitable for password hashing or secret protection
- Designed for education first, security second
Contributions are welcome, especially around test coverage, portability improvements, and documentation clarity.
This project is licensed under the MIT License. See LICENSE for details.
Built as a learning exercise to understand hash function design. It draws inspiration from classic constructions such as MD5 and SHA-family hashes, while remaining intentionally simple and inspectable.
I would appreciate a ⭐ if you liked this project
