Skip to content

Latest commit

 

History

History
522 lines (410 loc) · 19.4 KB

File metadata and controls

522 lines (410 loc) · 19.4 KB

Content Credential JSON (CrJSON) File Format Specification

1. Scope

This document describes a JSON serialization for Content Credentials (aka a C2PA manifest store) known as the Content Credential JSON format (abbreviated CrJSON). It’s purpose is to provide a JSON-based representation of a C2PA manifest store for profile evaluation, interoperability testing, and validation reporting.

2. Normative References

3. Relationship to C2PA v2.3

CrJSON does not replace C2PA claim stores, JUMBF, or COSE structures. Instead, it is a derived JSON view over C2PA data.

The following C2PA concepts are directly represented:

  • C2PA manifests → manifests[]

  • C2PA assertions → manifests[].assertions (object keyed by assertion label)

  • C2PA claim data → manifests[].claim.v2

  • C2PA claim signature and credential details → manifests[].signature

  • C2PA validation results → manifests[].status and validationResults

4. Data Model Overview

C2PA Asset/Manifest Store
  -> Reader validation + manifest extraction
  -> CrJSON transformation
  -> Output JSON object
       |- @context
       |- manifests[]
       |    |- label
       |    |- assertions{...}
       |    |- claim or claim.v2 (one required)
       |    |- signature (required)
       |    |- status (required)
       |- validationResults (optional)

5. Serialization Requirements

5.1. Root Object

A CrJSON document shall be a JSON object.

The following top-level properties are used:

Property Presence Description

@context

REQUIRED

JSON-LD context. Schema allows object or array of URI strings.

manifests

REQUIRED

Array of manifest objects. Each item conforms to the manifest definition (required: label, assertions, signature, status; one of claim or claim.v2).

validationResults

OPTIONAL

Output of validation_results(): active manifest status codes and ingredient deltas (success, informational, failure arrays).

5.2. @context

CrJSON uses a standard JSON-LD serialisation, and therefore shall contain a JSON-LD standard @context field whose value shall be either an object or an array listing terms (also known as namespaces) that are used in the CrJSON. In the case of an object, the terms shall be listed as key-value pairs, where the key is the term name and the value is the URI of the term. As described in clause 4.1.2 of JSON-LD, the @vocab key can be used for the default vocabulary, as shown in Example of an object-based JSON-LD @context field. In the case of an array, only the URI is required since it shall apply to all terms not otherwise identified by a specific term, as shown in Example of an array-based JSON-LD @context field.

Example of an object-based JSON-LD @context field
{
  "@context": {
    "@vocab": "https://contentcredentials.org/crjson/",
    "extras": "https://contentcredentials.org/crjson/extras/",
  }
}
Example of an array-based JSON-LD @context field
{
  "@context": [
    "https://contentcredentials.org/crjson/"
  ]
}

Since CrJSON may contain values that are specific to a given workflow, it is important that each one of these terms shall be defined in the @context field. This allows the CrJSON document to be self-describing and non-conflicting ensuring that any consumer of the CrJSON can understand the meaning of each term.

Since a @context element can appear inside of any object in JSON-LD, it is possible to have custom values, and their associated @context elements in multiple places throughout a single JSON-LD document, where the terms are localized to that specific object.

5.3. Manifests

A C2PA Manifest consists of, at least, a set of Assertions, Claims, and Claim Signatures that are bound together into a single entity. For all C2PA Manifests present in the C2PA Manifest Store, they shall be present in the manifests array. The order of the Manifests in the array shall match the reverse order that they are found in the Manifest Store, so that the active manifest is always first (i.e., manifests[0]).

Each manifest object shall include the following properties (per schema: required label, assertions, signature, status; exactly one of claim or claim.v2):

  • label (manifest label/URN)

  • assertions (object keyed by assertion label)

  • claim (v1, per C2PA claim-map) or claim.v2 (v2, per C2PA claim-map-v2)

  • signature (signature and credential details object)

  • status (per-manifest validation results object)

Note
The label field’s value is a string that identifies the C2PA Manifest using the label of its JUMBF box, such as urn:c2pa:2702fc84-a1ae-44d1-9825-dd86311e980b.

The manifest object does not allow additional properties (schema additionalProperties: false).

5.4. Claims

5.4.1. General

A Claim shall be serialised in the same manner as a CBOR serialised assertion. It shall be named based on the label of the claim box (e.g., claim or claim.v2). An example is found in A JSON-LD serialised claim.

If there are no assertions listed in the claim’s gathered_assertions or redacted_assertions fields, then the corresponding object shall be present, but its value shall be an empty object.

A JSON-LD serialised claim
"claim.v2": {
  "dc:title": "MIPAMS test image",
  "instanceID": "uuid:7b57930e-2f23-47fc-affe-0400d70b738d",
  "claim_generator": "MIPAMS GENERATOR 0.1",
  "alg": "SHA-256",
  "signature": "self#jumbf=c2pa.signature",
  "created_assertions": [
    {
      "url": "self#jumbf=c2pa.assertions/c2pa.actions.v2",
      "hash": "APqpWkPm91k98DD03sIQ+uYGspG+bxdy0c7+FMu8puU="
    },
    {
      "url": "self#jumbf=c2pa.assertions/c2pa.hash.data",
      "hash": "A8wNdhjiIyOOkGg8+GkJRSYJALG6orPQJRQKMFtq/rc="
    }
  ],
  "gathered_assertions": [],
  "redacted_assertions": []
},

5.4.2. claim.v2 (v2 claim)

claim.v2 conforms to the C2PA CDDL claim-map-v2. Required properties:

  • instanceID — uniquely identifies a specific version of an asset

  • claim_generator_info — single generator-info map (object with e.g. name, version, optional icon, operating_system)

  • signature — JUMBF URI reference to the signature of this claim (e.g. self#jumbf=/c2pa/{label}/c2pa.signature)

  • created_assertions — array of one or more hashed URI maps; each entry has url, hash, and optionally alg

Optional properties:

  • gathered_assertions — array of hashed URI maps (same structure as created_assertions)

  • dc:title — name of the asset

  • redacted_assertions — array of JUMBF URI strings (references to redacted ingredient manifest assertions)

  • alg — cryptographic hash algorithm for data hash assertions (e.g. SHA-256)

  • alg_soft — algorithm for soft binding assertions

  • specVersion — specification version (SemVer)

  • metadata — (DEPRECATED) additional information

All hash values in hashed URI maps shall be Base64 strings.

5.5. claim (v1 claim)

When a manifest uses claim instead of claim.v2, it conforms to the C2PA CDDL claim-map (claimV1). Required properties:

  • claim_generator — User-Agent string for the claim generator

  • claim_generator_info — array of one or more generator-info maps

  • signature — JUMBF URI reference to the signature

  • assertions — array of one or more hashed URI maps (url, hash, optional alg)

  • dc:format — media type of the asset

  • instanceID — uniquely identifies a specific version of an asset

Optional: dc:title, redacted_assertions (JUMBF URI strings), alg, alg_soft, metadata.

All hash values in hashed URI maps shall be Base64 strings.

5.6. Assertions

5.6.1. General

Each manifest object shall contain an assertions field whose value is an object keyed by assertion label. Each individual assertion shall be represented as an object, where they key is the label of the Assertion and the value is an object containing the JSON-LD serialization derived from that Assertion. If it is not possible to derive information from an assertion, then the key shall be present in the assertions object, but its value shall be an empty object.

5.6.2. JSON-LD serialised assertions

For any Assertion which is serialised in the C2PA Manifest as JSON-LD, that exact same JSON-LD shall be used as the value for the assertion. For example, the c2pa.metadata assertion is expressed in XMP, that XMP data is serialised as JSON-LD.

An example c2pa.metadata assertion is shown in A JSON-LD serialised assertion.

A JSON-LD serialised assertion
"c2pa.metadata": {
  "@context" : {
    "Iptc4xmpExt": "http://iptc.org/std/Iptc4xmpExt/2008-02-29/",
    "photoshop" : "http://ns.adobe.com/photoshop/1.0/"
  },
  "photoshop:DateCreated": "Aug 31, 2022",
  "Iptc4xmpExt:LocationCreated": {
    "Iptc4xmpExt:City": "Beijing, China"
  }
}
Note
The various terms and context namespaces in A JSON-LD serialised assertion are defined as part of [IPTC].
CBOR serialised assertions

For each Assertion, which is serialised in the C2PA Manifest as CBOR, the JSON-LD representation shall be described as the same key name in the CBOR map and its value type shall be determined by Mapping from CBOR to JSON-LD:

Table 1. Mapping from CBOR to JSON-LD
CBOR Type(s) JSON-LD Type

integer, unsigned integer

unsigned number

negative integer

integer

byte string

string (Base64 encoded, [RFC4648])

UTF-8 string

string

array

array

map

object

False, True

boolean

Null

null

half-precision float, single-precision float, double-precision float

float

date-time

string ([ISO8601])

Since CBOR allows map keys of any type, whereas JSON-LD only allows strings as keys in object values, CBOR maps with keys other than UTF-8 strings shall have those keys converted to UTF-8 strings. An example of a CBOR serialised assertion is shown in CBOR Diagnostics for an actions.v2 assertion, and its equivalent JSON-LD representation is shown in JSON-LD representation of CBOR Diagnostics for an actions.v2 assertion.

CBOR Diagnostics for an actions.v2 assertion
"c2pa.actions.v2": {
  "actions": [
    {
      "action": "c2pa.cropped",
      "when": 0("2020-02-11T09:30:00Z")
    },
    {
      "action": "c2pa.filtered",
      "when": 0("2020-02-11T09:00:00Z")
    }
  ]
}
"c2pa.actions.v2": {
  "actions": [
    {
      "action": "c2pa.cropped",
      "when": "2020-02-11T09:30:00Z"
    },
    {
      "action": "c2pa.filtered",
      "when": "2020-02-11T09:00:00Z"
    }
  ]
}

5.6.3. Binary normalization and hash encoding

CrJSON normalizes byte-array encodings into Base64 string encodings.

If fields are serialized as integer arrays, they are converted to Base64 strings for:

  • hash

  • pad

  • pad1

  • pad2

  • Certain signature byte payloads (decoded when possible; otherwise Base64)

This rule applies recursively to nested objects/arrays.

5.6.4. Binary assertion representation

For binary formatted assertions (e.g., thumbnails), CrJSON emits a reference form:

{
  "format": "<content-type>",
  "identifier": "<absolute-jumbf-uri>",
  "hash": "<base64>"
}

5.7. Signature

The JSON-LD representation of the X.509 certificate (as defined in [RFC5280]) from the claim signature is based on a logical mapping of its ASN.1 serialisation as defined in RFC 5280 into a JSON-LD serialized object whose key is signature. An example certificate is in Representation of an X.509 Certificate. Additional mappings, such as the mapping of the distinguished name to JSON-LD should also be done in the most logical fashion possible.

When signature information is available, it should include:

  • algorithm

  • serial_number

  • issuer (DN map, e.g., C, ST, L, O, OU, CN)

  • subject (DN map)

  • validity.not_before

  • validity.not_after

Note
An X.509 certificate can contain all sorts of information, and implementations may choose to include additional information in their JSON-LD representation.

Times shall be represented as RFC 3339 strings. When signature information is unavailable, signature shall be present as an empty object {}.

The value of the signature_algorithm field shall be one of the strings defined in clause=13.2.1, such as "ES256" or "Ed25519", or "Unknown" if it is not one of the defined values.

Representation of an X.509 Certificate
"signature": {
  "signature_algorithm": "ES256",
  "subject": {
    "ST": "CA",
    "CN": "C2PA Signer",
    "C": "US",
    "L": "Somewhere",
    "OU": "FOR TESTING_ONLY",
    "O": "C2PA Test Signing Cert"
  },
  "issuer": {
    "ST": "CA",
    "CN": "Intermediate CA",
    "C": "US",
    "L": "Somewhere",
    "OU": "FOR TESTING_ONLY",
    "O": "C2PA Test Intermediate Root CA"
  },
  "validity": {
    "not_after": "2030-08-26T18:46:40Z",
    "not_before": "2022-06-10T18:46:40Z"
  }
}

5.8. Status

The manifest object shall contain a status field whose value is an object containing the trust and/or validity status of the various parts of the C2PA Manifest. The status object shall contain the following fields:

signature

A field whose value is a single clause=15.2.2.1, status code determined from clause=15.7,validation of the signature.

assertions

A field whose value is an object, where each field is the label of an assertion and its value is the clause=15.2.2.1, status code determined from validation. All assertions in the Claim shall be listed in this object, whether they are in the created_assertions or gathered_assertions fields of the Claim.

trust

After the signing certificate is checked against one or more Trust Lists, this field shall contain a single clause=15.2.2.1, status code that specifies the status of the signing certificate against the Trust Lists. There may also be trust_list field that contains a URI that identifies the Trust List used to validate the signing certificate.

For the active manifest, the following additional fields shall also be present:

content

A field whose value is a single clause=15.2.2.1, status code determined from validation of the content bindings.

Note
This is present only in the active manifest, since that is the only one that can be used to check the validity of the content bindings. This applies to both standard and update manifests.

An example of a status object is shown in Example status object.

Example status object
"status": {
  "signature": "claimSignature.validated",
  "assertion": {
    "c2pa.actions.v2": "assertion.hashedURI.match",
    "c2pa.hash.data": "assertion.dataHash.match"
  },
  "content": "assertion.dataHash.match",
  "trust": "signingCredential.trusted",
  "trust_list": "https://example.com/trustlists/c2pa-trust-list.json"
}

When validation results are unavailable, status shall be present as an empty object {}.

5.9. Validation Results

The validationResults object is modelled on the validation-results-map data structure used to store the results of ingredient validation in the ingredient assertion. It is not a required property of crJSON (as it may be used outside of a validation workflow), but when present, it will always include the set of validation status codes for the active manifest. If the active manifest refers to one or more ingredients, there shall also be an ingredientDeltas field present that contains the list of validation deltas (if any).

Each validation status entry is an object with a code (required), and optionally url and explanation fields.

"validationResults": {
     "activeManifest": {
        "success": [],
        "informational": [
          {
            "code": "claimSignature.insideValidity",
            "url": "self#jumbf=/c2pa/urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268/c2pa.signature",
            "explanation": "claim signature valid",
          },
          {
            "code": "claimSignature.validated",
            "url": "self#jumbf=/c2pa/urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268/c2pa.signature",
            "explanation": "claim signature valid",
          },
          {
            "code": "assertion.hashedURI.match",
            "url": "self#jumbf=/c2pa/urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268/c2pa.assertions/c2pa.actions.v2",
            "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.actions.v2",
          },
          {
            "code": "assertion.hashedURI.match",
            "url": "self#jumbf=/c2pa/urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268/c2pa.assertions/c2pa.hash.data",
            "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.hash.data",
          },
          {
            "code": "assertion.dataHash.match",
            "url": "self#jumbf=/c2pa/urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268/c2pa.assertions/c2pa.hash.data",
            "explanation": "data hash valid",
          },

        ],
        "failure": [
          {
            "code": "signingCredential.untrusted",
            "url": "self#jumbf=/c2pa/urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268/c2pa.signature",
            "explanation": "signing certificate untrusted",
          },
          {
            "code": "assertion.action.malformed",
            "url": "urn:c2pa:4646c5e4-31e1-4d08-8156-d90a8e323268",
            "explanation": "first action must be created or opened",
          }
        ]
    }
}

6. Constraints and Current Implementation Limits

  • CrJSON is export-oriented; it is not the canonical source of cryptographic truth. Canonical validation remains bound to C2PA/JUMBF/COSE data structures per C2PA v2.3.

7. Minimal Example

The following example conforms to the CrJSON schema. In many of the example values, a …​ placeholder is used for a value that is not relevant to the example. Also, any values which would be Base64-encoded are represented as <base64>.

{
  "@context": {
    "@vocab": "https://contentcredentials.org/crjson",
    "extras": "https://contentcredentials.org/crjson/extras"
  },
  "manifests": [
    {
      "label": "urn:uuid:...",
      "claim.v2": {
        "instanceID": "xmp:iid:...",
        "claim_generator_info": {"name": "Example Tool", "version": "1.0"},
        "signature": "self#jumbf=/c2pa/urn:uuid:.../c2pa.signature",
        "created_assertions": [
          {"url": "self#jumbf=c2pa.assertions/c2pa.hash.data", "hash": "<base64>"}
        ],
        "gathered_assertions": [],
        "redacted_assertions": []
      },
      "assertions": {
        "c2pa.actions.v2": {"actions": []},
        "c2pa.hash.data": {"alg": "sha256", "hash": "<base64>"}
      },
      "signature": {},
      "status": {
        "signature": "claimSignature.validated",
        "trust": "signingCredential.trusted"
      }
    }
  ],
  "validationResults": {
    "activeManifest": {
      "success": [],
      "informational": [],
      "failure": []
    }
  }
}