Metamorphic_crypto - Post-quantum E2E encryption for Elixir (NaCl-compatible, precompiled NIFs)

Hey all — I open-sourced an Elixir library that a few people in my previous threads might find useful.

metamorphic_crypto is a NaCl-compatible encryption library for Elixir with post-quantum support, powered by a Rust NIF core with precompiled binaries. It provides:

  • Symmetric encryption — XSalsa20-Poly1305 (NaCl secretbox)
  • Public-key encryption — X25519 sealed boxes
  • Hybrid post-quantum encryption — ML-KEM-768 + X25519 (NIST FIPS 203)
  • Key derivation — Argon2id (libsodium-compatible parameters)
  • Recovery keys — human-readable backup codes (like Signal/Matrix)
  • Auto-detecting ciphertext — seals with PQ when available, falls back to classical, detects format on decrypt

The Rust core is #![forbid(unsafe_code)], built entirely on audited RustCrypto primitives, and ships via rustler_precompiled — so it’s just mix deps.get for end users. No Rust toolchain, no C compiler, no system packages.

Why I built this: If you’ve ever tried to get enacl compiling on a fresh machine or in CI, you know the pain — libsodium headers, C toolchain, breakage on OTP upgrades. This library produces identical NaCl ciphertext but ships as precompiled binaries. It also adds ML-KEM-768 post-quantum encryption that nothing else on Hex offers.

What it’s for:

  • Swap-in for some enacl functions — same wire format, no data migration, no libsodium dependency
  • Server-side NaCl-compatible crypto in Phoenix apps
  • Post-quantum encryption (ML-KEM-768 + X25519, first on Hex)
  • Wire compatibility with browser WASM clients (same Rust core compiles to both NIF and WASM)

What it’s NOT: This library runs server-side. It doesn’t give you client-side zero-knowledge encryption by itself. For full ZK where the server never sees plaintext, you need client-side crypto in the browser (we use WASM). But if your server needs NaCl crypto — key generation, sealing to recipients, test fixtures, or a migration path toward ZK — this is the tool.

If you’re using enacl today, the secretbox and sealed box operations are wire-compatible. You can swap those calls with no data migration.

What’s next: I’m transitioning Mosslet (currently using enacl) to this library as a first step — it’s a straightforward swap that gives us precompiled binaries and PQ-readiness before we add the client-side WASM layer.

Zero-Knowledge: This repo also seemed a fitting place to include a Zero-Knowledge Phoenix Guide that walks through implementing full client-side encryption in LiveView — the architecture behind Metamorphic. The guide covers the WASM client setup, key hierarchy, LiveView hooks, and where the server-side library fits in (spoiler: for a full ZK app, the server mostly just stores opaque blobs).

elixir

# mix.exs
{:metamorphic_crypto, "~> 0.1”}

elixir

key = MetamorphicCrypto.generate_key()
{:ok, ct} = MetamorphicCrypto.encrypt("hello", key)
{:ok, "hello"} = MetamorphicCrypto.decrypt(ct, key)

# Post-quantum hybrid
{pk, sk} = MetamorphicCrypto.Hybrid.generate_keypair()
{:ok, sealed} = MetamorphicCrypto.Hybrid.seal("quantum-safe", pk)
{:ok, "quantum-safe"} = MetamorphicCrypto.Hybrid.open(sealed, sk)

Hex: hex.pm/packages/metamorphic_crypto

github.com/moss-piglet/metamorphic_crypto

Happy to answer questions or hear feedback.

For context on the architecture behind this, I wrote about the zero-knowledge LiveView encryption approach and the Mosslet project in earlier posts.

2 Likes