contentauth / c2pa-js

JavaScript SDK for displaying and validating C2PA data
https://opensource.contentauthenticity.org
MIT License
74 stars 40 forks source link

Validation errors inconsistent through tools #125

Open clementh59 opened 11 months ago

clementh59 commented 11 months ago

Hi,

When validating the manifest of this image: https://truepic.com/wp-content/uploads/2023/03/transparency_original-capture.jpg with the library, I get this issue:

Screenshot 2023-12-05 at 12 41 33 Screenshot 2023-12-05 at 12 43 06

However, when validating with c2patool, it accepts the signature:

{
      "code": "claimSignature.validated",
      "url": "self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.signature",
      "explanation": "claim signature valid"
}

Do you know where it could come from?

Thanks, Clément

dkozma commented 11 months ago

Hi Clément,

What signing algorithm are you using? Unfortunately the JavaScript SDK doesn't support Ed25519 at the moment due to WebCrypto's lack of support for it, so I'm wondering if this is causing the issue.

clementh59 commented 11 months ago

It's es256. Please note that I am not generating this image. I took an image sample from truepic.

"signature": {
  "alg": "es256",
  "issuer": "Truepic",
  "time": "2023-03-20T14:29:27+00:00"
}

Also, I can verify it there: https://contentcredentials.org/verify. I'm not sure which library is used there, but it seems to accept it.

emensch commented 11 months ago

Hi @clementh59, what version of c2pa do you have installed?

clementh59 commented 11 months ago

Hi @emensch, that's 0.11.12 for c2pa-wc and 0.17.5 for c2pa

dkozma commented 11 months ago

@clementh59 Verify uses the open source c2pa-js under the hood. I made a minimal repro case in CodeSandbox showing the image loading from your server and showing the claim info.

What browser/version are you trying this on?

clementh59 commented 11 months ago

Thanks a lot for the answer. The issue probably comes from our end then. I am on Chrome, but that's probably not linked to my browser/version as all the people that tried our service (which is a chrome extension) had the same issue.

We'll investigate on our end to try to find the root of the issue.

Thanks.

emensch commented 11 months ago

@clementh59 if you'd like to post sample code—specifically anything about reading c2pa data and passing it to the web components—we might be able to help debug. There are a couple potential pitfalls that we don't do a great job of detailing in our docs at the moment 🙂

clementh59 commented 11 months ago

@emensch @dkozma Here is the code I tried:

const sampleImage = 'https://truepic.com/wp-content/uploads/2023/03/transparency_original-capture.jpg';

(async () => {
  const libraryUrl = './lib-temp/c2pa.esm.js';

  // Initialize the c2pa-js SDK
  const { createC2pa } = await import(libraryUrl);
  const c2pa = await createC2pa({
    wasmSrc: './lib-temp/toolkit_bg.wasm',
    workerSrc: './lib-temp/c2pa.worker.min.js',
  });

  // Read in our sample image and get a manifest store
  try {
    const { manifestStore } = await c2pa.read(sampleImage);
    console.log('errors from site reading: ', manifestStore.validationStatus);
  } catch (err) {
    console.error('Error reading image:', err);
  }
})();

This code works in a very basic web app, where I don't have any validation error. However, the same code, with the same libraries (0.17.6) does not work in a very basic chrome extension (it says one of the signature mismatches).

We'll continue to investigate on our side, but if you have any idea of what could be the issue, that would be super helpful.

Thanks a lot

clementh59 commented 11 months ago

It seems to come from this function that returns a validation error when fetching the manifest store data: Screenshot 2023-12-21 at 14 08 55

It might be the case only when it is run in a special environment (i.e a chrome extension). What is surprising is that it is only the case for a small set of images, most of them are validated without any issue.

dkozma commented 10 months ago

@clementh59 I'm not sure if your browser extension also runs in a Firefox or Safari environment but if so I'm curious as to if the same problem exists in those browsers, or if this is localized to Chrome.

clementh59 commented 10 months ago

Good point. It only runs on Chrome for now, so I can't help on that point, sorry

dkozma commented 10 months ago

No problem - let us try to replicate on a minimal browser extension environment and see if there is anything we can find.

github-jira-sync-bot commented 10 months ago

:white_check_mark: Jira issue https://jira.corp.adobe.com/browse/CAI-5271 is successfully created for this GitHub issue.

dkozma commented 10 months ago

@clementh59 Are you running the c2pa-js code in a content script or a background script in your extension?

clementh59 commented 10 months ago

@dkozma None of them was working, we had to run it from a sandbox. You can find our implementation here: https://github.com/digimarc-corp/c2pa-content-credentials-extension/blob/main/sandbox.js

Feel free to ask any questions or help regarding the integration into an extension, I am happy to help!

clementh59 commented 9 months ago

Hi @dkozma,

I created a minimalist extension that showcases the bug: https://github.com/digimarc-corp/c2pa-content-credentials-extension/tree/minimalist-extension

Here are the steps to have it work: npm i git clone https://github.com/contentauth/c2pa-js mv c2pa-js c2pa cd c2pa Rush install Rush build

Then, load the extension in chrome (https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked) Click on the extension icon and turn it on. Open an image (e.g. https://truepic.com/wp-content/uploads/2023/03/transparency_original-capture.jpg) and check the console, you should see the validation status.

I hope it will make the debug as easy as possible on your side! :)

Feel free to ask if you have any questions. I'm happy to jump on a call if you think it could be valuable.

dkozma commented 9 months ago

Thanks, this is very helpful - will give this a try Monday or Tuesday.

dkozma commented 9 months ago

@clementh59 Still looking into this. Will update when I have more - the minimal repro example you provided definitely makes things easier.

domguinard commented 9 months ago

Hi @dkozma any news on the issue? Also do let us know if we can do anything to help, anything you'd like us to explore. The extension is gaining quite a bit of momentum and we are eager to make sure it validates things correctly.

clementh59 commented 8 months ago

Hi @dkozma,

We continued to dig on our side to make it as easy as possible for you to debug it and here are some information that might be useful:

For you to debug, it might be useful to have actual data, so here it is. That's the data I get inside verify_cose_async in cose_validator.rs:

log::info!("Current alg: {:?}", alg);

// build result structure
let mut result = ValidationInfo::default();

log::info!("Result: {:?}", result);

// get the cert chain
let certs = get_sign_certs(&sign1)?;

log::info!("Certs: {:?}", certs);

sign1.payload = Some(data.clone()); // restore payload

log::info!("signature payload: {:?}", sign1.payload);

let tbs = sig_structure_data(
    coset::SignatureContext::CoseSign1,
    p_header,
    None,
    &additional_data,
    sign1.payload.as_ref().unwrap_or(&vec![]),
); // get "to be signed" bytes

log::info!("TBS: {:?}", tbs);

[...]

log::info!("result2: {:?}", result);
Ok(result)

I provided a JSON with all the values printed so that you can reproduce it in your environment easily: logs.json

Also, here is the validation log:

[
   "LogItem"{
      "label":"self#jumbf=/c2pa/adobe:urn:uuid:0ca37023-3dc5-4fee-8541-299998fb6b70/c2pa.signature",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1148",
      "description":"claim signature valid",
      "err_val":"None",
      "validation_status":"Some(""claimSignature.validated"")"
   },
   "LogItem"{
      "label":"self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=c2pa.assertions/c2pa.ingredient",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=c2pa.assertions/c2pa.ingredient",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=c2pa.assertions/stds.schema-org.CreativeWork",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=c2pa.assertions/stds.schema-org.CreativeWork",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=c2pa.assertions/c2pa.actions",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=c2pa.assertions/c2pa.actions",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=c2pa.assertions/c2pa.hash.data",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=c2pa.assertions/c2pa.hash.data",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/adobe:urn:uuid:0ca37023-3dc5-4fee-8541-299998fb6b70/c2pa.assertions/c2pa.hash.data",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1311",
      "description":"data hash valid",
      "err_val":"None",
      "validation_status":"Some(""assertion.dataHash.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.signature",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1139",
      "description":"claim signature is not valid",
      "err_val":"Some(""CoseSignature"")",
      "validation_status":"Some(""claimSignature.mismatch"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/com.truepic.libc2pa",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/com.truepic.libc2pa",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/stds.exif",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/stds.exif",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/stds.exif__1",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/stds.exif__1",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/stds.exif__2",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/stds.exif__2",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/c2pa.thumbnail.claim.jpeg",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/c2pa.thumbnail.claim.jpeg",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/com.truepic.custom.odometry",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/com.truepic.custom.odometry",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/com.truepic.custom.blur",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/com.truepic.custom.blur",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   },
   "LogItem"{
      "label":"self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/c2pa.hash.data",
      "file":"/Users/clement-hecquet-evt/Documents/clement/tempo/c2pa-rs/sdk/src/claim.rs",
      "function":"verify_internal",
      "line":"1235",
      "description":"hashed uri matched: self#jumbf=/c2pa/Truepic Lens SDK libc2pa vv1.0.0.182:urn:uuid:d6adaa79-0708-4b7a-9af0-10c7c42a21e5/c2pa.assertions/c2pa.hash.data",
      "err_val":"None",
      "validation_status":"Some(""assertion.hashedURI.match"")"
   }
]

It seems that only the COSE signature is problematic.

I hope it helps!

dkozma commented 8 months ago

@clementh59 Thank you for digging into this - I'll go over this with the Rust team shortly.

dkozma commented 8 months ago

~As far as the issues you are seeing in cose_validator above, do you see it happening from within JavaScript in the extension environment as well as in a web environment or is it just in the extension environment?~

Edit: Actually, I see the discrepancy in running in web via extension, and lines up with what you mentioned:

In a browser/web context, the full chain gets validated properly

Screenshot 2024-03-19 at 9 07 26 AM

In the extension code, it validates the first ps256 certificate, then stops at the es256

Screenshot 2024-03-19 at 9 08 45 AM

Continuing to look into this...

dkozma commented 8 months ago

@clementh59 Looking into this further, it looks like SubtleCrypto.importKey() cannot be found during verification:

Screenshot 2024-03-19 at 11 41 05 AM

I'm not sure if this is an issue with the extension running this in a web worker - I tried calling these functions manually in the extension console and they seem to work, so I'm not sure what is failing in this context.

clementh59 commented 8 months ago

@dkozma I tried to call this function:

async function importKey(rawKey) {
  try {
    const key = await window.crypto.subtle.importKey(
      "raw", // format of the key to import
      rawKey, // the key in ArrayBuffer format
      {   // algorithm the key will be used with
        name: "AES-CBC",
      },
      false, // whether the key is extractable
      ["encrypt", "decrypt"] // usages the key can be used for
    );

    console.log("Key imported successfully:", key);
    return key;
  } catch (error) {
    console.error("Error importing key:", error);
  }
}

In the same function as I call the c2pa verifications and the function runs correctly. So the issue doesn't seem to be linked to the fact that it is being executed in a web worker...

clementh59 commented 7 months ago

Hi @dkozma, we finally found a workaround for not using the library in a sandbox, which means we are not impacted by this bug anymore. Thanks anyway for the time you dedicated to this!

mauricefisher64 commented 3 months ago

Thanks for being persistent. We are a small team an do not have access to all possible build targets and platforms.