Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update c2patool to read CAWG data #907

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b1990a3
feat: c2patool to read cawg data (#904)
tmathern Feb 6, 2025
3521730
ci: Refactor
tmathern Feb 6, 2025
9af655d
ci: Refactor
tmathern Feb 6, 2025
7ed719c
ci: Refactor
tmathern Feb 6, 2025
a18e1f9
ci: Refactor and rerun tests
tmathern Feb 6, 2025
cb389d5
ci: Refactor
tmathern Feb 6, 2025
e1b268e
ci: Solidify error handling
tmathern Feb 6, 2025
cc6e7bd
ci: Add test
tmathern Feb 6, 2025
aba1ef6
ci: Merge branch 'main' into mathern/cawg-reading-trois
tmathern Feb 6, 2025
8a2ead2
ci: Prepare for detailed view
tmathern Feb 6, 2025
dd70c83
ci: Retrigger
tmathern Feb 6, 2025
a77004e
ci: Remove debug files
tmathern Feb 6, 2025
13875f4
ci: Review comment 1
tmathern Feb 7, 2025
3b45088
ci: Review comment 2
tmathern Feb 7, 2025
49adfd2
ci: Retrigger checks
tmathern Feb 7, 2025
151e312
ci: Retrigger checks
tmathern Feb 7, 2025
928b07b
ci: Merge branch 'main' into mathern/cawg-reading-trois
tmathern Feb 7, 2025
153be98
ci: Cargo lock too
tmathern Feb 7, 2025
c32ec31
Errors go to stdout
tmathern Feb 7, 2025
aba8668
feat: c2patool to read cawg data with `--detailed` flag too (#909)
tmathern Feb 7, 2025
85201a7
feat: Move cawg parsing for c2patool into own file
tmathern Feb 7, 2025
077a285
fix: Overhaul of error handling
tmathern Feb 7, 2025
b9c5c7a
fix: Update error handling for non detailed view too
tmathern Feb 7, 2025
5cffc27
fix: Format
tmathern Feb 7, 2025
ea92320
ci: Clarify comment
tmathern Feb 7, 2025
b8ad835
fix: Refactor
tmathern Feb 7, 2025
346f040
ci: Format
tmathern Feb 7, 2025
dc48058
fix: Add test
tmathern Feb 7, 2025
2a7d66d
fix: Add test
tmathern Feb 7, 2025
7d2418a
ci: Some more refactor
tmathern Feb 7, 2025
1c262b2
fix: Clarify comments
tmathern Feb 7, 2025
772570a
ci: Refactor
tmathern Feb 7, 2025
18f1a0d
ci: Refactor
tmathern Feb 7, 2025
7b74080
fix: No need to process stuff we don't use
tmathern Feb 7, 2025
9a12f0f
fix: Remove debug code
tmathern Feb 7, 2025
2b7e13d
fix: Remove some more stuff
tmathern Feb 7, 2025
fdd1906
ci: Undo new error
tmathern Feb 7, 2025
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
4 changes: 3 additions & 1 deletion Cargo.lock

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

2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ test-wasm:
test-wasm-web:
cd sdk && wasm-pack test --chrome --headless -- --features="serialize_thumbnails"

test-no-wasm: check-format check-docs clippy test-local

# Full local validation, build and test all features including wasm
# Run this before pushing a PR to pre-validate
test: check-format check-docs clippy test-local test-wasm-web
Expand Down
2 changes: 0 additions & 2 deletions cawg_identity/src/claim_aggregation/ica_signature_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,6 @@ impl SignatureVerifier for IcaSignatureVerifier {
));
};

dbg!(&jwk_prop);

// OMG SO HACKY!
let Ok(jwk_json) = serde_json::to_string_pretty(jwk_prop) else {
return Err(ValidationError::SignatureError(
Expand Down
2 changes: 0 additions & 2 deletions cawg_identity/src/claim_aggregation/w3c_vc/did_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ pub(crate) async fn resolve(did: &Did<'_>) -> Result<DidDocument, DidWebError> {

let method_specific_id = did.method_specific_id();

dbg!(method_specific_id);

let url = to_url(method_specific_id)?;
// TODO: https://w3c-ccg.github.io/did-method-web/#in-transit-security

Expand Down
2 changes: 0 additions & 2 deletions cawg_identity/src/claim_aggregation/w3c_vc/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(test)'] }
[dependencies]
anyhow = "1.0"
atree = "0.5.2"
cawg-identity = { path = "../cawg_identity", version = "0.6.0" }
c2pa = { path = "../sdk", version = "0.45.2", features = [
"fetch_remote_manifests",
"file_io",
Expand All @@ -37,6 +38,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0"
serde_json = "1.0"
tempfile = "3.3"
tokio = { version = "1.42", features = ["full"] }
treeline = "0.1.0"
pem = "3.0.3"
openssl = { version = "0.10.61", features = ["vendored"] }
Expand Down
228 changes: 228 additions & 0 deletions cli/src/decorators/cawg_decorator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// 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 c2pa::Reader;
use cawg_identity::{claim_aggregation::IcaSignatureVerifier, IdentityAssertion};
use serde_json::{Map, Value};
use tokio::runtime::Runtime;

/// Update/decorate the displayed JSON assertions for a more human-readable JSON output.
pub(crate) fn decorate_cawg_assertion_from_detailed_report(
reader: &Reader,
json_content: &mut Value,
tokio_runtime: &Runtime,
) -> Result<(), anyhow::Error> {
if let Value::Object(map) = json_content {
// Iterate over the key-value pairs
for (key, value) in &mut *map {
// Get additional CAWG details

// Get the assertions as array from the JSON
let assertions = match value.get_mut("assertion_store") {
Some(assertions) => assertions,
None => {
return Err(anyhow::Error::msg(
"Could not parse JSON assertions as object",
));
}
};

let cawg_assertion = match assertions.get_mut("cawg.identity") {
Some(cawg_assertion) => cawg_assertion,
None => {
return Err(anyhow::Error::msg(
"Could not parse CAWG identity details from assertion store",
));
}
};

let holding_manifest = match reader.get_manifest(key) {
Some(holding_manifest) => holding_manifest,
None => {
return Err(anyhow::Error::msg(
"Could not recover manifest holding CAWG data",
));
}
};

let parsed_cawg_json_string =
match get_cawg_details_for_manifest(holding_manifest, tokio_runtime) {
Some(parsed_cawg_json_string) => parsed_cawg_json_string,
None => {
// Not a show-stopper:
// Could not parse CAWG details for manifest,
// so leaving original raw data unformatted.
return Ok(());
}
};

cawg_assertion["signature"] = match serde_json::from_str(&parsed_cawg_json_string) {
Ok(decoded_cawg_assertion) => decoded_cawg_assertion,
Err(err) => {
return Err(anyhow::Error::msg(err.to_string()));
}
};

let cawg_assertion = match cawg_assertion.as_object_mut() {
Some(cawg_assertion) => cawg_assertion,
None => {
return Err(anyhow::Error::msg(
"Could not parse CAWG assertion data as object to decorate for display",
));
}
};
cawg_assertion.remove("pad1");
cawg_assertion.remove("pad2");
}
}

Ok(())
}

/// Update/decorate the displayed CAWG assertion for a more human-readable JSON output.
pub(crate) fn decorate_json_cawg_assertions(
holding_manifest: &c2pa::Manifest,
assertion: &mut Value,
tokio_runtime: &Runtime,
) -> Result<(), anyhow::Error> {
let parsed_cawg_json_string =
match get_cawg_details_for_manifest(holding_manifest, tokio_runtime) {
Some(parsed_cawg_json_string) => parsed_cawg_json_string,
None => {
// Could not parse CAWG details for manifest (leaving original raw data unformatted).
// Not a fatal failure, so leaving raw data unformatted.
return Ok(());
}
};

// Let's look at the assertion data
let assertion_data = match assertion.get_mut("data") {
Some(assertion_data) => assertion_data,
None => {
return Err(anyhow::Error::msg("Could not parse CAWG assertion data"));
}
};

// Update signature with parsed content
let parsed_signature = match serde_json::from_str(&parsed_cawg_json_string) {
Ok(parsed_signature) => parsed_signature,
Err(err) => {
return Err(anyhow::Error::msg(err.to_string()));
}
};
assertion_data["signature"] = parsed_signature;

// We don't need to show the padding fields either
let assertion_data_map = match assertion_data.as_object_mut() {
Some(assertion_data_map) => assertion_data_map,
None => {
return Err(anyhow::Error::msg(
"Could not parse CAWG assertion data as object",
));
}
};
assertion_data_map.remove("pad1");
assertion_data_map.remove("pad2");

Ok(())
}

/// Parse additional CAWG details from the manifest store to update displayed results.
/// As CAWG mostly async, this will block on network requests for checks using a tokio runtime.
fn get_cawg_details_for_manifest(
manifest: &c2pa::Manifest,
tokio_runtime: &Runtime,
) -> Option<String> {
let ia_iter = IdentityAssertion::from_manifest(manifest);

// TODO: Determine what should happen when multiple identities are reported (currently only 1 is supported)
let mut parsed_cawg_json = String::new();

ia_iter.for_each(|ia| {
let identity_assertion = match ia {
Ok(ia) => ia,
Err(err) => {
eprintln!("Could not parse CAWG identity assertion: {:?}", err);
return;
}
};

let isv = IcaSignatureVerifier {};
let ica_validated = tokio_runtime.block_on(identity_assertion.validate(manifest, &isv));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to capture the validation results from this operation and add them to the rest of the validation results. Most of this logic belongs in CAWG.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scouten-adobe ^ FYI - for your APIS.

let ica = match ica_validated {
Ok(ica) => ica,
Err(err) => {
eprintln!("Could not validate CAWG identity assertion: {:?}", err);
return;
}
};

parsed_cawg_json = match serde_json::to_string(&ica) {
Ok(parsed_cawg_json) => parsed_cawg_json,
Err(err) => {
eprintln!(
"Could not parse CAWG identity claims aggregation details for manifest: {:?}",
err
);
return;
}
};
});

if parsed_cawg_json.is_empty() {
return None;
}

// Get the JSON as mutable, so we can further parse and format CAWG data
let maybe_map = serde_json::from_str(parsed_cawg_json.as_str());
let mut map: Map<String, Value> = match maybe_map {
Ok(map) => map,
Err(err) => {
eprintln!(
"Could not parse convert CAWG identity claims details to JSON string map: {:?}",
err
);
return None;
}
};

// Get the credentials subject information...
let credentials_subject_maybe = map.get_mut("credentialSubject");
let credentials_subject = match credentials_subject_maybe {
Some(credentials_subject) => credentials_subject,
None => {
eprintln!("Could not find credential subject in CAWG details for manifest");
return None;
}
};
let credentials_subject_as_obj = credentials_subject.as_object_mut();
let credential_subject_details = match credentials_subject_as_obj {
Some(credentials_subject) => credentials_subject,
None => {
eprintln!("Could not parse credential subject as object in CAWG details for manifest");
return None;
}
};
// As per design CAWG has some repetition between assertion an signature (c2paAsset field)
// so we remove the c2paAsset field from the credential subject details too
credential_subject_details.remove("c2paAsset");

// return the for-display json-formatted string
let serialized_content = serde_json::to_string(&map);
match serialized_content {
Ok(serialized_content) => Some(serialized_content),
Err(err) => {
eprintln!("Could not parse CAWG details for manifest: {:?}", err);
None
}
}
}
13 changes: 13 additions & 0 deletions cli/src/decorators/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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.

pub mod cawg_decorator;
Loading