diff --git a/cawg_identity/src/identity_assertion/assertion.rs b/cawg_identity/src/identity_assertion/assertion.rs index 2371213b9..51fce7ee7 100644 --- a/cawg_identity/src/identity_assertion/assertion.rs +++ b/cawg_identity/src/identity_assertion/assertion.rs @@ -12,7 +12,7 @@ // each license. use std::{ - collections::HashMap, + collections::BTreeMap, fmt::{Debug, Formatter}, }; @@ -167,12 +167,12 @@ impl IdentityAssertion { ) -> impl Serialize { // NOTE: We can't write this using .map(...).collect() because there are async // calls. - let mut reports: HashMap< + let mut reports: BTreeMap< String, IdentityAssertionsForManifest< <::Output as ToCredentialSummary>::CredentialSummary, >, - > = HashMap::new(); + > = BTreeMap::new(); for (id, manifest) in store.manifests() { let report = Self::summarize_all_impl(manifest, verifier).await; @@ -193,12 +193,12 @@ impl IdentityAssertion { ) -> impl Serialize { // NOTE: We can't write this using .map(...).collect() because there are async // calls. - let mut reports: HashMap< + let mut reports: BTreeMap< String, IdentityAssertionsForManifest< <::Output as ToCredentialSummary>::CredentialSummary, >, - > = HashMap::new(); + > = BTreeMap::new(); for manifest in reader.iter_manifests() { let report = Self::summarize_all_impl(manifest, verifier).await; diff --git a/cawg_identity/src/identity_assertion/report.rs b/cawg_identity/src/identity_assertion/report.rs index c9610c628..af4451bd4 100644 --- a/cawg_identity/src/identity_assertion/report.rs +++ b/cawg_identity/src/identity_assertion/report.rs @@ -11,7 +11,7 @@ // specific language governing permissions and limitations under // each license. -use std::collections::HashMap; +use std::collections::BTreeMap; use serde::{ ser::{SerializeMap, SerializeSeq}, @@ -22,7 +22,7 @@ use crate::identity_assertion::signer_payload::SignerPayload; #[doc(hidden)] pub struct IdentityAssertionsForManifestStore { - pub(crate) assertions_for_manifest: HashMap>, + pub(crate) assertions_for_manifest: BTreeMap>, } impl Serialize for IdentityAssertionsForManifestStore { diff --git a/cawg_identity/src/tests/claim_aggregation/interop.rs b/cawg_identity/src/tests/claim_aggregation/interop.rs index d9c99ce5e..50b6df71c 100644 --- a/cawg_identity/src/tests/claim_aggregation/interop.rs +++ b/cawg_identity/src/tests/claim_aggregation/interop.rs @@ -100,3 +100,25 @@ async fn adobe_connected_identities() { r#"[{"sig_type":"cawg.identity_claims_aggregation","referenced_assertions":["c2pa.hash.data"],"named_actor":{"@context":["https://www.w3.org/ns/credentials/v2","https://creator-assertions.github.io/tbd/tbd"],"type":["VerifiableCredential","IdentityClaimsAggregationCredential"],"issuer":"did:web:connected-identities.identity-stage.adobe.com","validFrom":"2024-10-03T21:47:02Z","verifiedIdentities":[{"type":"cawg.social_media","username":"Robert Tiles","uri":"https://net.s2stagehance.com/roberttiles","verifiedAt":"2024-09-24T18:15:11Z","provider":{"id":"https://behance.net","name":"behance"}}],"credentialSchema":[{"id":"https://creator-assertions.github.io/schemas/v1/creator-identity-assertion.json","type":"JSONSchema"}]}}]"# ); } + +#[cfg_attr(not(target_arch = "wasm32"), tokio::test)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +async fn ims_multiple_manifests() { + let format = "image/jpeg"; + let test_image = include_bytes!("../fixtures/claim_aggregation/ims_multiple_manifests.jpg"); + + let mut test_image = Cursor::new(test_image); + + let reader = Reader::from_stream(format, &mut test_image).unwrap(); + assert_eq!(reader.validation_status(), None); + + // Check the summary report for the entire manifest store. + let isv = IcaSignatureVerifier {}; + let ia_summary = IdentityAssertion::summarize_from_reader(&reader, &isv).await; + let ia_json = serde_json::to_string(&ia_summary).unwrap(); + + assert_eq!( + ia_json, + r#"{"urn:uuid:7256ca36-2a90-44ec-914d-f17c8d70c31f":[{"sig_type":"cawg.identity_claims_aggregation","referenced_assertions":["c2pa.hash.data"],"named_actor":{"@context":["https://www.w3.org/ns/credentials/v2","https://creator-assertions.github.io/tbd/tbd"],"type":["VerifiableCredential","IdentityClaimsAggregationCredential"],"issuer":"did:web:connected-identities.identity-stage.adobe.com","validFrom":"2025-02-13T00:40:47Z","verifiedIdentities":[{"type":"cawg.social_media","username":"firstlast555","uri":"https://net.s2stagehance.com/firstlast555","verifiedAt":"2025-01-10T19:53:59Z","provider":{"id":"https://behance.net","name":"behance"}}],"credentialSchema":[{"id":"https://cawg.io/schemas/v1/creator-identity-assertion.json","type":"JSONSchema"}]}}],"urn:uuid:b55062ef-96b6-4f6e-bb7d-9c415f130471":[{"sig_type":"cawg.identity_claims_aggregation","referenced_assertions":["c2pa.hash.data"],"named_actor":{"@context":["https://www.w3.org/ns/credentials/v2","https://creator-assertions.github.io/tbd/tbd"],"type":["VerifiableCredential","IdentityClaimsAggregationCredential"],"issuer":"did:web:connected-identities.identity-stage.adobe.com","validFrom":"2024-10-03T21:47:02Z","verifiedIdentities":[{"type":"cawg.social_media","username":"Robert Tiles","uri":"https://net.s2stagehance.com/roberttiles","verifiedAt":"2024-09-24T18:15:11Z","provider":{"id":"https://behance.net","name":"behance"}}],"credentialSchema":[{"id":"https://creator-assertions.github.io/schemas/v1/creator-identity-assertion.json","type":"JSONSchema"}]}}]}"# + ); +} diff --git a/cawg_identity/src/tests/fixtures/claim_aggregation/ims_multiple_manifests.jpg b/cawg_identity/src/tests/fixtures/claim_aggregation/ims_multiple_manifests.jpg new file mode 100644 index 000000000..725613782 Binary files /dev/null and b/cawg_identity/src/tests/fixtures/claim_aggregation/ims_multiple_manifests.jpg differ