Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cawg_identity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(test)'] }
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[features]
v1_api = ["c2pa/v1_api"]

[dependencies]
async-trait = "0.1.78"
base64 = "0.22.1"
Expand Down
80 changes: 77 additions & 3 deletions cawg_identity/src/identity_assertion/assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@
// specific language governing permissions and limitations under
// each license.

use std::fmt::{Debug, Formatter};
use std::{
collections::HashMap,
fmt::{Debug, Formatter},
};

use c2pa::Manifest;
use c2pa::{Manifest, Reader};
use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;

use crate::{
identity_assertion::{
report::{IdentityAssertionReport, IdentityAssertionsForManifest, SignerPayloadReport},
report::{
IdentityAssertionReport, IdentityAssertionsForManifest,
IdentityAssertionsForManifestStore, SignerPayloadReport,
},
signer_payload::SignerPayload,
},
internal::debug_byte_slice::DebugByteSlice,
Expand Down Expand Up @@ -116,6 +122,15 @@
manifest: &Manifest,
verifier: &SV,
) -> impl Serialize {
Self::summarize_all_impl(manifest, verifier).await
}

pub(crate) async fn summarize_all_impl<SV: SignatureVerifier>(
manifest: &Manifest,
verifier: &SV,
) -> IdentityAssertionsForManifest<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
> {
// NOTE: We can't write this using .map(...).collect() because there are async
// calls.
let mut reports: Vec<
Expand All @@ -142,6 +157,65 @@
}
}

/// Summarize all of the identity assertions found for a [`ManifestStore`].
///
/// [`ManifestStore`]: c2pa::ManifestStore
#[cfg(feature = "v1_api")]
pub async fn summarize_manifest_store<SV: SignatureVerifier>(
store: &c2pa::ManifestStore,
verifier: &SV,
) -> impl Serialize {
// NOTE: We can't write this using .map(...).collect() because there are async
// calls.
let mut reports: HashMap<
String,
IdentityAssertionsForManifest<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
>,
> = HashMap::new();

Check warning on line 175 in cawg_identity/src/identity_assertion/assertion.rs

View check run for this annotation

Codecov / codecov/patch

cawg_identity/src/identity_assertion/assertion.rs#L164-L175

Added lines #L164 - L175 were not covered by tests

for (id, manifest) in store.manifests() {
let report = Self::summarize_all_impl(manifest, verifier).await;
reports.insert(id.clone(), report);

Check warning on line 179 in cawg_identity/src/identity_assertion/assertion.rs

View check run for this annotation

Codecov / codecov/patch

cawg_identity/src/identity_assertion/assertion.rs#L177-L179

Added lines #L177 - L179 were not covered by tests
}

IdentityAssertionsForManifestStore::<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
> {
assertions_for_manifest: reports,
}
}

Check warning on line 187 in cawg_identity/src/identity_assertion/assertion.rs

View check run for this annotation

Codecov / codecov/patch

cawg_identity/src/identity_assertion/assertion.rs#L182-L187

Added lines #L182 - L187 were not covered by tests

/// Summarize all of the identity assertions found for a [`Reader`].
pub async fn summarize_from_reader<SV: SignatureVerifier>(
reader: &Reader,
verifier: &SV,
) -> impl Serialize {
// NOTE: We can't write this using .map(...).collect() because there are async
// calls.
let mut reports: HashMap<
String,
IdentityAssertionsForManifest<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
>,
> = HashMap::new();

for manifest in reader.iter_manifests() {
let report = Self::summarize_all_impl(manifest, verifier).await;

// TO DO: What to do if manifest doesn't have a label?
if let Some(label) = manifest.label() {
reports.insert(label.to_owned(), report);
}
}

IdentityAssertionsForManifestStore::<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
> {
assertions_for_manifest: reports,
}
}

/// Using the provided [`SignatureVerifier`], check the validity of this
/// identity assertion.
///
Expand Down
25 changes: 24 additions & 1 deletion cawg_identity/src/identity_assertion/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,33 @@
// specific language governing permissions and limitations under
// each license.

use serde::{ser::SerializeSeq, Serialize};
use std::collections::HashMap;

use serde::{
ser::{SerializeMap, SerializeSeq},
Serialize,
};

use crate::identity_assertion::signer_payload::SignerPayload;

#[doc(hidden)]
pub struct IdentityAssertionsForManifestStore<IAR: Serialize> {
pub(crate) assertions_for_manifest: HashMap<String, IdentityAssertionsForManifest<IAR>>,
}

impl<IAR: Serialize> Serialize for IdentityAssertionsForManifestStore<IAR> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(self.assertions_for_manifest.len()))?;
for (manifest_id, report) in self.assertions_for_manifest.iter() {
map.serialize_entry(manifest_id, report)?;
}
map.end()
}
}

#[doc(hidden)]
pub struct IdentityAssertionsForManifest<IAR: Serialize> {
pub(crate) assertion_reports: Vec<IdentityAssertionReport<IAR>>,
Expand Down
15 changes: 12 additions & 3 deletions cawg_identity/src/tests/claim_aggregation/interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ async fn adobe_connected_identities() {

let mut test_image = Cursor::new(test_image);

let manifest_store = Reader::from_stream(format, &mut test_image).unwrap();
assert_eq!(manifest_store.validation_status(), None);
let reader = Reader::from_stream(format, &mut test_image).unwrap();
assert_eq!(reader.validation_status(), None);

let manifest = manifest_store.active_manifest().unwrap();
let manifest = reader.active_manifest().unwrap();
let mut ia_iter = IdentityAssertion::from_manifest(manifest);

// Should find exactly one identity assertion.
Expand Down Expand Up @@ -82,6 +82,15 @@ async fn adobe_connected_identities() {
}
);

// Check the summary report for the entire manifest store.
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: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"}]}}]}"#
);

// Check the summary report for this manifest.
let ia_summary = IdentityAssertion::summarize_all(manifest, &isv).await;
let ia_json = serde_json::to_string(&ia_summary).unwrap();
Expand Down