Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
8.6 kB · 286 lines
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287use std::{io::Cursor, sync::Arc};
use c2pa::Builder;use c2pa::CallbackSigner;use c2pa::Reader;use c2pa::settings::Settings;
use c2pa::Ingredient;use c2pa::jumbf_io;use c2pa::status_tracker::StatusTracker;use c2pa::store::Store;
use crate::streams::ManyStreams;use crate::streams::Stream;use crate::streams::StreamAdapter;use serde_json;
use crate::error::SPError;use base64::{Engine as _, engine::general_purpose::STANDARD};#[uniffi::export]pub fn get_manifest_and_cert(data: &dyn Stream) -> Result<String, SPError> { let reader = Reader::from_stream("video/mp4", StreamAdapter::from(data)) .map_err(|e| SPError::C2paError(e.to_string()))?;
if let Some(manifest) = reader.active_manifest() { let cert_chain = if let Some(si) = manifest.signature_info() { si.cert_chain() } else { return Err(SPError::NoCertificateChainFound); };
let result = serde_json::json!({ "manifest": manifest, "cert": cert_chain, "validation_results": reader.validation_results(), "validation_state": reader.validation_state(), });
return Ok(result.to_string()); } Err(SPError::NoCertificateChainFound)}
#[uniffi::export(with_foreign)]pub trait GoSigner: Send + Sync { fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>, SPError>;}
// #[derive(uniffi::Object)]// struct Authenticator {// gosigner: Arc<dyn GoSigner>,// }
// impl Authenticator {// pub fn new(gosigner: Arc<dyn GoSigner>) -> Self {// Self { gosigner }// }
// pub fn login(&self) {// let username = self.gosigner.get("username".into());// let password = self.gosigner.get("password".into());// }// }
const TOML_SETTINGS: &str = r#"version_major = 1version_minor = 0
[trust]
[core]debug = truehash_alg = "sha256"salt_jumbf_boxes = trueprefer_box_hash = falsemerkle_tree_max_proofs = 5compress_manifests = true
[verify]verify_after_reading = falseverify_after_sign = falseverify_trust = falseverify_timestamp_trust = falseocsp_fetch = falseremote_manifest_fetch = falsecheck_ingredient_trust = falseskip_ingredient_conflict_resolution = falsestrict_v1_validation = false
[builder.thumbnail]enabled = falseignore_errors = truelong_edge = 1024prefer_smallest_format = truequality = "medium"
[builder.actions]all_actions_included = false
[builder.actions.auto_created_action]enabled = truesource_type = "http://c2pa.org/digitalsourcetype/empty"
[builder.actions.auto_opened_action]enabled = true
[builder.actions.auto_placed_action]enabled = true"#;
#[uniffi::export]pub fn sign( manifest: String, data: &dyn Stream, certs_str: String, gosigner: Arc<dyn GoSigner>,) -> Result<Vec<u8>, SPError> { Settings::from_toml(TOML_SETTINGS).map_err(|e| SPError::C2paError(e.to_string()))?; let certs = STANDARD .decode(certs_str) .map_err(|e| SPError::C2paError(e.to_string()))?; let callback_signer = CallbackSigner::new( move |_context: *const (), data: &[u8]| { let signature = gosigner .sign(data.to_vec()) .map_err(|e| c2pa::Error::BadParam(e.to_string()))?; Ok(signature) }, c2pa::SigningAlg::Es256K, certs, ); let mut builder = Builder::from_json(&manifest).map_err(|e| SPError::C2paError(e.to_string()))?; let mut output = Vec::new(); let mut input_cursor = StreamAdapter::from(data); let mut output_cursor = Cursor::new(&mut output); builder .sign( &callback_signer, "video/mp4", &mut input_cursor, &mut output_cursor, ) .map_err(|e| SPError::C2paError(e.to_string()))?; Ok(output)}
#[uniffi::export]pub fn get_manifests(data: &dyn Stream) -> Result<String, SPError> { let store = Reader::from_stream("video/mp4", StreamAdapter::from(data)) .map_err(|e| SPError::C2paError(e.to_string()))?; let mut certs: std::collections::HashMap<String, String> = std::collections::HashMap::new(); for (label, manifest) in store.manifests() { let cert_chain = manifest .signature_info() .ok_or(SPError::C2paError(format!( "No signature info for manifest: {}", label )))? .cert_chain(); certs.insert(label.clone(), cert_chain.to_string()); } let result = serde_json::json!({ "manifests": store.manifests(), "certs": certs }); Ok(result.to_string())}
#[uniffi::export(with_foreign)]pub trait SegmentToSign: Send + Sync { fn unsigned_seg_stream(&self) -> Arc<dyn Stream>; fn manifest_id(&self) -> String; fn cert(&self) -> Vec<u8>; fn output_seg_stream(&self) -> Arc<dyn Stream>; fn close(&self);}
#[uniffi::export(with_foreign)]pub trait ManySegmentsToSign: Send + Sync { fn next(&self) -> Option<Arc<dyn SegmentToSign>>;}
#[uniffi::export]pub fn resign( segs_to_sign: &dyn ManySegmentsToSign, signed_concat_data: &dyn Stream,) -> Result<(), SPError> { let mut validation_log = StatusTracker::default();
let combined_store = Store::from_stream( "video/mp4", StreamAdapter::from(signed_concat_data), true, &mut validation_log, ) .map_err(|e| SPError::C2paError(format!("from_stream failed: {}", e)))?;
while let Some(seg) = segs_to_sign.next() { let unsigned_seg_data_stream = seg.unsigned_seg_stream(); let manifest_id = seg.manifest_id(); let cert = seg.cert(); let output_stream = seg.output_seg_stream(); let mut input_cursor = StreamAdapter::from(&*unsigned_seg_data_stream); let mut output_cursor = StreamAdapter::from(&*output_stream);
// let mut output = Vec::<u8>::new(); let seg_claim = combined_store .get_claim(manifest_id.as_str()) .ok_or(SPError::C2paError(format!( "Segment claim not found: {}", manifest_id )))?; let signature_val = seg_claim.signature_val().clone();
let callback_signer = CallbackSigner::new( move |_context: *const (), _data: &[u8]| Ok(signature_val.clone()), c2pa::SigningAlg::Es256K, cert, );
let mut seg_store = Store::new(); let _provenance = seg_store .commit_claim(seg_claim.clone()) .map_err(|e| SPError::C2paError(format!("commit_claim failed: {}", e)))?;
let jumbf_bytes = seg_store .to_jumbf(&callback_signer) .map_err(|e| SPError::C2paError(format!("to_jumbf failed: {}", e)))?;
jumbf_io::save_jumbf_to_stream( "video/mp4", &mut input_cursor, &mut output_cursor, &jumbf_bytes, ) .map_err(|e| SPError::C2paError(format!("save_jumbf_to_stream failed: {}", e)))?; seg.close(); }
Ok(())}
#[uniffi::export]pub fn sign_with_ingredients( manifest: String, data: &dyn Stream, certs_str: String, ingredients: &dyn ManyStreams, gosigner: Arc<dyn GoSigner>, output: &dyn Stream,) -> Result<(), SPError> { let certs = STANDARD .decode(certs_str) .map_err(|e| SPError::C2paError(e.to_string()))?; Settings::from_toml(TOML_SETTINGS).map_err(|e| SPError::C2paError(e.to_string()))?; let callback_signer = CallbackSigner::new( move |_context: *const (), data: &[u8]| { gosigner .sign(data.to_vec()) .map_err(|e| c2pa::Error::BadParam(e.to_string())) }, c2pa::SigningAlg::Es256K, certs, ); let mut builder = Builder::from_json(&manifest).map_err(|e| SPError::C2paError(e.to_string()))?; while let Some(ingredient) = ingredients.next() { let mut cursor = StreamAdapter::from(&*ingredient); let ingredient = Ingredient::from_stream("video/mp4", &mut cursor) .map_err(|e| SPError::C2paError(e.to_string()))?; builder.add_ingredient(ingredient); } let mut input_cursor = StreamAdapter::from(data); let mut output_cursor = StreamAdapter::from(output); builder .sign( &callback_signer, "video/mp4", &mut input_cursor, &mut output_cursor, ) .map_err(|e| SPError::C2paError(e.to_string()))?; Ok(())}