MobDev.Plugin.Crypto (mob_dev v0.5.16)

Copy Markdown View Source

Ed25519 sign/verify primitives + canonical-term encoding for plugin signing.

The signing scheme (see MOB_PLUGIN_SECURITY.md, Phase 2):

  • Plugin authors generate a per-plugin Ed25519 keypair; the public key ships in priv/mob_plugin.pub and the manifest+sources are signed with the private key into priv/mob_plugin.sig.
  • Hosts verify the signature against the public key at activation time and refuse to build untrusted/unsigned plugins.

This module is the single place the Ed25519 + canonical-encoding decisions are encoded. Keep it crypto-only — workflow (sign/verify orchestration, fingerprint storage) lives in Sign, Verify, and TrustStore.

Summary

Types

Host-visible trust identifier; base64-encoded SHA-256 of the public key.

Raw 32-byte Ed25519 private key.

Raw 32-byte Ed25519 public key.

Raw 64-byte Ed25519 signature.

Functions

Canonical encoding of an arbitrary Erlang term to a binary.

Computes the host-visible trust identifier for a public key.

Generates a fresh Ed25519 keypair.

Signs payload_term with priv_bin.

Verifies signature_bin against payload_term and pub_bin.

Types

fingerprint()

@type fingerprint() :: String.t()

Host-visible trust identifier; base64-encoded SHA-256 of the public key.

priv_key()

@type priv_key() :: <<_::256>>

Raw 32-byte Ed25519 private key.

pub_key()

@type pub_key() :: <<_::256>>

Raw 32-byte Ed25519 public key.

signature()

@type signature() :: <<_::512>>

Raw 64-byte Ed25519 signature.

Functions

canonical_encode(term)

@spec canonical_encode(term()) :: binary()

Canonical encoding of an arbitrary Erlang term to a binary.

Uses :erlang.term_to_binary/2 with :deterministic so the same logical value produces the same bytes regardless of map iteration order. Centralised here so the determinism flag is in one place: sign/2, verify/3, and any future hash-of-payload helper all agree.

fingerprint(pub_bin)

@spec fingerprint(pub_key()) :: fingerprint()

Computes the host-visible trust identifier for a public key.

Format: "ed25519:<base64>" where <base64> is the standard base64 encoding (with = padding) of the SHA-256 digest of the raw 32-byte public key. Suitable for storing in mob.exs under :trusted_plugins and for comparing two keys for equality without printing the key itself.

generate_keypair()

@spec generate_keypair() :: {priv_key(), pub_key()}

Generates a fresh Ed25519 keypair.

Returns {priv_bin, pub_bin} as raw 32-byte binaries. The format matches what :crypto.sign/4 and :crypto.verify/5 accept directly with the :eddsa/:ed25519 options.

sign(payload_term, priv_bin)

@spec sign(term(), priv_key()) :: signature()

Signs payload_term with priv_bin.

The term is canonically encoded via canonical_encode/1 before signing, so the signature is over the deterministic binary form — the same map with the same contents produces the same signature regardless of map insertion order.

verify(payload_term, signature_bin, pub_bin)

@spec verify(term(), signature(), pub_key()) :: :ok | {:error, :invalid_signature}

Verifies signature_bin against payload_term and pub_bin.

Returns :ok on valid signature, {:error, :invalid_signature} otherwise. Mirrors sign/2 — the same canonical encoding is applied before verifying.