Skip to content

feat: Ed25519 release signing for IPFS-distributed binaries #412

@lthibault

Description

@lthibault

Problem

The install script and ww perform upgrade fetch binaries from IPFS via IPNS (releases.wetware.run). Integrity is verified by checksums, but authenticity is not. The IPNS key is a single point of trust: if compromised, an attacker can serve malicious binaries with matching checksums.

Threat model

IPNS key compromised (via VPS breach, leaked SSH key, CI supply chain)
  → attacker publishes malicious CID to releases.wetware.run
  → binary + matching CHECKSUMS.txt served from attacker's CID
  → checksum verification passes
  → malicious binary runs `perform install` with full user access

Proposed solution

Sign CHECKSUMS.txt with an Ed25519 key held only in GitHub Actions (separate trust domain from VPS/IPNS). Verify the signature in both:

  • scripts/install.sh (using openssl pkeyutl, available on macOS + Linux)
  • ww perform upgrade (using ed25519-dalek, already a dependency)

Public key lives at share/keys/release.pub in the release tree (FHS convention for vendor-provided read-only data). The authoritative copy is hardcoded in install.sh (served via GitHub HTTPS) and compiled into the ww binary via include_bytes!.

Trust model

Signing key (private)           Signing key (public)
  │                               │
  │ stored as GitHub secret       │ hardcoded in install.sh (GitHub HTTPS)
  │ never touches VPS             │ compiled into ww binary (include_bytes!)
  │                               │ published at share/keys/release.pub
  ▼                               ▼
CI signs CHECKSUMS.txt    ────>  install.sh / ww verify CHECKSUMS.txt.sig

Two independent systems must both be compromised (IPNS key + signing key).

Implementation outline

  1. Generate Ed25519 keypair, store private as RELEASE_SIGNING_KEY GitHub secret
  2. Commit public key to share/keys/release.pub (base58) and share/keys/release.pub.raw (32 bytes for include_bytes!)
  3. CI publish job: sign CHECKSUMS.txt with openssl after assembly, place .sig in release tree
  4. scripts/install.sh: verify signature with openssl pkeyutl before trusting checksums (pubkey hardcoded as base64 DER)
  5. src/cli/main.rs perform_upgrade: verify signature with ed25519-dalek before trusting checksums
  6. Key rotation: update GitHub secret + repo pubkey + install.sh hardcoded key. Users on old binary must re-run install script.

Files to modify

  • share/keys/release.pub (new)
  • share/keys/release.pub.raw (new)
  • scripts/install.sh
  • src/cli/main.rs (perform_upgrade, ~line 2557)
  • .github/workflows/rust.yml (publish job)
  • .github/workflows/release.yml (publish-ipfs job)

Not in scope

  • Signed key rotation protocol
  • macOS notarization
  • Signing individual binaries (CHECKSUMS.txt covers them)
  • Signing WASM components separately

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions