Two sections
A signed DSO carries two extra ELF sections, both noload,readonly:
| Section | Bytes | Content |
|---|---|---|
.dso_manifest | variable | canonical UTF-8 JSON (schema below) |
.dso_sig | 64 | raw Ed25519 signature over the .dso_manifest bytes |
dso-sign adds them with objcopy; the host reads them with an mmap and a manual section-table walk (fedem::sign::Manifest::read) — no libelf dependency.
Manifest schema
{
"name": "circle-plugin",
"version": "1.2.0",
"author": "Alice <alice@example.com>",
"sha256": "<hex — SHA-256 of the DSO BEFORE the sections were added>",
"publicKeyId": "<hex — SHA-256 of the signer's DER-encoded public key>",
"abiMajor": 1,
"abiMinor": 0,
"timestamp": "2026-09-08T12:00:00Z"
}
Key order is fixed (Manifest::toJson) so the signed bytes are reproducible.
The stable-hash trick
sha256 is computed over the DSO before the two sections exist. To verify, Verifier:
- strips
.dso_manifest+.dso_sigto a temp file, - hashes that temp file,
- compares with
manifest.sha256.
So re-signing (which strips and re-adds the sections) does not change the hash, and a tampered code section does — mismatch is REJECTED.
Trusted-keys directory
A plain directory of PEM Ed25519 public keys:
/etc/app/trusted-keys.d/
alice.pub
build-server.pub
Any *.pub is a trusted signer; the file name is irrelevant (the match is by fingerprint). Unparseable files are skipped silently. Add a key = copy a file; revoke = delete it.
Trust levels
TRUSTED (valid sig + key known) · UNKNOWN (valid sig, key not in the dir) · UNSIGNED (no sections) · REJECTED (bad sig or hash mismatch). The library enforces nothing — see TrustLevel for the policy table.
What this model addresses
- Tampering after signing — any change to the code section fails the hash check (
REJECTED). - Unauthorised add-ons — an add-on not signed by a key you placed in the directory is
UNKNOWN(orUNSIGNED), which a strict host refuses. - Provenance — the manifest records who signed it and when.
What it does not address
- Revocation freshness — it is a directory, not a CRL/OCSP. Removing a key only affects verifications after that point (and cached results persist for the process lifetime).
- Supply-chain before the signature — it attests that this signer vouched for these bytes, not that the bytes are safe.
- Downgrade / rollback — nothing stops loading an older signed version; the
timestampandversionare informational. - Non-ELF platforms — the section embedding is ELF-only.
- Confidentiality — nothing is encrypted; the manifest is readable with
readelf -p .dso_manifest.
It is a pragmatic "did someone I trust build this, and has it changed since" check, sized for an application loading its own plugins — not a general code-signing PKI.

