Skip to content

Commit

Permalink
Merge branch 'main' into wasm32-wasi_0.41.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cdmurph32 authored Feb 14, 2025
2 parents fca1c33 + 1cda7bd commit e2cf2fb
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 15 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions cawg_identity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

The format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [0.8.0](https://github.com/contentauth/c2pa-rs/compare/cawg-identity-v0.7.0...cawg-identity-v0.8.0)
_12 February 2025_

### Added

* *(cawg_identity)* Add new functions for generating a `Serialize`-able report for entire manifest store (#920)

## [0.7.0](https://github.com/contentauth/c2pa-rs/compare/cawg-identity-v0.6.1...cawg-identity-v0.7.0)
_11 February 2025_

### Added

* *(cawg_identity)* Add `IdentityAssertion::to_summary` and `IdentityAssertion::summarize_all` (#913)

## [0.6.1](https://github.com/contentauth/c2pa-rs/compare/cawg-identity-v0.6.0...cawg-identity-v0.6.1)
_11 February 2025_

Expand Down
5 changes: 4 additions & 1 deletion cawg_identity/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cawg-identity"
version = "0.6.1"
version = "0.8.0"
description = "Rust SDK for CAWG (Creator Assertions Working Group) identity assertion"
authors = [
"Eric Scouten <[email protected]>",
Expand All @@ -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::BTreeMap,
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 @@ impl IdentityAssertion {
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 @@ impl IdentityAssertion {
}
}

/// 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: BTreeMap<
String,
IdentityAssertionsForManifest<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
>,
> = BTreeMap::new();

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

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

/// 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: BTreeMap<
String,
IdentityAssertionsForManifest<
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
>,
> = BTreeMap::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::BTreeMap;

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: BTreeMap<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
37 changes: 34 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 All @@ -91,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"}]}}]}"#
);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e2cf2fb

Please sign in to comment.