|
| 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 | +} |
0 commit comments