WIP: Rough draft for updated generic OCI sealing#226
WIP: Rough draft for updated generic OCI sealing#226cgwalters wants to merge 2 commits intocomposefs:mainfrom
Conversation
2c6851a to
1ce192a
Compare
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 PKCS#7 signing and verification for composefs fsverity digests, following the OCI referrers pattern for signature artifact storage. Key features: - cfsctl oci sign: Sign images with X.509 cert/key pairs - cfsctl oci verify: Verify signatures against trusted certificates - cfsctl oci pull --require-signature: Enforce signature verification - cfsctl keyring add-cert: Inject certs into kernel .fs-verity keyring - Signature artifacts stored as OCI referrers with subject field The signature artifact format uses: - artifactType: application/vnd.composefs.signature.v1 - Layers contain PKCS#7 DER signatures - Algorithm annotation: composefs.algorithm: fsverity-sha512-12 Assisted-by: OpenCode (Claude claude-opus-4-5@20251101)
1ce192a to
063ff54
Compare
| 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()?; |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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>( |
There was a problem hiding this comment.
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>( |
There was a problem hiding this comment.
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"); |
| format!("{seed:02x}").repeat(32) | ||
| } | ||
|
|
||
| fn sample_subject() -> Descriptor { |
There was a problem hiding this comment.
Let's unify this stuff with shared infra to generate an ocidir with known content
|
|
||
| #[test] | ||
| fn test_fs_ioc_enable_verity_with_sig_bad_fd() { | ||
| let fd = ManuallyDrop::new(unsafe { OwnedFd::from_raw_fd(123456) }); |
There was a problem hiding this comment.
Gross, no. This unit test has no value, should just be integration testing this.
| /// <base64 encoded DER> | ||
| /// -----END CERTIFICATE----- | ||
| /// ``` | ||
| fn pem_to_der(pem: &[u8]) -> Result<Vec<u8>, KeyringError> { |
There was a problem hiding this comment.
| } | ||
|
|
||
| /// Simple base64 decoder for PEM parsing. | ||
| fn base64_decode(input: &str) -> Result<Vec<u8>, String> { |
There was a problem hiding this comment.
No, use base64 crate.
This is just some rough draft raw material that builds on: