-
Notifications
You must be signed in to change notification settings - Fork 126
feat: Embeddable manifest API with Context/Settings, CAWG and BMFF.v3 support. #1847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
e18dfbe
16045a7
022f7b1
e3060a5
8471ac7
6c15777
b2cb1b6
0e61d9b
667ed7b
a8dfb56
4c01df8
6c9dce4
ef7c351
a791569
b27cf31
ca1239a
84a9557
641cd55
45a52e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1907,6 +1907,8 @@ impl Builder { | |
| /// Create a placeholder for a hashed data manifest. | ||
| /// | ||
| /// This is only used for applications doing their own data_hashed asset management. | ||
| /// This function does not support dynamic assertions (e.g., CAWG identity). | ||
| /// Use [`Builder::placeholder`] if you need dynamic assertion support. | ||
| /// | ||
| /// # Arguments | ||
| /// * `reserve_size` - The size to reserve for the signature (taken from the signer). | ||
|
|
@@ -1920,6 +1922,7 @@ impl Builder { | |
| reserve_size: usize, | ||
| format: &str, | ||
| ) -> Result<Vec<u8>> { | ||
| // Add DataHash to builder's definition if not present, so it persists for sign_data_hashed_embeddable | ||
| let dh: Result<DataHash> = self.find_assertion(DataHash::LABEL); | ||
| if dh.is_err() { | ||
| let mut ph = DataHash::new("jumbf manifest", "sha256"); | ||
|
|
@@ -1935,6 +1938,93 @@ impl Builder { | |
| Ok(placeholder) | ||
| } | ||
|
|
||
| /// Create a placeholder manifest with dynamic assertion support. | ||
| /// | ||
| /// This returns raw JUMBF bytes for the placeholder manifest. | ||
| /// Use [`Builder::composed_manifest`] to compose for a specific format if needed. | ||
| /// The signer is obtained from the Builder's context. | ||
| /// | ||
| /// **Important:** You must add a hash assertion (DataHash or BmffHash) to the Builder | ||
| /// before calling this method. The hash doesn't need final values, but should be sized | ||
| /// large enough to hold the final hash (e.g., with enough exclusions). | ||
| /// | ||
| /// # Workflow | ||
| /// 1. Add a placeholder hash assertion to the Builder with proper sizing | ||
|
||
| /// 2. Call this method to create a placeholder manifest | ||
| /// 3. Embed the placeholder into your asset | ||
| /// 4. Calculate the hash of the asset (excluding the placeholder) | ||
| /// 5. Update the hash assertion in the Builder: `builder.add_assertion(DataHash::LABEL, &updated_hash)` | ||
| /// 6. Call [`Builder::sign_placeholder`] to sign the manifest | ||
| /// | ||
| /// # Returns | ||
| /// * The raw JUMBF bytes of the `c2pa_manifest` placeholder. | ||
| /// | ||
| /// # Errors | ||
| /// * Returns an [`Error`] if the placeholder cannot be created or if no hash assertion exists. | ||
| pub fn placeholder(&self, format: &str) -> Result<Vec<u8>> { | ||
| // Check that a hash assertion exists before proceeding | ||
| let has_data_hash = self.find_assertion::<DataHash>(DataHash::LABEL).is_ok(); | ||
| let has_bmff_hash = self.find_assertion::<BmffHash>(BmffHash::LABEL).is_ok(); | ||
|
|
||
|
||
| if !has_data_hash && !has_bmff_hash { | ||
| return Err(Error::BadParam( | ||
| "Builder must have a hash assertion (DataHash or BmffHash) before calling placeholder()".to_string() | ||
| )); | ||
| } | ||
|
|
||
| let mut store = self.to_store()?; | ||
| store.get_placeholder(format, self.context()) | ||
| } | ||
|
|
||
| /// Sign a placeholder manifest with updated hash assertions from the Builder. | ||
| /// | ||
| /// This method takes a placeholder JUMBF created by [`Builder::placeholder`], updates | ||
| /// any hash assertions (DataHash, BmffHash) from the Builder's current state, and signs | ||
| /// the manifest. This supports dynamic assertions if configured in the signer. | ||
| /// | ||
| /// # Workflow | ||
| /// 1. Call [`Builder::placeholder`] to create a placeholder manifest | ||
| /// 2. Embed the placeholder into your asset | ||
| /// 3. Calculate the hash of the asset (excluding the placeholder) | ||
| /// 4. Update the hash assertion in the Builder: `builder.add_assertion(DataHash::LABEL, &updated_hash)` | ||
| /// 5. Call this method to sign: `builder.sign_placeholder(&placeholder, signer, format)` | ||
|
||
| /// | ||
| /// # Arguments | ||
| /// * `placeholder_jumbf` - The placeholder JUMBF bytes from [`Builder::placeholder`] | ||
| /// | ||
| /// # Returns | ||
| /// * The signed manifest bytes ready for embedding | ||
| /// | ||
| /// # Errors | ||
| /// * Returns an [`Error`] if the placeholder cannot be deserialized or signed | ||
| pub fn sign_placeholder(&mut self, placeholder_jumbf: &[u8], format: &str) -> Result<Vec<u8>> { | ||
| // Deserialize placeholder without validation (it has a placeholder signature) | ||
| let mut validation_log = crate::status_tracker::StatusTracker::default(); | ||
| let mut no_verify_settings = self.context.settings().clone(); | ||
| no_verify_settings.verify.verify_after_reading = false; | ||
| let temp_context = Context::new().with_settings(no_verify_settings)?; | ||
|
|
||
| let mut store = | ||
| Store::from_jumbf_with_context(placeholder_jumbf, &mut validation_log, &temp_context)?; | ||
|
|
||
| // Update hash assertions from Builder into the Store | ||
| let pc = store.provenance_claim_mut().ok_or(Error::ClaimEncoding)?; | ||
|
|
||
| if let Ok(data_hash) = self.find_assertion::<DataHash>(DataHash::LABEL) { | ||
| pc.update_data_hash(data_hash)?; | ||
| } | ||
|
|
||
| if let Ok(bmff_hash) = self.find_assertion::<BmffHash>(BmffHash::LABEL) { | ||
| pc.update_bmff_hash(bmff_hash)?; | ||
| } | ||
|
|
||
| let signer = self.context().signer()?; | ||
| let jumbf = store.sign_manifest(signer, self.context().settings())?; | ||
|
|
||
| // Compose for the target format | ||
| Store::get_composed_manifest(&jumbf, format) | ||
| } | ||
|
|
||
| /// Create a signed data hashed embeddable manifest using a supplied signer. | ||
| /// | ||
| /// This is used to create a manifest that can be embedded into a stream. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.