Back to Blog

Cryptographic Signing for AI Tools: What Ed25519 Gives You

MPP Protocol·March 20, 2026·12 min read
MPPEd25519CryptographySupply ChainSecuritySigning

In January 2024, a maintainer of the xz compression library — a dependency embedded in nearly every Linux distribution — was discovered to have inserted a backdoor into the project over a two-year period of trust-building. The backdoor targeted OpenSSH authentication and would have given the attacker remote code execution on every affected system.

The xz attack succeeded because the software supply chain had no mechanism to detect that the published artifact differed from what a trusted party intended to publish. Code review caught it — by luck, weeks before it shipped in stable releases. Cryptographic signing would have caught it at the infrastructure level, automatically, before it reached a single consumer.

AI tool registries are the next target. The same attack patterns that have plagued npm, PyPI, and crates.io will be applied to AI tool ecosystems — and most of those ecosystems have no signing infrastructure at all. MPP was designed to close that gap from the start.


The Trust Problem

When an AI agent installs and runs a tool, it is implicitly trusting three things:

  1. Integrity. The tool it downloaded is the same artifact the author published. No bytes were modified in transit, at rest in the registry, or by a compromise of the distribution infrastructure.

  2. Authorship. The tool was produced by the entity it claims to be from. The package named org.acme.database-tool was actually published by Acme, not by an attacker who registered a similar name or compromised the registry.

  3. Freshness. The tool hasn't been silently replaced by a malicious version. An attacker who gains access to the registry or the author's credentials hasn't pushed a backdoored update.

Without cryptographic signing, none of these properties can be verified. The consumer downloads a blob, hopes it's the right one, and runs it. This is the trust model of the early web — download an executable, double-click it, and pray.

Cryptographic signing gives you a mechanism to verify all three properties, automatically, before a single byte of tool code executes.


Why Ed25519

MPP uses Ed25519 for all signing and verification operations. This is not an arbitrary choice. Ed25519 has specific properties that make it the right fit for a package-signing use case.

No Configuration Knobs

RSA requires choosing a key size (2048? 3072? 4096?), a padding scheme (PKCS#1 v1.5? PSS? OAEP?), and a hash algorithm (SHA-256? SHA-384? SHA-512?). Getting any of these wrong can weaken or break the security guarantee. ECDSA requires choosing a curve (P-256? P-384? secp256k1?) and has been the subject of multiple implementation-level vulnerabilities related to nonce generation.

Ed25519 has one key size (256-bit), one curve (Curve25519), one hash (SHA-512 for internal use), and one set of parameters. There is nothing to configure and nothing to get wrong. Every Ed25519 implementation produces and verifies the same signatures. This eliminates an entire category of deployment errors.

Small Keys and Signatures

| Algorithm | Public Key Size | Signature Size | |-----------|----------------|---------------| | RSA-2048 | 256 bytes | 256 bytes | | RSA-4096 | 512 bytes | 512 bytes | | ECDSA P-256 | 64 bytes | 64 bytes | | Ed25519 | 32 bytes | 64 bytes |

An MPP package embeds the publisher's public key and signature in the certs/ directory. At 32 + 64 = 96 bytes total, the cryptographic overhead is negligible — less than the size of a DNS response. Compare this to RSA-4096 at 1,024 bytes, which adds measurable overhead to small packages.

Fast Operations

Ed25519 signing and verification complete in approximately 60 microseconds on commodity hardware. For comparison:

| Operation | Ed25519 | RSA-2048 | RSA-4096 | |-----------|---------|----------|----------| | Sign | ~60μs | ~1ms | ~6ms | | Verify | ~60μs | ~30μs | ~100μs | | Key generation | ~30μs | ~300ms | ~3s |

Verification speed matters because the Gatekeeper runs on every tool invocation. At 60μs, signature verification adds no perceptible latency to the execution pipeline. At 6ms (RSA-4096 signing in CI/CD), the overhead is still acceptable but 100× higher.

Key generation at 30μs means developers can generate keys instantly. RSA-4096 key generation at 3 seconds creates a noticeable pause — and in CI/CD environments running thousands of jobs, the aggregate cost is meaningful.

Deterministic Signatures

Ed25519 is deterministic: the same private key signing the same message always produces the same signature. This eliminates an entire class of vulnerabilities related to random number generation.

ECDSA, by contrast, requires a fresh random nonce for every signature. If the nonce is predictable, reused, or has insufficient entropy, the private key can be recovered from the signature. This has happened in production (the PlayStation 3 ECDSA key recovery, the Android Bitcoin wallet vulnerability). Ed25519 sidesteps the issue entirely.

No OpenSSL Dependency

MPP's Ed25519 implementation uses the ring library — a pure-Rust cryptographic library that does not depend on OpenSSL. This matters for two reasons:

  1. Supply-chain surface. OpenSSL is a massive, complex C library with a long history of critical vulnerabilities (Heartbleed, the padding oracle attacks, numerous buffer overflows). Not depending on it removes a significant attack surface.
  2. Build simplicity. Compiling OpenSSL requires system-level C toolchains and platform-specific configuration. ring compiles with standard Rust tooling on every platform.

The Signing Workflow

The signing process in MPP is a four-step pipeline that produces a tamper-evident artifact:

Step 1: Content Hashing

The signer computes a SHA-256 hash over all entries in the .mpp archive, excluding the certs/ directory (which will contain the signature itself — you can't include the signature in the data being signed).

Entries are sorted alphabetically by path before hashing. This is critical: ZIP archives don't guarantee entry order, so the same set of files could produce different hashes depending on how the archive was constructed. Sorting makes the hash deterministic — the same contents always produce the same hash.

content_hash = SHA-256(
  path_bytes("bin/tool.wasm") + file_bytes("bin/tool.wasm") +
  path_bytes("manifest.json") + file_bytes("manifest.json") +
  path_bytes("resources/config.json") + file_bytes("resources/config.json")
)

Step 2: Signing

The content hash is signed with the publisher's Ed25519 private key:

signature = Ed25519.sign(private_key, content_hash)

The signature is 64 bytes. It binds the content hash to the private key: anyone with the corresponding public key can verify that (a) this specific content hash was signed by (b) the holder of this specific private key.

Step 3: Embedding

Two files are injected into the archive's certs/ directory:

  • certs/signature.sig — the 64-byte Ed25519 signature (base64-encoded)
  • certs/publisher.pub — the 32-byte public key (base64-encoded)

The package is now a self-contained, verifiable artifact. Everything needed to verify its integrity — the content, the signature, and the public key — is in the archive.

Step 4: Verification (Consumer Side)

When the Gatekeeper runs, it:

  1. Recomputes the content hash using the same sorted-entry algorithm
  2. Reads certs/publisher.pub and certs/signature.sig
  3. Verifies: Ed25519.verify(publisher.pub, content_hash, signature.sig)

If verification succeeds, it proves:

  • Integrity: Not a single byte of any file in the package has changed since signing.
  • Authorship: The package was signed by the holder of the private key corresponding to publisher.pub.

If verification fails — because a file was modified, the manifest was altered, or the signature was forged — execution is refused.


What Signing Catches

Tampered Binaries

An attacker who gains access to a registry's storage backend and modifies a WASM binary will produce a package whose content hash no longer matches the signature. The Gatekeeper catches this at Step 4 of verification.

Modified Manifests

An attacker who alters a tool's manifest to add undeclared capabilities (hoping the permission engine will grant broader access) will invalidate the content hash. The Gatekeeper catches this before the permission engine ever sees the manifest.

Package Substitution

An attacker who replaces an entire package with a malicious one will have a different content hash and a different (or missing) signature. Unless the attacker also possesses the original publisher's private key, the signature cannot be forged.

Registry Compromise

If a registry's database or storage is compromised, the attacker can modify packages — but every modified package will fail verification on the consumer side. The signing infrastructure means that registry compromise does not automatically lead to consumer compromise.

Man-in-the-Middle

If an attacker intercepts the package download and modifies it in transit, the content hash will not match. Signing provides integrity protection independent of the transport layer (though HTTPS should be used as well).


Key Management in Practice

Cryptographic signing is only as strong as the key management behind it. This is where the operational discipline matters.

Key Generation

mpp keygen -o ~/.mpp/keys

This generates a PKCS#8 DER private key and a 32-byte public key. The Key ID — a human-readable identifier derived from SHA-256(public_key)[0:8] — provides a convenient shorthand:

Key ID: pub_a1b2c3d4e5f67890

Key Storage by Environment

| Environment | Recommended Storage | Rationale | |-------------|-------------------|-----------| | Development | ~/.mpp/keys/ (local filesystem) | Convenience; developer's own machine | | CI/CD | Secret manager (GitHub Secrets, Vault) | No keys in version control; rotatable | | Production | HSM or cloud KMS | Hardware-backed; access-logged; tamper-resistant |

The private key should never be committed to version control. Add keys/ and *.key to .gitignore on day one.

Key Rotation

Keys should be rotated on a regular schedule and immediately if compromise is suspected:

  1. Generate a new keypair
  2. Register the new public key with all registries the publisher uses
  3. Re-sign and republish packages with the new key (as new versions)
  4. Remove the old public key from consumer trust stores
  5. Securely destroy the old private key (zero the file, then delete)

The rotation process results in new package versions. This is by design — consumers should be able to distinguish between "package signed with old key" and "package signed with new key" and make trust decisions accordingly.

Key Compromise Response

If a private key is compromised:

  1. Immediately yank all package versions signed with the compromised key via the registry API. Yanking prevents new installations while leaving existing installations functional.
  2. Generate a new keypair.
  3. Re-sign all packages and publish new versions with bumped version numbers.
  4. Notify consumers to update and remove the compromised key from trust stores.
  5. Report to the registry operator if the key was used on a public registry.

The yank mechanism is a soft-delete: it blocks new downloads without breaking existing deployments. This gives consumers time to update without causing a cascading outage.


Trust Establishment

A valid signature proves integrity and authorship. But a valid signature from an unknown key proves only that someone signed the package — not that the signer is trustworthy.

MPP provides three mechanisms for establishing trust:

Manual Trust

An administrator explicitly adds a publisher's public key to the host's trusted keystore:

let mut gatekeeper = Gatekeeper::new();
gatekeeper.trust_key(publisher_public_key_bytes);

This is the most direct model. The organisation reviews the publisher, verifies their identity through an out-of-band process, and adds their key. Any package signed by that key is trusted automatically.

Registry-Verified Trust

The MPP registry requires publishers to register with proof-of-key-ownership — they must sign a challenge with their private key to prove they hold it. The registry stores the verified mapping between publisher identity and public key.

When a host downloads a package from a trusted registry, it can rely on the registry's verification: the registry has confirmed that the publisher of org.acme.database-tool actually controls the key used to sign the package.

Policy-Based Trust

For low-risk tools (no capabilities, low security level), the permission policy can be configured to auto-approve any properly signed package. This is useful in development and evaluation environments where the risk of a read-only, sandboxed tool is minimal.


What This Means for Enterprise Security Teams

Cryptographic signing is not a feature to evaluate in isolation. It is the foundation on which every other MPP security guarantee depends.

Without signing, the Gatekeeper is checking the integrity of an artifact that might be counterfeit. The permission engine is evaluating capabilities declared by an author who might be an attacker. The sandbox is isolating code that might have been substituted. The privacy filters are processing output from a tool that might be designed to exfiltrate data through side-channels.

With signing, every downstream component operates on a verified foundation. The manifest is authentic. The binary is what the publisher compiled. The capabilities are what the publisher declared. The chain of trust starts at the signature and flows through every subsequent security layer.

For enterprise teams, this means:

  • Incident response has a starting point. When something goes wrong, you can determine exactly which publisher signed the package, when it was signed, and what binary it contained.
  • Compliance has evidence. Auditors can verify that every tool in the deployment has a valid signature from a trusted publisher.
  • Policy can be automated. Trust decisions based on publisher keys can be codified and enforced without manual review of every package.
  • the supply chain is auditable. The combination of publisher identity, content hash, and immutable signatures creates a verifiable chain from author to execution.

For the Gatekeeper's complete verification pipeline, read Inside the Gatekeeper. For key management operational procedures, see the Security Operations documentation.