A pure-MoonBit, zero-dependency native cryptographic library for the MoonBit
ecosystem. No FFI, no C — every primitive is implemented in plain MoonBit and
verified against official standard vectors.
Highlights
Correct — every algorithm is checked against FIPS / NIST / RFC test
vectors (plus a Python reference for BLAKE2b/BLAKE3/Poly1305). 151 tests, run
with moon test --deny-warn.
Fast where it matters — hex / Base64 encoding are O(n); AES MixColumns
uses precomputed GF(2^8) tables (~5x over bit-sliced math); throughput is
measured by moon bench.
Fail-fast input validation — AES / ChaCha20 / SHAKE / hex functions abort
with a clear message on wrong key / IV / nonce / tag lengths instead of
producing garbage.
Single source of truth — one-shot hash entry points delegate to the
streaming hashers, so the incremental and one-shot paths share one
implementation.
Base64 (RFC 4648) — standard alphabet with padding
Hex — bytes ↔ lowercase hex
Streaming API
MD5, SHA-224/256/384/512, SHA3-224/256/384/512, and SHAKE128/256 support
incremental new / update / finalize for streaming or large inputs.
Installation
moon add cc06b/mooncry
Quick start
Create a new project, add the dependency, then import the lib package and
call its functions through the @lib alias. Dependencies are declared in
moon.pkg (per-package), not as a top-level import statement, and
assert_true is only available inside test blocks — so use println in
main.
fn main {
// SHA-256 one-shot
let digest = @lib.sha256(b"Hello, world!")
println("SHA-256: " + @lib.bytes_to_hex(digest))
// AES-GCM round-trip (256-bit key, 96-bit nonce, with AAD)
let key = Bytes::make(32, b'\x00')
let iv = Bytes::make(12, b'\x00')
let (ciphertext, tag) = @lib.aes_gcm_encrypt(b"secret data", key, iv, b"aad")
let (plaintext, ok) = @lib.aes_gcm_decrypt(ciphertext, key, iv, b"aad", tag)
let status = if ok { "OK" } else { "FAIL" }
println("AES-GCM round-trip: " + status)
println("Recovered: " + @lib.bytes_to_hex(plaintext))
}
Run it:
moon run cmd/main
SHA-256: 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3
AES-GCM round-trip: OK
Recovered: 7365637265742064617461
A larger runnable example that validates the implementation against NIST/RFC
standard vectors lives in cmd/main of the repository
itself. Run it with moon run cmd/main.
Public API
All functions live in the lib package (cc06b/mooncry/lib), called as
@lib.<fn> after declaring the import in your moon.pkg.
Streaming hashers (<algo>_new / sha3_update / sha3_finalize /
shake_finalize) are available for MD5, SHA-224/256/384/512, SHA3-224/256/384/512,
and SHAKE128/256. For SHA-3/SHAKE, sha3_update is shared and the finalize
method depends on the variant (sha3_finalize for fixed-length, shake_finalize(h, out_len) for XOF).
AES-CBC/GCM/CTR keys may be 128, 192, or 256 bits; the nonce for GCM and
ChaCha20 is 96 bits (12 bytes), the recommended length per spec. Wrong lengths
cause an abort with a descriptive message.
Security & performance boundaries
Not audited. The code is correct against known vectors but has had no
formal security review. Do not use it to protect high-value assets without an
independent audit.
AES is not constant-time. MixColumns uses precomputed GF(2^8) lookup
tables (mul2/3/9/11/13/14) for ~5x throughput. This leaks key-dependent
table indices through the CPU cache — acceptable for many use cases but
not side-channel-safe against a local attacker. (GHASH and the GCM tag /
CBC PKCS#7 verification are still bit-sliced and constant-time.)
Nonce reuse is catastrophic for AES-GCM and ChaCha20(-Poly1305). Never
reuse a (key, nonce) pair. The library does not track nonces — generate a
fresh one per message (e.g. a counter or CSPRNG).
PBKDF2 is a KDF, not a password hasher. For interactive password storage
prefer Argon2 / bcrypt / scrypt elsewhere; PBKDF2 is included for
compatibility with existing protocols.
MD5 is collision-broken. It is included for legacy compatibility only —
do not sign or authenticate with it.
BLAKE3 supports arbitrary-length input via the tree-Merkle mode (verified
vs the reference blake3 Python package up to 5000 bytes).
Inputs are validated, not silently padded. Wrong key / IV / nonce / tag
lengths abort immediately rather than producing wrong output.
No RNG. The library provides deterministic primitives; obtain keys, IVs,
and nonces from a secure source.
Performance
Throughput is measured by the lib benchmark suite (moon bench) on 1 KiB
inputs. Figures below are from the development sandbox; absolute numbers vary
by host — run moon bench locally for comparable figures.
moon bench
Algorithm
1 KiB (approx.)
MD5
~8.5 µs
SHA-256
~16 µs
SHA-512
~14 µs
SHA3-256
~140 µs
BLAKE2b
~27 µs
BLAKE3
~46 µs
HMAC-SHA256
~23 µs
ChaCha20
~46 µs
AES-256-CBC
~315 µs (table-based GF mul)
AES-256-GCM
~420 µs (table-based)
Base64 encode
~10 µs
Hex encode
~6.7 µs
Hashes, ChaCha20, and hex/Base64 are throughput-bound by the algorithm; AES
trades constant-time property for ~5x speed via lookup tables (see
Security & performance boundaries).
Testing
Every algorithm is verified against official standard vectors:
The CI (.github/workflows/moonbit-ci.yml) installs the latest MoonBit
toolchain and runs the four required checks — moon check --deny-warn,
moon fmt --check, moon info, moon test --deny-warn — and verifies that
no build artifacts are tracked. Run them locally:
moon check --deny-warn
moon fmt --check
moon info
moon test --deny-warn
moon bench # run the benchmark suite
The module manifest is moon.mod (TOML); per-package manifests are moon.pkg
(TOML). Build outputs (_build/, generated .mbti) are gitignored and must
not be committed.
Publishing (maintainers)
moon.mod declares name = "cc06b/mooncry", license Apache-2.0. Publishing
requires the owner of the cc06b namespace to be logged in:
moon login # one time, with the account that owns cc06b
moon publish # publishes the current version
Before publishing, ensure all four checks above pass and the tree is clean.
mooncry
A pure-MoonBit, zero-dependency native cryptographic library for the MoonBit ecosystem. No FFI, no C — every primitive is implemented in plain MoonBit and verified against official standard vectors.
Highlights
moon test --deny-warn.moon bench.Repositories
Both are kept in sync. To add the mirror locally:
Algorithms
Hash functions
Extendable-output functions (XOF)
Message authentication
Symmetric ciphers / AEAD
Key derivation
Encoding
Streaming API
MD5, SHA-224/256/384/512, SHA3-224/256/384/512, and SHAKE128/256 support incremental
new/update/finalizefor streaming or large inputs.Installation
Quick start
Create a new project, add the dependency, then import the
libpackage and call its functions through the@libalias. Dependencies are declared inmoon.pkg(per-package), not as a top-levelimportstatement, andassert_trueis only available insidetestblocks — so useprintlninmain.Edit
cmd/main/moon.pkgto import the library:Edit
cmd/main/main.mbt:Run it:
A larger runnable example that validates the implementation against NIST/RFC standard vectors lives in
cmd/mainof the repository itself. Run it withmoon run cmd/main.Public API
All functions live in the
libpackage (cc06b/mooncry/lib), called as@lib.<fn>after declaring the import in yourmoon.pkg.md5(data : Bytes) -> Bytessha224 / sha256 / sha384 / sha512(data : Bytes) -> Bytessha3_224 / sha3_256 / sha3_384 / sha3_512(data : Bytes) -> Bytesshake_128 / shake_256(data : Bytes, out_len : Int) -> Bytesout_lenbytesblake2b(data : Bytes, out_len : Int) -> Bytesout_len1..64blake3(data : Bytes) -> Bytesblake3_xof(data : Bytes, out_len : Int) -> Byteshmac_sha256 / hmac_sha512(key, msg : Bytes) -> Bytespoly1305(key, msg : Bytes) -> Bytesaes_encrypt_cbc / aes_decrypt_cbc(data, key, iv) -> Bytesaes_gcm_encrypt(pt, key, iv, aad) -> (Bytes, Bytes)aes_gcm_decrypt(ct, key, iv, aad, tag) -> (Bytes, Bool)aes_ctr(data, key, iv) -> Byteschacha20_xor(input, key, nonce, counter) -> Byteschacha20_poly1305_encrypt(key, nonce, aad, pt) -> Byteschacha20_poly1305_decrypt(key, nonce, aad, input) -> Byteshkdf_sha256(salt, ikm, info, len) -> Bytespbkdf2_hmac_sha256(password, salt, iterations, len) -> Bytesbase64_encode(data : Bytes) -> Stringbase64_decode(encoded : String) -> Bytesbytes_to_hex(data : Bytes) -> Stringhex_to_bytes(hex : String) -> Bytesbytes_equal(a, b : Bytes) -> BoolStreaming hashers (
<algo>_new/sha3_update/sha3_finalize/shake_finalize) are available for MD5, SHA-224/256/384/512, SHA3-224/256/384/512, and SHAKE128/256. For SHA-3/SHAKE,sha3_updateis shared and the finalize method depends on the variant (sha3_finalizefor fixed-length,shake_finalize(h, out_len)for XOF).AES-CBC/GCM/CTR keys may be 128, 192, or 256 bits; the nonce for GCM and ChaCha20 is 96 bits (12 bytes), the recommended length per spec. Wrong lengths cause an
abortwith a descriptive message.Security & performance boundaries
mul2/3/9/11/13/14) for ~5x throughput. This leaks key-dependent table indices through the CPU cache — acceptable for many use cases but not side-channel-safe against a local attacker. (GHASH and the GCM tag / CBC PKCS#7 verification are still bit-sliced and constant-time.)blake3Python package up to 5000 bytes).abortimmediately rather than producing wrong output.Performance
Throughput is measured by the
libbenchmark suite (moon bench) on 1 KiB inputs. Figures below are from the development sandbox; absolute numbers vary by host — runmoon benchlocally for comparable figures.Hashes, ChaCha20, and hex/Base64 are throughput-bound by the algorithm; AES trades constant-time property for ~5x speed via lookup tables (see Security & performance boundaries).
Testing
Every algorithm is verified against official standard vectors:
Coverage: MD5 (RFC 1321), SHA-2 family (FIPS 180-4 + million-
a), SHA-3 (NIST KAT), SHAKE (FIPS 202), BLAKE2b (RFC 7693 + hashlib), BLAKE3 (python blake3), HMAC (RFC 4231), Poly1305 (RFC 8439), ChaCha20-Poly1305 (RFC 8439 + pycryptodome), HKDF (RFC 5869), PBKDF2 (RFC 6070), AES-CBC/GCM/CTR (NIST SP 800-38A/D), ChaCha20 (RFC 8439), Base64 (RFC 4648), hex round-trip, and streaming-vs-one-shot consistency. 151 tests.Development
The CI (
.github/workflows/moonbit-ci.yml) installs the latest MoonBit toolchain and runs the four required checks —moon check --deny-warn,moon fmt --check,moon info,moon test --deny-warn— and verifies that no build artifacts are tracked. Run them locally:The module manifest is
moon.mod(TOML); per-package manifests aremoon.pkg(TOML). Build outputs (_build/, generated.mbti) are gitignored and must not be committed.Publishing (maintainers)
moon.moddeclaresname = "cc06b/mooncry", licenseApache-2.0. Publishing requires the owner of thecc06bnamespace to be logged in:Before publishing, ensure all four checks above pass and the tree is clean.
License
Apache-2.0