Skip to content

Baseline Keys

TrustSight verifies signed baselines against ed25519 public keys. There are two distinct mechanisms, and this page records the keys for each.

Corpus baseline (A13): repo-pinned key

The corpus baseline (package profiles, PKGBUILD snapshots, and the metadata snapshot that feed the novelty and maturity models) is verified against a single key pinned in the repository at src/trustsight/full_aur/baseline_pubkey.pem. The file holds the 32 raw bytes of the public key, not a PEM wrapper, because that is what the loader reads.

Purpose Algorithm Fingerprint (sha256 of the raw public key) Since
Corpus baseline distribution Ed25519 sha256:6000c216fc85e4efd523a47b6399ec781f05539f33536ac75799775496f41928 v0.12.0

Raw public key (hex): 3bc5a24d09072c4aed2165f29d5a8fd42aeacb9f96962c12eccde64c79786210

The private key never enters the repository. It is held by the maintainer and, since the release channel exists, is also placed in the GitHub Actions secret BASELINE_SIGNING_KEY, which the release baseline workflow reads to sign the baseline-* assets at release time. Rotation means cutting a release that pins a new public key; there is no in-band revocation.

The release channel

Every baseline the tool consumes at runtime is distributed as a release asset named with the baseline- prefix, and a channel release (tag baseline-<date>) ships the whole family. Channel releases are kept apart from software releases: a vX.Y.Z tag carries the program and its notes, never baseline assets, and a channel release is published after its software release so latest (the tool's default channel) resolves to it; the publishing guide documents the cadence and seed fetch --tag for pinning.

Asset What it is Consumed by
baseline-seed.tar.gz the v2 hashed novelty seed trustsight seed fetch, first-run auto-import
baseline-ioc-<source>-<incident>-manifest.json one IOC baseline's signed manifest (carries the curator key) trustsight ioc update
baseline-ioc-<source>-<incident>-iocs.jsonl the corresponding indicator list trustsight ioc update
baseline-corpus.tar.zst the corpus baseline artifact downloaded and imported with trustsight import-baseline
baseline-manifest.json per-asset SHA-256, size and signature, plus the public key used release inventory

Every asset ships with a detached .sig sibling: the raw 64-byte Ed25519 signature over the exact asset bytes, under the pinned distribution key. The tool verifies that signature before it reads the payload, so a download that does not verify is refused (ReleaseSignatureError), never imported. scripts/build_release_baselines.py assembles and signs the family, and self-verifies every signature with the program's own verifier before it writes, so it cannot emit a release the tool would refuse.

Building and importing a signed corpus baseline

Signing is built into the exporter, so there is no separate build script:

trustsight full-aur --export corpus.tar.zst --sign <raw-ed25519-private-key>
python scripts/build_release_baselines.py --out dist/ --sign-key <raw-key> --corpus corpus.tar.zst
gh release upload <tag> dist/baseline-*

Both keys the tooling reads are raw 32-byte ed25519 keys, not PEM. An openssl-generated PEM is converted once with the cryptography library:

from pathlib import Path
from cryptography.hazmat.primitives.serialization import (
    load_pem_private_key, load_pem_public_key,
)

# public: PEM -> the 32 raw bytes pinned in the repo
pub = load_pem_public_key(Path("baseline_pubkey.pem").read_bytes())
Path("src/trustsight/full_aur/baseline_pubkey.pem").write_bytes(pub.public_bytes_raw())

# private (kept outside the repo): PEM -> the 32 raw bytes `--sign` expects
priv = load_pem_private_key(Path("trustsight-release.pem").read_bytes(), password=None)
Path("trustsight-release.raw").write_bytes(priv.private_bytes_raw())  # never commit this

The signature covers full_aur/export.canonical_artifact_bytes (a canonical JSON of the manifest fields, sorted profiles and snapshots, and the metadata snapshot hash), verified by verify_artifact against the pinned key. A baseline you built yourself but did not sign still imports with --allow-unsigned; a build that ever pins a non-key file refuses with NoTrustedKeyError rather than reporting a valid artifact as forged. Distribution signing of the released .tar.zst bytes (the detached .sig sibling) is a second, transport-level check on top of the artifact's own internal signature.

IOC federation baselines (A13b): per-source keys in config

IOC baselines are not verified against the repo-pinned key. Each IOC baseline carries its curator's public key in its own manifest.json, and the operator pins the public key they trust for each feed in configuration (see [baselines.ioc] and the IOC federation reference). This is what keeps IOC matches attributable per curator and lets one curator's key rotate without touching another's. The maintainer may use the same ed25519 keypair as the corpus baseline above, but the two verification paths are independent (and a released IOC pair additionally carries the distribution signature of the pinned repo key, checked before the curator's own signature is consulted).

Not the PGP disclosure key

The ed25519 baseline key here is distinct from the PGP key used for private vulnerability reporting (F759D6D49B0A395AB922414A5CC3B4C50D37E793). One signs release artifacts; the other encrypts security reports. They are not interchangeable.