MetamorphicCrypto (metamorphic_crypto v0.3.0)

Copy Markdown View Source

NaCl-compatible encryption for Elixir with post-quantum support.

MetamorphicCrypto provides NaCl-compatible cryptographic primitives powered by Rust NIFs with precompiled binaries — no Rust toolchain, no C compiler, no system packages required.

Quick Start

# Generate keys
key = MetamorphicCrypto.generate_key()
{public_key, private_key} = MetamorphicCrypto.generate_keypair()

# Symmetric encryption (XSalsa20-Poly1305)
{:ok, ciphertext} = MetamorphicCrypto.encrypt("hello", key)
{:ok, "hello"} = MetamorphicCrypto.decrypt(ciphertext, key)

# Public-key encryption (X25519 sealed box)
{:ok, sealed} = MetamorphicCrypto.seal("secret", public_key)
{:ok, "secret"} = MetamorphicCrypto.unseal(sealed, public_key, private_key)

Modules

For full control, use the specialized modules directly:

Wire Format

All functions accept and return base64-encoded strings. Ciphertext produced by this library is byte-compatible with libsodium/NaCl and the metamorphic-crypto WASM module used in browser clients.

Summary

Functions

Decrypt a ciphertext back to a UTF-8 string.

Encrypt a UTF-8 string with a symmetric key.

Generate a random 32-byte symmetric key (base64-encoded).

Generate an X25519 keypair.

Encrypt a UTF-8 string to a recipient's public key (anonymous sealed box).

SHA3-512 of base64-encoded data (base64 digest out).

Domain-separated SHA3-512 — recommended for key fingerprints, safety numbers, and key-transparency-log entries.

Decrypt a sealed box using the recipient's keypair.

Functions

decrypt(ciphertext, key)

@spec decrypt(ciphertext :: String.t(), key :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}

Decrypt a ciphertext back to a UTF-8 string.

Example

{:ok, "hello, world!"} = MetamorphicCrypto.decrypt(ciphertext, key)

encrypt(plaintext, key)

@spec encrypt(plaintext :: String.t(), key :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}

Encrypt a UTF-8 string with a symmetric key.

Uses XSalsa20-Poly1305 (NaCl secretbox). Returns base64-encoded ciphertext.

Example

key = MetamorphicCrypto.generate_key()
{:ok, ciphertext} = MetamorphicCrypto.encrypt("hello, world!", key)

generate_key()

@spec generate_key() :: String.t()

Generate a random 32-byte symmetric key (base64-encoded).

Example

key = MetamorphicCrypto.generate_key()

generate_keypair()

@spec generate_keypair() :: {String.t(), String.t()}

Generate an X25519 keypair.

Returns {public_key, private_key} as base64-encoded strings.

Example

{public_key, private_key} = MetamorphicCrypto.generate_keypair()

seal(plaintext, public_key)

@spec seal(plaintext :: String.t(), public_key :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}

Encrypt a UTF-8 string to a recipient's public key (anonymous sealed box).

The sender remains anonymous — only the recipient can decrypt.

Example

{public_key, _private_key} = MetamorphicCrypto.generate_keypair()
{:ok, sealed} = MetamorphicCrypto.seal("secret message", public_key)

sha3_512(data_b64)

@spec sha3_512(data_b64 :: String.t()) :: {:ok, String.t()} | {:error, String.t()}

SHA3-512 of base64-encoded data (base64 digest out).

General-purpose digest for public data. For key fingerprints, safety numbers, and key-transparency-log entries, prefer sha3_512_with_context/2. See MetamorphicCrypto.Hash for the full menu and the "public data only" security note.

Example

{:ok, digest} = MetamorphicCrypto.sha3_512(Base.encode64("abc"))

sha3_512_with_context(context, data_b64)

@spec sha3_512_with_context(context :: String.t(), data_b64 :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}

Domain-separated SHA3-512 — recommended for key fingerprints, safety numbers, and key-transparency-log entries.

context is a versioned UTF-8 label (e.g. "mosslet/key-fingerprint/v1"); data_b64 is the base64-encoded payload. See MetamorphicCrypto.Hash.

Example

{:ok, fp} =
  MetamorphicCrypto.sha3_512_with_context(
    "mosslet/key-fingerprint/v1",
    Base.encode64("public key bytes")
  )

unseal(ciphertext, public_key, private_key)

@spec unseal(
  ciphertext :: String.t(),
  public_key :: String.t(),
  private_key :: String.t()
) ::
  {:ok, String.t()} | {:error, String.t()}

Decrypt a sealed box using the recipient's keypair.

Example

{public_key, private_key} = MetamorphicCrypto.generate_keypair()
{:ok, sealed} = MetamorphicCrypto.seal("secret", public_key)
{:ok, "secret"} = MetamorphicCrypto.unseal(sealed, public_key, private_key)