assertions.rs (10360B)
1 use std::borrow::Cow; 2 use std::result::Result; 3 4 use c2pa::assertions::{c2pa_action, labels, Action, Actions, Exif, SchemaDotOrg}; 5 use serde::{Deserialize, Serialize}; 6 7 use crate::common::SimpleC2PAError; 8 use crate::content_credentials::ContentCredentials; 9 10 pub struct ExifData<'a> { 11 pub gps_version_id: Option<Cow<'a, str>>, 12 pub latitude: Option<Cow<'a, str>>, 13 pub longitude: Option<Cow<'a, str>>, 14 pub altitude_ref: Option<u8>, 15 pub altitude: Option<Cow<'a, str>>, 16 pub timestamp: Option<Cow<'a, str>>, 17 pub speed_ref: Option<Cow<'a, str>>, 18 pub speed: Option<Cow<'a, str>>, 19 pub direction_ref: Option<Cow<'a, str>>, 20 pub direction: Option<Cow<'a, str>>, 21 pub destination_bearing_ref: Option<Cow<'a, str>>, 22 pub destination_bearing: Option<Cow<'a, str>>, 23 pub positioning_error: Option<Cow<'a, str>>, 24 pub exposure_time: Option<Cow<'a, str>>, 25 pub f_number: Option<f64>, 26 pub color_space: Option<u8>, 27 pub digital_zoom_ratio: Option<f64>, 28 pub make: Option<Cow<'a, str>>, 29 pub model: Option<Cow<'a, str>>, 30 pub lens_make: Option<Cow<'a, str>>, 31 pub lens_model: Option<Cow<'a, str>>, 32 pub lens_specification: Option<Vec<f64>>, 33 } 34 35 #[derive(Serialize, Deserialize, Clone)] 36 #[serde(rename_all = "camelCase")] 37 struct AIDataMiningUsageJSON<'a> { 38 r#use: Cow<'a, str>, 39 r#constraint_info: Option<Cow<'a, str>>, 40 } 41 42 pub enum AIDataMiningUsage<'a> { 43 Allowed, 44 NotAllowed, 45 Constrained { constraint_info: Cow<'a, str> }, 46 } 47 48 pub struct CustomAITrainingOptions<'a> { 49 pub ai_training: AIDataMiningUsage<'a>, 50 pub ai_generative_training: AIDataMiningUsage<'a>, 51 pub data_mining: AIDataMiningUsage<'a>, 52 pub inference: AIDataMiningUsage<'a>, 53 } 54 55 impl AIDataMiningUsage<'_> { 56 fn to_json(&self) -> AIDataMiningUsageJSON { 57 match self { 58 AIDataMiningUsage::Allowed => AIDataMiningUsageJSON { 59 r#use: Cow::Borrowed("allowed"), 60 r#constraint_info: None, 61 }, 62 AIDataMiningUsage::NotAllowed => AIDataMiningUsageJSON { 63 r#use: Cow::Borrowed("notAllowed"), 64 r#constraint_info: None, 65 }, 66 AIDataMiningUsage::Constrained { constraint_info } => AIDataMiningUsageJSON { 67 r#use: Cow::Borrowed("constrained"), 68 r#constraint_info: Some(constraint_info.clone()), 69 }, 70 } 71 } 72 } 73 74 fn get_creative_work_assertion( 75 name: &str, 76 identifier: &str, 77 id: &str, 78 ) -> Result<SchemaDotOrg, SimpleC2PAError> { 79 let person = SchemaDotOrg::new("Person".to_string()) 80 .set_default_context() 81 .insert("name".to_string(), name)? 82 .insert("identifier".to_string(), identifier)? 83 .insert("@id".to_string(), id)?; 84 85 let work = SchemaDotOrg::new("CreativeWork".to_string()) 86 .set_default_context() 87 .insert("author".to_string(), vec![person])?; 88 Ok(work) 89 } 90 91 fn get_actions_assertion(action: String) -> Result<Actions, SimpleC2PAError> { 92 let action = Action::new(action.as_str()); 93 let actions = Actions::new().add_action(action); 94 Ok(actions) 95 } 96 97 impl ContentCredentials { 98 pub fn add_created_assertion(&self) -> Result<(), SimpleC2PAError> { 99 let actions = get_actions_assertion(c2pa_action::CREATED.to_string())?; 100 let mut manifest = self.manifest.lock().unwrap(); 101 manifest.add_assertion(&actions)?; 102 Ok(()) 103 } 104 105 pub fn add_placed_assertion(&self) -> Result<(), SimpleC2PAError> { 106 let actions = get_actions_assertion(c2pa_action::PLACED.to_string())?; 107 let mut manifest = self.manifest.lock().unwrap(); 108 manifest.add_assertion(&actions)?; 109 Ok(()) 110 } 111 112 pub fn add_email_assertion( 113 &self, 114 _email: String, 115 _display_name: String, 116 ) -> Result<(), SimpleC2PAError> { 117 Ok(()) 118 } 119 120 pub fn add_instagram_assertion( 121 &self, 122 username: &str, 123 display_name: &str, 124 ) -> Result<(), SimpleC2PAError> { 125 let work = get_creative_work_assertion(display_name, username, "https://instagram.com")?; 126 let mut manifest = self.manifest.lock().unwrap(); 127 manifest.add_labeled_assertion(labels::CREATIVE_WORK, &work)?; 128 Ok(()) 129 } 130 131 pub fn add_pgp_assertion( 132 &self, 133 fingerprint: &str, 134 display_name: &str, 135 ) -> Result<(), SimpleC2PAError> { 136 let work = 137 get_creative_work_assertion(display_name, fingerprint, "https://keys.openpgp.org")?; 138 let mut manifest = self.manifest.lock().unwrap(); 139 manifest.add_labeled_assertion(labels::CREATIVE_WORK, &work)?; 140 Ok(()) 141 } 142 143 pub fn add_website_assertion(&self, url: String) -> Result<(), SimpleC2PAError> { 144 let work = SchemaDotOrg::new("CreativeWork".to_owned()) 145 .set_default_context() 146 .insert("url".to_owned(), url)?; 147 let mut manifest = self.manifest.lock().unwrap(); 148 manifest.add_labeled_assertion(labels::CREATIVE_WORK, &work)?; 149 Ok(()) 150 } 151 152 pub fn add_exif_assertion(&self, exif_data: ExifData) -> Result<(), SimpleC2PAError> { 153 let mut exif = Exif::new(); 154 if let Some(gps_version_id) = exif_data.gps_version_id { 155 exif = exif.insert("exif:GPSVersionID", gps_version_id)?; 156 } 157 if let Some(latitude) = exif_data.latitude { 158 exif = exif.insert("exif:GPSLatitude", latitude)?; 159 } 160 if let Some(longitude) = exif_data.longitude { 161 exif = exif.insert("exif:GPSLongitude", longitude)?; 162 } 163 if let Some(altitude_ref) = exif_data.altitude_ref { 164 exif = exif.insert("exif:GPSAltitudeRef", altitude_ref)?; 165 } 166 if let Some(altitude) = exif_data.altitude { 167 exif = exif.insert("exif:GPSAltitude", altitude)?; 168 } 169 if let Some(timestamp) = exif_data.timestamp { 170 exif = exif.insert("exif:GPSTimeStamp", timestamp)?; 171 } 172 if let Some(speed_ref) = exif_data.speed_ref { 173 exif = exif.insert("exif:GPSSpeedRef", speed_ref)?; 174 } 175 if let Some(speed) = exif_data.speed { 176 exif = exif.insert("exif:GPSSpeed", speed)?; 177 } 178 if let Some(direction_ref) = exif_data.direction_ref { 179 exif = exif.insert("exif:GPSImgDirectionRef", direction_ref)?; 180 } 181 if let Some(direction) = exif_data.direction { 182 exif = exif.insert("exif:GPSImgDirection", direction)?; 183 } 184 if let Some(destination_bearing_ref) = exif_data.destination_bearing_ref { 185 exif = exif.insert("exif:GPSDestBearingRef", destination_bearing_ref)?; 186 } 187 if let Some(destination_bearing) = exif_data.destination_bearing { 188 exif = exif.insert("exif:GPSDestBearing", destination_bearing)?; 189 } 190 if let Some(positioning_error) = exif_data.positioning_error { 191 exif = exif.insert("exif:GPSHPositioningError", positioning_error)?; 192 } 193 if let Some(exposure_time) = exif_data.exposure_time { 194 exif = exif.insert("exif:ExposureTime", exposure_time)?; 195 } 196 if let Some(f_number) = exif_data.f_number { 197 exif = exif.insert("exif:FNumber", f_number)?; 198 } 199 if let Some(color_space) = exif_data.color_space { 200 exif = exif.insert("exif:ColorSpace", color_space)?; 201 } 202 if let Some(digital_zoom_ratio) = exif_data.digital_zoom_ratio { 203 exif = exif.insert("exif:DigitalZoomRatio", digital_zoom_ratio)?; 204 } 205 if let Some(make) = exif_data.make { 206 exif = exif.insert("tiff:Make", make)?; 207 } 208 if let Some(model) = exif_data.model { 209 exif = exif.insert("tiff:Model", model)?; 210 } 211 if let Some(lens_make) = exif_data.lens_make { 212 exif = exif.insert("exifEX:LensMake", lens_make)?; 213 } 214 if let Some(lens_model) = exif_data.lens_model { 215 exif = exif.insert("exifEX:LensModel", lens_model)?; 216 } 217 if let Some(lens_specification) = exif_data.lens_specification { 218 exif = exif.insert("exifEX:LensSpecification", lens_specification)?; 219 } 220 let mut manifest = self.manifest.lock().unwrap(); 221 manifest.add_assertion(&exif)?; 222 Ok(()) 223 } 224 225 pub fn add_json_assertion(&self, label: &str, json: String) -> Result<(), SimpleC2PAError> { 226 let mut manifest = self.manifest.lock().unwrap(); 227 manifest.add_labeled_assertion(label, &json)?; 228 Ok(()) 229 } 230 231 pub fn add_restricted_ai_training_assertions(&self) -> Result<(), SimpleC2PAError> { 232 let training_not_allowed = AIDataMiningUsage::NotAllowed.to_json(); 233 let mut manifest = self.manifest.lock().unwrap(); 234 manifest.add_labeled_assertion("c2pa.ai_training", &training_not_allowed)?; 235 manifest.add_labeled_assertion("c2pa.ai_generative_training", &training_not_allowed)?; 236 manifest.add_labeled_assertion("c2pa.data_mining", &training_not_allowed)?; 237 manifest.add_labeled_assertion("c2pa.inference", &training_not_allowed)?; 238 Ok(()) 239 } 240 241 pub fn add_permissive_ai_training_assertions(&self) -> Result<(), SimpleC2PAError> { 242 let training_allowed = AIDataMiningUsage::Allowed.to_json(); 243 let mut manifest = self.manifest.lock().unwrap(); 244 manifest.add_labeled_assertion("c2pa.ai_training", &training_allowed)?; 245 manifest.add_labeled_assertion("c2pa.ai_generative_training", &training_allowed)?; 246 manifest.add_labeled_assertion("c2pa.data_mining", &training_allowed)?; 247 manifest.add_labeled_assertion("c2pa.inference", &training_allowed)?; 248 Ok(()) 249 } 250 251 pub fn add_custom_ai_training_assertions( 252 &self, 253 options: CustomAITrainingOptions, 254 ) -> Result<(), SimpleC2PAError> { 255 let mut manifest = self.manifest.lock().unwrap(); 256 manifest.add_labeled_assertion("c2pa.ai_training", &options.ai_training.to_json())?; 257 manifest.add_labeled_assertion( 258 "c2pa.ai_generative_training", 259 &options.ai_generative_training.to_json(), 260 )?; 261 manifest.add_labeled_assertion("c2pa.data_mining", &options.data_mining.to_json())?; 262 manifest.add_labeled_assertion("c2pa.inference", &options.inference.to_json())?; 263 Ok(()) 264 } 265 }