tooling/c2pa-oracle signs a manifest store into a slot of exactly the length
Builder::placeholder asked for, and reserve_then_fill refuses anything else:
if store.len() != slot.len() {
return Err(OracleError::Asset(format!(
"signed store is {} bytes but the reserved slot is {}", store.len(), slot.len()
)));
}
So the one configuration nothing exercises is a reserved slot larger than the signed store —
and that is exactly what a signer does when it reserves generously before it knows the final
certificate chain's size, then pads the remainder.
Why it matters
It is the only shape in which gamut's two locators legitimately diverge:
gamut-avif bounds a store by the enclosing ContentProvenanceBox, so it reports the store
and the padding after it (C2paSlot::slot_bytes: "the store, then any padding").
gamut-heic bounds it by the store's own JUMBF LBox, so it reports the store alone.
Today the oracle cannot tell those two bounds apart, because c2pa-rs writes no padding: every
fixture makes the two ranges identical, and
tests/locate_embedded.rs::c2pa_rs_leaves_no_padding_between_the_store_and_the_end_of_its_box
records that as an observation about the reference implementation, not a rule. When a padding
writer appears, the divergence is correct behaviour by both locators — but nothing in the tree
demonstrates that either of them handles it, and the containment assertions in
gamut_avif_bounds_the_store_c2pa_rs_embedded_by_the_box_that_carries_it were written
padding-tolerant on the strength of an argument rather than a fixture.
What to do
- Teach the oracle to reserve a slot longer than the placeholder store and zero-pad the signed
store out to it, instead of refusing the mismatch — a reserve_then_fill variant, or a
padding parameter on the existing one; c2pa-rs's own Builder::placeholder /
sign_embeddable pair already zero-pads to a pinned length, so the mechanism exists.
- Assert that c2pa-rs still reads the padded asset
Valid, which is what says padding inside the
box is in-spec carriage rather than corruption.
- Assert the two locators diverge as documented:
gamut-avif's range covers store + padding,
gamut-heic's range is the store exactly, and gamut-avif's range starts where
gamut-heic's does and ends at or after it.
- Assert
read_with_external_store accepts the LBox-bounded bytes and — the interesting half —
what it does with the box-bounded ones.
Out of scope here
Whether gamut should reserve padded slots on its own account; this is about the oracle covering
a configuration a signer produces.
Deferred deliberately from #447 / PR #535, whose round-5 scope was the refusal-arm enumeration in
src/lib.rs. Related: #534 (pointing the oracle at more containers).
tooling/c2pa-oraclesigns a manifest store into a slot of exactly the lengthBuilder::placeholderasked for, andreserve_then_fillrefuses anything else:So the one configuration nothing exercises is a reserved slot larger than the signed store —
and that is exactly what a signer does when it reserves generously before it knows the final
certificate chain's size, then pads the remainder.
Why it matters
It is the only shape in which gamut's two locators legitimately diverge:
gamut-avifbounds a store by the enclosingContentProvenanceBox, so it reports the storeand the padding after it (
C2paSlot::slot_bytes: "the store, then any padding").gamut-heicbounds it by the store's own JUMBFLBox, so it reports the store alone.Today the oracle cannot tell those two bounds apart, because c2pa-rs writes no padding: every
fixture makes the two ranges identical, and
tests/locate_embedded.rs::c2pa_rs_leaves_no_padding_between_the_store_and_the_end_of_its_boxrecords that as an observation about the reference implementation, not a rule. When a padding
writer appears, the divergence is correct behaviour by both locators — but nothing in the tree
demonstrates that either of them handles it, and the containment assertions in
gamut_avif_bounds_the_store_c2pa_rs_embedded_by_the_box_that_carries_itwere writtenpadding-tolerant on the strength of an argument rather than a fixture.
What to do
store out to it, instead of refusing the mismatch — a
reserve_then_fillvariant, or apadding parameter on the existing one; c2pa-rs's own
Builder::placeholder/sign_embeddablepair already zero-pads to a pinned length, so the mechanism exists.Valid, which is what says padding inside thebox is in-spec carriage rather than corruption.
gamut-avif's range covers store + padding,gamut-heic's range is the store exactly, andgamut-avif's range starts wheregamut-heic's does and ends at or after it.read_with_external_storeaccepts theLBox-bounded bytes and — the interesting half —what it does with the box-bounded ones.
Out of scope here
Whether gamut should reserve padded slots on its own account; this is about the oracle covering
a configuration a signer produces.
Deferred deliberately from #447 / PR #535, whose round-5 scope was the refusal-arm enumeration in
src/lib.rs. Related: #534 (pointing the oracle at more containers).