Skip to content

Commit faf5a02

Browse files
fix: Add example file with CAWG X.509 signing (#948)
1 parent 85f97b5 commit faf5a02

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2025 Adobe. All rights reserved.
2+
// This file is licensed to you under the Apache License,
3+
// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
4+
// or the MIT license (http://opensource.org/licenses/MIT),
5+
// at your option.
6+
7+
// Unless required by applicable law or agreed to in writing,
8+
// this software is distributed on an "AS IS" BASIS, WITHOUT
9+
// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
10+
// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
11+
// specific language governing permissions and limitations under
12+
// each license.
13+
14+
#[cfg(not(target_arch = "wasm32"))]
15+
mod x509_signing;
Loading
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2025 Adobe. All rights reserved.
2+
// This file is licensed to you under the Apache License,
3+
// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
4+
// or the MIT license (http://opensource.org/licenses/MIT),
5+
// at your option.
6+
7+
// Unless required by applicable law or agreed to in writing,
8+
// this software is distributed on an "AS IS" BASIS, WITHOUT
9+
// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
10+
// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
11+
// specific language governing permissions and limitations under
12+
// each license.
13+
14+
//! Test case that demonstrates generating a new C2PA asset (JPEG in this case)
15+
//! with a C2PA claim signer signature and a CAWG identity assertion with its
16+
//! own X.509 signature from a different credential holder.
17+
18+
use std::io::{Cursor, Seek};
19+
20+
use c2pa::{Builder, Reader, SigningAlg};
21+
use c2pa_crypto::raw_signature;
22+
23+
use crate::{
24+
builder::{AsyncIdentityAssertionBuilder, AsyncIdentityAssertionSigner},
25+
tests::fixtures::{cert_chain_and_private_key_for_alg, manifest_json, parent_json},
26+
x509::{X509CredentialHolder, X509SignatureVerifier},
27+
IdentityAssertion,
28+
};
29+
30+
const TEST_IMAGE: &[u8] = include_bytes!("../../../../sdk/tests/fixtures/CA.jpg");
31+
const TEST_THUMBNAIL: &[u8] = include_bytes!("../../../../sdk/tests/fixtures/thumbnail.jpg");
32+
33+
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
34+
#[ignore] // We'll only run this occasionally if we need to update this test.
35+
async fn x509_signing() {
36+
let format = "image/jpeg";
37+
let mut source = Cursor::new(TEST_IMAGE);
38+
let mut dest = Cursor::new(Vec::new());
39+
40+
let mut builder = Builder::from_json(&manifest_json()).unwrap();
41+
builder
42+
.add_ingredient_from_stream(parent_json(), format, &mut source)
43+
.unwrap();
44+
45+
builder
46+
.add_resource("thumbnail.jpg", Cursor::new(TEST_THUMBNAIL))
47+
.unwrap();
48+
49+
let mut c2pa_signer = AsyncIdentityAssertionSigner::from_test_credentials(SigningAlg::Ps256);
50+
51+
let (cawg_cert_chain, cawg_private_key) =
52+
cert_chain_and_private_key_for_alg(SigningAlg::Ed25519);
53+
54+
let cawg_raw_signer = raw_signature::async_signer_from_cert_chain_and_private_key(
55+
&cawg_cert_chain,
56+
&cawg_private_key,
57+
SigningAlg::Ed25519,
58+
None,
59+
)
60+
.unwrap();
61+
62+
let x509_holder = X509CredentialHolder::from_async_raw_signer(cawg_raw_signer);
63+
let iab = AsyncIdentityAssertionBuilder::for_credential_holder(x509_holder);
64+
c2pa_signer.add_identity_assertion(iab);
65+
66+
builder
67+
.sign_async(&c2pa_signer, format, &mut source, &mut dest)
68+
.await
69+
.unwrap();
70+
71+
// Write the sample file.
72+
std::fs::write("src/tests/examples/x509_signing.jpg", dest.get_ref()).unwrap();
73+
74+
// --- THE REST OF THIS EXAMPLE IS TEST CODE ONLY. ---
75+
//
76+
// The following code reads back the content from the file that was just
77+
// generated and verifies that it is valid.
78+
//
79+
// In a normal scenario when generating an asset with a CAWG identity assertion,
80+
// you could stop at this point.
81+
82+
dest.rewind().unwrap();
83+
84+
let manifest_store = Reader::from_stream(format, &mut dest).unwrap();
85+
assert_eq!(manifest_store.validation_status(), None);
86+
87+
let manifest = manifest_store.active_manifest().unwrap();
88+
let mut ia_iter = IdentityAssertion::from_manifest(manifest);
89+
90+
let ia = ia_iter.next().unwrap().unwrap();
91+
assert!(ia_iter.next().is_none());
92+
93+
let x509_verifier = X509SignatureVerifier {};
94+
let sig_info = ia.validate(manifest, &x509_verifier).await.unwrap();
95+
96+
let cert_info = &sig_info.cert_info;
97+
assert_eq!(cert_info.alg.unwrap(), SigningAlg::Ed25519);
98+
assert_eq!(
99+
cert_info.issuer_org.as_ref().unwrap(),
100+
"C2PA Test Signing Cert"
101+
);
102+
}

cawg_identity/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
mod builder;
2222
mod claim_aggregation;
23+
mod examples;
2324
pub(crate) mod fixtures;
2425
mod identity_assertion;
2526
mod internal;

0 commit comments

Comments
 (0)