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

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 }