simple-c2pa

This is a fork of https://gitlab.com/guardianproject/proofmode/simple-c2pa
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/simple-c2pa.git
Log | Files | Refs | README | LICENSE

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 }