Files
fds-os/docs/developer/releases.md
T

7.4 KiB
Raw Blame History

Release signatures and verification

Development reference and historical context. For current operating instructions, use the user manual. Acceptance applies only to the source and artifacts identified in each record.

Documentation index · M12 evidence

fds-release signs an artifact manifest and verifies its signature and every listed file. The static ARM executable is included in the base CLI package, so CLI, development and recovery images can all verify downloads. The workstation build supplies an x86_64 signer with the same format.

The signing tool has passed host, static ARM and independent OpenSSL acceptance. The complete frozen-input, versioned local 0.1.0 release passed acceptance; see M12 validation for its identity and Offline rebuilds for the workflow. The examples below describe the working signing interface; they do not identify a published or hardware-qualified FDS release.

Build and test the tools

After bootstrap, run:

make signing
make signing-test

The workstation executable is target/x86_64-unknown-linux-gnu/release/fds-release. The ARM executable is target/aarch64-unknown-linux-musl/release/fds-release; make tooling also exports it to out/fds-release. An ARM file cannot execute directly on the x86_64 workstation. make signing-test runs that file through the project-local QEMU interpreter as well as testing the host executable.

OpenSSL is a host test dependency used as an independent Ed25519 implementation. It is not linked into the FDS verifier. The test creates private disposable keys under its out/m12-signing.* directory, checks exact signatures, then exercises wrong keys, altered manifests, damaged or missing artifacts, symlinks, FIFOs, malformed fields and unsafe paths. Test keys are not release keys.

Create and retain a signing key

For a local signing identity, choose an existing private directory outside the checkout and its build output. This example creates a new directory; choose a different name if it already exists:

mkdir -m 700 "$HOME/fds-signing"
target/x86_64-unknown-linux-gnu/release/fds-release keygen \
  "$HOME/fds-signing/release-01"

The command creates release-01.key (32 secret bytes, mode 0600) and release-01.pub (the hexadecimal public key). Existing files are never overwritten. It prints SHA-256 of the decoded 32-byte public key, never the secret. This fingerprint differs from sha256sum release-01.pub, which hashes the hexadecimal text and its newline. Retain the private key separately from release downloads and source archives. Anyone who has that key can sign a release under that identity.

If public-key export failed after private-key creation, retain the private file and export it to a new path:

target/x86_64-unknown-linux-gnu/release/fds-release public-key \
  "$HOME/fds-signing/release-01.key" "$HOME/fds-signing/recovered.pub"

Private keys must be regular files owned by the invoking user with no group or other permissions. The tool rejects symlinks and invalid key lengths. Key generation uses Linux getrandom; temporary seed storage and the signing key are zeroized when dropped.

Sign an assembled directory

An assembled release directory must already contain its artifacts and a manifest.json matching the format below. Signing verifies every artifact's length and hash before creating manifest.sig:

target/x86_64-unknown-linux-gnu/release/fds-release sign \
  /path/to/release-directory --key "$HOME/fds-signing/release-01.key"

If a signature already exists, use a new release directory. The tool deliberately does not replace signatures or silently rewrite the manifest. A failed operation may leave its new output file for inspection; it never declares it verified.

Verify before using images

Obtain the signer's public key through a channel you already trust, or compare its fingerprint against a separately authenticated value. A public key included beside an untrusted download does not establish its identity by itself.

On FDS, including recovery:

fds-release verify /data/downloads/fds-release --key /data/keys/fds-release.pub

On the workstation, use the host executable instead:

target/x86_64-unknown-linux-gnu/release/fds-release verify \
  /path/to/release-directory --key /path/to/trusted-fds-release.pub

A successful command reports VERIFIED FDS/OS 0.1.0, the number of checked artifacts, and Hardware validation: deferred. Failure returns exit status 2 and an explanation. Keep downloaded images unchanged between verification and use. Verification covers only files listed in the manifest; unrelated extra files are not endorsed. The media writer performs its own image/layout validation and readback checks when writing a cartridge.

These are distribution signatures. The Pi firmware and stage0 currently do not enforce them at boot, so this is not a secure-boot implementation or a claim that the hardware has been tested.

Manifest and signature format

The JSON document has exactly these fields:

{
  "format": 1,
  "version": "0.1.0",
  "source_epoch": 1789909701,
  "source_sha256": "<64 lowercase hexadecimal characters>",
  "void_commit": "02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1",
  "hardware_validation": "deferred",
  "files": [
    {
      "name": "fds-system-cli-0.1.0.img",
      "bytes": 123456,
      "sha256": "<64 lowercase hexadecimal characters>"
    }
  ]
}

This is a schema illustration, not a usable manifest: the size and hash placeholders must be replaced with values from the actual artifact. Format 1 accepts a bounded numeric-leading release/development version, a positive source epoch, a 40-character Void commit, and 1–64 nonempty artifacts. Filenames must be flat safe ASCII names; paths, duplicates, hidden names, reserved manifest filenames and unknown fields are rejected. The manifest is bounded to 1 MiB. Files must be regular files, opened without following symlinks; hashing streams their bytes and checks for changes during the read.

The signature is Ed25519 over the exact bytes FDS/OS release manifest v1 followed by a NUL byte and the raw manifest.json bytes. JSON whitespace is therefore authenticated too. manifest.sig contains 128 lowercase hexadecimal characters and a newline. The public-key file contains 64 lowercase hexadecimal characters and a newline. Verification rejects weak public keys and uses strict signature verification.

The implementation uses locked ed25519-dalek 3.0.0 for Ed25519, zeroize 1.9.0 for secret buffers, and the existing SHA-256/JSON crates. Its locked transitive dependencies provide curve arithmetic, digest and signature types. Default dalek features are disabled; no asynchronous runtime, OpenSSL binding or shared target library is added. The dependencies and their license metadata are recorded in Cargo.lock and the cached crate sources. The build collects their license texts, including build dependencies and vendored libusb notices, into /usr/share/licenses/fds-cli/RUST-NOTICES.txt. The usage guide is installed at /usr/share/doc/fds/releases.md in every profile. Algorithm interoperability is checked against the RFC 8032 test vector and OpenSSL; strict verification follows the dalek API.