Architecture¶
NexHash is organised as a small set of clearly separated modules. The source tree is intentionally flat enough to read top-to-bottom in a single sitting, while still separating concerns between CLI parsing, dispatch, cryptography, and individual engines.
Source tree¶
nexhash/
├── src/
│ ├── cli/
│ │ └── nexhash.cpp # CLI entry point and argument parsing
│ ├── core/
│ │ ├── nexhash_core.h # Dispatcher public API
│ │ ├── nexhash_core.cpp # Engine routing, alias parsing, backward compat
│ │ ├── warning.h # Warning system API
│ │ └── warning.cpp # Yellow warning message printer
│ ├── crypto/
│ │ ├── sha256.h / sha256.cpp # SHA-256 implementation
│ │ └── sha512.h / sha512.cpp # SHA-512 implementation
│ └── engines/
│ ├── engine_argon.h / .cpp # Argon2id wrapper
│ ├── engine_bcrypt.h / .cpp # bcrypt wrapper (openwall)
│ ├── engine_nex3ph1.h / .cpp # 432-hex password engine
│ ├── engine_nex4px1.h / .cpp # 1240-hex password engine
│ ├── engine_nex3fh1.h / .cpp # file hashing engine
│ ├── engine_nex4px2.h / .cpp # 8743-hex password engine
│ ├── engine_nex4mx1.h / .cpp # 2048-hex message engine
│ └── engine_nex5mx1.h / .cpp # 16384-hex message engine (Argon2-based)
├── lib/
│ ├── argon2/ # PHC-winner Argon2 reference source (C)
│ │ ├── argon2.h / argon2.c # public API + entry point
│ │ ├── core.h / core.c # core filling logic
│ │ ├── blake2/ # BLAKE2b used by Argon2
│ │ ├── encoding.h / encoding.c # PHC string encoder/decoder
│ │ ├── thread.h / thread.c # portable thread wrapper
│ │ ├── ref.c # pure-C implementation (default)
│ │ └── opt.c # SSE2 / AVX2 implementation (optional)
│ └── bcrypt/
│ ├── crypt_blowfish.h / .c # bcrypt implementation
│ └── ow-crypt.h # public crypt(3)-style API
├── build/ # object files and static libraries
│ ├── libargon2.a
│ └── libcrypt_blowfish.a
├── dist/
│ └── nexhash # final binary
├── docs/ # this documentation
├── .github/workflows/run_mkdocs.yml # CI workflow for docs deployment
├── mkdocs.yaml # MkDocs configuration
├── Makefile # cross-platform build
└── README.md
Module responsibilities¶
src/cli/¶
The CLI layer parses command-line arguments, prints user-facing output (with colour when stdout is a TTY), and forwards the parsed inputs to the dispatcher. It contains no cryptographic logic and no direct calls to engines.
src/core/¶
The dispatcher exposes a small API:
parse_engine(name, &engine)— accepts both canonical names and legacy aliases.engine_name(engine)— returns the canonical name.is_file_engine(engine)/is_message_engine(engine)— type predicates.encode(...)/verify(...)— password engines.encode_file(...)/verify_file(...)— file engines.encode_message(...)/verify_message(...)— message engines.
The verify functions auto-detect the engine from the PHC prefix, so callers do not need to know which engine produced a hash.
The warning module centralises user-facing warnings so they are consistent across commands.
src/crypto/¶
Self-contained SHA-256 and SHA-512 implementations. They expose both an incremental update / final API and a one-shot hash(string) helper. They are not optimised for raw throughput — the dominant cost in NexHash is the iteration loop, not a single hash call.
src/engines/¶
Each engine is a single header + source pair implementing encode and verify (and encode_file / verify_file for the file engine). Engines do not depend on each other; they only depend on src/crypto/ and (for some) lib/argon2 or lib/bcrypt.
lib/¶
Vendored third-party sources. These are compiled as C (not C++) and statically linked. No modifications have been made to the upstream algorithms; the only NexHash-specific change is the BF_ASM=0 define passed to bcrypt to force the pure-C path for portability.
Build pipeline¶
The Makefile uses pattern rules to compile each source subtree with the appropriate flags:
- C++ sources (
src/**/*.cpp) are compiled with C++17,-O2, and include paths pointing at everysrc/subdirectory pluslib/argon2andlib/bcrypt. This lets engine sources use#include "engine_nex5mx1.h"without path prefixes. - Argon2 C sources are compiled with
-std=c89 -pthreadand the Argon2 include path. The blake2 subdirectory has its own pattern rule to handle the extra path component. - bcrypt C sources are compiled with
-DBF_ASM=0to force the pure-C path, ensuring portability across MinGW and 32-bit targets. - Static libraries
libargon2.aandlibcrypt_blowfish.aare produced withar rcs. - The final binary links all application object files plus the two static libraries and pthreads.
Object files are placed flat in build/ (no subdirectories) with prefixes (argon2_, bcrypt_) to avoid name collisions.
Adding a new engine¶
To add a new engine:
- Create
src/engines/engine_<name>.handengine_<name>.cpp. Implement<name>_encodeand<name>_verify(or_encode_file/_verify_filefor a file engine, or_encode/_verifytaking both password and text for a message engine). - Add a new value to the
Engineenum insrc/core/nexhash_core.h. - Add the canonical name (and any legacy alias) to
parse_engineand the reverse mapping toengine_nameinsrc/core/nexhash_core.cpp. - Add the new engine to the appropriate dispatch function (
encode/encode_file/encode_messageand the corresponding verify). - Update
is_file_engine/is_message_engineif applicable. - Add the engine to the CLI:
print_engines()list, the--helptext, the--versionoutput, and any relevant detection logic in--decode. - Add the source files to
APP_SRCSinMakefile. - Add the engine to the test matrix in the
testtarget.
Verification flow¶
When --decode is invoked:
- The CLI inspects the PHC prefix of
--cryptto determine which engine produced it. - If the prefix matches a message engine (
nex4mx1,nex5mx1), the CLI requires--passwordand/or--textand callsverify_message. - Otherwise, the CLI requires
--passwordand callsverify. - The dispatcher forwards to the engine's
verifyfunction. - The engine parses the PHC string, re-derives the hash from the supplied input(s) using the stored parameters, and compares with the stored hash using a constant-time XOR accumulator.
- The CLI prints
[OK]or[FAIL]and exits with code0or2.
Legacy prefixes (nex4dc6, nex9d7, nex7f1, nex9jx5) are accepted transparently by the corresponding engine's verify function.
Output format¶
Every custom engine produces a PHC-style string with $-delimited fields:
The <params> field is engine-specific:
- For pure iteration engines (
nex3ph1,nex4px1,nex4px2,nex4mx1): just the iteration count. - For the file engine (
nex3fh1): iteration count and file size. - For the Argon2-based message engine (
nex5mx1):m,t,p,sha_iter(Argon2 memory, iterations, parallelism, plus the SHA stretch count).
All hex strings are lowercase. Salt and hash lengths are fixed per engine and validated on verify.