use std::io::Cursor;
use c2pa_macros::c2pa_test_async;
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;
use crate::{
identity::{
claim_aggregation::{IcaSignatureVerifier, IcaValidationError},
tests::fixtures::claim_aggregation::ica_credential_example,
IdentityAssertion, ValidationError,
},
status_tracker::{LogKind, StatusTracker},
Reader,
};
#[c2pa_test_async]
async fn success_case() {
let format = "image/jpeg";
let test_image = include_bytes!("../fixtures/claim_aggregation/ica_validation/success.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
assert!(subject.time_stamp.is_none());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Success);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:71b584f1-da28-4bf7-89a8-417be6bb07ac/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "ICA credential is valid");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.credential_valid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_cose_sign1() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_cose_sign1.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_err = ia.validate(manifest, &mut st, &isv).await.unwrap_err();
assert_eq!(
ica_err,
ValidationError::SignatureError(IcaValidationError::CoseDecodeError(
"extraneous data in CBOR input".to_owned()
))
);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:3572182b-dc6d-4781-a237-f866924d2f47/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid COSE_Sign1 data structure");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(extraneous data in CBOR input)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_cose_sign1"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_cose_sign_alg() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_cose_sign_alg.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_err = ia.validate(manifest, &mut st, &isv).await.unwrap_err();
assert_eq!(
ica_err,
ValidationError::SignatureError(IcaValidationError::UnsupportedSignatureType(
"Assigned(SHA_1)".to_owned()
))
);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:44f2c7e6-66f0-40d9-bbac-49bac24abe65/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid COSE_Sign1 signature algorithm");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(UnsupportedSignatureType(\"Assigned(SHA_1)\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_alg"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn missing_cose_sign_alg() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/missing_cose_sign_alg.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_err = ia.validate(manifest, &mut st, &isv).await.unwrap_err();
assert_eq!(
ica_err,
ValidationError::SignatureError(IcaValidationError::SignatureTypeMissing)
);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:0b13bcdc-4942-4d73-9666-0ea2e9e124aa/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Missing COSE_Sign1 signature algorithm");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(SignatureTypeMissing)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_alg"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_content_type() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_content_type.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:d9286754-694e-44cb-a465-e7016516dade/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid COSE_Sign1 content type header");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(UnsupportedContentType(\"\\\"application/bogus\\\"\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_content_type"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_content_type_assigned() {
let format = "image/jpeg";
let test_image = include_bytes!(
"../fixtures/claim_aggregation/ica_validation/invalid_content_type_assigned.jpg"
);
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:d7a97a73-2508-474b-b4fc-2d273b643e73/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid COSE_Sign1 content type header");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(UnsupportedContentType(\"Assigned(OctetStream)\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_content_type"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn missing_content_type() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/missing_content_type.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:4b29a885-a12b-49e6-83b6-e3701abc6a24/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid COSE_Sign1 content type header");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(ContentTypeMissing)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_content_type"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn missing_vc() {
let format = "image/jpeg";
let test_image = include_bytes!("../fixtures/claim_aggregation/ica_validation/missing_vc.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_err = ia.validate(manifest, &mut st, &isv).await.unwrap_err();
assert_eq!(
ica_err,
ValidationError::SignatureError(IcaValidationError::CredentialPayloadMissing)
);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:2db725ac-fd2a-496c-ab1c-6c0fafe7989d/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Missing COSE_Sign1 payload");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(CredentialPayloadMissing)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_verifiable_credential"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_vc() {
let format = "image/jpeg";
let test_image = include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_vc.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_err = ia.validate(manifest, &mut st, &isv).await.unwrap_err();
assert_eq!(
ica_err,
ValidationError::SignatureError(IcaValidationError::JsonDecodeError(
"expected value at line 1 column 1".to_owned()
))
);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:10a7d93c-b747-4ef5-b734-032d5a3628f7/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid JSON-LD for verifiable credential");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(JsonDecodeError(\"expected value at line 1 column 1\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_verifiable_credential"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_issuer_did() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_issuer_did.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:3bf72495-6f83-4634-be3f-ca8c423e830e/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid issuer DID");
assert_eq!(li.crate_name, "c2pa");
assert!(li
.err_val
.as_ref()
.unwrap()
.starts_with("SignatureError(UnsupportedIssuerDid(\"invalid DID `not-did:jwk:"));
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_issuer"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn unsupported_did_method() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_issuer_did.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:3bf72495-6f83-4634-be3f-ca8c423e830e/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Invalid issuer DID");
assert_eq!(li.crate_name, "c2pa");
assert!(li
.err_val
.as_ref()
.unwrap()
.starts_with("SignatureError(UnsupportedIssuerDid(\"invalid DID `not-did:jwk:"));
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.invalid_issuer"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn unresolvable_did() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/unresolvable_did.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(li.label, "self#jumbf=/c2pa/test:urn:uuid:e3d867e8-c875-4daa-910e-b5ae2b1b45f3/c2pa.assertions/cawg.identity");
assert_eq!(li.description, "Unable to resolve issuer DID");
assert_eq!(li.crate_name, "c2pa");
let err_val = li.err_val.as_ref().unwrap();
assert!(err_val.contains("SignatureError") && err_val.contains("DidResolutionError"));
assert!(err_val.contains("unresolvable-did/did.json"));
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.did_unavailable"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn did_doc_without_assertion_method() {
let format = "image/jpeg";
let test_image = include_bytes!(
"../fixtures/claim_aggregation/ica_validation/did_doc_without_assertion_method.jpg"
);
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(li.label, "self#jumbf=/c2pa/test:urn:uuid:f3fdb6a6-46d3-41f5-ad13-0ff57948347e/c2pa.assertions/cawg.identity");
let valid_description = li.description == "Invalid issuer DID document"
|| li.description == "Unable to resolve issuer DID";
assert!(valid_description);
assert_eq!(li.crate_name, "c2pa");
let err_val = li.err_val.as_ref().unwrap();
assert!(err_val.contains("SignatureError"));
let status: &str = li.validation_status.as_ref().unwrap();
let valid_status =
status == "cawg.ica.invalid_did_document" || status == "cawg.ica.did_unavailable";
assert!(valid_status);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn signature_mismatch() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/signature_mismatch.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:0dcbec68-4952-40d9-bb01-3be603f32a33/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Signature does not match credential");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(li.err_val.as_ref().unwrap(), "SignatureMismatch");
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.signature_mismatch"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn valid_time_stamp() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/valid_time_stamp.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let tst_info = subject.time_stamp.as_ref().unwrap();
assert_eq!(tst_info.gen_time.to_string(), "20250423194523Z");
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Success);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:0e16ab9b-e3e8-425e-a83b-fa2846f178e9/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Time stamp validated");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.time_stamp.validated"
);
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Success);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:0e16ab9b-e3e8-425e-a83b-fa2846f178e9/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "ICA credential is valid");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.credential_valid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn invalid_time_stamp() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/invalid_time_stamp.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
assert!(subject.time_stamp.is_none());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:4caa21a4-0d9c-43ed-aa7b-5dcd4ae20e20/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Time stamp does not match credential");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(InvalidTimeStamp)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.time_stamp.invalid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn valid_from_missing() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/valid_from_missing.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
assert!(ica_vc.valid_from.is_none());
assert!(subject.time_stamp.is_none());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:0a1587c4-b125-4f0d-aeaa-994f10d1f736/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "credential does not have a validFrom date");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(MissingValidFromDate)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.valid_from.missing"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn valid_from_in_future() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/valid_from_in_future.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
assert!(subject.time_stamp.is_none());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:ebec2691-55ae-4255-a116-14e721c0a3cc/c2pa.assertions/cawg.identity"
);
assert_eq!(
li.description,
"credential's validFrom date is unacceptable (validFrom is after current date/time)"
);
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(InvalidValidFromDate(\"validFrom is after current date/time\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.valid_from.invalid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn valid_from_after_time_stamp() {
let format = "image/jpeg";
let test_image = include_bytes!(
"../fixtures/claim_aggregation/ica_validation/valid_from_after_time_stamp.jpg"
);
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
assert!(subject.time_stamp.is_some());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Success);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:8e926af3-e3d4-4945-bcc3-c2680bc50526/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "Time stamp validated");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.time_stamp.validated"
);
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:8e926af3-e3d4-4945-bcc3-c2680bc50526/c2pa.assertions/cawg.identity"
);
assert_eq!(
li.description,
"credential's validFrom date is unacceptable (validFrom is after CAWG signature time stamp)"
);
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(InvalidValidFromDate(\"validFrom is after CAWG signature time stamp\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.valid_from.invalid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn valid_until_in_future() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/valid_until_in_future.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Success);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:13e59d1a-1373-4d18-94ad-3116713ba95a/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "ICA credential is valid");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.credential_valid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn valid_until_in_past() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/valid_until_in_past.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_eq!(subject.c2pa_asset, ia.signer_payload);
assert!(subject.time_stamp.is_none());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:afffd936-e004-4bd0-aad3-7965f8eccb7c/c2pa.assertions/cawg.identity"
);
assert_eq!(
li.description,
"credential's validUntil date is unacceptable (validUntil is before current date/time)"
);
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(InvalidValidUntilDate(\"validUntil is before current date/time\"))"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.valid_until.invalid"
);
assert!(log_items.next().is_none());
}
#[c2pa_test_async]
async fn signer_payload_mismatch() {
let format = "image/jpeg";
let test_image =
include_bytes!("../fixtures/claim_aggregation/ica_validation/signer_payload_mismatch.jpg");
let mut test_image = Cursor::new(test_image);
let reader = Reader::default()
.with_stream(format, &mut test_image)
.unwrap();
assert_eq!(reader.validation_status(), None);
let manifest = reader.active_manifest().unwrap();
let mut st = StatusTracker::default();
let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st);
let ia = ia_iter.next().unwrap().unwrap();
assert!(ia_iter.next().is_none());
drop(ia_iter);
let isv = IcaSignatureVerifier {};
let ica_vc = ia.validate(manifest, &mut st, &isv).await.unwrap();
let expected_identities = ica_credential_example::ica_example_identities();
let subject = ica_vc.credential_subjects.first();
assert_eq!(subject.verified_identities, expected_identities);
assert_ne!(subject.c2pa_asset, ia.signer_payload);
assert!(subject.time_stamp.is_none());
let mut log_items = st.logged_items().iter();
let li = log_items.next().unwrap();
assert_eq!(li.kind, LogKind::Failure);
assert_eq!(
li.label,
"self#jumbf=/c2pa/test:urn:uuid:96f26ecf-c335-4a43-ba4f-55acb5fdcd79/c2pa.assertions/cawg.identity"
);
assert_eq!(li.description, "c2paAsset does not match signer_payload");
assert_eq!(li.crate_name, "c2pa");
assert_eq!(
li.err_val.as_ref().unwrap(),
"SignatureError(SignerPayloadMismatch)"
);
assert_eq!(
li.validation_status.as_ref().unwrap(),
"cawg.ica.signer_payload.mismatch"
);
assert!(log_items.next().is_none());
}