Skip to content

tooling/c2pa-oracle: exercise a reserved slot larger than the signed store, where the two locators diverge #598

Description

@justin13888

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

  1. 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.
  2. 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.
  3. 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.
  4. 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions