v2_api_integration.rs (5608B)
1 // Copyright 2024 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 /// Complete functional integration test with acquisitions and ingredients. 15 // Isolate from wasm by wrapping in module. 16 #[cfg(not(target_arch = "wasm32"))] // wasm doesn't support ed25519 yet 17 mod integration_v2 { 18 19 use std::io::{Cursor, Seek}; 20 21 use anyhow::Result; 22 use c2pa::{Builder, CallbackSigner, Reader, SigningAlg}; 23 use serde_json::json; 24 25 const PARENT_JSON: &str = r#" 26 { 27 "title": "Parent Test", 28 "format": "image/jpeg", 29 "relationship": "parentOf" 30 } 31 "#; 32 33 const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); 34 const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub"); 35 const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem"); 36 37 fn get_manifest_def(title: &str, format: &str) -> String { 38 json!({ 39 "title": title, 40 "format": format, 41 "claim_generator_info": [ 42 { 43 "name": "c2pa test", 44 "version": env!("CARGO_PKG_VERSION") 45 } 46 ], 47 "thumbnail": { 48 "format": "image/jpeg", 49 "identifier": "manifest_thumbnail.jpg" 50 }, 51 "ingredients": [ 52 { 53 "title": "Test", 54 "format": "image/jpeg", 55 "instance_id": "12345", 56 "relationship": "inputTo" 57 } 58 ], 59 "assertions": [ 60 { 61 "label": "c2pa.actions", 62 "data": { 63 "actions": [ 64 { 65 "action": "c2pa.edited", 66 "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia", 67 "softwareAgent": "Adobe Firefly 0.1.0" 68 } 69 ] 70 } 71 } 72 ] 73 }).to_string() 74 } 75 76 #[test] 77 fn test_v2_integration() -> Result<()> { 78 let title = "CA.jpg"; 79 let format = "image/jpeg"; 80 let mut source = Cursor::new(TEST_IMAGE); 81 82 let json = get_manifest_def(title, format); 83 84 // don't try to verify on wasm since it doesn't support ed25519 yet 85 86 let mut builder = Builder::from_json(&json)?; 87 builder.add_ingredient(PARENT_JSON, format, &mut source)?; 88 89 // add a manifest thumbnail ( just reuse the image for now ) 90 source.rewind()?; 91 builder.add_resource("manifest_thumbnail.jpg", &mut source)?; 92 93 // write the manifest builder to a zipped stream 94 let mut zipped = Cursor::new(Vec::new()); 95 builder.to_archive(&mut zipped)?; 96 97 // write the zipped stream to a file for debugging 98 //let debug_path = format!("{}/../target/test.zip", env!("CARGO_MANIFEST_DIR")); 99 // std::fs::write(debug_path, zipped.get_ref())?; 100 101 // unzip the manifest builder from the zipped stream 102 zipped.rewind()?; 103 104 let mut dest = { 105 let ed_signer = |_context: *const _, data: &[u8]| ed_sign(data, PRIVATE_KEY); 106 let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS); 107 let mut builder = Builder::from_archive(&mut zipped)?; 108 // sign the ManifestStoreBuilder and write it to the output stream 109 let mut dest = Cursor::new(Vec::new()); 110 builder.sign(&signer, format, &mut source, &mut dest)?; 111 112 // read and validate the signed manifest store 113 dest.rewind()?; 114 dest 115 }; 116 117 let reader = Reader::from_stream(format, &mut dest)?; 118 119 // extract a thumbnail image from the ManifestStore 120 let mut thumbnail = Cursor::new(Vec::new()); 121 if let Some(manifest) = reader.active_manifest() { 122 if let Some(thumbnail_ref) = manifest.thumbnail_ref() { 123 reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?; 124 println!( 125 "wrote thumbnail {} of size {}", 126 thumbnail_ref.format, 127 thumbnail.get_ref().len() 128 ); 129 } 130 } 131 132 println!("{}", reader.json()); 133 assert!(reader.validation_status().is_none()); 134 assert_eq!(reader.active_manifest().unwrap().title().unwrap(), title); 135 136 Ok(()) 137 } 138 139 fn ed_sign(data: &[u8], private_key: &[u8]) -> c2pa::Result<Vec<u8>> { 140 use ed25519_dalek::{Signature, Signer, SigningKey}; 141 use pem::parse; 142 143 // Parse the PEM data to get the private key 144 let pem = parse(private_key).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; 145 // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data 146 let key_bytes = &pem.contents()[16..]; 147 let signing_key = 148 SigningKey::try_from(key_bytes).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; 149 // Sign the data 150 let signature: Signature = signing_key.sign(data); 151 152 Ok(signature.to_bytes().to_vec()) 153 } 154 }