Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Brendan We recently did a short demo for a Project Origin workshop showing how the Content Authenticity Initiative’s open source toolkits for C2PA can work with IPTC Photo Metadata.

It shows a practical way that developers and power users can start working with the C2PA Specification straight away.

Here we document record the steps we took to create the demo, along with a few notes and suggestions for improvements. It includes several “manual” steps involving editing things with text editors.

Overall, it shows that it is possible to comply with the C2PA spec, but it also shows that there is a long way to go before we can lot of work to be done to make it easy for users.

Step 1: Create an image with embedded IPTC Photo Metadata.

I We used Adobe Photoshop but many other tools can be used - see IPTC’s list of software that supports IPTC Photo Metadata Standard.

...

Step 2

...

: Use exiftool to export the embedded metadata in XMP format

Using a Terminal window on my Mac, I ran the following command:

...

We have just created an XML file that looks like this: (actually the real version is all on one long line, so I have inserted line breaks here to make it slightly more readable):

Code Block
languagexml
<?xpacket begin="<feff>" id="W5M0MpCehiHzreSzNTczkc9d"?>
  <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 7.2-c000 79.566ebc5b4, 2022/05/09-08:25:55        ">
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about=""
        xmlns:xmp="http://ns.adobe.com/xap/1.0/"
        xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
        xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
        xmlns:exifEX="http://cipa.jp/exif/1.0/"
        xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
        xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
        xmlns:xmpRights="http://ns.adobe.com/xap/1.0/rights/"
        xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/"
        xmlns:plus="http://ns.useplus.org/ldf/xmp/1.0/"
      xmp:CreateDate="2022-07-09T17:47:17" xmp:CreatorTool="15.5"
      xmp:ModifyDate="2022-07-21T15:46:32+03:00" xmp:MetadataDate="2022-07-21T15:46:32+03:00"
      photoshop:DateCreated="2022-07-09T17:47:17.538" photoshop:LegacyIPTCDigest="74680B31492930D266D0FAE3B244D1BD"
      photoshop:ColorMode="3" photoshop:ICCProfile="Display P3" photoshop:AuthorsPosition="Managing Director"
      photoshop:Headline="Wildflowers at the Rose Garden, Greenwich Park, London" photoshop:City="London"
      photoshop:State="London" photoshop:Country="United Kingdom" photoshop:CaptionWriter="Brendan Quinn"
 ... and much more ...
<?xpacket end="w"?>

If you’ve never seen an XMP packet before, this is probably quite confusing, but don’t worry - it just shows all the IPTC metadata properties in the XMP format that’s specified by ISO ISO 16884.

...

Info

Note that exiftool also has a JSON export function (-json), but the format used is slightly different from the JSON-LD version that we need for the C2PA assertion. Another approach could be to generate the JSON output using exiftool -json -struct -G1 and then convert the field names to their correct XMP values.

Step 3: Convert the RDF/XML to JSON-LD

XMP’s standard data storage format is RDF/XML, which is a representation of the RDF data model in the XML format. To add these fields to a C2PA manifest, we need to convert the RDF/XML to another serialisation of RDF called JSON-LD.

There is a standard for expressing XMP in JSON-LD (ISO16884-3: JSON-LD serialisation of XMP) but exiftool doesn’t doesn't export in that format directly, so we have to do some conversion ourselves.

...

But riot doesn't like the XML processing directives in the XMP packet. So first, we have to remove the XMP headers by hand. (Side note: if anyone knows of a good way of removing the processing headers using the command-line, please let me know! Probably some sed magic would do the trick?)

Open the XML file in a text editor and remove the <?xpacket ... ?> sections at the start and end of the file. If you have it, you can also use xmllint to make the xml look a bit nicer:

...

riot converts between various syntaxes of RDF. This command converts the original RDF/XML format to JSON-LD, a JSON-friendly version of RDF.

Unfortunately, that’s not yet the end of our story in preparing metadata for embedding into a C2PA assertion: . riot does a straight conversion to JSON-LD, including the structures such as rdf:Bag, rdf:Seq and various “blank nodes” used to handle multi-valued properties, but XMP's use of JSON-LD is much simpler. So we need to do some more conversion by hand.

...

Code Block
languagejson
{
    "@graph": [
        {
            "@id": "_:b0",
            "rdf:_1": "Brendan Quinn",
            "@type": "rdf:Seq"
        },
        {
            "@id": "_:b1",
            "plus:ImageSupplierName": "IPTC"
        },
        {
            "@id": "_:b2",
            "stEvt:changed": "/",
            "stEvt:softwareAgent": "Adobe Photoshop 23.4 (Macintosh)",
            "stEvt:when": "2022-07-21T15:46:32+03:00",
            "stEvt:instanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
            "stEvt:action": "saved"
        },
        {
            "@id": "_:b3",
            "stEvt:changed": "/",
            "stEvt:softwareAgent": "Adobe Photoshop 23.4 (Macintosh)",
            "stEvt:when": "2022-07-21T15:46:32+03:00",
            "stEvt:instanceID": "xmp.iid:f9c65ac7-2ad9-4016-84b3-97065eb121b4",
            "stEvt:action": "saved"
        },
        {
            "@id": "_:b4",
            "rdf:_1": {
                "@language": "x-default",
                "@value": "Wildflowers at the Rose Garden, Greenwich Park, London"
            },
            "@type": "rdf:Alt"
        },
        ... much more ...

All those _:b0, l _:b1, _:b2 objects are parts of the XMP packet that can actually be converted into simple arrays and objects in JSON. But right Right now there is no tool that can do this conversion, so I did it by hand! It took a while… while... It looks like it would be handy to create a Python and/or JavaScript tool to do it for us.

...

Code Block
languagejson
{
    "@context": {
        "xmpRights": "http://ns.adobe.com/xap/1.0/rights/",
        "aux": "http://ns.adobe.com/exif/1.0/aux/",
        "exifEX": "http://cipa.jp/exif/1.0/",
        "stEvt": "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#",
        "Iptc4xmpExt": "http://iptc.org/std/Iptc4xmpExt/2008-02-29/",
        "photoshop": "http://ns.adobe.com/photoshop/1.0/",
        "plus": "http://ns.useplus.org/ldf/xmp/1.0/",
        "Iptc4xmpCore": "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/",
        "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
        "xmpMM": "http://ns.adobe.com/xap/1.0/mm/",
        "xmp": "http://ns.adobe.com/xap/1.0/",
        "dc": "http://purl.org/dc/elements/1.1/"
    },
    "@graph": {
        "@id": "file:///Users/brendan/dev/iptc/c2pa/c2patool-play/greenwich_flowers_xmp_pretty.xml",
        "dc:description": "Wildflowers at the Rose Garden, Greenwich Park, London",
        "exifEX:LensModel": "iPhone 11 back dual wide camera 4.25mm f/1.8",
        "xmp:MetadataDate": "2022-07-21T15:46:32+03:00",
        "aux:LensInfo": "807365/524263 17/4 9/5 12/5",
        "exifEX:LensMake": "Apple",
        "xmp:rights/UsageTerms": "Usage permitted under Creative Commons Attribution (CC-BY 4.0) licence.",
        "xmp:ModifyDate": "2022-07-21T15:46:32+03:00",
        "photoshop:CaptionWriter": "Brendan Quinn",
        "xmp:mm/OriginalDocumentID": "4B267F2683ABA382F9E0C1DE10BF4210",
        "photoshop:ColorMode": "3",
        "photoshop:AuthorsPosition": "Managing Director",
        "plus:Licensor": {
            "plus:LicensorEmail": "office@iptc.org",
            "plus:LicensorURL": "www.iptc.org/",
            "plus:LicensorTelephoneType2": "http://ns.useplus.org/ldf/vocab/work",
            "plus:LicensorName": "Brendan Quinn"
        },
        "dc:subject": [
            "Rose Garden",
            "Greenwich",
            "flowers",
            "Wild flowers"
        ],
        "dc:creator": "Brendan Quinn",
        "Iptc4xmpCore:CountryCode": "GB",
        "Iptc4xmpCore:IntellectualGenre": "https://cv.iptc.org/newscodes/genre/Actuality",
        "Iptc4xmpExt:LocationShown": {
            "Iptc4xmpExt:WorldRegion": "Europe",
            "Iptc4xmpExt:CountryCode": "GB",
            "Iptc4xmpExt:CountryName": "United Kingdom",
            "Iptc4xmpExt:ProvinceState": "London",
            "Iptc4xmpExt:City": "London",
            "Iptc4xmpExt:Sublocation": "Greenwich"
        },
        "photoshop:City": "London",
        "photoshop:DateCreated": "2022-07-09T17:47:17.538",
        "photoshop:ICCProfile": "Display P3",
        "plus:ModelReleaseStatus": "http://ns.useplus.org/ldf/vocab/MR-NAP",
        "photoshop:LegacyIPTCDigest": "74680B31492930D266D0FAE3B244D1BD",
        "photoshop:State": "London",
        "xmp:CreateDate": "2022-07-09T17:47:17",
        "dc:format": "image/jpeg",
        "plus:CopyrightOwner": {
            "plus:CopyrightOwnerName": "Brendan Quinn"
        },
        "plus:ImageCreator": {
            "plus:ImageCreatorName": "Brendan Quinn"
        },
        "xmp:CreatorTool": "15.5",
        "plus:ImageSupplier": {
            "plus:ImageSupplierName": "IPTC"
        },
        "Iptc4xmpCore:Scene": "https://cv.iptc.org/newscodes/scene/011600",
        "Iptc4xmpCore:Location": "Greenwich",
        "Iptc4xmpCore:CreatorContactInfo": {
            "Iptc4xmpCore:CiAdrCtry": "United Kingdom",
            "Iptc4xmpCore:CiTelWork": "+44 (0)20 3178 4922 ",
            "Iptc4xmpCore:CiUrlWork": "https://www.iptc.org/",
            "Iptc4xmpCore:CiEmailWork": "mdirector@iptc.org",
            "Iptc4xmpCore:CiAdrPcode": "WC2A 1AL",
            "Iptc4xmpCore:CiAdrRegion": "London",
            "Iptc4xmpCore:CiAdrCity": "London",
            "Iptc4xmpCore:CiAdrExtadr": "25 Southampton Buildings"
        },
        "Iptc4xmpExt:LocationCreated": {
            "Iptc4xmpExt:WorldRegion": "Europe",
            "Iptc4xmpExt:CountryCode": "GB",
            "Iptc4xmpExt:CountryName": "United Kingdom",
            "Iptc4xmpExt:ProvinceState": "London",
            "Iptc4xmpExt:City": "London",
            "Iptc4xmpExt:Sublocation": "Greenwich"
        },
        "plus:PropertyReleaseStatus": "http://ns.useplus.org/ldf/vocab/PR-NAP",
        "xmp:mm/InstanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
        "photoshop:Headline": "Wildflowers at the Rose Garden, Greenwich Park, London",
        "photoshop:Country": "United Kingdom",
        "aux:Lens": "iPhone 11 back dual wide camera 4.25mm f/1.8",
        "dc:rights": "Copyright (C) Brendan Quinn 2022. Some rights reserved.",
        "Iptc4xmpExt:DigitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture"
    }
}

Step 4

...

: Add the JSON-LD to a c2patool assertion block

Now we can finally start using c2patool!

...

It then returned with no information, but my ~/.cai folder now contained files called es256_certs.pem and es256_private.key as I had requested in the command.Finally I could now run my c2patool command

Step 5: Embedding the assertion into our image using c2patool

Finally we can now run the c2patool command to sign and embed the assertion:

Code Block
languagebash
c2patool test_assertion.json -p greenwich_flowers_with_metadata.jpg \
         -o greenwich_flowers_with_c2pa.jpg

This returns a full copy of the embedded, signed manifest, which is also embedded into the file:

Code Block
languagejson
{
  "active_manifest": "iptc:urn:uuid:69e35782-99da-4be1-83b1-85ede6dfd4b5",
  "manifests": {
    "iptc:urn:uuid:69e35782-99da-4be1-83b1-85ede6dfd4b5": {
      "claim_generator": "BQ Test with c2patool/0.1 c2pa-rs/0.1.3",
      "claim_generator_hints": {
        "Sec-CH-UA": "\"c2pa-rs\";v=\"0.1.3\""
      },
      "asset": {
        "title": "greenwich_flowers_with_c2pa.jpg",
        "format": "image/jpeg",
        "instance_id": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c"
      },
      "ingredients": [
        {
          "title": "greenwich_flowers_with_metadata.jpg",
          "format": "image/jpeg",
          "document_id": "adobe:docid:photoshop:91a3bd3c-4510-ca4a-8d21-8f2fb4edaf91",
          "instance_id": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
          "is_parent": true
        }
      ],
      "credentials": [],
      "assertions": [
        {
          "label": "stds.iptc.photo-metadata",
          "data": {
            "@context": {
              "Iptc4xmpCore": "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/",
              "Iptc4xmpExt": "http://iptc.org/std/Iptc4xmpExt/2008-02-29/",
              "aux": "http://ns.adobe.com/exif/1.0/aux/",
              "dc": "http://purl.org/dc/elements/1.1/",
              "exifEX": "http://cipa.jp/exif/1.0/",
              "photoshop": "http://ns.adobe.com/photoshop/1.0/",
              "plus": "http://ns.useplus.org/ldf/xmp/1.0/",
              "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
              "stEvt": "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#",
              "xmp": "http://ns.adobe.com/xap/1.0/",
              "xmpMM": "http://ns.adobe.com/xap/1.0/mm/",
              "xmpRights": "http://ns.adobe.com/xap/1.0/rights/"
            },
            "@graph": {
              "@id": "file:///Users/brendan/dev/iptc/c2pa/c2patool-play/greenwich_flowers_xmp_pretty.xml",
              "Iptc4xmpCore:CountryCode": "GB",
              "Iptc4xmpCore:CreatorContactInfo": {
                "Iptc4xmpCore:CiAdrCity": "London",
                "Iptc4xmpCore:CiAdrCtry": "United Kingdom",
                "Iptc4xmpCore:CiAdrExtadr": "25 Southampton Buildings",
                "Iptc4xmpCore:CiAdrPcode": "WC2A 1AL",
                "Iptc4xmpCore:CiAdrRegion": "London",
                "Iptc4xmpCore:CiEmailWork": "mdirector@iptc.org",
                "Iptc4xmpCore:CiTelWork": "+44 (0)20 3178 4922 ",
                "Iptc4xmpCore:CiUrlWork": "https://www.iptc.org/"
              },
              "Iptc4xmpCore:IntellectualGenre": "https://cv.iptc.org/newscodes/genre/Actuality",
              "Iptc4xmpCore:Location": "Greenwich",
              "Iptc4xmpCore:Scene": "https://cv.iptc.org/newscodes/scene/011600",
              "Iptc4xmpExt:DigitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture",
              "Iptc4xmpExt:LocationCreated": {
                "Iptc4xmpExt:City": "London",
                "Iptc4xmpExt:CountryCode": "GB",
                "Iptc4xmpExt:CountryName": "United Kingdom",
                "Iptc4xmpExt:ProvinceState": "London",
                "Iptc4xmpExt:Sublocation": "Greenwich",
                "Iptc4xmpExt:WorldRegion": "Europe"
              },
              "Iptc4xmpExt:LocationShown": {
                "Iptc4xmpExt:City": "London",
                "Iptc4xmpExt:CountryCode": "GB",
                "Iptc4xmpExt:CountryName": "United Kingdom",
                "Iptc4xmpExt:ProvinceState": "London",
                "Iptc4xmpExt:Sublocation": "Greenwich",
                "Iptc4xmpExt:WorldRegion": "Europe"
              },
              "aux:Lens": "iPhone 11 back dual wide camera 4.25mm f/1.8",
              "aux:LensInfo": "807365/524263 17/4 9/5 12/5",
              "dc:creator": "Brendan Quinn",
              "dc:description": "Wildflowers at the Rose Garden, Greenwich Park, London",
              "dc:format": "image/jpeg",
              "dc:rights": "Copyright (C) Brendan Quinn 2022. Some rights reserved.",
              "dc:subject": [
                "Rose Garden",
                "Greenwich",
                "flowers",
                "Wild flowers"
              ],
              "exifEX:LensMake": "Apple",
              "exifEX:LensModel": "iPhone 11 back dual wide camera 4.25mm f/1.8",
              "photoshop:AuthorsPosition": "Managing Director",
              "photoshop:CaptionWriter": "Brendan Quinn",
              "photoshop:City": "London",
              "photoshop:ColorMode": "3",
              "photoshop:Country": "United Kingdom",
              "photoshop:DateCreated": "2022-07-09T17:47:17.538",
              "photoshop:Headline": "Wildflowers at the Rose Garden, Greenwich Park, London",
              "photoshop:ICCProfile": "Display P3",
              "photoshop:LegacyIPTCDigest": "74680B31492930D266D0FAE3B244D1BD",
              "photoshop:State": "London",
              "plus:CopyrightOwner": {
                "plus:CopyrightOwnerName": "Brendan Quinn"
              },
              "plus:ImageCreator": {
                "plus:ImageCreatorName": "Brendan Quinn"
              },
              "plus:ImageSupplier": {
                "plus:ImageSupplierName": "IPTC"
              },
              "plus:Licensor": {
                "plus:LicensorEmail": "office@iptc.org",
                "plus:LicensorName": "Brendan Quinn",
                "plus:LicensorTelephoneType2": "http://ns.useplus.org/ldf/vocab/work",
                "plus:LicensorURL": "www.iptc.org/"
              },
              "plus:ModelReleaseStatus": "http://ns.useplus.org/ldf/vocab/MR-NAP",
              "plus:PropertyReleaseStatus": "http://ns.useplus.org/ldf/vocab/PR-NAP",
              "xmp:CreateDate": "2022-07-09T17:47:17",
              "xmp:CreatorTool": "15.5",
              "xmp:MetadataDate": "2022-07-21T15:46:32+03:00",
              "xmp:ModifyDate": "2022-07-21T15:46:32+03:00",
              "xmp:mm/InstanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
              "xmp:mm/OriginalDocumentID": "4B267F2683ABA382F9E0C1DE10BF4210",
              "xmp:rights/UsageTerms": "Usage permitted under Creative Commons Attribution (CC-BY 4.0) licence."
            }
          }
        },
        {
          "label": "c2pa.hash.data",
          "data": {
            "alg": "sha256",
            "exclusions": [
              {
                "length": 766693,
                "start": 11334
              }
            ],
            "hash": "p4CB4O1GytvYNTcRG4xuy7UlX53wFLNsxNHfDKChs2w=",
            "name": "jumbf manifest",
            "pad": "<omitted>"
          }
        }
      ],
      "signature_info": {
        "issuer": "IPTC",
        "time": "2022-07-21T13:51:22.223549+00:00"
      }
    }
  }
}

Step 6: Checking what we’ve done

Firstly, we can use c2patool itself in read-only mode, to view the embedded manifest including our assertion:

Code Block
c2patool -d greenwich_flowers_with_c2pa.jpg
{
  "active_manifest": "iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0",
  "manifests": {
    "iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0": {
      "claim": {
        "alg": "sha256",
        "assertions": [
          {
            "hash": "Mes+SkL6ATGlyTDj9oQB83r5bzzuONDG3AGEnzEYdhk=",
            "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg"
          },
          {
            "hash": "/UBCRIHKhkBBGZz/xvU4/Xfoy1FnGXRAGWT+FTgVyd4=",
            "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
          },
          {
            "hash": "VK6sfqioMibOyyF2oqJVPBQpae/ObPk/sZBGqNGQZWs=",
            "url": "self#jumbf=c2pa.assertions/c2pa.ingredient"
          },
          {
            "hash": "WiLn4YZAvAxxOyYdcwYJ57/DPQQd0BZ7BhvgvdXp0II=",
            "url": "self#jumbf=c2pa.assertions/stds.iptc.photo-metadata"
          },
          {
            "hash": "ZnfuVaZ+3msvlXtNMO/4Lg101KKvGRPQtHFNWW/cU2o=",
            "url": "self#jumbf=c2pa.assertions/c2pa.hash.data"
          }
        ],
        "claim_generator": "BQ Test with c2patool/0.1 c2pa-rs/0.1.3",
        "claim_generator_hints": {
          "Sec-CH-UA": "\"c2pa-rs\";v=\"0.1.3\""
        },
        "dc:format": "image/jpeg",
        "dc:title": "greenwich_flowers_with_c2pa.jpg",
        "instanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
        "signature": "self#jumbf=c2pa.signature"
      },
      "assertion_store": {
        "c2pa.thumbnail.claim.jpeg": "<omitted> len = 376929",
        "c2pa.thumbnail.ingredient.jpeg": "<omitted> len = 376929",
        "c2pa.hash.data": {
          "alg": "sha256",
          "exclusions": [
            {
              "length": 766708,
              "start": 11334
            }
          ],
          "hash": "M7kXVTNy8VZak4B+p6lPCJbgevGRhB4Y7EK+dOVZM90=",
          "name": "jumbf manifest",
          "pad": "<omitted>"
        },
        "stds.iptc.photo-metadata": {
          "@context": {
            "Iptc4xmpCore": "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/",
            "Iptc4xmpExt": "http://iptc.org/std/Iptc4xmpExt/2008-02-29/",
            "aux": "http://ns.adobe.com/exif/1.0/aux/",
            "dc": "http://purl.org/dc/elements/1.1/",
            "exifEX": "http://cipa.jp/exif/1.0/",
            "photoshop": "http://ns.adobe.com/photoshop/1.0/",
            "plus": "http://ns.useplus.org/ldf/xmp/1.0/",
            "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
            "stEvt": "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#",
            "xmp": "http://ns.adobe.com/xap/1.0/",
            "xmpMM": "http://ns.adobe.com/xap/1.0/mm/",
            "xmpRights": "http://ns.adobe.com/xap/1.0/rights/"
          },
          "@graph": {
            "@id": "file:///Users/brendan/dev/iptc/c2pa/c2patool-play/greenwich_flowers_xmp_pretty.xml",
            "Iptc4xmpCore:CountryCode": "GB",
            "Iptc4xmpCore:CreatorContactInfo": {
              "Iptc4xmpCore:CiAdrCity": "London",
              "Iptc4xmpCore:CiAdrCtry": "United Kingdom",
              "Iptc4xmpCore:CiAdrExtadr": "25 Southampton Buildings",
              "Iptc4xmpCore:CiAdrPcode": "WC2A 1AL",
              "Iptc4xmpCore:CiAdrRegion": "London",
              "Iptc4xmpCore:CiEmailWork": "mdirector@iptc.org",
              "Iptc4xmpCore:CiTelWork": "+44 (0)20 3178 4922 ",
              "Iptc4xmpCore:CiUrlWork": "https://www.iptc.org/"
            },
...etc...
        },
        "c2pa.ingredient": {
          "dc:format": "image/jpeg",
          "dc:title": "greenwich_flowers_with_metadata.jpg",
          "documentID": "adobe:docid:photoshop:91a3bd3c-4510-ca4a-8d21-8f2fb4edaf91",
          "instanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
          "relationship": "parentOf",
          "thumbnail": {
            "hash": "/UBCRIHKhkBBGZz/xvU4/Xfoy1FnGXRAGWT+FTgVyd4=",
            "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
          }
        }
      },
      "signature": {
        "alg": "ps256",
        "issuer": "IPTC",
        "time": "2022-07-29T15:29:24.974920+00:00"
      }
    }
  },
  "validation_status": [
    {
      "code": "claimSignature.validated",
      "url": "self#jumbf=/c2pa/iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0/c2pa.signature",
      "explanation": "claim signature valid"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.ingredient",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.ingredient"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/stds.iptc.photo-metadata",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/stds.iptc.photo-metadata"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.hash.data",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.hash.data"
    },
    {
      "code": "assertion.dataHash.match",
      "url": "self#jumbf=/c2pa/iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0/c2pa.assertions/c2pa.hash.data",
      "explanation": "data hash valid"
    }
  ]
}

So we can see that the signatures match and the image is un-tampered. (A fun exercise might be to try to change the metadata in a hex editor and see what happens!)

We can then load the image into verify.contentauthenticity.org to see what it looks like:

...

So we can see that the image is “signed by” the string that we entered into the certificate “Organization Name” field. For “produced with,” verify.contentauthenticity.org seems to use the first word in the “claim generator” string (everything up to the first space character).

But none of the IPTC metadata included in the assertion is shown on verify.contentauthenticity.org 😞

We are in active discussions with the CAI team to work out exactly how to display asserted IPTC metadata on this screen, so watch this space!

How about if we load the image into Photoshop, with Content Credentials (the Photoshop name for C2PA) switched on? Well it doesn’t seem to do much at all…

...

As can be seen from the screenshot, loading the image into Photoshop and viewing the “Content Credentials Preview” window seems to just show that an Adobe signature would be added when the image is saved. Perhaps I’m missing something here - please let me know if you can see I’m doing something wrong!

(As more C2PA-compliant tools are released, we will add more content to this section.)

Step 7: Tampering with the metadata

To see if the hashing and signing feature works, I tried tampering with the embedded metadata.

To do this I created a hex dump of the C2PA version of the image using xxd on my Mac command line, edited it using a text editor, and used xxd again to re-create the binary from the tampered image.

Code Block
languagebash
cat greenwich_flowers_with_c2pa.jpg| xxd >hex-dump
vi hex-dump
# ... make changes by hand ...
# recreate the binary files from the edited hex dump:
cat hex-dump | xxd -r >greenwich_flowers_with_c2pa_tampered.jpg

The specific change I made was to change the copyright year (dc:rights field) from 2022 to 2099.

Let’s see what c2patool says about the tampered file:

Code Block
c2patool -d greenwich_flowers_with_c2pa_tampered.jpg 
{
  "active_manifest": "iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0",
  "manifests": {
    "iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0": {
      "claim": {
        "alg": "sha256",
        "assertions": [
          {
            "hash": "Mes+SkL6ATGlyTDj9oQB83r5bzzuONDG3AGEnzEYdhk=",
            "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg"
          },
          {
            "hash": "/UBCRIHKhkBBGZz/xvU4/Xfoy1FnGXRAGWT+FTgVyd4=",
            "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
          },
          {
            "hash": "VK6sfqioMibOyyF2oqJVPBQpae/ObPk/sZBGqNGQZWs=",
            "url": "self#jumbf=c2pa.assertions/c2pa.ingredient"
          },
          {
            "hash": "WiLn4YZAvAxxOyYdcwYJ57/DPQQd0BZ7BhvgvdXp0II=",
            "url": "self#jumbf=c2pa.assertions/stds.iptc.photo-metadata"
          },
          {
            "hash": "ZnfuVaZ+3msvlXtNMO/4Lg101KKvGRPQtHFNWW/cU2o=",
            "url": "self#jumbf=c2pa.assertions/c2pa.hash.data"
          }
        ],
        "claim_generator": "BQ Test with c2patool/0.1 c2pa-rs/0.1.3",
        "claim_generator_hints": {
          "Sec-CH-UA": "\"c2pa-rs\";v=\"0.1.3\""
        },
        "dc:format": "image/jpeg",
        "dc:title": "greenwich_flowers_with_c2pa.jpg",
        "instanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
        "signature": "self#jumbf=c2pa.signature"
      },
      "assertion_store": {
        "c2pa.hash.data": {
          "alg": "sha256",
          "exclusions": [
            {
              "length": 766708,
              "start": 11334
            }
          ],
          "hash": "M7kXVTNy8VZak4B+p6lPCJbgevGRhB4Y7EK+dOVZM90=",
          "name": "jumbf manifest",
          "pad": "<omitted>"
        },
        "c2pa.ingredient": {
          "dc:format": "image/jpeg",
          "dc:title": "greenwich_flowers_with_metadata.jpg",
          "documentID": "adobe:docid:photoshop:91a3bd3c-4510-ca4a-8d21-8f2fb4edaf91",
          "instanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
          "relationship": "parentOf",
          "thumbnail": {
            "hash": "/UBCRIHKhkBBGZz/xvU4/Xfoy1FnGXRAGWT+FTgVyd4=",
            "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
          }
        },
        "c2pa.thumbnail.ingredient.jpeg": "<omitted> len = 376929",
        "c2pa.thumbnail.claim.jpeg": "<omitted> len = 376929",
        "stds.iptc.photo-metadata": {
          "@context": {
            "Iptc4xmpCore": "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/",
            "Iptc4xmpExt": "http://iptc.org/std/Iptc4xmpExt/2008-02-29/",
            "aux": "http://ns.adobe.com/exif/1.0/aux/",
            "dc": "http://purl.org/dc/elements/1.1/",
            "exifEX": "http://cipa.jp/exif/1.0/",
            "photoshop": "http://ns.adobe.com/photoshop/1.0/",
            "plus": "http://ns.useplus.org/ldf/xmp/1.0/",
            "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
            "stEvt": "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#",
            "xmp": "http://ns.adobe.com/xap/1.0/",
            "xmpMM": "http://ns.adobe.com/xap/1.0/mm/",
            "xmpRights": "http://ns.adobe.com/xap/1.0/rights/"
          },
          "@graph": {
            "@id": "file:///Users/brendan/dev/iptc/c2pa/c2patool-play/greenwich_flowers_xmp_pretty.xml",
            "Iptc4xmpCore:CountryCode": "GB",
            "Iptc4xmpCore:CreatorContactInfo": {
              "Iptc4xmpCore:CiAdrCity": "London",
              "Iptc4xmpCore:CiAdrCtry": "United Kingdom",
              "Iptc4xmpCore:CiAdrExtadr": "25 Southampton Buildings",
              "Iptc4xmpCore:CiAdrPcode": "WC2A 1AL",
              "Iptc4xmpCore:CiAdrRegion": "London",
              "Iptc4xmpCore:CiEmailWork": "mdirector@iptc.org",
              "Iptc4xmpCore:CiTelWork": "+44 (0)20 3178 4922 ",
              "Iptc4xmpCore:CiUrlWork": "https://www.iptc.org/"
            },
            "Iptc4xmpCore:IntellectualGenre": "https://cv.iptc.org/newscodes/genre/Actuality",
            "Iptc4xmpCore:Location": "Greenwich",
            "Iptc4xmpCore:Scene": "https://cv.iptc.org/newscodes/scene/011600",
            "Iptc4xmpExt:DigitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture",
            "Iptc4xmpExt:LocationCreated": {
              "Iptc4xmpExt:City": "London",
              "Iptc4xmpExt:CountryCode": "GB",
              "Iptc4xmpExt:CountryName": "United Kingdom",
              "Iptc4xmpExt:ProvinceState": "London",
              "Iptc4xmpExt:Sublocation": "Greenwich",
              "Iptc4xmpExt:WorldRegion": "Europe"
            },
            "Iptc4xmpExt:LocationShown": {
              "Iptc4xmpExt:City": "London",
              "Iptc4xmpExt:CountryCode": "GB",
              "Iptc4xmpExt:CountryName": "United Kingdom",
              "Iptc4xmpExt:ProvinceState": "London",
              "Iptc4xmpExt:Sublocation": "Greenwich",
              "Iptc4xmpExt:WorldRegion": "Europe"
            },
            "aux:Lens": "iPhone 11 back dual wide camera 4.25mm f/1.8",
            "aux:LensInfo": "807365/524263 17/4 9/5 12/5",
            "dc:creator": "Brendan Quinn",
            "dc:description": "Wildflowers at the Rose Garden, Greenwich Park, London",
            "dc:format": "image/jpeg",
            "dc:rights": "Copyright (C) Brendan Quinn 2099. Some rights reserved.",
            "dc:subject": [
              "Rose Garden",
              "Greenwich",
              "flowers",
              "Wild flowers"
            ],
            "exifEX:LensMake": "Apple",
            "exifEX:LensModel": "iPhone 11 back dual wide camera 4.25mm f/1.8",
            "photoshop:AuthorsPosition": "Managing Director",
            "photoshop:CaptionWriter": "Brendan Quinn",
            "photoshop:City": "London",
            "photoshop:ColorMode": "3",
            "photoshop:Country": "United Kingdom",
            "photoshop:DateCreated": "2022-07-09T17:47:17.538",
            "photoshop:Headline": "Wildflowers at the Rose Garden, Greenwich Park, London",
            "photoshop:ICCProfile": "Display P3",
            "photoshop:LegacyIPTCDigest": "74680B31492930D266D0FAE3B244D1BD",
            "photoshop:State": "London",
            "plus:CopyrightOwner": {
              "plus:CopyrightOwnerName": "Brendan Quinn"
            },
            "plus:ImageCreator": {
              "plus:ImageCreatorName": "Brendan Quinn"
            },
            "plus:ImageSupplier": {
              "plus:ImageSupplierName": "IPTC"
            },
            "plus:Licensor": {
              "plus:LicensorEmail": "office@iptc.org",
              "plus:LicensorName": "Brendan Quinn",
              "plus:LicensorTelephoneType2": "http://ns.useplus.org/ldf/vocab/work",
              "plus:LicensorURL": "www.iptc.org/"
            },
            "plus:ModelReleaseStatus": "http://ns.useplus.org/ldf/vocab/MR-NAP",
            "plus:PropertyReleaseStatus": "http://ns.useplus.org/ldf/vocab/PR-NAP",
            "xmp:CreateDate": "2022-07-09T17:47:17",
            "xmp:CreatorTool": "15.5",
            "xmp:MetadataDate": "2022-07-21T15:46:32+03:00",
            "xmp:ModifyDate": "2022-07-21T15:46:32+03:00",
            "xmp:mm/InstanceID": "xmp.iid:ef558a6e-e419-4aae-8890-f6b2b2bed77c",
            "xmp:mm/OriginalDocumentID": "4B267F2683ABA382F9E0C1DE10BF4210",
            "xmp:rights/UsageTerms": "Usage permitted under Creative Commons Attribution (CC-BY 4.0) licence."
          }
        }
      },
      "signature": {
        "alg": "ps256",
        "issuer": "IPTC",
        "time": "2022-07-29T15:29:24.974920+00:00"
      }
    }
  },
  "validation_status": [
    {
      "code": "claimSignature.validated",
      "url": "self#jumbf=/c2pa/iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0/c2pa.signature",
      "explanation": "claim signature valid"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.ingredient",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.ingredient"
    },
    {
      "code": "assertion.hashedURI.mismatch",
      "url": "self#jumbf=c2pa.assertions/stds.iptc.photo-metadata",
      "explanation": "hash does not match assertion data: self#jumbf=c2pa.assertions/stds.iptc.photo-metadata"
    },
    {
      "code": "assertion.hashedURI.match",
      "url": "self#jumbf=c2pa.assertions/c2pa.hash.data",
      "explanation": "hashed uri matched: self#jumbf=c2pa.assertions/c2pa.hash.data"
    },
    {
      "code": "assertion.dataHash.mismatch",
      "url": "self#jumbf=/c2pa/iptc:urn:uuid:d2bf46f1-cb56-4899-b386-1270ae9581a0/c2pa.assertions/c2pa.hash.data",
      "explanation": "asset hash error, name: jumbf manifest, error: hash verification( Hashes do not match )"
    }
  ]
}

As you can see in the validation_status section at the bottom of the output, the hashes no longer match. We have detected the tampering!

Loading the image into verify.contentauthenticity.org doesn’t say that the image has been signed, but on the other hand it doesn’t say that it has been tampered with, either. I would have thought that it’s better to signal tampering to users than to simply say that there are no credentials. (Note: I now see that this doesn’t work for the previous image either, so something may have changed on verify.contentauthenticity.org since I took the above screenshots a couple of weeks ago)

...

Conclusions

So we have proven the concept and we have shown that it works, albeit in a limited context. What have we learned, and what comes next?

  • We can see that the technology works: we have created an assertion, hashed and signed it, and we can extract it and check the signature afterwards. This is a big step forward.

  • Creating the JSON-LD version of the XMP packet is awkward and time-consuming. We need to develop some tools to help.

  • Unfortunately there are no tools that can currently show IPTC Photo Metadata assertions to users, or Exif assertions for that matter, except for the command-line c2patool which is not accessible to regular users. We are working with CAI and C2PA on addressing this problem.

  • Notice that I simply typed in “IPTC” as the organisation when I created the certificate, and verify.contentauthenticity.org displayed it unquestioningly. I could have typed “BBC” or “The New York Times”, used it to sign anything, and the tool would dutifully display it as if it is genuine. Obviously this is the opposite of content authenticity! So we need to work on certificate chains and webs of trust (the same way HTTPS certificates work) in the next phase of adoption. We’re already talking about this in Project Origin, CAI and C2PA circles.