MacCrabRave
Documentation

Verification

Check plugin signatures and digests, with explicit trust and reproducibility limits

MacCrab verifies signed catalog metadata, publisher identity, downloaded bytes, and applicable revocations before installing from Rave. These checks establish integrity relative to trusted keys. They do not establish complete collection, safe behavior, or runtime containment.

For routine installation, use the current MacCrab app’s catalog or the command on the plugin page. The manual example below lets you inspect the published Posture Pro 0.3.1 archive without installing or executing it.

First-party plugins

Published MacCrab v1.21.5 runs first-party plugins in a Developer-ID-validated, unsandboxed process with the host’s privileges, including Full Disk Access if granted to MacCrab. Posture Pro declares read-only metadata collection and no outbound network use. This execution path does not enforce those filesystem/network restrictions.

The current Posture Pro 0.3.1 entry has no public source_repo and records reproducibility: not_applicable. Its signatures, raw archive digest, manifest digest, and signature digest can be checked. Its source cannot be independently rebuilt from a public source location supplied by that entry.

The version’s automated_passes includes a recorded build check, but that label does not override the explicit reproducibility status or establish public-source reproducibility. Use the version’s vetting.vetted_at date, not the initial entry approval date, when discussing its recorded review.

Earlier versions 0.1.0–0.3.0 are revoked because they lack the Developer ID signature required by newer MacCrab releases. Historical records remain inspectable. See current status for architecture, compatibility, and collection limitations.

Before checking manually

Use a fresh working directory, Python 3 with the cryptography package, curl, and jq. These dependencies are not all preinstalled by macOS.

Place a trusted raw 32-byte catalog public key at catalog.pub. Obtain it from an authenticated MacCrab distribution or confirm the published key’s SHA-256 fingerprint through a separately authenticated source. Downloading a key and fingerprint from the same server as the artifact does not independently establish trust.

A signature failure is not permission to replace your trusted key with whatever the server currently returns. Confirm an announced key rotation through your trusted channel before changing the pin.

1. Verify catalog documents before reading their claims

The example uses the current published plugin ID and version:

set -eu
PLUGIN_ID="com.maccrab.forensics.posture-pro"

curl --fail --silent --show-error -o catalog.json \
  https://rave.maccrab.com/catalog.json
curl --fail --silent --show-error -o catalog.json.sig \
  https://rave.maccrab.com/catalog.json.sig
curl --fail --silent --show-error -o entry.json \
  "https://rave.maccrab.com/catalog/${PLUGIN_ID}.json"
curl --fail --silent --show-error -o entry.json.sig \
  "https://rave.maccrab.com/catalog/${PLUGIN_ID}.json.sig"
curl --fail --silent --show-error -o revocations.json \
  https://rave.maccrab.com/revocations.json
curl --fail --silent --show-error -o revocations.json.sig \
  https://rave.maccrab.com/revocations.json.sig

python3 - <<'PY'
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

key = Ed25519PublicKey.from_public_bytes(Path("catalog.pub").read_bytes())
for name in ("catalog.json", "entry.json", "revocations.json"):
    key.verify(Path(name + ".sig").read_bytes(), Path(name).read_bytes())
    print(name + ": signature OK")
PY

Any failed download or signature must stop verification. This example verifies the documents’ signatures; it does not replicate the app’s persisted serial/rollback checks or prove that the server supplied the latest documents. A recently fetched document may have an old signed issue date.

2. Inspect version facts and revocations

jq '.versions["0.3.1"]' entry.json
jq '.revocations[] |
    select(.plugin_id == "com.maccrab.forensics.posture-pro")' revocations.json

Inspect all records returned for the plugin. A scope can revoke one version, all versions, or an inclusive version range. The current range from 0.1.0 through 0.3.0 does not include 0.3.1. Do not use a query that checks only exact versions: it would miss the actual historical revocation.

Stop if a record applies to the selected version. Also honor local publisher-key revocations and your operator trust policy. The manual signature example does not load the app’s local trust store or replace the installer’s complete admission checks.

3. Verify the archive, manifest, publisher key, and signature

The example URL below corresponds to the signed 0.3.1 release record. For another version, derive the URL from its verified release_url_template and recorded tag; do not assume tags always equal v<version>.

curl --fail --silent --show-error -o plugin.zip \
  https://rave.maccrab.com/releases/v0.3.1/com.maccrab.forensics.posture-pro.maccrabplugin.zip

python3 - <<'PY'
import hashlib
import json
from pathlib import Path
from zipfile import ZipFile
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

plugin_id = "com.maccrab.forensics.posture-pro"
version = "0.3.1"
entry = json.loads(Path("entry.json").read_bytes())
if entry["id"] != plugin_id:
    raise SystemExit("Catalog plugin ID mismatch")
facts = entry["versions"][version]

def check_digest(data, expected, label):
    if not expected or expected == "0" * 64:
        raise SystemExit(label + ": missing or placeholder digest")
    if hashlib.sha256(data).hexdigest() != expected:
        raise SystemExit(label + ": digest mismatch")
    print(label + ": digest OK")

check_digest(Path("plugin.zip").read_bytes(), facts["artifact_sha256"], "Archive")
with ZipFile("plugin.zip") as archive:
    root = plugin_id + "/"
    manifest = archive.read(root + "manifest.json")
    binary = archive.read(root + "binary")
    signature = archive.read(root + "signature")
    publisher = archive.read(root + "signing.key.pub")

check_digest(manifest, facts["manifest_sha256"], "Manifest")
check_digest(signature, facts["signature_sha256"], "Signature file")
check_digest(publisher, entry["signer_public_key_sha256"], "Publisher key")
parsed = json.loads(manifest)
if parsed["id"] != plugin_id or parsed["version"] != version:
    raise SystemExit("Manifest identity mismatch")
payload = (b"maccrab-tierb-plugin-v1\n"
           + hashlib.sha256(manifest).digest()
           + hashlib.sha256(binary).digest())
Ed25519PublicKey.from_public_bytes(publisher).verify(signature, payload)
print("Plugin Ed25519 signature: OK (archive not executed)")
PY

The plugin’s signature covers the domain-separation prefix maccrab-tierb-plugin-v1\n, followed by the raw SHA-256 digests of the exact manifest and binary bytes.

This check does not validate Developer ID or the host-team execution requirement. The published host checks the executable’s required code signature before first-party execution. A successful Ed25519 verification alone is not evidence that the binary is admitted by that execution path.

4. Understand canonical hashes and rebuild evidence

The canonical-hash tool normalizes packaging and certain Mach-O differences. Obtain a trusted copy of the tool before running it; it is executable code, not a signed catalog document.

./maccrab-rave-canonical-hash plugin.zip

Compare its output with the version’s canonical_tree_sha256. A match means the downloaded archive’s normalized contents match the recorded digest. It does not prove the binary was built from a particular source.

Independent reproducibility additionally requires:

  1. An accessible source repository at the exact recorded commit.
  2. The declared toolchain, dependencies, build environment, and packaging procedure.
  3. A rebuild whose canonical digest matches both the released archive and the catalog record.

A matching release tag alone does not establish the recorded source commit. Do not execute an unfamiliar repository’s build scripts without reviewing them and using an appropriate build environment. For the current first-party entry, public independent rebuilding is not available through the catalog.

Installed-plugin verification

maccrabctl plugin verify checks the installed plugin registry against current local trust and revocation state. In the current implementation it is not a general detached-JSON verifier and does not verify an arbitrary downloaded bundle path. Do not pass --public-key, --signature, or an archive path expecting the manual checks above.

If verification fails, retain the error and the exact version/digests and contact [email protected]. See the catalog API and submission requirements.