Skip to content

Commit fcf2f0f

Browse files
authored
feat: Legacy timestamp grace (#24)
* fix: Change tests to expect validation status Temporarily point to sha instead of published version of c2pa-rs * feat: Legacy Timestamp Grace bump c2pa version for older manifest timestamp fix. Add CAWG validation to reader. * feat: add test for cawg decoding * chore: add changeset
1 parent 58defa2 commit fcf2f0f

File tree

8 files changed

+94
-30
lines changed

8 files changed

+94
-30
lines changed

.changeset/four-poems-reply.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@contentauth/c2pa-node": patch
3+
---
4+
5+
CAWG reader validation improvements

Cargo.lock

Lines changed: 21 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
1111
[dependencies]
1212
async-trait = "0.1.77"
1313
ciborium = "0.2.2"
14-
c2pa = { version = "0.67", default-features = false, features = ["file_io", "pdf", "fetch_remote_manifests", "add_thumbnails", "rust_native_crypto"] }
14+
c2pa = { version = "0.68", default-features = false, features = ["file_io", "pdf", "fetch_remote_manifests", "add_thumbnails", "rust_native_crypto"] }
1515
futures = "0.3"
1616
image = "0.25.6"
1717
neon = { version = "1.0.0", default-features = false, features = [
@@ -29,7 +29,7 @@ reqwest = { version = "0.12.2", default-features = false, features = [
2929
] }
3030
serde = { version = "1.0.203", features = ["derive"] }
3131
serde_bytes = "0.11.15"
32-
serde_json = "1.0.117"
32+
serde_json = "1.0.145"
3333
toml = "0.8"
3434
thiserror = "1.0.61"
3535
tokio = { version = "1.43.0", features = ["rt-multi-thread"] }

js-src/Builder.spec.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ describe("Builder", () => {
212212
const reader = await Reader.fromAsset(dest);
213213
const manifestStore = reader.json();
214214
const activeManifest = reader.getActive();
215-
expect(manifestStore.validation_status).toBeUndefined();
215+
expect(manifestStore.validation_status![0].code).toBe(
216+
"signingCredential.untrusted",
217+
);
216218
expect(manifestStore.active_manifest).not.toBeUndefined();
217219
expect(activeManifest?.title).toBe("Test_Manifest");
218220
});
@@ -265,7 +267,9 @@ describe("Builder", () => {
265267
});
266268
const manifestStore = reader.json();
267269
const activeManifest = reader.getActive();
268-
expect(manifestStore.validation_status).toBeUndefined();
270+
expect(manifestStore.validation_status![0].code).toBe(
271+
"signingCredential.untrusted",
272+
);
269273
expect(manifestStore.active_manifest).not.toBeUndefined();
270274
expect(activeManifest?.title).toBe("Test_Manifest");
271275
});
@@ -294,7 +298,9 @@ describe("Builder", () => {
294298
const reader = await Reader.fromAsset(dest);
295299
const manifestStore = reader.json();
296300
const activeManifest = reader.getActive();
297-
expect(manifestStore.validation_status).toBeUndefined();
301+
expect(manifestStore.validation_status![0].code).toBe(
302+
"signingCredential.untrusted",
303+
);
298304
expect(manifestStore.active_manifest).not.toBeUndefined();
299305
expect(activeManifest?.title).toBe("Test_Manifest");
300306
});
@@ -326,7 +332,9 @@ describe("Builder", () => {
326332
});
327333
const manifestStore = reader.json();
328334
const activeManifest = reader.getActive();
329-
expect(manifestStore.validation_status).toBeUndefined();
335+
expect(manifestStore.validation_status![0].code).toBe(
336+
"signingCredential.untrusted",
337+
);
330338
expect(manifestStore.active_manifest).not.toBeUndefined();
331339
expect(activeManifest?.title).toBe("Test_Manifest");
332340
});
@@ -354,7 +362,9 @@ describe("Builder", () => {
354362
});
355363
const manifestStore = reader.json();
356364
const activeManifest = reader.getActive();
357-
expect(manifestStore.validation_status).toBeUndefined();
365+
expect(manifestStore.validation_status![0].code).toBe(
366+
"signingCredential.untrusted",
367+
);
358368
expect(manifestStore.active_manifest).not.toBeUndefined();
359369
expect(activeManifest?.title).toBe("Test_Manifest");
360370
});

js-src/Reader.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,50 @@ describe("Reader", () => {
198198
);
199199
expect(reader.isEmbedded()).toBeFalsy();
200200
});
201+
202+
it("should decode CAWG identity assertion with signature_info", async () => {
203+
// This test verifies that postValidateCawg() properly decodes CAWG assertions
204+
// and extracts signature_info from the signature data, matching c2pa-js behavior
205+
const reader = await Reader.fromAsset({
206+
path: "./tests/fixtures/C_with_CAWG_data.jpg",
207+
});
208+
209+
// Call postValidateCawg to decode CAWG assertions
210+
await reader.postValidateCawg();
211+
212+
const activeManifest = reader.getActive();
213+
214+
// Find the cawg.identity assertion
215+
const cawgIdentityAssertion = activeManifest?.assertions?.find(
216+
(assertion: any) => assertion.label === "cawg.identity",
217+
) as any;
218+
219+
expect(cawgIdentityAssertion).toBeDefined();
220+
expect(cawgIdentityAssertion?.data).toBeDefined();
221+
expect(cawgIdentityAssertion?.data.signature_info).toBeDefined();
222+
223+
// Verify signature_info structure matches c2pa-js behavior
224+
const signatureInfo = cawgIdentityAssertion?.data.signature_info;
225+
const signerPayload = cawgIdentityAssertion?.data.signer_payload;
226+
227+
// Verify signature_info values
228+
expect(signatureInfo.alg).toBe("Ed25519");
229+
expect(signatureInfo.issuer).toBe("C2PA Test Signing Cert");
230+
expect(signatureInfo.cert_serial_number).toBe(
231+
"638838410810235485828984295321338730070538954823",
232+
);
233+
expect(signatureInfo.revocation_status).toBe(true);
234+
235+
// Verify signer_payload values
236+
expect(signerPayload.sig_type).toBe("cawg.x509.cose");
237+
expect(signerPayload.referenced_assertions).toHaveLength(2);
238+
expect(signerPayload.referenced_assertions[0]).toEqual({
239+
url: "self#jumbf=c2pa.assertions/cawg.training-mining",
240+
hash: "rBBgURB+/0Bc2Uk3+blNpYTGQTxOwzXQ2xhjA3gsqI4=",
241+
});
242+
expect(signerPayload.referenced_assertions[1]).toEqual({
243+
url: "self#jumbf=c2pa.assertions/c2pa.hash.data",
244+
hash: "sASozh9KFSkW+cyMI0Pw5KYoD2qn7MkUEq9jUUhe/sM=",
245+
});
246+
});
201247
});

js-src/Settings.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ describe("Settings", () => {
128128

129129
// Verify settings are still intact
130130
const currentSettings = JSON.parse(getSettingsJson());
131-
expect(currentSettings.core.hash_alg).toBe("sha256");
131+
expect(currentSettings.core.merkle_tree_max_proofs).toBe(5);
132132
expect(currentSettings.verify.verify_trust).toBe(true);
133133
expect(currentSettings.trust.verify_trust_list).toBe(true);
134134
});

js-src/Signer.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ describe("CallbackSigner", () => {
176176
const activeManifest = reader.getActive();
177177

178178
// If validation_status is undefined, the signature is valid
179-
expect(manifestStore.validation_status).toBeUndefined();
179+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
180+
expect(manifestStore.validation_status![0].code).toBe(
181+
"signingCredential.untrusted",
182+
);
180183
expect(manifestStore.active_manifest).not.toBeUndefined();
181184
expect(activeManifest?.title).toBe("Test_Manifest_Buffer");
182185
});
136 KB
Loading

0 commit comments

Comments
 (0)