Skip to content

WIP: Rough draft for updated generic OCI sealing#226

Draft
cgwalters wants to merge 2 commits intocomposefs:mainfrom
cgwalters:sealing-impl
Draft

WIP: Rough draft for updated generic OCI sealing#226
cgwalters wants to merge 2 commits intocomposefs:mainfrom
cgwalters:sealing-impl

Conversation

@cgwalters
Copy link
Collaborator

This is just some rough draft raw material that builds on:

@cgwalters cgwalters force-pushed the sealing-impl branch 2 times, most recently from 1ce192a to 063ff54 Compare February 12, 2026 16:49
composefs_oci::signing::FsVeritySigningKey::from_pem(&cert_pem, &key_pem)?;

// Build subject descriptor from the source image's manifest
let manifest_json = img.manifest().to_string()?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm we actually need to operate on the raw original representation, can't rely on to_string() always giving us the same thing.

/// Image reference (tag name)
image: String,
/// Path to the OCI layout directory (must already exist)
oci_layout_path: PathBuf,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use clap(value_parser) into an ocidir directly or so

/// the container to be mounted with integrity protection.
///
/// Returns a tuple of (sha256 content hash, fs-verity hash value) for the updated configuration.
pub fn seal<ObjectID: FsVerityHashValue>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be cleaner if we do a prep commit that removes the old sealing as we know we're not going to do it anymore.

/// # Returns
///
/// The number of referrer artifacts exported.
pub fn export_referrers_to_oci_layout<ObjectID: FsVerityHashValue>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this could land as a prep commit

use std::fs;
use std::io::Write;

let blobs_dir = oci_layout_path.join("blobs").join("sha256");
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use ocidir

format!("{seed:02x}").repeat(32)
}

fn sample_subject() -> Descriptor {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's unify this stuff with shared infra to generate an ocidir with known content

@cgwalters cgwalters force-pushed the sealing-impl branch 3 times, most recently from 361eeb7 to 2f93e4a Compare March 6, 2026 12:24
@cgwalters
Copy link
Collaborator Author

This one will need to logically depend on #225 because that one has a lot of hardening for the EROFS parser

The biggest goal here is support for Linux kernel-native fsverity
signatures to be attached to layers, which enables integration with
IPE.

Add support for a fully separate OCI "composefs signature" artifact
which can be attached to an image.

Drop the -impl.md doc...it's not useful to try to write this
stuff in markdown. The spec has some implementation considerations,
but it's easier to look at implementation side from a code draft.

Add standardized-erofs-meta.md as a placeholder document outlining the
goal of standardizing composefs EROFS serialization across implementations
(canonical model: tar -> dumpfile -> EROFS).

Assisted-by: OpenCode (Claude Opus 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
Implement end-to-end support for cryptographically signing composefs
OCI images using PKCS#7/fsverity detached signatures, stored as OCI
referrer artifacts following the 'composefs erofs-alongside' spec.

Core signing infrastructure (composefs crate):
- Add fsverity algorithm constants and ComposeFsAlgorithm type
- Add formatted_digest module for kernel-compatible fsverity digest
  construction (the 12-byte header + raw hash used by the kernel's
  FS_IOC_ENABLE_VERITY ioctl)
- Add kernel keyring support via composefs-ioctls keyring module
  (inject X.509 certs into .fs-verity keyring for kernel-level
  signature enforcement)

OCI signing library (composefs-oci crate):
- signing.rs: FsVeritySigningKey (sign) and FsVeritySignatureVerifier
  (verify) using openssl PKCS#7 with DETACHED|BINARY|NOATTR flags,
  compatible with Linux kernel fsverity builtin signature verification
- signature.rs: OCI artifact manifest builder/parser for the
  'application/vnd.composefs.erofs-alongside.v1' artifact type,
  storing per-layer and merged EROFS images alongside their PKCS#7
  signatures as typed layers with composefs.* annotations
- image.rs: compute_per_layer_digests() and compute_merged_digest()
  for deterministic EROFS image generation from OCI layer stacks
- oci_image.rs: seal_image() to compute and embed the composefs
  fsverity digest into the OCI config, export/import to OCI layout
  directories (migrated to ocidir crate for atomic I/O), referrer
  index management

CLI commands (cfsctl):
- 'oci seal <image>' — compute composefs EROFS, embed fsverity digest
- 'oci sign <image> --cert --key' — create signature artifact
- 'oci verify <image> [--cert]' — verify signatures (digest-only
  without --cert, full PKCS#7 with --cert)
- 'oci mount <name> <mountpoint> [--require-signature --trust-cert]'
  — verify signatures before kernel mount
- 'oci pull ... --require-signature --trust-cert' — verify after pull
- 'oci push <image> <dest> [--signatures]' — export to OCI layout
- 'oci export-signatures <image> <dest>' — export just artifacts
- 'oci inspect' — show referrer info in JSON output
- 'keyring add-cert <pem>' — inject cert into kernel keyring

The mount and pull --require-signature paths share a common
verify_image_signatures() helper that recomputes expected EROFS
digests and verifies each PKCS#7 signature blob against the trusted
certificate.

The mount command now also resolves tag names (via OciImage::open_ref)
instead of requiring raw config digests, consistent with seal/sign/
verify.

Integration tests:
- signing.rs: 17 unprivileged tests covering sign, verify, wrong cert,
  export, seal+sign roundtrip, artifact structure, --require-signature
  on pull and mount
- privileged.rs: 7 tests for real fsverity enforcement, kernel keyring
  injection, kernel signature acceptance/rejection
- podman.rs: 3 tests building real container images via podman
- cli.rs: updated for richer OCI test layout (4 entries) and new
  oci push/roundtrip tests
- test-oci-sign-verify.sh: standalone shell-based integration tests

Assisted-by: OpenCode (Claude claude-opus-4-6)
Signed-off-by: Colin Walters <walters@verbum.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant