DiskpipeSTORAGE / OPERATIONS

Offline FIPS compilation

The canonical build prepares its inputs online, compiles the native AWS-LC provider and all application executables without network access, then assembles and signs the release. Input acquisition, GitHub orchestration, and publication still use the network. This is not an air-gapped end-to-end workflow.

Compilation

scripts/fips-reproducible-provider.py invokes the prescribed AWS-LC procedure inside a new network namespace containing only loopback. An unprivileged runner creates it through a user namespace; passwordless sudo is unnecessary. The source archive is verified before execution. The resulting provider package is the input to the application build; Cargo does not recompile the native module.

The compiler consumes the provider headers, objects, and stable provider receipt. That receipt binds source, tools, platform, procedure, and packaged module hashes; its hash is embedded in the application. Archive timestamps and owner/group IDs are zeroed, and object modes use 0644. Object contents, names, order, duplicates, symbol indexes, and offsets stay unchanged. The full provider-build-record.json retains the timestamp, boot ID, and intermediate-output hashes and enters the image during assembly. Those per-run observations do not change the executable. The original archive is retained with that record. Acceptance verifies the normalized linker input against the original, allowing only these archive metadata changes.

Dockerfile.fips prepares OS packages, the Rust toolchain, and locked source dependencies in build-inputs. Cargo's download cache seeds a complete copy of those dependencies in /opt/cargo-inputs; compilation does not mount that cache or reuse compiled outputs. offline-build starts with an empty target directory and runs Cargo with --frozen under RUN --network=none. Its manifest records the source/compiler identity, provider hash, observed network interfaces, and SHA-256 of every shipped executable.

The final image takes its executables from offline-build. Before publication, the image tooling extracts the actual packaged executables and verifies their hashes against offline-build.json. This guards selection of the offline outputs; it does not perform an online rebuild.

Tests and acceptance-report collection happen after compilation. SBOM generation, signing, and publication happen after image assembly. Workflow files and acceptance collectors needed to document the build are copied into the assembly stage, rather than into the compiler's input stage.

Independent reproduction and authorized signing

Release checks have two separate scopes:

The current AWS KMS key uses P-256 ECDSA. Fresh signatures must not be treated as deterministic build outputs. Public verifiers can verify and retain existing signatures, but creating a fresh authorized signature requires access to the signing service. Payload comparison must report its exact scope separately from signature verification; neither result extends a CMVP certificate.

The image tooling now emits zcblock-csi-<variant>.unsigned-executable-bundle.tar and a separate Cosign signature bundle. The tar contains the shipped executables and a stable input/hash manifest. Its scope excludes the complete OCI filesystem, SBOMs, workflow records, and signatures. Cross-machine success must be demonstrated by comparing actual bundles; the existence of this tooling is not that result.

Both build variants accept --effective-build-timestamp (Unix seconds). When omitted, the build chooses it once at runtime, supplies it as SOURCE_DATE_EPOCH, and exports it in the bundle and attestation manifests. Reuse that value for a rebuild. Static-library member timestamps are always zero; they do not need the effective timestamp. No new wall-clock start/end fields are added.

--expected-unsigned-executable-bundle-sha256 optionally asserts the earlier bundle hash. A mismatch stops before signing or publication. The FIPS workflow exposes the corresponding expected_unsigned_executable_bundle_sha256 and effective_build_timestamp dispatch inputs and exports both values as job outputs. Every build also writes zcblock-csi-<variant>.unsigned-executable-bundle.sha256.

The bundle hash appears as zcutils:unsigned-executable-bundle:sha256 in a CycloneDX metadata property and an SPDX annotation. Both signed in-toto SBOM statements also bind the bundle digest alongside the image digest. Standard verification checks both SBOM signatures against an independently trusted key, the detached bundle signature, and equality of the SBOM, exported, and actual bundle hashes. The unsigned checksum file alone is not an authenticity check.

python3 scripts/zc-image-attest.py verify --variant fips-aspiring \
  --output-dir /path/to/release/image-attestations --require-signature \
  --cosign-verification-key /path/to/trusted-signing-public-key.pem
# The verified output prints the bundle SHA-256 and effective timestamp.
python3 scripts/test-github-ec2-runner.py --fips-build \
  --effective-build-timestamp EPOCH \
  --expected-unsigned-executable-bundle-sha256 SHA256

Independent rebuilds can use scripts/zc-release-payload.py create, verify, and compare. verify authenticates an existing detached signature with the public key; it does not need signing access. compare checks complete unsigned bundle bytes and deliberately ignores the separate signature envelopes.

Optional comparison experiment

Normal workflow dispatches set compare_online=false: they do not compile an online reference or run the same-machine comparison. For an explicit experiment, set compare_online=true, or run the local workflow helper with --fips-build --compare-online.

In that experiment, the native runner rebuilds the provider from clean source at the same absolute path with networking enabled and disabled, then compares the module object, metadata-normalized archive, tool, and identity probe. Original archive hashes remain recorded separately. The application Dockerfile selects the explicit reproducibility-check stage: two clean compiler branches use the same prepared inputs, and every shipped executable must have matching SHA-256 hashes. A mismatch stops the build. Only offline outputs are selected.

These experiments establish repeatability on one builder. Reproducibility across machines must be assessed by rebuilding on separate machines with the same source, tools, provider procedure, and input versions and comparing the actual outputs. Preserve the release's offline manifest and native artifact hashes for that comparison. Per-run receipts, signatures, and OCI metadata are not expected to match byte for byte; matching executable hashes must not be described as matching complete OCI image digests.

After downloading and verifying two releases and their launch records, compare independent workers with:

python3 scripts/fips-compare-builds.py \
  --first /path/to/first/artifact --first-launch /path/to/first/launch.json \
  --second /path/to/second/artifact --second-launch /path/to/second/launch.json \
  --report independent-build-comparison.json

The tool requires distinct worker instance IDs, verifies the actual packaged files against the offline manifests, and compares source/tool identity and native-provider outputs. It does not authenticate the supplied launch records; verify the downloaded artifacts and their origin before running it.

Built from Markdown with Rust. No client-side dependencies.