content_credentials.rs (4665B)
1 use std::fmt::{Display, Formatter, Result as FmtResult}; 2 use std::path::PathBuf; 3 use std::result::Result; 4 use std::sync::{Arc, Mutex}; 5 use std::vec; 6 7 use c2pa::{create_signer, Ingredient, Manifest, SigningAlg}; 8 use tracing::{debug, info}; 9 use tracing::error; 10 11 use crate::certificates::Certificate; 12 use crate::common::{FileData, SimpleC2PAError}; 13 14 const APPLICATION_NAME: &str = "Simple-C2PA"; 15 const APPLICATION_VERSION: &str = env!("CARGO_PKG_VERSION"); 16 17 #[derive(Debug, Clone)] 18 pub struct ApplicationInfo { 19 pub name: String, 20 pub version: String, 21 pub icon_uri: Option<String>, 22 } 23 24 impl ApplicationInfo { 25 pub fn new(name: String, version: String, icon_uri: Option<String>) -> Arc<Self> { 26 Arc::new(ApplicationInfo { 27 name, 28 version, 29 icon_uri, 30 }) 31 } 32 } 33 34 impl Display for ApplicationInfo { 35 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 36 write!(f, "{}/{}", self.name, self.version) 37 } 38 } 39 40 #[derive(Debug)] 41 pub struct ContentCredentials { 42 certificate: Arc<Certificate>, 43 file: Arc<FileData>, 44 #[allow(dead_code)] 45 application_info: Arc<ApplicationInfo>, 46 pub(crate) manifest: Mutex<Manifest>, 47 } 48 49 impl ContentCredentials { 50 pub fn new( 51 certificate: Arc<Certificate>, 52 file: Arc<FileData>, 53 application_info: Option<Arc<ApplicationInfo>>, 54 ) -> Arc<Self> { 55 let app_info = application_info.unwrap_or(ApplicationInfo::new( 56 APPLICATION_NAME.to_owned(), 57 APPLICATION_VERSION.to_owned(), 58 None, 59 )); 60 let path = file.get_path().unwrap(); 61 let ingredient = Ingredient::from_file(path).unwrap(); 62 // TODO: We shouldnt load it into bytes here 63 // ingredient 64 // .set_thumbnail("image/jpeg", file.get_bytes().unwrap()) 65 // .unwrap(); 66 let claim_generator = app_info.to_string(); 67 let mut manifest = Manifest::new(claim_generator); 68 manifest.set_parent(ingredient).unwrap(); 69 70 Arc::new(ContentCredentials { 71 certificate, 72 file, 73 application_info: app_info, 74 manifest: Mutex::new(manifest), 75 }) 76 } 77 78 fn sign_manifest_with_certificate( 79 &self, 80 certificate: &Arc<Certificate>, 81 output_file: &Arc<FileData>, 82 ) -> Result<(), SimpleC2PAError> { 83 let cert = certificate.get_certificate_bytes()?; 84 let pkey = certificate.get_private_key_bytes()?; 85 let algorithms = vec![ 86 SigningAlg::Es256, 87 SigningAlg::Es384, 88 SigningAlg::Es512, 89 SigningAlg::Ps256, 90 SigningAlg::Ps384, 91 SigningAlg::Ps512, 92 SigningAlg::Ed25519, 93 ]; 94 for alg in algorithms { 95 info!("Trying algorithm {:?}... ", alg); 96 let signer = create_signer::from_keys(&cert, &pkey, alg, None); 97 info!("embedding manifest using signer"); 98 match signer { 99 Ok(signer) => { 100 let mut manifest = self.manifest.lock().unwrap(); 101 match manifest.embed( 102 &self.file.get_path()?, 103 &output_file.get_path()?, 104 signer.as_ref(), 105 ) { 106 Ok(_) => { 107 debug!("Using provided certificate and private key"); 108 break; 109 } 110 Err(err) => { 111 error!("failed: {}\n", err); 112 continue; 113 } 114 } 115 } 116 Err(err) => { 117 error!("failed: {}\n", err); 118 continue; 119 } 120 }; 121 } 122 Ok(()) 123 } 124 125 fn sign_manifest( 126 &self, 127 embed: bool, 128 output_path: Option<PathBuf>, 129 ) -> Result<Arc<FileData>, SimpleC2PAError> { 130 let output_file = FileData::new(output_path, None, None); 131 if !embed { 132 let mut manifest = self.manifest.lock().unwrap(); 133 manifest.set_sidecar_manifest(); 134 } 135 136 self.sign_manifest_with_certificate(&self.certificate, &output_file)?; 137 Ok(output_file) 138 } 139 140 pub fn embed_manifest( 141 &self, 142 output_path: Option<PathBuf>, 143 ) -> Result<Arc<FileData>, SimpleC2PAError> { 144 self.sign_manifest(true, output_path) 145 } 146 147 pub fn export_manifest( 148 &self, 149 output_path: Option<PathBuf>, 150 ) -> Result<Arc<FileData>, SimpleC2PAError> { 151 self.sign_manifest(false, output_path) 152 } 153 }