diff --git a/CHANGELOG.md b/CHANGELOG.md index c17255b3a..879097f1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm Since version 0.36.2, the format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.45.3](https://github.com/contentauth/c2pa-rs/compare/c2pa-v0.45.2...c2pa-v0.45.3) +_11 February 2025_ + +### Fixed + +* Restore support for claim_generator_hints (#915) + ## [0.45.2](https://github.com/contentauth/c2pa-rs/compare/c2pa-v0.45.1...c2pa-v0.45.2) _06 February 2025_ diff --git a/Cargo.lock b/Cargo.lock index da4628182..fb31ab0c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,7 +711,7 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "c2pa" -version = "0.45.2" +version = "0.45.3" dependencies = [ "actix", "anyhow", @@ -854,7 +854,7 @@ version = "0.5.0" [[package]] name = "c2patool" -version = "0.13.2" +version = "0.13.3" dependencies = [ "anyhow", "assert_cmd", @@ -881,7 +881,7 @@ dependencies = [ [[package]] name = "cawg-identity" -version = "0.6.0" +version = "0.6.1" dependencies = [ "async-trait", "base64 0.22.1", @@ -914,9 +914,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.12" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -1296,15 +1296,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "data-encoding-macro" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" +checksum = "9f9724adfcf41f45bf652b3995837669d73c4d49a1b5ac1ff82905ac7d9b5558" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1312,9 +1312,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" +checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f" dependencies = [ "data-encoding", "syn 2.0.98", @@ -2914,9 +2914,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" @@ -2952,9 +2952,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.4.1+3.4.0" +version = "300.4.2+3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +checksum = "168ce4e058f975fe43e89d9ccf78ca668601887ae736090aacc23ae353c298e2" dependencies = [ "cc", ] @@ -2996,9 +2996,9 @@ dependencies = [ [[package]] name = "p384" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", @@ -3819,9 +3819,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.22" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "log", "once_cell", @@ -4586,9 +4586,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.7.1", "serde", @@ -5109,9 +5109,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] diff --git a/cawg_identity/CHANGELOG.md b/cawg_identity/CHANGELOG.md index 67511d45d..e826b5671 100644 --- a/cawg_identity/CHANGELOG.md +++ b/cawg_identity/CHANGELOG.md @@ -6,6 +6,13 @@ 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.6.1](https://github.com/contentauth/c2pa-rs/compare/cawg-identity-v0.6.0...cawg-identity-v0.6.1) +_11 February 2025_ + +### Fixed + +* *(cawg_identity)* No-op change to trigger re-release of cawg-identity crate (#918) + ## [0.6.0](https://github.com/contentauth/c2pa-rs/compare/cawg-identity-v0.5.0...cawg-identity-v0.6.0) _30 January 2025_ diff --git a/cawg_identity/Cargo.toml b/cawg_identity/Cargo.toml index f23d10c3f..3926a5e2c 100644 --- a/cawg_identity/Cargo.toml +++ b/cawg_identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cawg-identity" -version = "0.6.0" +version = "0.6.1" description = "Rust SDK for CAWG (Creator Assertions Working Group) identity assertion" authors = [ "Eric Scouten ", @@ -27,7 +27,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] async-trait = "0.1.78" base64 = "0.22.1" -c2pa = { path = "../sdk", version = "0.45.2", features = ["openssl"] } +c2pa = { path = "../sdk", version = "0.45.3", features = ["openssl"] } c2pa-crypto = { path = "../internal/crypto", version = "0.6.2" } c2pa-status-tracker = { path = "../internal/status-tracker", version = "0.5.0" } chrono = { version = "0.4.38", features = ["serde"] } diff --git a/cawg_identity/src/claim_aggregation/ica_credential.rs b/cawg_identity/src/claim_aggregation/ica_credential.rs index 92cae5021..6868f7a8a 100644 --- a/cawg_identity/src/claim_aggregation/ica_credential.rs +++ b/cawg_identity/src/claim_aggregation/ica_credential.rs @@ -11,14 +11,17 @@ // specific language governing permissions and limitations under // each license. +use std::collections::BTreeMap; + use chrono::{DateTime, FixedOffset}; -use iref::{Iri, UriBuf}; +use iref::{Iri, IriBuf, UriBuf}; use non_empty_string::NonEmptyString; use nonempty_collections::NEVec; use serde::{Deserialize, Serialize}; use crate::{ claim_aggregation::w3c_vc::credential::{CredentialV2, VerifiableCredentialSubtype}, + identity_assertion::signature_verifier::ToCredentialSummary, SignerPayload, }; @@ -26,6 +29,14 @@ use crate::{ /// case. pub type IcaCredential = CredentialV2; +impl ToCredentialSummary for IcaCredential { + type CredentialSummary = IcaCredentialSummary; + + fn to_summary(&self) -> Self::CredentialSummary { + IcaCredentialSummary::from_credential(self) + } +} + /// Identity claims aggregation context IRI. pub const IDENTITY_CLAIMS_AGGREGATION_CONTEXT_IRI: &Iri = static_iref::iri!("https://creator-assertions.github.io/tbd/tbd"); @@ -175,3 +186,53 @@ pub struct IdentityProvider { /// is the user-visible name of the _identity provider._ pub name: NonEmptyString, } + +#[doc(hidden)] +#[derive(Serialize)] +pub struct IcaCredentialSummary { + #[serde(rename = "@context")] + contexts: NEVec, + + #[serde( + default, + deserialize_with = "not_null", + skip_serializing_if = "Option::is_none" + )] + id: Option, + + #[serde(rename = "type")] + types: NEVec, + + issuer: UriBuf, + + #[serde(rename = "validFrom")] + #[serde(default, skip_serializing_if = "Option::is_none")] + valid_from: Option>, + + #[serde(rename = "validUntil")] + #[serde(default, skip_serializing_if = "Option::is_none")] + valid_until: Option>, + + #[serde(rename = "verifiedIdentities")] + verified_identities: NEVec, + + #[serde(flatten)] + extra_properties: BTreeMap, +} + +impl IcaCredentialSummary { + fn from_credential(ica: &IcaCredential) -> Self { + let subject = ica.credential_subjects.first(); + + Self { + contexts: ica.contexts.clone(), + id: ica.id.clone(), + issuer: ica.issuer.clone(), + types: ica.types.clone(), + valid_from: ica.valid_from, + valid_until: ica.valid_until, + verified_identities: subject.verified_identities.clone(), + extra_properties: ica.extra_properties.clone(), + } + } +} diff --git a/cawg_identity/src/claim_aggregation/w3c_vc/serialization.rs b/cawg_identity/src/claim_aggregation/w3c_vc/serialization.rs index 2ea80288d..21fe2a5dd 100644 --- a/cawg_identity/src/claim_aggregation/w3c_vc/serialization.rs +++ b/cawg_identity/src/claim_aggregation/w3c_vc/serialization.rs @@ -57,8 +57,6 @@ pub(crate) mod one_or_many { where M: de::MapAccess<'de>, { - eprintln!("Yo!"); - let one = Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?; Ok(nev!(one)) diff --git a/cawg_identity/src/identity_assertion/assertion.rs b/cawg_identity/src/identity_assertion/assertion.rs index 9acd05030..0928d9e3a 100644 --- a/cawg_identity/src/identity_assertion/assertion.rs +++ b/cawg_identity/src/identity_assertion/assertion.rs @@ -18,8 +18,12 @@ use serde::{Deserialize, Serialize}; use serde_bytes::ByteBuf; use crate::{ - identity_assertion::signer_payload::SignerPayload, internal::debug_byte_slice::DebugByteSlice, - SignatureVerifier, ValidationError, + identity_assertion::{ + report::{IdentityAssertionReport, IdentityAssertionsForManifest, SignerPayloadReport}, + signer_payload::SignerPayload, + }, + internal::debug_byte_slice::DebugByteSlice, + SignatureVerifier, ToCredentialSummary, ValidationError, }; /// This struct represents the raw content of the identity assertion. @@ -63,6 +67,81 @@ impl IdentityAssertion { .map(|a| a.to_assertion()) } + /// Create a summary report from this `IdentityAssertion`. + /// + /// This will [`validate`] the assertion and then render the result as + /// an opaque [`Serialize`]-able struct that describes the decoded content + /// of the identity assertion. + /// + /// [`validate`]: Self::validate + pub async fn to_summary( + &self, + manifest: &Manifest, + verifier: &SV, + ) -> impl Serialize + where + ::Output: 'static, + { + self.to_summary_impl(manifest, verifier).await + } + + pub(crate) async fn to_summary_impl( + &self, + manifest: &Manifest, + verifier: &SV, + ) -> IdentityAssertionReport< + <::Output as ToCredentialSummary>::CredentialSummary, + > + where + ::Output: 'static, + { + match self.validate(manifest, verifier).await { + Ok(named_actor) => { + let summary = named_actor.to_summary(); + + IdentityAssertionReport { + signer_payload: SignerPayloadReport::from_signer_payload(&self.signer_payload), + named_actor: Some(summary), + } + } + + Err(_err) => { + todo!("Handle summary report for failure case"); + } + } + } + + /// Summarize all of the identity assertions found for a [`Manifest`]. + pub async fn summarize_all( + manifest: &Manifest, + verifier: &SV, + ) -> impl Serialize { + // NOTE: We can't write this using .map(...).collect() because there are async + // calls. + let mut reports: Vec< + IdentityAssertionReport< + <::Output as ToCredentialSummary>::CredentialSummary, + >, + > = vec![]; + + for assertion in Self::from_manifest(manifest) { + let report = match assertion { + Ok(assertion) => assertion.to_summary_impl(manifest, verifier).await, + Err(_) => { + todo!("Handle assertion failed to parse case"); + } + }; + + reports.push(report); + } + + IdentityAssertionsForManifest::< + <::Output as ToCredentialSummary>::CredentialSummary, + > { + assertion_reports: reports, + } + } + /// Using the provided [`SignatureVerifier`], check the validity of this /// identity assertion. /// diff --git a/cawg_identity/src/identity_assertion/mod.rs b/cawg_identity/src/identity_assertion/mod.rs index f77284288..b861bf8ae 100644 --- a/cawg_identity/src/identity_assertion/mod.rs +++ b/cawg_identity/src/identity_assertion/mod.rs @@ -12,6 +12,7 @@ // each license. pub(crate) mod assertion; +pub(crate) mod report; pub(crate) mod signature_verifier; pub(crate) mod signer_payload; pub(crate) mod validation_error; diff --git a/cawg_identity/src/identity_assertion/report.rs b/cawg_identity/src/identity_assertion/report.rs new file mode 100644 index 000000000..98f165572 --- /dev/null +++ b/cawg_identity/src/identity_assertion/report.rs @@ -0,0 +1,65 @@ +// Copyright 2025 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use serde::{ser::SerializeSeq, Serialize}; + +use crate::identity_assertion::signer_payload::SignerPayload; + +#[doc(hidden)] +pub struct IdentityAssertionsForManifest { + pub(crate) assertion_reports: Vec>, +} + +impl Serialize for IdentityAssertionsForManifest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.assertion_reports.len()))?; + for report in self.assertion_reports.iter() { + seq.serialize_element(report)?; + } + seq.end() + } +} + +#[doc(hidden)] +#[derive(Serialize)] +pub struct IdentityAssertionReport { + #[serde(flatten)] + pub(crate) signer_payload: SignerPayloadReport, + + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) named_actor: Option, +} + +#[derive(Serialize)] +pub(crate) struct SignerPayloadReport { + sig_type: String, + referenced_assertions: Vec, + // TO DO: Add role and expected_* fields. + // (https://github.com/contentauth/c2pa-rs/issues/816) +} + +impl SignerPayloadReport { + pub(crate) fn from_signer_payload(sp: &SignerPayload) -> Self { + Self { + referenced_assertions: sp + .referenced_assertions + .iter() + .map(|a| a.url().replace("self#jumbf=c2pa.assertions/", "")) + .collect(), + sig_type: sp.sig_type.clone(), + } + } +} diff --git a/cawg_identity/src/identity_assertion/signature_verifier.rs b/cawg_identity/src/identity_assertion/signature_verifier.rs index 44d9fac2d..bac866da6 100644 --- a/cawg_identity/src/identity_assertion/signature_verifier.rs +++ b/cawg_identity/src/identity_assertion/signature_verifier.rs @@ -12,6 +12,7 @@ // each license. use async_trait::async_trait; +use serde::Serialize; use crate::{SignerPayload, ValidationError}; @@ -28,7 +29,7 @@ pub trait SignatureVerifier: Sync { /// derived from the signature. Typically, this describes the named actor, /// but may also contain information about the time of signing or the /// credential's source. - type Output; + type Output: ToCredentialSummary + 'static; /// The `Error` type provides a credential-specific explanation for why an /// identity assertion signature could not be accepted. This value may be @@ -61,7 +62,7 @@ pub trait SignatureVerifier { /// derived from the signature. Typically, this describes the named actor, /// but may also contain information about the time of signing or the /// credential's source. - type Output; + type Output: ToCredentialSummary + 'static; /// The `Error` type provides a credential-specific explanation for why an /// identity assertion signature could not be accepted. This value may be @@ -80,3 +81,23 @@ pub trait SignatureVerifier { signature: &[u8], ) -> Result>; } + +/// The `Output` result type from [`SignatureVerifier::check_signature`] may be +/// called upon to summarize its contents in a form suitable for JSON or similar +/// serialization. +/// +/// This report is kept separate from any [`Serialize`] implementation +/// because that original credential type may have a native serialization that +/// is not suitable for summarizaton. +/// +/// This trait allows the credential type to reshape its output into a suitable +/// summary form. +pub trait ToCredentialSummary { + /// A `CredentialSummary` is a serializable type that describes the + /// credential in JSON or similar format. + type CredentialSummary: Serialize; + + /// Convert the underlying credential type into a summary type which can be + /// serialized for JSON or similar reporting format. + fn to_summary(&self) -> Self::CredentialSummary; +} diff --git a/cawg_identity/src/internal/debug_byte_slice.rs b/cawg_identity/src/internal/debug_byte_slice.rs index 29cf86cf9..a29624166 100644 --- a/cawg_identity/src/internal/debug_byte_slice.rs +++ b/cawg_identity/src/internal/debug_byte_slice.rs @@ -11,8 +11,6 @@ // specific language governing permissions and limitations under // each license. -#![allow(unused)] // TEMPORARY while updating ... - use std::fmt::{Debug, Error, Formatter}; pub(crate) struct DebugByteSlice<'a>(pub(crate) &'a [u8]); diff --git a/cawg_identity/src/lib.rs b/cawg_identity/src/lib.rs index 33fa27eba..1528ec882 100644 --- a/cawg_identity/src/lib.rs +++ b/cawg_identity/src/lib.rs @@ -23,8 +23,10 @@ pub mod claim_aggregation; mod identity_assertion; pub use identity_assertion::{ - assertion::IdentityAssertion, signature_verifier::SignatureVerifier, - signer_payload::SignerPayload, validation_error::ValidationError, + assertion::IdentityAssertion, + signature_verifier::{SignatureVerifier, ToCredentialSummary}, + signer_payload::SignerPayload, + validation_error::ValidationError, }; pub(crate) mod internal; diff --git a/cawg_identity/src/tests/builder/simple_case.rs b/cawg_identity/src/tests/builder/simple_case.rs index 7d78a6b91..d6d5dfb00 100644 --- a/cawg_identity/src/tests/builder/simple_case.rs +++ b/cawg_identity/src/tests/builder/simple_case.rs @@ -24,7 +24,7 @@ use crate::{ IdentityAssertionSigner, }, tests::fixtures::{NaiveAsyncCredentialHolder, NaiveCredentialHolder, NaiveSignatureVerifier}, - IdentityAssertion, + IdentityAssertion, ToCredentialSummary, }; const TEST_IMAGE: &[u8] = include_bytes!("../../../../sdk/tests/fixtures/CA.jpg"); @@ -76,7 +76,11 @@ async fn simple_case() { // And that identity assertion should be valid for this manifest. let nsv = NaiveSignatureVerifier {}; - ia.validate(manifest, &nsv).await.unwrap(); + let naive_credential = ia.validate(manifest, &nsv).await.unwrap(); + + let nc_summary = naive_credential.to_summary(); + let nc_json = serde_json::to_string(&nc_summary).unwrap(); + assert_eq!(nc_json, "{}"); } #[cfg_attr(not(target_arch = "wasm32"), tokio::test)] @@ -123,7 +127,11 @@ async fn simple_case_async() { // And that identity assertion should be valid for this manifest. let nsv = NaiveSignatureVerifier {}; - ia.validate(manifest, &nsv).await.unwrap(); + let naive_credential = ia.validate(manifest, &nsv).await.unwrap(); + + let nc_summary = naive_credential.to_summary(); + let nc_json = serde_json::to_string(&nc_summary).unwrap(); + assert_eq!(nc_json, "{}"); } fn manifest_json() -> String { diff --git a/cawg_identity/src/tests/claim_aggregation/interop.rs b/cawg_identity/src/tests/claim_aggregation/interop.rs index 64ed994f1..c6dd74687 100644 --- a/cawg_identity/src/tests/claim_aggregation/interop.rs +++ b/cawg_identity/src/tests/claim_aggregation/interop.rs @@ -81,4 +81,13 @@ async fn adobe_connected_identities() { sig_type: "cawg.identity_claims_aggregation".to_owned(), } ); + + // 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(); + + assert_eq!( + ia_json, + 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"}]}}]"# + ); } diff --git a/cawg_identity/src/tests/fixtures/naive_credential_holder.rs b/cawg_identity/src/tests/fixtures/naive_credential_holder.rs index 3a6d29ed5..d37e89e84 100644 --- a/cawg_identity/src/tests/fixtures/naive_credential_holder.rs +++ b/cawg_identity/src/tests/fixtures/naive_credential_holder.rs @@ -23,9 +23,11 @@ use std::fmt::Debug; use async_trait::async_trait; +use serde::Serialize; use crate::{ builder::{AsyncCredentialHolder, CredentialHolder, IdentityBuilderError}, + identity_assertion::signature_verifier::ToCredentialSummary, SignatureVerifier, SignerPayload, ValidationError, }; @@ -78,7 +80,7 @@ pub(crate) struct NaiveSignatureVerifier {} #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl SignatureVerifier for NaiveSignatureVerifier { type Error = (); - type Output = (); + type Output = NaiveCredential; async fn check_signature( &self, @@ -92,7 +94,21 @@ impl SignatureVerifier for NaiveSignatureVerifier { if signer_payload_cbor != signature { Err(ValidationError::InvalidSignature) } else { - Ok(()) + Ok(NaiveCredential {}) } } } + +pub(crate) struct NaiveCredential {} + +impl ToCredentialSummary for NaiveCredential { + type CredentialSummary = NaiveCredentialSummary; + + fn to_summary(&self) -> Self::CredentialSummary { + NaiveCredentialSummary {} + } +} + +#[doc(hidden)] +#[derive(Serialize)] +pub struct NaiveCredentialSummary {} diff --git a/cawg_identity/src/x509/x509_signature_verifier.rs b/cawg_identity/src/x509/x509_signature_verifier.rs index fdfacbe11..d90120cd6 100644 --- a/cawg_identity/src/x509/x509_signature_verifier.rs +++ b/cawg_identity/src/x509/x509_signature_verifier.rs @@ -18,8 +18,12 @@ use c2pa_crypto::{ }; use c2pa_status_tracker::DetailedStatusTracker; use coset::CoseSign1; +use serde::Serialize; -use crate::{SignatureVerifier, SignerPayload, ValidationError}; +use crate::{ + identity_assertion::signature_verifier::ToCredentialSummary, SignatureVerifier, SignerPayload, + ValidationError, +}; /// An implementation of [`SignatureVerifier`] that supports COSE signatures /// generated from X.509 credentials as specified in [ยง8.2, X.509 certificates @@ -85,3 +89,24 @@ pub struct X509SignatureInfo { /// Information about the X.509 certificate chain. pub cert_info: CertificateInfo, } + +impl ToCredentialSummary for X509SignatureInfo { + type CredentialSummary = X509SignatureReport; + + fn to_summary(&self) -> Self::CredentialSummary { + X509SignatureReport {} + } +} + +// #[derive(Serialize)] <- uncomment once the type is populated +#[doc(hidden)] +pub struct X509SignatureReport {} + +impl Serialize for X509SignatureReport { + fn serialize(&self, _serializer: S) -> Result + where + S: serde::Serializer, + { + todo!("X509SignatureReport type not defined yet"); + } +} diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index c3325f8ab..e70bdb4cc 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -6,6 +6,13 @@ This project adheres to [Semantic Versioning](https://semver.org), except that Since version 0.10.0, the format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.13.3](https://github.com/contentauth/c2pa-rs/compare/c2patool-v0.13.1...c2patool-v0.13.3) +_11 February 2025_ + +### Fixed + +* Trigger a release of c2patool to pick up latest c2pa-rs changes + ## [0.13.1](https://github.com/contentauth/c2pa-rs/compare/c2patool-v0.13.0...c2patool-v0.13.1) _31 January 2025_ diff --git a/cli/Cargo.toml b/cli/Cargo.toml index da47633d4..8a1568e3e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "c2patool" default-run = "c2patool" -version = "0.13.2" +version = "0.13.3" description = "Tool for displaying and creating C2PA manifests." authors = [ "Gavin Peacock ", @@ -22,7 +22,7 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(test)'] } [dependencies] anyhow = "1.0" atree = "0.5.2" -c2pa = { path = "../sdk", version = "0.45.2", features = [ +c2pa = { path = "../sdk", version = "0.45.3", features = [ "fetch_remote_manifests", "file_io", "add_thumbnails", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index ead939a62..f99544a35 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c2pa" -version = "0.45.2" +version = "0.45.3" description = "Rust SDK for C2PA (Coalition for Content Provenance and Authenticity) implementors" authors = [ "Maurice Fisher ", diff --git a/sdk/src/claim.rs b/sdk/src/claim.rs index e6e238bc2..5e5c181f9 100644 --- a/sdk/src/claim.rs +++ b/sdk/src/claim.rs @@ -206,6 +206,7 @@ impl fmt::Debug for ClaimAssertion { // Claim field names const CLAIM_GENERATOR_F: &str = "claim_generator"; const CLAIM_GENERATOR_INFO_F: &str = "claim_generator_info"; +const CLAIM_GENERATOR_HINTS_F: &str = "claim_generator_hints"; const SIGNATURE_F: &str = "signature"; const ASSERTIONS_F: &str = "assertions"; const DC_FORMAT_F: &str = "dc:format"; @@ -495,6 +496,7 @@ impl Claim { if claim_version == 1 { /* Claim V1 fields "claim_generator": tstr, + "claim_generator_hints", "claim_generator_info": [1* generator-info-map], "signature": jumbf-uri-type, "assertions": [1* $hashed-uri-map], @@ -507,8 +509,9 @@ impl Claim { ? "metadata": $assertion-metadata-map, */ - static V1_FIELDS: [&str; 11] = [ + static V1_FIELDS: [&str; 12] = [ CLAIM_GENERATOR_F, + CLAIM_GENERATOR_HINTS_F, CLAIM_GENERATOR_INFO_F, SIGNATURE_F, ASSERTIONS_F, @@ -535,9 +538,6 @@ impl Claim { let claim_generator: String = map_cbor_to_type(CLAIM_GENERATOR_F, &claim_value).ok_or(Error::ClaimDecoding)?; - let claim_generator_info: Vec = - map_cbor_to_type(CLAIM_GENERATOR_INFO_F, &claim_value).unwrap_or_default(); - let signature: String = map_cbor_to_type(SIGNATURE_F, &claim_value).ok_or(Error::ClaimDecoding)?; let assertions: Vec = @@ -548,6 +548,10 @@ impl Claim { map_cbor_to_type(INSTANCE_ID_F, &claim_value).ok_or(Error::ClaimDecoding)?; // optional V1 fields + let claim_generator_info: Option> = + map_cbor_to_type(CLAIM_GENERATOR_INFO_F, &claim_value); + let claim_generator_hints: Option> = + map_cbor_to_type(CLAIM_GENERATOR_HINTS_F, &claim_value); let title: Option = map_cbor_to_type(DC_TITLE_F, &claim_value); let redacted_assertions: Option> = map_cbor_to_type(REDACTED_ASSERTIONS_F, &claim_value); @@ -568,7 +572,7 @@ impl Claim { assertion_store: Vec::new(), vc_store: Vec::new(), claim_generator: Some(claim_generator), - claim_generator_info: Some(claim_generator_info), + claim_generator_info, signature, assertions, original_bytes: Some(data.to_owned()), @@ -576,7 +580,7 @@ impl Claim { redacted_assertions, alg, alg_soft, - claim_generator_hints: None, + claim_generator_hints, metadata, data_boxes: Vec::new(), claim_version,