f0rest8

f0rest8

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.

Most Liked

f0rest8

f0rest8

Update: v0.1.2 released

Okay, I updated Mosslet to fully replace :enacl in production. In doing so, I discovered a few fixes we needed to make to the metamorphic_crypto library, but the crypto itself worked as hoped. :relieved_face:

metamorphic_crypto v0.1.2 (library fixes):

  • Fixed v0.1.1 tar naming and v0.1.2 targets list — works for everyone on Hex without Rust installed

Mosslet (production swap):

  • Removed enacl entirely — no more C compiler, libsodium system dependency, or CI patches
  • Swapped in metamorphic_crypto v0.1.2 — precompiled Rust NIFs, same NaCl wire format
  • Added application-level decrypt fallback — tries decrypt_string (UTF-8 text) first, falls back to rawdecrypt for binary data like images. The library provides both APIs by design; Mosslet’s wrapperhandles the routing.
  • All existing production data decrypts without any migration. Drop-in swap, as promised.

Performance: Noticeably faster — the Rust NIFs outperform enacl’s C NIFs, and on a timeline page that decrypts dozens of posts (usernames, bodies, avatar key unsealing), the gains compound.

What’s next — Mosslet’s path to zero-knowledge:

Phase 2: Post-quantum hybrid key wrapping (server-side NIFs)

  • Add pq_public_key / encrypted_pq_private_key columns to users
  • Generate hybrid keypairs on login via MetamorphicCrypto.Hybrid
  • MetamorphicCrypto.Seal.seal_for_user/3 with pq_public_key: option
  • unseal_from_user/4 auto-detects legacy vs hybrid format — old data keeps working
  • Progressive re-seal of existing context keys on login

Phase 3: Full ZK with WASM (like Metamorphic)

  • Add the metamorphic-crypto WASM build to assets/vendor/
  • New features encrypt/decrypt entirely in the browser via LiveView hooks
  • Server becomes a dumb blob store for those features

I’m hopeful because the metamorphic_crypto library is so far working as expected. Since the NIF and WASM are compiled from the same Rust crate, data sealed by the server (Phase 2) can be unsealed by the browser (Phase 3) and vice versa. I can move features to client-side ZK one at a time without breaking anything.

https://github.com/moss-piglet/metamorphic_crypto

Where Next?

Popular in Announcing Top

danschultzer
In short Plug n’ play OAuth 2.0 provider library. Just set up a resource owner schema with Ecto (your user schema), install the dependen...
New
OvermindDL1
I created a new library (rather I pulled out a couple files from my big project), it manages an operating system PID file for the BEAM. ...
New
tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
mplatts
With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components. We also have a boi...
New
kelvinst
Hey everyone! Well, we made this lib a while ago and now we decided to finally go out and public with it! It’s a tool for creating and m...
New
mbuhot
Leverage Open Api 3.0 (Swagger) to document, test, validate and explore your Plug and Phoenix APIs. Generate and serve a JSON Open API ...
New
Azolo
Hey everyone, I just released WebSockex which is a Elixir WebSocket client. WebSockex strives to work as a OTP special process, be RFC6...
New
OvermindDL1
Been making an MLElixir thing (not released yet…) for fun in spare time in the past day. I’m just trying to see how much I can get an ML...
132 13966 106
New
tmbb
I’ve decided to create this topic to discuss optimization possibilities for something like Phoenix LiveView. I’ve created this topic unde...
144 10187 141
New

Other popular topics Top

AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement