Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
41d19ae
TDD: Test that expects a success code for identity assertion but does…
scouten-adobe Aug 5, 2025
ac8acc1
Sketch in proposed changes for CAWG validation in Reader interface
scouten-adobe Sep 2, 2025
61c2ced
Merge branch 'scouten/cai-9212-identity-assertion-in-reader' into sco…
scouten-adobe Sep 2, 2025
adc1715
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
scouten-adobe Sep 4, 2025
650d954
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
scouten-adobe Sep 11, 2025
d8c04e3
Remove unused import
scouten-adobe Sep 5, 2025
faf671b
Getting back into this project; a little more clarity on what is need…
scouten-adobe Sep 11, 2025
0f90e21
Clean up test noise now that I know where I need to place updates
scouten-adobe Sep 11, 2025
5270494
Hmmm … maybe this doesn't need to be so complicated
scouten-adobe Sep 12, 2025
c7928b8
"Fix" WASI build errors
scouten-adobe Sep 12, 2025
ccb6038
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
gpeacock Sep 18, 2025
25d029f
feat: cawg in native Reader
gpeacock Sep 19, 2025
c87e7f5
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
scouten-adobe Oct 1, 2025
aeab1bc
Reader::from_stream_async should use Store::from_store_async
scouten-adobe Oct 1, 2025
13523aa
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
scouten-adobe Oct 3, 2025
a612b45
Plan to resolve compatibility issues for `IdentityAssertion::from_man…
scouten-adobe Oct 3, 2025
ad03081
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
scouten-adobe Oct 7, 2025
1d4b79f
Add new settings value (core.decode_identity_assertions) which specif…
scouten-adobe Oct 7, 2025
909ba26
Downgrade most IdentityAssertion APIs to pub(crate)
scouten-adobe Oct 7, 2025
5178230
Update CAWG signing example
scouten-adobe Oct 7, 2025
c9df434
TO DO is only relevant if API becomes public again
scouten-adobe Oct 7, 2025
4622847
Retool this test to be properly async
scouten-adobe Oct 7, 2025
316091e
Retool this test to be more sync
scouten-adobe Oct 7, 2025
0ab0e53
Add missing doc links
scouten-adobe Oct 7, 2025
33aab39
Remove commented-out code
scouten-adobe Oct 7, 2025
c51180b
cargo fmt
scouten-adobe Oct 7, 2025
8bebd11
Merge branch 'main' into scouten/cai-9212-add-cawg-to-reader
scouten-adobe Oct 8, 2025
bbebf93
Remove dbg!
scouten-adobe Oct 8, 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
Binary file added cawgi.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 1 addition & 5 deletions sdk/examples/cawg_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ mod cawg {
crypto::raw_signature,
identity::{
builder::{AsyncIdentityAssertionBuilder, AsyncIdentityAssertionSigner},
validator::CawgValidator,
x509::AsyncX509CredentialHolder,
},
AsyncSigner, Builder, Reader, SigningAlg,
Expand Down Expand Up @@ -125,10 +124,7 @@ mod cawg {

builder.sign_file_async(&signer, source, &dest).await?;

let mut reader = Reader::from_file(dest)?;

reader.post_validate_async(&CawgValidator {}).await?;

let reader = Reader::from_file_async(dest).await?;
println!("{reader}");
Ok(())
}
Expand Down
18 changes: 12 additions & 6 deletions sdk/src/identity/identity_assertion/assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,15 @@ pub struct IdentityAssertion {
pub(crate) label: Option<String>,
}

#[allow(unused)] // TEMPORARY while considering API simplification
impl IdentityAssertion {
/// Find the `IdentityAssertion`s that may be present in a given
/// [`Manifest`].
///
/// Iterator returns a [`Result`] because each assertion may fail to parse.
///
/// Aside from CBOR parsing, no further validation is performed.
pub fn from_manifest<'a>(
pub(crate) fn from_manifest<'a>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@emensch @tmathern please review these access changes and LMK if any of these need to remain public.

Copy link
Contributor

Choose a reason for hiding this comment

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

From my point of view the access changes are OK.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can the fields of IdentityAssertion above be public, or can I have a more general constructor? I redefine it in the Node SDK, which is not a good practice.
https://github.com/contentauth/c2pa-node-v2/blob/main/src/neon_identity_assertion_builder.rs#L34

manifest: &'a Manifest,
status_tracker: &'a mut StatusTracker,
) -> impl Iterator<Item = Result<Self, crate::Error>> + use<'a> {
Expand All @@ -93,6 +94,11 @@ impl IdentityAssertion {
ia.label = Some(to_assertion_uri(manifest_label, a.label()));
}
}
// TO DO: Add error readout if the proposed new setting resulted
// in this assertion being parsed and converted to JSON. This function
// has become incompatible with the now-default behavior to validate
// identity assertions during parsing. This applies only if this API
// becomes public again.
(a.label().to_owned(), ia)
})
.inspect(|(label, r)| {
Expand Down Expand Up @@ -120,7 +126,7 @@ impl IdentityAssertion {
/// of the identity assertion.
///
/// [`validate`]: Self::validate
pub async fn to_summary<SV: SignatureVerifier>(
pub(crate) async fn to_summary<SV: SignatureVerifier>(
&self,
manifest: &Manifest,
status_tracker: &mut StatusTracker,
Expand Down Expand Up @@ -161,7 +167,7 @@ impl IdentityAssertion {
}

/// Summarize all of the identity assertions found for a [`Manifest`].
pub async fn summarize_all<SV: SignatureVerifier>(
pub(crate) async fn summarize_all<SV: SignatureVerifier>(
manifest: &Manifest,
status_tracker: &mut StatusTracker,
verifier: &SV,
Expand Down Expand Up @@ -210,7 +216,7 @@ impl IdentityAssertion {
}

/// Summarize all of the identity assertions found for a [`Reader`].
pub async fn summarize_from_reader<SV: SignatureVerifier>(
pub(crate) async fn summarize_from_reader<SV: SignatureVerifier>(
reader: &Reader,
status_tracker: &mut StatusTracker,
verifier: &SV,
Expand Down Expand Up @@ -243,7 +249,7 @@ impl IdentityAssertion {
/// be derived from the signature. This is the [`SignatureVerifier::Output`]
/// type which typically describes the named actor, but may also contain
/// information about the time of signing or the credential's source.
pub async fn validate<SV: SignatureVerifier>(
pub(crate) async fn validate<SV: SignatureVerifier>(
&self,
manifest: &Manifest,
status_tracker: &mut StatusTracker,
Expand Down Expand Up @@ -285,7 +291,7 @@ impl IdentityAssertion {
/// be derived from the signature. This is the [`SignatureVerifier::Output`]
/// type which typically describes the named actor, but may also contain
/// information about the time of signing or the credential's source.
pub async fn validate_partial_claim(
pub(crate) async fn validate_partial_claim(
&self,
partial_claim: &PartialClaim,
status_tracker: &mut StatusTracker,
Expand Down
26 changes: 18 additions & 8 deletions sdk/src/identity/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ mod tests {
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use super::*;
use crate::{Reader, ValidationState};

const CONNECTED_IDENTITIES_VALID: &[u8] =
Expand All @@ -78,9 +77,13 @@ mod tests {
crate::settings::set_settings_value("verify.verify_trust", false).unwrap();

let mut stream = Cursor::new(CONNECTED_IDENTITIES_VALID);
let mut reader = Reader::from_stream("image/jpeg", &mut stream).unwrap();
reader.post_validate_async(&CawgValidator {}).await.unwrap();

let reader = Reader::from_stream_async("image/jpeg", &mut stream)
.await
.unwrap();

//println!("validation results: {}", reader);

assert_eq!(
reader
.validation_results()
Expand All @@ -100,9 +103,13 @@ mod tests {
crate::settings::set_settings_value("verify.verify_trust", false).unwrap();

let mut stream = Cursor::new(MULTIPLE_IDENTITIES_VALID);
let mut reader = Reader::from_stream("image/jpeg", &mut stream).unwrap();
reader.post_validate_async(&CawgValidator {}).await.unwrap();

let reader = Reader::from_stream_async("image/jpeg", &mut stream)
.await
.unwrap();

println!("validation results: {reader}");

assert_eq!(
reader
.validation_results()
Expand All @@ -116,10 +123,13 @@ mod tests {
}

#[c2pa_test_async]
async fn test_post_validate_with_hard_binding_missing() {
async fn test_cawg_validate_with_hard_binding_missing() {
let mut stream = Cursor::new(NO_HARD_BINDING);
let mut reader = Reader::from_stream("image/jpeg", &mut stream).unwrap();
reader.post_validate_async(&CawgValidator {}).await.unwrap();

let reader = Reader::from_stream_async("image/jpeg", &mut stream)
.await
.unwrap();

assert_eq!(
reader
.validation_results()
Expand Down
13 changes: 12 additions & 1 deletion sdk/src/identity/x509/async_x509_credential_holder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ mod tests {
x509::{AsyncX509CredentialHolder, X509SignatureVerifier},
IdentityAssertion,
},
settings::{get_settings_value, set_settings_value},
status_tracker::StatusTracker,
Builder, Reader, SigningAlg,
};
Expand All @@ -125,6 +126,11 @@ mod tests {

#[c2pa_test_async]
async fn simple_case_async() {
let old_decode_identity_assertions =
get_settings_value::<bool>("core.decode_identity_assertions").unwrap_or_default();

set_settings_value("core.decode_identity_assertions", false).unwrap();

let format = "image/jpeg";
let mut source = Cursor::new(TEST_IMAGE);
let mut dest = Cursor::new(Vec::new());
Expand Down Expand Up @@ -164,7 +170,7 @@ mod tests {
// Read back the Manifest that was generated.
dest.rewind().unwrap();

let manifest_store = Reader::from_stream(format, &mut dest).unwrap();
let manifest_store = Reader::from_stream_async(format, &mut dest).await.unwrap();
assert_eq!(manifest_store.validation_status(), None);

let manifest = manifest_store.active_manifest().unwrap();
Expand Down Expand Up @@ -194,5 +200,10 @@ mod tests {
);

// TO DO: Not sure what to check from COSE_Sign1.
set_settings_value(
"core.decode_identity_assertions",
old_decode_identity_assertions,
)
.unwrap();
}
}
12 changes: 12 additions & 0 deletions sdk/src/identity/x509/x509_credential_holder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ mod tests {
x509::{X509CredentialHolder, X509SignatureVerifier},
IdentityAssertion,
},
settings::{get_settings_value, set_settings_value},
status_tracker::StatusTracker,
Builder, Reader, SigningAlg,
};
Expand All @@ -97,6 +98,11 @@ mod tests {

#[c2pa_test_async]
async fn simple_case() {
let old_decode_identity_assertions =
get_settings_value::<bool>("core.decode_identity_assertions").unwrap_or_default();

set_settings_value("core.decode_identity_assertions", false).unwrap();

let format = "image/jpeg";
let mut source = Cursor::new(TEST_IMAGE);
let mut dest = Cursor::new(Vec::new());
Expand Down Expand Up @@ -164,5 +170,11 @@ mod tests {
);

// TO DO: Not sure what to check from COSE_Sign1.

set_settings_value(
"core.decode_identity_assertions",
old_decode_identity_assertions,
)
.unwrap();
}
}
46 changes: 46 additions & 0 deletions sdk/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ use crate::{
crypto::raw_signature::SigningAlg,
error::{Error, Result},
hashed_uri::HashedUri,
identity::IdentityAssertion,
ingredient::Ingredient,
jumbf::labels::{to_absolute_uri, to_assertion_uri},
manifest_assertion::ManifestAssertion,
resource_store::{mime_from_uri, skip_serializing_resources, ResourceRef, ResourceStore},
settings::get_settings_value,
status_tracker::StatusTracker,
store::Store,
ClaimGeneratorInfo, ManifestAssertionKind,
};
Expand Down Expand Up @@ -350,6 +353,7 @@ impl Manifest {
store: &Store,
manifest_label: &str,
options: &mut StoreOptions,
validation_log: &mut StatusTracker,
) -> Result<Self> {
let claim = store
.get_claim(manifest_label)
Expand Down Expand Up @@ -434,6 +438,9 @@ impl Manifest {
})
.collect();

let decode_identity_assertions =
get_settings_value::<bool>("core.decode_identity_assertions").unwrap_or_default();

for assertion in claim.assertions() {
let claim_assertion = match store
.get_claim_assertion_from_uri(&to_absolute_uri(claim.label(), &assertion.url()))
Expand Down Expand Up @@ -535,6 +542,45 @@ impl Manifest {
.set_instance(claim_assertion.instance());
manifest.assertions.push(manifest_assertion);
}
label
if decode_identity_assertions
&& (label == "cawg.identity" || label.starts_with("cawg.identity__")) =>
{
let value = assertion.as_json_object()?;
let mut ma = ManifestAssertion::new(label.to_string(), value)
.set_instance(claim_assertion.instance());

let mut partial_claim = crate::dynamic_assertion::PartialClaim::default();
for a in claim.assertions() {
partial_claim.add_assertion(a);
}

let uri = to_assertion_uri(manifest_label, label);
validation_log.push_current_uri(&uri);
let value: Option<serde_json::Value> = if _sync {
crate::log_item!(
uri,
"decoding identity assertions not supported in sync",
"from_store - validating cawg.identity"
)
.validation_status("cawg.validation_skipped")
.informational(validation_log);
None
} else {
let identity_assertion: IdentityAssertion = ma.to_assertion()?;
identity_assertion
.validate_partial_claim(&partial_claim, validation_log)
.await
.ok()
};
if let Some(v) = value {
//debug!("cawg.identity validation returned: {v}");
ma = ManifestAssertion::new(label.to_string(), v)
.set_instance(claim_assertion.instance());
}
validation_log.pop_current_uri();
manifest.assertions.push(ma);
}
_ => {
// inject assertions for all other assertions
match assertion.decode_data() {
Expand Down
1 change: 1 addition & 0 deletions sdk/src/manifest_store_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ impl ManifestReport {

json = b64_tag(json, "hash");
json = omit_tag(json, "pad");
json = omit_tag(json, "pad1");

json
}
Expand Down
Loading