1use std::{
15 os::raw::{c_char, c_int, c_uchar, c_void},
16 sync::Arc,
17};
18
19#[cfg(feature = "file_io")]
21use c2pa::Ingredient;
22use c2pa::{
23 assertions::DataHash, identity::validator::CawgValidator, Builder as C2paBuilder,
24 CallbackSigner, Context, ProgressPhase, Reader as C2paReader, Settings as C2paSettings,
25 SigningAlg,
26};
27use tokio::runtime::Builder;
28
29#[cfg(feature = "file_io")]
30use crate::json_api::{read_file, sign_file};
31#[cfg(test)]
32use crate::safe_slice_from_raw_parts;
33#[allow(unused_imports)] use crate::{
36 box_tracked, bytes_or_return_int, bytes_or_return_null, c2pa_stream::C2paStream, cimpl_free,
37 cstr_or_return_int, cstr_or_return_null, deref_mut_or_return, deref_mut_or_return_int,
38 deref_mut_or_return_null, deref_or_return_int, deref_or_return_null, error::Error,
39 ok_or_return_int, ok_or_return_null, option_to_c_string, ptr_or_return_int,
40 signer_info::SignerInfo, to_c_bytes, to_c_string, CimplError,
41};
42
43#[allow(dead_code)]
54unsafe fn is_safe_buffer_size(size: usize, ptr: *const c_uchar) -> bool {
55 if size == 0 || size > isize::MAX as usize {
57 return false;
58 }
59
60 if !ptr.is_null() {
62 let end_ptr = ptr.add(size);
63 if end_ptr < ptr {
64 return false; }
66 }
67
68 true
69}
70
71mod cbindgen_fix {
73 #[repr(C)]
74 #[allow(dead_code)]
75 pub struct C2paBuilder;
76
77 #[repr(C)]
78 #[allow(dead_code)]
79 pub struct C2paReader;
80
81 #[repr(C)]
82 #[allow(dead_code)]
83 pub struct C2paContextBuilder;
84
85 #[repr(C)]
86 #[allow(dead_code)]
87 pub struct C2paContext;
88
89 #[repr(C)]
90 #[allow(dead_code)]
91 pub struct C2paSettings;
92
93 #[repr(C)]
94 #[allow(dead_code)]
95 pub struct C2paHttpResolver;
96}
97
98type C2paContextBuilder = Context;
99type C2paContext = Arc<Context>;
100
101#[repr(C)]
104pub enum C2paProgressPhase {
105 Reading = 0,
106 VerifyingManifest = 1,
107 VerifyingSignature = 2,
108 VerifyingIngredient = 3,
109 VerifyingAssetHash = 4,
110 AddingIngredient = 5,
111 Thumbnail = 6,
112 Hashing = 7,
113 Signing = 8,
114 Embedding = 9,
115 FetchingRemoteManifest = 10,
116 Writing = 11,
117 FetchingOCSP = 12,
118 FetchingTimestamp = 13,
119}
120
121impl From<ProgressPhase> for C2paProgressPhase {
122 fn from(phase: ProgressPhase) -> Self {
123 match phase {
124 ProgressPhase::Reading => Self::Reading,
125 ProgressPhase::VerifyingManifest => Self::VerifyingManifest,
126 ProgressPhase::VerifyingSignature => Self::VerifyingSignature,
127 ProgressPhase::VerifyingIngredient => Self::VerifyingIngredient,
128 ProgressPhase::VerifyingAssetHash => Self::VerifyingAssetHash,
129 ProgressPhase::AddingIngredient => Self::AddingIngredient,
130 ProgressPhase::Thumbnail => Self::Thumbnail,
131 ProgressPhase::Hashing => Self::Hashing,
132 ProgressPhase::Signing => Self::Signing,
133 ProgressPhase::Embedding => Self::Embedding,
134 ProgressPhase::FetchingRemoteManifest => Self::FetchingRemoteManifest,
135 ProgressPhase::Writing => Self::Writing,
136 ProgressPhase::FetchingOCSP => Self::FetchingOCSP,
137 ProgressPhase::FetchingTimestamp => Self::FetchingTimestamp,
138 _ => Self::Reading, }
140 }
141}
142
143#[repr(C)]
145pub enum C2paSigningAlg {
146 Es256,
147 Es384,
148 Es512,
149 Ps256,
150 Ps384,
151 Ps512,
152 Ed25519,
153}
154
155impl From<C2paSigningAlg> for SigningAlg {
156 fn from(alg: C2paSigningAlg) -> Self {
157 match alg {
158 C2paSigningAlg::Es256 => SigningAlg::Es256,
159 C2paSigningAlg::Es384 => SigningAlg::Es384,
160 C2paSigningAlg::Es512 => SigningAlg::Es512,
161 C2paSigningAlg::Ps256 => SigningAlg::Ps256,
162 C2paSigningAlg::Ps384 => SigningAlg::Ps384,
163 C2paSigningAlg::Ps512 => SigningAlg::Ps512,
164 C2paSigningAlg::Ed25519 => SigningAlg::Ed25519,
165 }
166 }
167}
168
169#[repr(C)]
171pub enum C2paDigitalSourceType {
172 Empty,
173 TrainedAlgorithmicData,
174 DigitalCapture,
175 ComputationalCapture,
176 NegativeFilm,
177 PositiveFilm,
178 Print,
179 HumanEdits,
180 CompositeWithTrainedAlgorithmicMedia,
181 AlgorithmicallyEnhanced,
182 DigitalCreation,
183 DataDrivenMedia,
184 TrainedAlgorithmicMedia,
185 AlgorithmicMedia,
186 ScreenCapture,
187 VirtualRecording,
188 Composite,
189 CompositeCapture,
190 CompositeSynthetic,
191}
192
193impl From<C2paDigitalSourceType> for c2pa::DigitalSourceType {
194 fn from(source_type: C2paDigitalSourceType) -> Self {
195 match source_type {
196 C2paDigitalSourceType::Empty => c2pa::DigitalSourceType::Empty,
197 C2paDigitalSourceType::TrainedAlgorithmicData => {
198 c2pa::DigitalSourceType::TrainedAlgorithmicData
199 }
200 C2paDigitalSourceType::DigitalCapture => c2pa::DigitalSourceType::DigitalCapture,
201 C2paDigitalSourceType::ComputationalCapture => {
202 c2pa::DigitalSourceType::ComputationalCapture
203 }
204 C2paDigitalSourceType::NegativeFilm => c2pa::DigitalSourceType::NegativeFilm,
205 C2paDigitalSourceType::PositiveFilm => c2pa::DigitalSourceType::PositiveFilm,
206 C2paDigitalSourceType::Print => c2pa::DigitalSourceType::Print,
207 C2paDigitalSourceType::HumanEdits => c2pa::DigitalSourceType::HumanEdits,
208 C2paDigitalSourceType::CompositeWithTrainedAlgorithmicMedia => {
209 c2pa::DigitalSourceType::CompositeWithTrainedAlgorithmicMedia
210 }
211 C2paDigitalSourceType::AlgorithmicallyEnhanced => {
212 c2pa::DigitalSourceType::AlgorithmicallyEnhanced
213 }
214 C2paDigitalSourceType::DigitalCreation => c2pa::DigitalSourceType::DigitalCreation,
215 C2paDigitalSourceType::DataDrivenMedia => c2pa::DigitalSourceType::DataDrivenMedia,
216 C2paDigitalSourceType::TrainedAlgorithmicMedia => {
217 c2pa::DigitalSourceType::TrainedAlgorithmicMedia
218 }
219 C2paDigitalSourceType::AlgorithmicMedia => c2pa::DigitalSourceType::AlgorithmicMedia,
220 C2paDigitalSourceType::ScreenCapture => c2pa::DigitalSourceType::ScreenCapture,
221 C2paDigitalSourceType::VirtualRecording => c2pa::DigitalSourceType::VirtualRecording,
222 C2paDigitalSourceType::Composite => c2pa::DigitalSourceType::Composite,
223 C2paDigitalSourceType::CompositeCapture => c2pa::DigitalSourceType::CompositeCapture,
224 C2paDigitalSourceType::CompositeSynthetic => {
225 c2pa::DigitalSourceType::CompositeSynthetic
226 }
227 }
228 }
229}
230
231#[repr(C)]
234pub enum C2paBuilderIntent {
235 Create,
239 Edit,
244 Update,
248}
249
250#[repr(C)]
252pub enum C2paHashType {
253 DataHash = 0,
255
256 BmffHash = 1,
258
259 BoxHash = 2,
261}
262
263#[repr(C)]
264pub struct C2paSigner {
265 pub signer: Box<dyn crate::maybe_send_sync::C2paSignerObject>,
266}
267
268pub type SignerCallback = unsafe extern "C" fn(
273 context: *const (),
274 data: *const c_uchar,
275 len: usize,
276 signed_bytes: *mut c_uchar,
277 signed_len: usize,
278) -> isize;
279
280#[repr(C)]
285pub struct C2paHttpRequest {
286 pub url: *const c_char,
288 pub method: *const c_char,
290 pub headers: *const c_char,
292 pub body: *const c_uchar,
294 pub body_len: usize,
296}
297
298#[repr(C)]
304pub struct C2paHttpResponse {
305 pub status: i32,
307 pub body: *mut c_uchar,
310 pub body_len: usize,
312}
313
314struct OwnedC2paHttpRequest {
319 url: std::ffi::CString,
320 method: std::ffi::CString,
321 headers: std::ffi::CString,
322 body: Vec<u8>,
323}
324
325impl TryFrom<c2pa::http::http::Request<Vec<u8>>> for OwnedC2paHttpRequest {
326 type Error = c2pa::http::HttpResolverError;
327
328 fn try_from(request: c2pa::http::http::Request<Vec<u8>>) -> Result<Self, Self::Error> {
329 use std::ffi::CString;
330
331 use c2pa::http::HttpResolverError;
332
333 let url = CString::new(request.uri().to_string())
334 .map_err(|e| HttpResolverError::Other(Box::new(e)))?;
335 let method = CString::new(request.method().as_str())
336 .map_err(|e| HttpResolverError::Other(Box::new(e)))?;
337 let headers_str: String = request
338 .headers()
339 .iter()
340 .filter_map(|(k, v)| v.to_str().ok().map(|v| format!("{k}: {v}\n")))
341 .collect();
342 let headers =
343 CString::new(headers_str).map_err(|e| HttpResolverError::Other(Box::new(e)))?;
344 let body = request.into_body();
345
346 Ok(Self {
347 url,
348 method,
349 headers,
350 body,
351 })
352 }
353}
354
355impl OwnedC2paHttpRequest {
356 fn as_ffi(&self) -> C2paHttpRequest {
359 let (body, body_len) = if self.body.is_empty() {
360 (std::ptr::null(), 0)
361 } else {
362 (self.body.as_ptr(), self.body.len())
363 };
364 C2paHttpRequest {
365 url: self.url.as_ptr(),
366 method: self.method.as_ptr(),
367 headers: self.headers.as_ptr(),
368 body,
369 body_len,
370 }
371 }
372}
373
374impl TryFrom<C2paHttpResponse> for c2pa::http::http::Response<Box<dyn std::io::Read>> {
383 type Error = c2pa::http::HttpResolverError;
384
385 fn try_from(resp: C2paHttpResponse) -> Result<Self, Self::Error> {
386 let body_vec = if resp.body.is_null() || resp.body_len == 0 {
387 Vec::new()
388 } else {
389 let v = unsafe { std::slice::from_raw_parts(resp.body, resp.body_len) }.to_vec();
390 unsafe { libc::free(resp.body as *mut c_void) };
391 v
392 };
393
394 c2pa::http::http::Response::builder()
395 .status(resp.status as u16)
396 .body(Box::new(std::io::Cursor::new(body_vec)) as Box<dyn std::io::Read>)
397 .map_err(c2pa::http::HttpResolverError::Http)
398 }
399}
400
401pub type C2paHttpResolverCallback = unsafe extern "C" fn(
409 context: *mut c_void,
410 request: *const C2paHttpRequest,
411 response: *mut C2paHttpResponse,
412) -> c_int;
413
414pub struct C2paHttpResolver {
418 context: *const c_void,
419 callback: C2paHttpResolverCallback,
420}
421
422unsafe impl Send for C2paHttpResolver {}
426unsafe impl Sync for C2paHttpResolver {}
427
428impl c2pa::http::SyncHttpResolver for C2paHttpResolver {
429 fn http_resolve(
430 &self,
431 request: c2pa::http::http::Request<Vec<u8>>,
432 ) -> Result<c2pa::http::http::Response<Box<dyn std::io::Read>>, c2pa::http::HttpResolverError>
433 {
434 use c2pa::http::HttpResolverError;
435
436 let owned = OwnedC2paHttpRequest::try_from(request)?;
437 let c_request = owned.as_ffi();
438
439 let mut c_response = C2paHttpResponse {
440 status: 0,
441 body: std::ptr::null_mut(),
442 body_len: 0,
443 };
444
445 let rc =
446 unsafe { (self.callback)(self.context as *mut c_void, &c_request, &mut c_response) };
447
448 if rc != 0 {
449 if !c_response.body.is_null() {
451 unsafe { libc::free(c_response.body as *mut c_void) };
452 }
453 let msg = CimplError::last_message()
454 .unwrap_or_else(|| "HTTP callback returned error".to_string());
455 return Err(HttpResolverError::Other(msg.into()));
456 }
457
458 c_response.try_into()
459 }
460}
461
462#[no_mangle]
478pub unsafe extern "C" fn c2pa_version() -> *mut c_char {
479 let version = format!(
480 "{}/{} {}/{}",
481 env!("CARGO_PKG_NAME"),
482 env!("CARGO_PKG_VERSION"),
483 c2pa::NAME,
484 c2pa::VERSION
485 );
486 to_c_string(version)
487}
488
489#[no_mangle]
495pub unsafe extern "C" fn c2pa_error() -> *mut c_char {
496 to_c_string(Error::last_message())
497}
498
499#[no_mangle]
509pub unsafe extern "C" fn c2pa_error_set_last(error_str: *const c_char) -> c_int {
510 let error_str = cstr_or_return_int!(error_str);
511 CimplError::from(Error::from(error_str)).set_last();
512 0
513}
514
515#[no_mangle]
525#[deprecated(
526 note = "Use c2pa_settings_new() and c2pa_context_builder_set_settings() to configure a context explicitly."
527)]
528pub unsafe extern "C" fn c2pa_load_settings(
529 settings: *const c_char,
530 format: *const c_char,
531) -> c_int {
532 let settings = cstr_or_return_int!(settings);
533 let format = cstr_or_return_int!(format);
534 #[allow(deprecated)]
537 let result = C2paSettings::from_string(&settings, &format);
538 ok_or_return_int!(result);
539 0 }
541
542#[no_mangle]
552pub unsafe extern "C" fn c2pa_settings_new() -> *mut C2paSettings {
553 box_tracked!(C2paSettings::new())
554}
555
556#[no_mangle]
572pub unsafe extern "C" fn c2pa_settings_update_from_string(
573 settings: *mut C2paSettings,
574 settings_str: *const c_char,
575 format: *const c_char,
576) -> c_int {
577 let settings = deref_mut_or_return_int!(settings, C2paSettings);
578 let settings_str = cstr_or_return_int!(settings_str);
579 let format = cstr_or_return_int!(format);
580 let result = settings.update_from_str(&settings_str, &format);
581 ok_or_return_int!(result);
582 0
583}
584
585#[no_mangle]
602pub unsafe extern "C" fn c2pa_settings_set_value(
603 settings: *mut C2paSettings,
604 path: *const c_char,
605 value: *const c_char,
606) -> c_int {
607 let settings = deref_mut_or_return_int!(settings, C2paSettings);
608 let path = cstr_or_return_int!(path);
609 let value_str = cstr_or_return_int!(value);
610
611 let parsed_value: serde_json::Value = ok_or_return_int!(serde_json::from_str(&value_str)
613 .map_err(|e| c2pa::Error::BadParam(format!("Invalid JSON value: {e}"))));
614
615 let result = match parsed_value {
617 serde_json::Value::Bool(b) => settings.set_value(&path, b),
618 serde_json::Value::Number(n) if n.is_i64() => {
619 settings.set_value(&path, n.as_i64().unwrap())
620 }
621 serde_json::Value::Number(n) if n.is_f64() => {
622 settings.set_value(&path, n.as_f64().unwrap())
623 }
624 serde_json::Value::Number(_) => {
625 Err(c2pa::Error::BadParam("Invalid number format".to_string()))
626 }
627 serde_json::Value::String(s) => settings.set_value(&path, s),
628 serde_json::Value::Array(arr) => {
629 let strings: Result<Vec<String>, _> = arr
631 .into_iter()
632 .map(|v| {
633 v.as_str().map(String::from).ok_or_else(|| {
634 c2pa::Error::BadParam("Array values must be strings".to_string())
635 })
636 })
637 .collect();
638 strings.and_then(|vec| settings.set_value(&path, vec))
639 }
640 serde_json::Value::Null => Err(c2pa::Error::BadParam("Cannot set null values".to_string())),
641 serde_json::Value::Object(_) => Err(c2pa::Error::BadParam(
642 "Cannot set object values directly, use update_from_string instead".to_string(),
643 )),
644 };
645
646 ok_or_return_int!(result);
647 0
648}
649
650#[no_mangle]
679pub unsafe extern "C" fn c2pa_context_builder_new() -> *mut C2paContextBuilder {
680 box_tracked!(Context::new())
681}
682
683#[no_mangle]
701pub unsafe extern "C" fn c2pa_context_builder_set_settings(
702 builder: *mut C2paContextBuilder,
703 settings: *mut C2paSettings,
704) -> c_int {
705 let builder = deref_mut_or_return_int!(builder, C2paContextBuilder);
706 let settings = deref_or_return_int!(settings, C2paSettings);
707 let result = builder.set_settings(settings);
708 ok_or_return_int!(result);
709 0
710}
711
712#[no_mangle]
731pub unsafe extern "C" fn c2pa_context_builder_set_signer(
732 builder: *mut C2paContextBuilder,
733 signer_ptr: *mut C2paSigner,
734) -> c_int {
735 let builder = deref_mut_or_return_int!(builder, C2paContextBuilder);
736 untrack_or_return_int!(signer_ptr, C2paSigner);
739 let c2pa_signer = Box::from_raw(signer_ptr);
740 let result = builder.set_signer(c2pa_signer.signer);
741 ok_or_return_int!(result);
742 0
743}
744
745pub type ProgressCCallback = unsafe extern "C" fn(
763 context: *const c_void,
764 phase: C2paProgressPhase,
765 step: u32,
766 total: u32,
767) -> c_int;
768
769#[no_mangle]
788pub unsafe extern "C" fn c2pa_context_builder_set_progress_callback(
789 builder: *mut C2paContextBuilder,
790 user_data: *const c_void,
791 callback: ProgressCCallback,
792) -> c_int {
793 let builder = deref_mut_or_return_int!(builder, C2paContextBuilder);
794 let ud = user_data as usize;
795 let c_callback = move |phase: ProgressPhase, step: u32, total: u32| unsafe {
796 (callback)(ud as *const c_void, phase.into(), step, total) != 0
797 };
798 builder.set_progress_callback(c_callback);
799 0
800}
801
802#[no_mangle]
821pub unsafe extern "C" fn c2pa_http_resolver_create(
822 context: *const c_void,
823 callback: C2paHttpResolverCallback,
824) -> *mut C2paHttpResolver {
825 box_tracked!(C2paHttpResolver { context, callback })
826}
827
828#[no_mangle]
841pub unsafe extern "C" fn c2pa_context_builder_set_http_resolver(
842 builder: *mut C2paContextBuilder,
843 resolver_ptr: *mut C2paHttpResolver,
844) -> c_int {
845 let builder = deref_mut_or_return_int!(builder, C2paContextBuilder);
846 untrack_or_return_int!(resolver_ptr, C2paHttpResolver);
847 let c2pa_resolver = Box::from_raw(resolver_ptr);
848 let result = builder.set_resolver(*c2pa_resolver);
849 ok_or_return_int!(result);
850 0
851}
852
853#[no_mangle]
869pub unsafe extern "C" fn c2pa_context_builder_build(
870 builder: *mut C2paContextBuilder,
871) -> *mut C2paContext {
872 untrack_or_return_null!(builder, C2paContextBuilder);
873 let context = Box::from_raw(builder);
874 box_tracked!((*context).into_shared())
875}
876
877#[no_mangle]
896pub unsafe extern "C" fn c2pa_context_new() -> *mut C2paContext {
897 box_tracked!(Context::new().into_shared())
898}
899
900#[no_mangle]
916pub unsafe extern "C" fn c2pa_context_cancel(ctx: *mut C2paContext) -> c_int {
917 let ctx = deref_or_return_int!(ctx, C2paContext);
918 ctx.cancel();
919 0
920}
921
922#[cfg(feature = "file_io")]
932#[no_mangle]
933#[deprecated(
934 note = "Use c2pa_reader_from_context() with an explicit context for new implementations."
935)]
936#[allow(deprecated)]
937pub unsafe extern "C" fn c2pa_read_file(
938 path: *const c_char,
939 data_dir: *const c_char,
940) -> *mut c_char {
941 let path = cstr_or_return_null!(path);
942 let data_dir = cstr_option!(data_dir);
943
944 let result = read_file(&path, data_dir);
945 let json = ok_or_return_null!(result);
946 to_c_string(json)
947}
948
949#[cfg(feature = "file_io")]
963#[no_mangle]
964#[deprecated(
965 note = "Use c2pa_builder_add_ingredient_from_stream() with an explicit context for new implementations."
966)]
967#[allow(deprecated)]
968pub unsafe extern "C" fn c2pa_read_ingredient_file(
969 path: *const c_char,
970 data_dir: *const c_char,
971) -> *mut c_char {
972 let path = cstr_or_return_null!(path);
973 let data_dir = cstr_or_return_null!(data_dir);
974 let result = Ingredient::from_file_with_folder(path, data_dir).map_err(Error::from_c2pa_error);
976 let ingredient = ok_or_return_null!(result);
977 let json = serde_json::to_string(&ingredient).unwrap_or_default();
978 to_c_string(json)
979}
980
981#[repr(C)]
982pub struct C2paSignerInfo {
987 pub alg: *const c_char,
989 pub sign_cert: *const c_char,
991 pub private_key: *const c_char,
993 pub ta_url: *const c_char,
995}
996
997#[cfg(feature = "file_io")]
1007#[no_mangle]
1008#[deprecated(
1009 note = "Use c2pa_builder_from_context() with c2pa_builder_sign_to_stream() for new implementations."
1010)]
1011#[allow(deprecated)]
1012pub unsafe extern "C" fn c2pa_sign_file(
1013 source_path: *const c_char,
1014 dest_path: *const c_char,
1015 manifest: *const c_char,
1016 signer_info: &C2paSignerInfo,
1017 data_dir: *const c_char,
1018) -> *mut c_char {
1019 let source_path = cstr_or_return_null!(source_path);
1021 let dest_path = cstr_or_return_null!(dest_path);
1022 let manifest = cstr_or_return_null!(manifest);
1023 let data_dir = cstr_option!(data_dir);
1024
1025 let signer_info = SignerInfo {
1026 alg: cstr_or_return_null!(signer_info.alg),
1027 sign_cert: cstr_or_return_null!(signer_info.sign_cert).into_bytes(),
1028 private_key: cstr_or_return_null!(signer_info.private_key).into_bytes(),
1029 ta_url: cstr_option!(signer_info.ta_url),
1030 };
1031 let result = sign_file(&source_path, &dest_path, &manifest, &signer_info, data_dir);
1033 ok_or_return_null!(result); to_c_string("".to_string())
1035}
1036
1037#[no_mangle]
1043#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
1044pub unsafe extern "C" fn c2pa_release_string(s: *mut c_char) {
1045 cimpl_free!(s);
1046}
1047
1048#[no_mangle]
1101pub unsafe extern "C" fn c2pa_free(ptr: *const c_void) -> c_int {
1102 cimpl_free!(ptr as *mut c_void)
1103}
1104
1105#[no_mangle]
1115#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
1116pub unsafe extern "C" fn c2pa_string_free(s: *mut c_char) {
1117 cimpl_free!(s);
1118}
1119
1120#[no_mangle]
1132pub unsafe extern "C" fn c2pa_free_string_array(ptr: *const *const c_char, count: usize) {
1133 if ptr.is_null() {
1134 return;
1135 }
1136
1137 let mut_ptr = ptr as *mut *mut c_char;
1138 #[allow(deprecated)]
1140 for i in 0..count {
1141 c2pa_string_free(*mut_ptr.add(i));
1142 }
1143
1144 Vec::from_raw_parts(mut_ptr, count, count);
1146}
1147
1148fn post_validate(result: Result<C2paReader, c2pa::Error>) -> Result<C2paReader, c2pa::Error> {
1150 match result {
1151 Ok(mut reader) => {
1152 #[cfg(target_arch = "wasm32")]
1153 let runtime = Builder::new_current_thread().enable_all().build();
1154
1155 #[cfg(not(target_arch = "wasm32"))]
1156 let runtime = Builder::new_multi_thread().enable_all().build();
1157
1158 let runtime = match runtime {
1159 Ok(runtime) => runtime,
1160 Err(err) => return Err(c2pa::Error::OtherError(Box::new(err))),
1161 };
1162
1163 match runtime.block_on(reader.post_validate_async(&CawgValidator {})) {
1164 Ok(_) => Ok(reader),
1165 Err(err) => Err(err),
1166 }
1167 }
1168 Err(err) => Err(err),
1169 }
1170}
1171
1172#[no_mangle]
1182pub unsafe extern "C" fn c2pa_reader_new() -> *mut C2paReader {
1183 box_tracked!(C2paReader::from_context(Context::default()))
1184}
1185
1186#[no_mangle]
1199pub unsafe extern "C" fn c2pa_reader_from_context(context: *mut C2paContext) -> *mut C2paReader {
1200 let context = deref_or_return_null!(context, C2paContext);
1201 box_tracked!(C2paReader::from_shared_context(context))
1202}
1203
1204#[no_mangle]
1218#[deprecated(
1219 note = "Use c2pa_reader_from_context() with an explicit context instead of relying on thread-local settings."
1220)]
1221pub unsafe extern "C" fn c2pa_reader_from_stream(
1222 format: *const c_char,
1223 stream: *mut C2paStream,
1224) -> *mut C2paReader {
1225 let format = cstr_or_return_null!(format);
1226 let stream = deref_mut_or_return_null!(stream, C2paStream);
1227
1228 #[allow(deprecated)]
1231 let result = C2paReader::from_stream(&format, stream);
1232 let result = ok_or_return_null!(post_validate(result));
1233 box_tracked!(result)
1234}
1235
1236#[no_mangle]
1252pub unsafe extern "C" fn c2pa_reader_with_stream(
1253 reader: *mut C2paReader,
1254 format: *const c_char,
1255 stream: *mut C2paStream,
1256) -> *mut C2paReader {
1257 let format = cstr_or_return_null!(format);
1259 let stream = deref_mut_or_return_null!(stream, C2paStream);
1260
1261 untrack_or_return_null!(reader, C2paReader);
1263 let reader = Box::from_raw(reader);
1264 let result = (*reader).with_stream(&format, stream);
1265 let result = ok_or_return_null!(post_validate(result));
1266 box_tracked!(result)
1267}
1268
1269#[no_mangle]
1289pub unsafe extern "C" fn c2pa_reader_with_manifest_data_and_stream(
1290 reader: *mut C2paReader,
1291 format: *const c_char,
1292 stream: *mut C2paStream,
1293 manifest_data: *const c_uchar,
1294 manifest_size: usize,
1295) -> *mut C2paReader {
1296 let format = cstr_or_return_null!(format);
1297 let stream = deref_mut_or_return_null!(stream, C2paStream);
1298 let manifest_bytes = bytes_or_return_null!(manifest_data, manifest_size, "manifest_data");
1299
1300 untrack_or_return_null!(reader, C2paReader);
1302 let reader = Box::from_raw(reader);
1303 let result = (*reader).with_manifest_data_and_stream(manifest_bytes, &format, stream);
1304 let result = ok_or_return_null!(post_validate(result));
1305
1306 box_tracked!(result)
1308}
1309
1310#[no_mangle]
1336pub unsafe extern "C" fn c2pa_reader_with_fragment(
1337 reader: *mut C2paReader,
1338 format: *const c_char,
1339 stream: *mut C2paStream,
1340 fragment: *mut C2paStream,
1341) -> *mut C2paReader {
1342 let format = cstr_or_return_null!(format);
1344 let stream = deref_mut_or_return_null!(stream, C2paStream);
1345 let fragment = deref_mut_or_return_null!(fragment, C2paStream);
1346
1347 untrack_or_return_null!(reader, C2paReader);
1349 let reader = Box::from_raw(reader);
1350 let result = (*reader).with_fragment(&format, stream, fragment);
1351 let result = ok_or_return_null!(post_validate(result));
1352 box_tracked!(result)
1353}
1354
1355#[cfg(feature = "file_io")]
1390#[no_mangle]
1391#[deprecated(
1392 note = "Use c2pa_reader_from_context() with an explicit context instead of relying on thread-local settings."
1393)]
1394#[allow(deprecated)]
1395pub unsafe fn c2pa_reader_from_file(path: *const c_char) -> *mut C2paReader {
1396 let path = cstr_or_return_null!(path);
1397 let result = C2paReader::from_file(&path);
1399 box_tracked!(ok_or_return_null!(post_validate(result)))
1400}
1401
1402#[no_mangle]
1419#[deprecated(
1420 note = "Use c2pa_reader_from_context() then c2pa_reader_with_manifest_data_and_stream() instead."
1421)]
1422pub unsafe extern "C" fn c2pa_reader_from_manifest_data_and_stream(
1423 format: *const c_char,
1424 stream: *mut C2paStream,
1425 manifest_data: *const c_uchar,
1426 manifest_size: usize,
1427) -> *mut C2paReader {
1428 let format = cstr_or_return_null!(format);
1429 let stream = deref_mut_or_return_null!(stream, C2paStream);
1430
1431 let manifest_bytes = bytes_or_return_null!(manifest_data, manifest_size, "manifest_data");
1432
1433 #[allow(deprecated)]
1435 let result = C2paReader::from_manifest_data_and_stream(manifest_bytes, &format, stream);
1436 box_tracked!(ok_or_return_null!(post_validate(result)))
1437}
1438
1439#[no_mangle]
1447#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
1448pub unsafe extern "C" fn c2pa_reader_free(reader_ptr: *mut C2paReader) {
1449 cimpl_free!(reader_ptr);
1450}
1451
1452#[no_mangle]
1458pub unsafe extern "C" fn c2pa_reader_json(reader_ptr: *mut C2paReader) -> *mut c_char {
1459 let c2pa_reader = deref_or_return_null!(reader_ptr, C2paReader);
1460 to_c_string(c2pa_reader.json())
1461}
1462
1463#[no_mangle]
1469pub unsafe extern "C" fn c2pa_reader_detailed_json(reader_ptr: *mut C2paReader) -> *mut c_char {
1470 let c2pa_reader = deref_or_return_null!(reader_ptr, C2paReader);
1471
1472 to_c_string(c2pa_reader.detailed_json())
1473}
1474
1475#[no_mangle]
1483pub unsafe extern "C" fn c2pa_reader_remote_url(reader_ptr: *mut C2paReader) -> *const c_char {
1484 let c2pa_reader = deref_or_return_null!(reader_ptr, C2paReader);
1485
1486 option_to_c_string!(c2pa_reader.remote_url())
1487}
1488
1489#[no_mangle]
1497pub unsafe extern "C" fn c2pa_reader_is_embedded(reader_ptr: *mut C2paReader) -> bool {
1498 let c2pa_reader = deref_or_return_false!(reader_ptr, C2paReader);
1499
1500 c2pa_reader.is_embedded()
1501}
1502
1503#[no_mangle]
1528pub unsafe extern "C" fn c2pa_reader_resource_to_stream(
1529 reader_ptr: *mut C2paReader,
1530 uri: *const c_char,
1531 stream: *mut C2paStream,
1532) -> i64 {
1533 let uri = cstr_or_return_int!(uri);
1534 let reader = deref_mut_or_return_int!(reader_ptr, C2paReader);
1535 let result = reader.resource_to_stream(&uri, &mut (*stream));
1536 let len = ok_or_return_int!(result);
1537 len as i64
1538}
1539
1540#[no_mangle]
1550pub unsafe extern "C" fn c2pa_reader_supported_mime_types(
1551 count: *mut usize,
1552) -> *const *const c_char {
1553 c2pa_mime_types_to_c_array(C2paReader::supported_mime_types(), count)
1554}
1555
1556#[no_mangle]
1577#[deprecated(note = "Use c2pa_builder_from_context() then c2pa_builder_set_definition() instead.")]
1578pub unsafe extern "C" fn c2pa_builder_from_json(manifest_json: *const c_char) -> *mut C2paBuilder {
1579 let manifest_json = cstr_or_return_null!(manifest_json);
1580 #[allow(deprecated)]
1583 let result = C2paBuilder::from_json(&manifest_json);
1584 let result = ok_or_return_null!(result);
1585 box_tracked!(result)
1586}
1587
1588#[no_mangle]
1611pub unsafe extern "C" fn c2pa_builder_from_context(context: *mut C2paContext) -> *mut C2paBuilder {
1612 let context = deref_or_return_null!(context, C2paContext);
1613 box_tracked!(C2paBuilder::from_shared_context(context))
1614}
1615
1616#[no_mangle]
1637#[deprecated(note = "Use c2pa_builder_from_context() then c2pa_builder_with_archive() instead.")]
1638#[allow(deprecated)]
1639pub unsafe extern "C" fn c2pa_builder_from_archive(stream: *mut C2paStream) -> *mut C2paBuilder {
1640 let stream = deref_mut_or_return_null!(stream, C2paStream);
1641 box_tracked!(ok_or_return_null!(C2paBuilder::from_archive(
1642 &mut (*stream)
1643 )))
1644}
1645
1646#[no_mangle]
1656pub unsafe extern "C" fn c2pa_builder_supported_mime_types(
1657 count: *mut usize,
1658) -> *const *const c_char {
1659 c2pa_mime_types_to_c_array(C2paBuilder::supported_mime_types(), count)
1660}
1661
1662#[no_mangle]
1670#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
1671pub unsafe extern "C" fn c2pa_builder_free(builder_ptr: *mut C2paBuilder) {
1672 cimpl_free!(builder_ptr);
1673}
1674
1675#[no_mangle]
1699pub unsafe extern "C" fn c2pa_builder_with_definition(
1700 builder: *mut C2paBuilder,
1701 manifest_json: *const c_char,
1702) -> *mut C2paBuilder {
1703 let manifest_json = cstr_or_return_null!(manifest_json);
1705
1706 untrack_or_return_null!(builder, C2paBuilder);
1708 let builder = Box::from_raw(builder);
1709 let result = (*builder).with_definition(manifest_json);
1710 box_tracked!(ok_or_return_null!(result))
1711}
1712
1713#[no_mangle]
1736pub unsafe extern "C" fn c2pa_builder_with_archive(
1737 builder: *mut C2paBuilder,
1738 stream: *mut C2paStream,
1739) -> *mut C2paBuilder {
1740 let stream = deref_mut_or_return_null!(stream, C2paStream);
1742
1743 untrack_or_return_null!(builder, C2paBuilder);
1745 let builder = Box::from_raw(builder);
1746 let result = (*builder).with_archive(stream);
1747 box_tracked!(ok_or_return_null!(result))
1748}
1749
1750#[no_mangle]
1774pub unsafe extern "C" fn c2pa_builder_set_intent(
1775 builder_ptr: *mut C2paBuilder,
1776 intent: C2paBuilderIntent,
1777 digital_source_type: C2paDigitalSourceType,
1778) -> c_int {
1779 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
1780
1781 let builder_intent = match intent {
1782 C2paBuilderIntent::Create => c2pa::BuilderIntent::Create(digital_source_type.into()),
1783 C2paBuilderIntent::Edit => c2pa::BuilderIntent::Edit,
1784 C2paBuilderIntent::Update => c2pa::BuilderIntent::Update,
1785 };
1786
1787 builder.set_intent(builder_intent);
1788 0 as c_int
1789}
1790
1791#[no_mangle]
1799#[allow(clippy::unused_unit)] pub unsafe extern "C" fn c2pa_builder_set_no_embed(builder_ptr: *mut C2paBuilder) {
1801 let builder = deref_mut_or_return!(builder_ptr, C2paBuilder, ());
1802 builder.set_no_embed(true);
1803}
1804
1805#[no_mangle]
1817pub unsafe extern "C" fn c2pa_builder_set_remote_url(
1818 builder_ptr: *mut C2paBuilder,
1819 remote_url: *const c_char,
1820) -> c_int {
1821 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
1822 let remote_url = cstr_or_return_int!(remote_url);
1823 builder.set_remote_url(&remote_url);
1824 0 as c_int
1825}
1826
1827#[no_mangle]
1842pub unsafe extern "C" fn c2pa_builder_set_base_path(
1843 builder_ptr: *mut C2paBuilder,
1844 base_path: *const c_char,
1845) -> c_int {
1846 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
1847 let base_path = cstr_or_return_int!(base_path);
1848 builder.set_base_path(&base_path);
1849 0 as c_int
1850}
1851
1852#[no_mangle]
1867pub unsafe extern "C" fn c2pa_builder_add_resource(
1868 builder_ptr: *mut C2paBuilder,
1869 uri: *const c_char,
1870 stream: *mut C2paStream,
1871) -> c_int {
1872 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
1873 let uri = cstr_or_return_int!(uri);
1874 let result = builder.add_resource(&uri, &mut (*stream));
1875 ok_or_return_int!(result);
1876 0 }
1878
1879#[no_mangle]
1894pub unsafe extern "C" fn c2pa_builder_add_ingredient_from_stream(
1895 builder_ptr: *mut C2paBuilder,
1896 ingredient_json: *const c_char,
1897 format: *const c_char,
1898 source: *mut C2paStream,
1899) -> c_int {
1900 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
1901 let ingredient_json = cstr_or_return_int!(ingredient_json);
1902 let format = cstr_or_return_int!(format);
1903 let source = deref_mut_or_return_int!(source, C2paStream);
1904 let result = builder.add_ingredient_from_stream(&ingredient_json, &format, &mut (*source));
1905 ok_or_return_int!(result);
1906 0 }
1908
1909#[no_mangle]
1963pub unsafe extern "C" fn c2pa_builder_add_action(
1964 builder_ptr: *mut C2paBuilder,
1965 action_json: *const c_char,
1966) -> c_int {
1967 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
1968 let action_json = cstr_or_return_int!(action_json);
1969
1970 let action_value: serde_json::Value = ok_or_return_int!(serde_json::from_str(&action_json));
1972
1973 ok_or_return_int!(builder.add_action(action_value));
1974
1975 0
1976}
1977
1978#[no_mangle]
2001pub unsafe extern "C" fn c2pa_builder_to_archive(
2002 builder_ptr: *mut C2paBuilder,
2003 stream: *mut C2paStream,
2004) -> c_int {
2005 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2006 let stream = deref_mut_or_return_int!(stream, C2paStream);
2007 let result = builder.to_archive(&mut *stream);
2008 ok_or_return_int!(result);
2009 0 }
2011
2012#[no_mangle]
2040pub unsafe extern "C" fn c2pa_builder_add_ingredient_from_archive(
2041 builder_ptr: *mut C2paBuilder,
2042 stream: *mut C2paStream,
2043) -> c_int {
2044 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2045 let stream = deref_mut_or_return_int!(stream, C2paStream);
2046 let result = builder.add_ingredient_from_archive(&mut *stream);
2047 ok_or_return_int!(result);
2048 0 }
2050
2051#[no_mangle]
2080pub unsafe extern "C" fn c2pa_builder_write_ingredient_archive(
2081 builder_ptr: *mut C2paBuilder,
2082 ingredient_id: *const c_char,
2083 stream: *mut C2paStream,
2084) -> c_int {
2085 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2086 let ingredient_id = cstr_or_return_int!(ingredient_id);
2087 let stream = deref_mut_or_return_int!(stream, C2paStream);
2088 let result = builder.write_ingredient_archive(&ingredient_id, &mut *stream);
2089 ok_or_return_int!(result);
2090 0 }
2092
2093#[no_mangle]
2112pub unsafe extern "C" fn c2pa_builder_sign(
2113 builder_ptr: *mut C2paBuilder,
2114 format: *const c_char,
2115 source: *mut C2paStream,
2116 dest: *mut C2paStream,
2117 signer_ptr: *mut C2paSigner,
2118 manifest_bytes_ptr: *mut *const c_uchar,
2119) -> i64 {
2120 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2121 let format = cstr_or_return_int!(format);
2122 let source = deref_mut_or_return_int!(source, C2paStream);
2123 let dest = deref_mut_or_return_int!(dest, C2paStream);
2124 let c2pa_signer = deref_mut_or_return_int!(signer_ptr, C2paSigner);
2125 ptr_or_return_int!(manifest_bytes_ptr);
2126
2127 let result = builder.sign(
2128 c2pa_signer.signer.as_ref(),
2129 &format,
2130 &mut *source,
2131 &mut *dest,
2132 );
2133 let manifest_bytes = ok_or_return_int!(result);
2134 let len = manifest_bytes.len() as i64;
2135 if !manifest_bytes_ptr.is_null() {
2136 *manifest_bytes_ptr = to_c_bytes(manifest_bytes);
2137 }
2138 len
2139}
2140
2141#[no_mangle]
2167pub unsafe extern "C" fn c2pa_builder_sign_context(
2168 builder_ptr: *mut C2paBuilder,
2169 format: *const c_char,
2170 source: *mut C2paStream,
2171 dest: *mut C2paStream,
2172 manifest_bytes_ptr: *mut *const c_uchar,
2173) -> i64 {
2174 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2175 let format = cstr_or_return_int!(format);
2176 let source = deref_mut_or_return_int!(source, C2paStream);
2177 let dest = deref_mut_or_return_int!(dest, C2paStream);
2178 ptr_or_return_int!(manifest_bytes_ptr);
2179
2180 let result = builder.save_to_stream(&format, &mut *source, &mut *dest);
2181 let manifest_bytes = ok_or_return_int!(result);
2182 let len = manifest_bytes.len() as i64;
2183 if !manifest_bytes_ptr.is_null() {
2184 *manifest_bytes_ptr = to_c_bytes(manifest_bytes);
2185 }
2186 len
2187}
2188
2189#[no_mangle]
2197#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
2198pub unsafe extern "C" fn c2pa_manifest_bytes_free(manifest_bytes_ptr: *const c_uchar) {
2199 cimpl_free!(manifest_bytes_ptr);
2200}
2201
2202#[no_mangle]
2220pub unsafe extern "C" fn c2pa_builder_data_hashed_placeholder(
2221 builder_ptr: *mut C2paBuilder,
2222 reserved_size: usize,
2223 format: *const c_char,
2224 manifest_bytes_ptr: *mut *const c_uchar,
2225) -> i64 {
2226 ptr_or_return_int!(manifest_bytes_ptr);
2227 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2228 let format = cstr_or_return_int!(format);
2229 let result = builder.data_hashed_placeholder(reserved_size, &format);
2230 let manifest_bytes = ok_or_return_int!(result);
2231 let len = manifest_bytes.len() as i64;
2232 if !manifest_bytes_ptr.is_null() {
2233 *manifest_bytes_ptr = to_c_bytes(manifest_bytes);
2234 }
2235 len
2236}
2237
2238#[no_mangle]
2259pub unsafe extern "C" fn c2pa_builder_sign_data_hashed_embeddable(
2260 builder_ptr: *mut C2paBuilder,
2261 signer_ptr: *mut C2paSigner,
2262 data_hash: *const c_char,
2263 format: *const c_char,
2264 asset: *mut C2paStream,
2265 manifest_bytes_ptr: *mut *const c_uchar,
2266) -> i64 {
2267 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2268 let c2pa_signer = deref_mut_or_return_int!(signer_ptr, C2paSigner);
2269 let data_hash_json = cstr_or_return_int!(data_hash);
2270 let format = cstr_or_return_int!(format);
2271 ptr_or_return_int!(manifest_bytes_ptr);
2272
2273 let mut data_hash: DataHash = ok_or_return_int!(serde_json::from_str(&data_hash_json)
2274 .map_err(|e| Error::from_c2pa_error(c2pa::Error::JsonError(e))));
2275
2276 if !asset.is_null() {
2277 ok_or_return_int!(data_hash
2279 .gen_hash_from_stream(&mut *asset)
2280 .map_err(Error::from_c2pa_error));
2281 }
2282
2283 let result =
2284 builder.sign_data_hashed_embeddable(c2pa_signer.signer.as_ref(), &data_hash, &format);
2285
2286 let manifest_bytes = ok_or_return_int!(result);
2287 let len = manifest_bytes.len() as i64;
2288 if !manifest_bytes_ptr.is_null() {
2289 *manifest_bytes_ptr = to_c_bytes(manifest_bytes);
2290 }
2291 len
2292}
2293
2294#[no_mangle]
2307pub unsafe extern "C" fn c2pa_builder_needs_placeholder(
2308 builder_ptr: *mut C2paBuilder,
2309 format: *const c_char,
2310) -> c_int {
2311 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2312 let format = cstr_or_return_int!(format);
2313 if builder.needs_placeholder(&format) {
2314 1
2315 } else {
2316 0
2317 }
2318}
2319
2320#[no_mangle]
2333pub unsafe extern "C" fn c2pa_builder_hash_type(
2334 builder_ptr: *mut C2paBuilder,
2335 format: *const c_char,
2336 out_hash_type: *mut C2paHashType,
2337) -> c_int {
2338 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2339 let format = cstr_or_return_int!(format);
2340 if out_hash_type.is_null() {
2341 return -1;
2342 }
2343 let hash_type = match builder.hash_type(&format) {
2344 c2pa::HashType::Data => C2paHashType::DataHash,
2345 c2pa::HashType::Bmff => C2paHashType::BmffHash,
2346 c2pa::HashType::Box => C2paHashType::BoxHash,
2347 };
2348 *out_hash_type = hash_type;
2349 0
2350}
2351
2352#[no_mangle]
2377pub unsafe extern "C" fn c2pa_builder_placeholder(
2378 builder_ptr: *mut C2paBuilder,
2379 format: *const c_char,
2380 manifest_bytes_ptr: *mut *const c_uchar,
2381) -> i64 {
2382 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2383 let format = cstr_or_return_int!(format);
2384 let result = builder.placeholder(&format);
2385 let manifest_bytes = ok_or_return_int!(result);
2386 let len = manifest_bytes.len() as i64;
2387 if !manifest_bytes_ptr.is_null() {
2388 *manifest_bytes_ptr = to_c_bytes(manifest_bytes);
2389 }
2390 len
2391}
2392
2393#[no_mangle]
2422pub unsafe extern "C" fn c2pa_builder_sign_embeddable(
2423 builder_ptr: *mut C2paBuilder,
2424 format: *const c_char,
2425 manifest_bytes_ptr: *mut *const c_uchar,
2426) -> i64 {
2427 ptr_or_return_int!(manifest_bytes_ptr);
2428 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2429 let format = cstr_or_return_int!(format);
2430 let result = builder.sign_embeddable(&format);
2431 let manifest_bytes = ok_or_return_int!(result);
2432 let len = manifest_bytes.len() as i64;
2433 if !manifest_bytes_ptr.is_null() {
2434 *manifest_bytes_ptr = to_c_bytes(manifest_bytes);
2435 }
2436 len
2437}
2438
2439#[no_mangle]
2465pub unsafe extern "C" fn c2pa_builder_set_data_hash_exclusions(
2466 builder_ptr: *mut C2paBuilder,
2467 exclusions_ptr: *const u64,
2468 exclusion_count: usize,
2469) -> c_int {
2470 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2471
2472 if exclusion_count == 0 || exclusions_ptr.is_null() {
2473 ok_or_return_int!(builder.set_data_hash_exclusions(vec![]));
2474 return 0;
2475 }
2476
2477 let flat = std::slice::from_raw_parts(exclusions_ptr, exclusion_count * 2);
2478 let exclusions: Vec<c2pa::HashRange> = flat
2479 .chunks_exact(2)
2480 .map(|pair| c2pa::HashRange::new(pair[0], pair[1]))
2481 .collect();
2482
2483 ok_or_return_int!(builder.set_data_hash_exclusions(exclusions));
2484 0
2485}
2486
2487#[no_mangle]
2502pub unsafe extern "C" fn c2pa_builder_set_fixed_size_merkle(
2503 builder_ptr: *mut C2paBuilder,
2504 fixed_size_kb: usize,
2505) -> c_int {
2506 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2507
2508 builder.set_bmff_hash_fixed_leaf_size(fixed_size_kb);
2509
2510 0
2511}
2512
2513#[no_mangle]
2540pub unsafe extern "C" fn c2pa_builder_hash_mdat_bytes(
2541 builder_ptr: *mut C2paBuilder,
2542 mdat_id: usize,
2543 data_ptr: *const c_uchar,
2544 data_len: usize,
2545 large_size: bool,
2546) -> c_int {
2547 ptr_or_return_int!(data_ptr);
2548 ptr_or_return_int!(builder_ptr);
2549
2550 let data = bytes_or_return_int!(data_ptr, data_len, "mdat_data");
2551
2552 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2553
2554 ok_or_return_int!(builder.hash_bmff_mdat_bytes(mdat_id, data, large_size));
2556 0
2557}
2558
2559#[no_mangle]
2591pub unsafe extern "C" fn c2pa_builder_update_hash_from_stream(
2592 builder_ptr: *mut C2paBuilder,
2593 format: *const c_char,
2594 stream: *mut C2paStream,
2595) -> c_int {
2596 let builder = deref_mut_or_return_int!(builder_ptr, C2paBuilder);
2597 let format = cstr_or_return_int!(format);
2598 let stream = deref_mut_or_return_int!(stream, C2paStream);
2599 ok_or_return_int!(builder.update_hash_from_stream(&format, &mut *stream));
2600 0
2601}
2602
2603#[no_mangle]
2624pub unsafe extern "C" fn c2pa_format_embeddable(
2625 format: *const c_char,
2626 manifest_bytes_ptr: *const c_uchar,
2627 manifest_bytes_size: usize,
2628 result_bytes_ptr: *mut *const c_uchar,
2629) -> i64 {
2630 let format = cstr_or_return_int!(format);
2631 ptr_or_return_int!(manifest_bytes_ptr);
2632 ptr_or_return_int!(result_bytes_ptr);
2633
2634 let bytes = bytes_or_return_int!(
2635 manifest_bytes_ptr,
2636 manifest_bytes_size,
2637 "manifest_bytes_ptr"
2638 );
2639
2640 let result = c2pa::Builder::composed_manifest(bytes, &format);
2641 let result_bytes = ok_or_return_int!(result);
2642 let len = result_bytes.len() as i64;
2643 if !result_bytes_ptr.is_null() {
2644 *result_bytes_ptr = to_c_bytes(result_bytes);
2645 }
2646 len
2647}
2648
2649#[no_mangle]
2679pub unsafe extern "C" fn c2pa_signer_create(
2680 context: *const c_void,
2681 callback: SignerCallback,
2682 alg: C2paSigningAlg,
2683 certs: *const c_char,
2684 tsa_url: *const c_char,
2685) -> *mut C2paSigner {
2686 let certs = cstr_or_return_null!(certs);
2687 let tsa_url = cstr_option!(tsa_url);
2688 let context = context as *const ();
2689
2690 let c_callback = move |context: *const (), data: &[u8]| {
2694 let signed_len_max = data.len() * 2;
2696 let mut signed_bytes: Vec<u8> = vec![0; signed_len_max];
2697 let signed_size = unsafe {
2698 (callback)(
2699 context,
2700 data.as_ptr(),
2701 data.len(),
2702 signed_bytes.as_mut_ptr(),
2703 signed_len_max,
2704 )
2705 };
2706 if signed_size < 0 {
2707 return Err(c2pa::Error::CoseSignature); }
2709 signed_bytes.set_len(signed_size as usize);
2710 Ok(signed_bytes)
2711 };
2712
2713 let mut signer = CallbackSigner::new(c_callback, alg.into(), certs).set_context(context);
2714 if let Some(tsa_url) = tsa_url.as_ref() {
2715 signer = signer.set_tsa_url(tsa_url);
2716 }
2717 box_tracked!(C2paSigner {
2718 signer: Box::new(signer),
2719 })
2720}
2721
2722#[no_mangle]
2745pub unsafe extern "C" fn c2pa_signer_from_info(signer_info: &C2paSignerInfo) -> *mut C2paSigner {
2746 let signer_info = SignerInfo {
2747 alg: cstr_or_return_null!(signer_info.alg),
2748 sign_cert: cstr_or_return_null!(signer_info.sign_cert).into_bytes(),
2749 private_key: cstr_or_return_null!(signer_info.private_key).into_bytes(),
2750 ta_url: cstr_option!(signer_info.ta_url),
2751 };
2752
2753 let signer = signer_info.signer();
2754 match signer {
2755 Ok(signer) => box_tracked!(C2paSigner {
2756 signer: Box::new(signer),
2757 }),
2758 Err(err) => {
2759 CimplError::from(err).set_last();
2760 std::ptr::null_mut()
2761 }
2762 }
2763}
2764
2765#[no_mangle]
2775#[deprecated(
2776 note = "Use c2pa_context_builder_set_signer() to configure a signer on a context instead."
2777)]
2778pub unsafe extern "C" fn c2pa_signer_from_settings() -> *mut C2paSigner {
2779 #[allow(deprecated)]
2781 let signer = ok_or_return_null!(C2paSettings::signer());
2782 box_tracked!(C2paSigner {
2783 signer: Box::new(signer),
2784 })
2785}
2786
2787#[no_mangle]
2799pub unsafe extern "C" fn c2pa_signer_reserve_size(signer_ptr: *mut C2paSigner) -> i64 {
2800 let c2pa_signer = deref_mut_or_return_int!(signer_ptr, C2paSigner);
2801 c2pa_signer.signer.reserve_size() as i64
2802}
2803
2804#[no_mangle]
2812#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
2813pub unsafe extern "C" fn c2pa_signer_free(signer_ptr: *const C2paSigner) {
2814 cimpl_free!(signer_ptr);
2815}
2816
2817#[no_mangle]
2818pub unsafe extern "C" fn c2pa_ed25519_sign(
2823 bytes: *const c_uchar,
2824 len: usize,
2825 private_key: *const c_char,
2826) -> *const c_uchar {
2827 let private_key = cstr_or_return_null!(private_key);
2828
2829 let bytes = bytes_or_return_null!(bytes, len, "bytes");
2830
2831 let signed_bytes =
2832 ok_or_return_null!(CallbackSigner::ed25519_sign(bytes, private_key.as_bytes()));
2833
2834 to_c_bytes(signed_bytes)
2835}
2836
2837#[no_mangle]
2838#[deprecated(note = "Use c2pa_free() instead, which works for all pointer types.")]
2846pub unsafe extern "C" fn c2pa_signature_free(signature_ptr: *const u8) {
2847 cimpl_free!(signature_ptr);
2848}
2849
2850unsafe fn c2pa_mime_types_to_c_array(strs: Vec<String>, count: *mut usize) -> *const *const c_char {
2868 let mut mime_ptrs: Vec<*mut c_char> = strs.into_iter().map(to_c_string).collect();
2873 mime_ptrs.shrink_to_fit();
2874
2875 debug_assert_eq!(mime_ptrs.len(), mime_ptrs.capacity());
2878
2879 *count = mime_ptrs.len();
2880
2881 let ptr = mime_ptrs.as_ptr();
2882 std::mem::forget(mime_ptrs);
2883
2884 ptr as *const *const c_char
2885}
2886
2887#[cfg(test)]
2888#[allow(deprecated)]
2889mod tests {
2890 use std::{ffi::CString, io::Seek, panic::catch_unwind};
2891
2892 use super::*;
2893 use crate::TestStream;
2894
2895 macro_rules! fixture_path {
2896 ($path:expr) => {
2897 concat!("../../sdk/tests/fixtures/", $path)
2898 };
2899 }
2900
2901 fn setup_signer_and_builder_for_signing_tests() -> (*mut C2paSigner, *mut C2paBuilder) {
2904 let certs = include_str!(fixture_path!("certs/ed25519.pub"));
2905 let private_key = include_bytes!(fixture_path!("certs/ed25519.pem"));
2906 let alg = CString::new("Ed25519").unwrap();
2907 let sign_cert = CString::new(certs).unwrap();
2908 let private_key = CString::new(private_key).unwrap();
2909 let signer_info = C2paSignerInfo {
2910 alg: alg.as_ptr(),
2911 sign_cert: sign_cert.as_ptr(),
2912 private_key: private_key.as_ptr(),
2913 ta_url: std::ptr::null(),
2914 };
2915 let signer = unsafe { c2pa_signer_from_info(&signer_info) };
2916 assert!(!signer.is_null());
2917
2918 let manifest_def = CString::new("{}").unwrap();
2919 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
2920 assert!(!builder.is_null());
2921
2922 (signer, builder)
2923 }
2924
2925 #[test]
2926 fn test_ed25519_sign() {
2927 let bytes = b"test";
2928 let private_key = include_bytes!(fixture_path!("certs/ed25519.pem"));
2929 let private_key = CString::new(private_key).unwrap();
2930 let signature =
2931 unsafe { c2pa_ed25519_sign(bytes.as_ptr(), bytes.len(), private_key.as_ptr()) };
2932 assert!(!signature.is_null());
2933 unsafe { c2pa_signature_free(signature) };
2934 }
2935
2936 #[test]
2937 fn test_c2pa_signer_from_info() {
2938 let certs = include_str!(fixture_path!("certs/ed25519.pub"));
2939 let private_key = include_bytes!(fixture_path!("certs/ed25519.pem"));
2940 let alg = CString::new("Ed25519").unwrap();
2941 let sign_cert = CString::new(certs).unwrap();
2942 let private_key = CString::new(private_key).unwrap();
2943 let signer_info = C2paSignerInfo {
2944 alg: alg.as_ptr(),
2945 sign_cert: sign_cert.as_ptr(),
2946 private_key: private_key.as_ptr(),
2947 ta_url: std::ptr::null(),
2948 };
2949 let signer = unsafe { c2pa_signer_from_info(&signer_info) };
2950 assert!(!signer.is_null());
2951 unsafe { c2pa_signer_free(signer) };
2952 }
2953
2954 #[test]
2955 fn test_signer_from_info_bad_alg() {
2956 let alg = CString::new("BadAlg").unwrap();
2957 let sign_cert = CString::new("certs").unwrap();
2958 let private_key = CString::new("private_key").unwrap();
2959 let signer_info = C2paSignerInfo {
2960 alg: alg.as_ptr(),
2961 sign_cert: sign_cert.as_ptr(),
2962 private_key: private_key.as_ptr(),
2963 ta_url: std::ptr::null(),
2964 };
2965 let signer = unsafe { c2pa_signer_from_info(&signer_info) };
2966 assert!(signer.is_null());
2967 let error = unsafe { c2pa_error() };
2968 let error = unsafe { CString::from_raw(error) };
2969 assert_eq!(error.to_str().unwrap(), "Other: Invalid signing algorithm");
2970 }
2971
2972 #[test]
2973 fn test_sign_with_info() {
2974 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
2975 let mut source_stream = TestStream::new(source_image.to_vec());
2976 let dest_vec = Vec::new();
2977 let mut dest_stream = TestStream::new(dest_vec);
2978
2979 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
2980
2981 let format = CString::new("image/jpeg").unwrap();
2982 let mut manifest_bytes_ptr = std::ptr::null();
2983 let _ = unsafe {
2984 c2pa_builder_sign(
2985 builder,
2986 format.as_ptr(),
2987 source_stream.as_ptr(),
2988 dest_stream.as_ptr(),
2989 signer,
2990 &mut manifest_bytes_ptr,
2991 )
2992 };
2993 unsafe {
2998 c2pa_manifest_bytes_free(manifest_bytes_ptr);
2999 }
3000 unsafe { c2pa_builder_free(builder) };
3001 unsafe { c2pa_signer_free(signer) };
3002 }
3003
3004 #[test]
3005 fn builder_add_actions_and_sign() {
3006 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
3007 let mut source_stream = TestStream::new(source_image.to_vec());
3008 let dest_vec = Vec::new();
3009 let mut dest_stream = TestStream::new(dest_vec);
3010
3011 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
3012
3013 let action_json = CString::new(
3014 r#"{
3015 "action": "com.example.test-action",
3016 "parameters": {
3017 "key1": "value1",
3018 "key2": "value2"
3019 }
3020 }"#,
3021 )
3022 .unwrap();
3023
3024 let result = unsafe { c2pa_builder_add_action(builder, action_json.as_ptr()) };
3026 assert_eq!(result, 0);
3027
3028 let format = CString::new("image/jpeg").unwrap();
3029 let mut manifest_bytes_ptr = std::ptr::null();
3030 let _ = unsafe {
3031 c2pa_builder_sign(
3032 builder,
3033 format.as_ptr(),
3034 source_stream.as_ptr(),
3035 dest_stream.as_ptr(),
3036 signer,
3037 &mut manifest_bytes_ptr,
3038 )
3039 };
3040
3041 dest_stream.stream_mut().rewind().unwrap();
3043
3044 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), dest_stream.as_ptr()) };
3045 if reader.is_null() {
3046 if let Some(msg) = CimplError::last_message() {
3047 panic!("Reader creation failed: {}", msg);
3048 }
3049 }
3050 assert!(!reader.is_null());
3051
3052 let json = unsafe { c2pa_reader_json(reader) };
3053 assert!(!json.is_null());
3054 let json_str = unsafe { CString::from_raw(json) };
3055 let json_content = json_str.to_str().unwrap();
3056
3057 assert!(json_content.contains("manifest"));
3058 assert!(json_content.contains("com.example.test-action"));
3059
3060 unsafe {
3061 c2pa_manifest_bytes_free(manifest_bytes_ptr);
3062 c2pa_builder_free(builder);
3063 c2pa_signer_free(signer);
3064 c2pa_reader_free(reader);
3065 }
3066 }
3067
3068 #[test]
3069 fn builder_create_intent_digital_creation_and_sign() {
3070 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
3071 let mut source_stream = TestStream::new(source_image.to_vec());
3072 let dest_vec = Vec::new();
3073 let mut dest_stream = TestStream::new(dest_vec);
3074
3075 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
3076
3077 let result = unsafe {
3079 c2pa_builder_set_intent(
3080 builder,
3081 C2paBuilderIntent::Create,
3082 C2paDigitalSourceType::DigitalCreation,
3083 )
3084 };
3085 assert_eq!(result, 0);
3086
3087 let format = CString::new("image/jpeg").unwrap();
3088 let mut manifest_bytes_ptr = std::ptr::null();
3089 let _ = unsafe {
3090 c2pa_builder_sign(
3091 builder,
3092 format.as_ptr(),
3093 source_stream.as_ptr(),
3094 dest_stream.as_ptr(),
3095 signer,
3096 &mut manifest_bytes_ptr,
3097 )
3098 };
3099
3100 dest_stream.stream_mut().rewind().unwrap();
3102 let format = CString::new("image/jpeg").unwrap();
3103
3104 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), dest_stream.as_ptr()) };
3105 assert!(!reader.is_null());
3106
3107 let json = unsafe { c2pa_reader_json(reader) };
3108 assert!(!json.is_null());
3109
3110 let json_str = unsafe { CString::from_raw(json) };
3111 let json_content = json_str.to_str().unwrap();
3112
3113 assert!(json_content.contains("c2pa.created"));
3114 assert!(json_content.contains("digitalSourceType"));
3116 assert!(json_content.contains("digitalCreation"));
3117 assert_eq!(
3119 json_content.matches("\"action\": \"c2pa.created\"").count(),
3120 1
3121 );
3122
3123 unsafe {
3124 c2pa_manifest_bytes_free(manifest_bytes_ptr);
3125 c2pa_builder_free(builder);
3126 c2pa_signer_free(signer);
3127 c2pa_reader_free(reader);
3128 }
3129 }
3130
3131 #[test]
3132 fn builder_create_intent_empty_and_sign() {
3133 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
3134 let mut source_stream = TestStream::new(source_image.to_vec());
3135 let dest_vec = Vec::new();
3136 let mut dest_stream = TestStream::new(dest_vec);
3137
3138 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
3139
3140 let result = unsafe {
3142 c2pa_builder_set_intent(
3143 builder,
3144 C2paBuilderIntent::Create,
3145 C2paDigitalSourceType::Empty,
3146 )
3147 };
3148 assert_eq!(result, 0);
3149
3150 let format = CString::new("image/jpeg").unwrap();
3151 let mut manifest_bytes_ptr = std::ptr::null();
3152 let _ = unsafe {
3153 c2pa_builder_sign(
3154 builder,
3155 format.as_ptr(),
3156 source_stream.as_ptr(),
3157 dest_stream.as_ptr(),
3158 signer,
3159 &mut manifest_bytes_ptr,
3160 )
3161 };
3162
3163 dest_stream.stream_mut().rewind().unwrap();
3165 let format = CString::new("image/jpeg").unwrap();
3166
3167 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), dest_stream.as_ptr()) };
3168 assert!(!reader.is_null());
3169
3170 let json = unsafe { c2pa_reader_json(reader) };
3171 assert!(!json.is_null());
3172
3173 let json_str = unsafe { CString::from_raw(json) };
3174 let json_content = json_str.to_str().unwrap();
3175
3176 assert!(json_content.contains("c2pa.created"));
3177 assert!(json_content.contains("digitalsourcetype/empty"));
3179
3180 unsafe {
3181 c2pa_manifest_bytes_free(manifest_bytes_ptr);
3182 c2pa_builder_free(builder);
3183 c2pa_signer_free(signer);
3184 c2pa_reader_free(reader);
3185 }
3186 }
3187
3188 #[test]
3189 fn builder_edit_intent_and_sign() {
3190 let signed_source_image = include_bytes!(fixture_path!("C.jpg"));
3192 let mut source_stream = TestStream::new(signed_source_image.to_vec());
3193 let dest_vec = Vec::new();
3194 let mut dest_stream = TestStream::new(dest_vec);
3195
3196 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
3197
3198 let result = unsafe {
3201 c2pa_builder_set_intent(
3202 builder,
3203 C2paBuilderIntent::Edit,
3204 C2paDigitalSourceType::Empty,
3205 )
3206 };
3207 assert_eq!(result, 0);
3208
3209 let format = CString::new("image/jpeg").unwrap();
3211 let mut manifest_bytes_ptr = std::ptr::null();
3212 let _ = unsafe {
3213 c2pa_builder_sign(
3214 builder,
3215 format.as_ptr(),
3216 source_stream.as_ptr(),
3217 dest_stream.as_ptr(),
3218 signer,
3219 &mut manifest_bytes_ptr,
3220 )
3221 };
3222
3223 dest_stream.stream_mut().rewind().unwrap();
3224 let format = CString::new("image/jpeg").unwrap();
3225
3226 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), dest_stream.as_ptr()) };
3227 assert!(!reader.is_null());
3228
3229 let json = unsafe { c2pa_reader_json(reader) };
3230 assert!(!json.is_null());
3231 let json_str = unsafe { CString::from_raw(json) };
3232 let json_content = json_str.to_str().unwrap();
3233
3234 assert!(json_content.contains("c2pa.opened"));
3235 assert!(!json_content.contains("digitalsourcetype/empty"));
3238
3239 unsafe {
3240 c2pa_manifest_bytes_free(manifest_bytes_ptr);
3241 c2pa_builder_free(builder);
3242 c2pa_signer_free(signer);
3243 c2pa_reader_free(reader);
3244 }
3245 }
3246
3247 #[test]
3248 fn test_c2pa_builder_no_embed_null() {
3249 let builder: *mut c2pa::Builder = std::ptr::null_mut();
3250 unsafe { c2pa_builder_set_no_embed(builder) };
3251 }
3252
3253 #[test]
3254 fn test_c2pa_builder_set_remote_url_null() {
3255 let builder: *mut c2pa::Builder = std::ptr::null_mut();
3256 let remote_url = CString::new("https://example.com").unwrap();
3257 let result = unsafe { c2pa_builder_set_remote_url(builder, remote_url.as_ptr()) };
3258 assert_eq!(result, -1);
3259 let error = unsafe { c2pa_error() };
3260 let error = unsafe { CString::from_raw(error) };
3261 assert_eq!(error.to_str().unwrap(), "NullParameter: builder_ptr");
3262 }
3263
3264 #[test]
3265 fn test_c2pa_builder_no_embed() {
3266 let manifest_def = CString::new("{}").unwrap();
3267 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3268 assert!(!builder.is_null());
3269 unsafe { c2pa_builder_set_no_embed(builder) };
3270 unsafe { c2pa_builder_free(builder) };
3271 }
3272
3273 #[test]
3274 fn test_c2pa_version() {
3275 let version = unsafe { c2pa_version() };
3276 assert!(!version.is_null());
3277 let version_str = unsafe { CString::from_raw(version) };
3278 assert!(!version_str.to_str().unwrap().is_empty());
3279 }
3280
3281 #[test]
3282 fn test_c2pa_error_no_error() {
3283 let error = unsafe { c2pa_error() };
3284 assert!(!error.is_null());
3285 let error_str = unsafe { CString::from_raw(error) };
3286 assert_eq!(error_str.to_str().unwrap(), "");
3287 }
3288
3289 #[test]
3290 fn test_c2pa_load_settings() {
3291 let settings = CString::new("{}").unwrap();
3292 let format = CString::new("json").unwrap();
3293 let result = unsafe { c2pa_load_settings(settings.as_ptr(), format.as_ptr()) };
3294 assert_eq!(result, 0);
3295 }
3296
3297 #[test]
3298 #[cfg(feature = "file_io")]
3299 fn test_c2pa_read_file_null_path() {
3300 let data_dir = CString::new("/tmp").unwrap();
3301 let result = unsafe { c2pa_read_file(std::ptr::null(), data_dir.as_ptr()) };
3302 assert!(result.is_null());
3303 let error = unsafe { c2pa_error() };
3304 let error_str = unsafe { CString::from_raw(error) };
3305 assert_eq!(error_str.to_str().unwrap(), "NullParameter: path");
3306 }
3307
3308 #[test]
3309 #[cfg(feature = "file_io")]
3310 fn test_c2pa_read_ingredient_file_null_path() {
3311 let data_dir = CString::new("/tmp").unwrap();
3312 let result = unsafe { c2pa_read_ingredient_file(std::ptr::null(), data_dir.as_ptr()) };
3313 assert!(result.is_null());
3314 let error = unsafe { c2pa_error() };
3315 let error_str = unsafe { CString::from_raw(error) };
3316 assert_eq!(error_str.to_str().unwrap(), "NullParameter: path");
3317 }
3318
3319 #[test]
3320 #[cfg(feature = "file_io")]
3321 fn test_c2pa_sign_file_null_source_path() {
3322 let dest_path = CString::new("/tmp/output.jpg").unwrap();
3323 let manifest = CString::new("{}").unwrap();
3324 let signer_info = C2paSignerInfo {
3325 alg: std::ptr::null(),
3326 sign_cert: std::ptr::null(),
3327 private_key: std::ptr::null(),
3328 ta_url: std::ptr::null(),
3329 };
3330 let result = unsafe {
3331 c2pa_sign_file(
3332 std::ptr::null(),
3333 dest_path.as_ptr(),
3334 manifest.as_ptr(),
3335 &signer_info,
3336 std::ptr::null(),
3337 )
3338 };
3339 assert!(result.is_null());
3340 let error = unsafe { c2pa_error() };
3341 let error_str = unsafe { CString::from_raw(error) };
3342 assert_eq!(error_str.to_str().unwrap(), "NullParameter: source_path");
3343 }
3344
3345 #[test]
3346 #[cfg(feature = "file_io")]
3347 fn test_c2pa_sign_file_success() {
3348 use std::{fs, path::PathBuf};
3349
3350 let base = env!("CARGO_MANIFEST_DIR");
3352 let source = format!("{base}/../sdk/tests/fixtures/IMG_0003.jpg");
3353 let temp_dir = PathBuf::from(base).join("../target/tmp");
3354 fs::create_dir_all(&temp_dir).unwrap();
3355 let dest = temp_dir.join("c2pa_sign_file_test_output.jpg");
3356
3357 let source_path = CString::new(source).unwrap();
3358 let dest_path = CString::new(dest.to_str().unwrap()).unwrap();
3359 let manifest = CString::new("{}").unwrap();
3360
3361 let alg = CString::new("es256").unwrap();
3363 let cert = CString::new(include_str!(fixture_path!("certs/es256.pub"))).unwrap();
3364 let key =
3365 CString::new(include_bytes!(fixture_path!("certs/es256.pem")).as_slice()).unwrap();
3366
3367 let signer_info = C2paSignerInfo {
3368 alg: alg.as_ptr(),
3369 sign_cert: cert.as_ptr(),
3370 private_key: key.as_ptr(),
3371 ta_url: std::ptr::null(),
3372 };
3373
3374 let result = unsafe {
3376 c2pa_sign_file(
3377 source_path.as_ptr(),
3378 dest_path.as_ptr(),
3379 manifest.as_ptr(),
3380 &signer_info,
3381 std::ptr::null(),
3382 )
3383 };
3384
3385 assert!(
3387 !result.is_null(),
3388 "c2pa_sign_file should return non-null on success"
3389 );
3390 let result_str = unsafe { CString::from_raw(result) };
3391 assert_eq!(
3392 result_str.to_str().unwrap(),
3393 "",
3394 "c2pa_sign_file should return empty string on success"
3395 );
3396
3397 assert!(dest.exists(), "Output file should exist");
3399 let metadata = fs::metadata(&dest).unwrap();
3400 assert!(metadata.len() > 0, "Output file should have content");
3401
3402 fs::remove_file(dest).ok();
3404 }
3405
3406 #[test]
3407 fn test_c2pa_reader_remote_url() {
3408 let mut stream = TestStream::new(include_bytes!(fixture_path!("cloud.jpg")).to_vec());
3409
3410 let format = CString::new("image/jpeg").unwrap();
3411 let result = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
3412 if result.is_null() {
3413 if let Some(msg) = CimplError::last_message() {
3414 panic!("Reader creation failed: {}", msg);
3415 } else {
3416 panic!("Reader creation failed with no error message");
3417 }
3418 }
3419 assert!(!result.is_null());
3420 let remote_url = unsafe { c2pa_reader_remote_url(result) };
3421 assert!(!remote_url.is_null());
3422 let remote_url = unsafe { std::ffi::CStr::from_ptr(remote_url) };
3423 assert_eq!(remote_url, c"https://cai-manifests.adobe.com/manifests/adobe-urn-uuid-5f37e182-3687-462e-a7fb-573462780391");
3424 unsafe { c2pa_reader_free(result) };
3425 }
3426
3427 #[test]
3429 fn test_reader_file_with_wrong_label() {
3430 let mut stream = TestStream::new(
3431 include_bytes!(fixture_path!("adobe-20220124-E-clm-CAICAI.jpg")).to_vec(),
3432 );
3433
3434 let format = CString::new("image/jpeg").unwrap();
3435 let result: *mut C2paReader =
3436 unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
3437 assert!(!result.is_null());
3438 unsafe { c2pa_reader_free(result) };
3439 }
3440
3441 #[test]
3442 fn test_c2pa_reader_from_stream_null_format() {
3443 let mut stream = TestStream::new(Vec::new());
3444
3445 let result = unsafe { c2pa_reader_from_stream(std::ptr::null(), stream.as_ptr()) };
3446 assert!(result.is_null());
3447 let error = unsafe { c2pa_error() };
3448 let error_str = unsafe { CString::from_raw(error) };
3449 assert_eq!(error_str.to_str().unwrap(), "NullParameter: format");
3450 }
3451
3452 #[test]
3453 fn test_c2pa_reader_from_stream_cawg() {
3454 let source_image = include_bytes!(
3455 "../../sdk/src/identity/tests/fixtures/claim_aggregation/ica_validation/success.jpg"
3456 );
3457 let mut stream = TestStream::new(source_image.to_vec());
3458 let format = CString::new("image/jpeg").unwrap();
3459 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
3460 assert!(!reader.is_null());
3461 let json = unsafe { c2pa_reader_json(reader) };
3462 assert!(!json.is_null());
3463 let json_str = unsafe { CString::from_raw(json) };
3464 assert!(json_str.to_str().unwrap().contains("Silly Cats 929"));
3465 assert!(json_str
3466 .to_str()
3467 .unwrap()
3468 .contains("cawg.ica.credential_valid"));
3469 unsafe { c2pa_reader_free(reader) };
3470 }
3471
3472 #[test]
3473 fn test_c2pa_reader_with_stream_from_context() {
3474 let builder = unsafe { c2pa_context_builder_new() };
3476 assert!(!builder.is_null());
3477
3478 let settings = unsafe { c2pa_settings_new() };
3479 assert!(!settings.is_null());
3480
3481 let json = CString::new(r#"{"verify": {"verify_after_sign": true}}"#).unwrap();
3482 let format = CString::new("json").unwrap();
3483 let result =
3484 unsafe { c2pa_settings_update_from_string(settings, json.as_ptr(), format.as_ptr()) };
3485 assert_eq!(result, 0);
3486
3487 let result = unsafe { c2pa_context_builder_set_settings(builder, settings) };
3488 assert_eq!(result, 0);
3489
3490 let context = unsafe { c2pa_context_builder_build(builder) };
3491 assert!(!context.is_null());
3492
3493 let reader = unsafe { c2pa_reader_from_context(context) };
3495 assert!(!reader.is_null());
3496
3497 let source_image = include_bytes!(fixture_path!("adobe-20220124-E-clm-CAICAI.jpg"));
3499 let mut stream = TestStream::new(source_image.to_vec());
3500
3501 let format = CString::new("image/jpeg").unwrap();
3503 let configured_reader =
3504 unsafe { c2pa_reader_with_stream(reader, format.as_ptr(), stream.as_ptr()) };
3505 assert!(!configured_reader.is_null());
3506
3507 let free_result = unsafe { c2pa_free(reader as *const c_void) };
3509 assert_eq!(free_result, -1);
3510
3511 let json = unsafe { c2pa_reader_json(configured_reader) };
3513 assert!(!json.is_null());
3514 let json_str = unsafe { CString::from_raw(json) };
3515 let json_content = json_str.to_str().unwrap();
3516 assert!(
3518 json_content.contains("claim") || json_content.contains("manifest"),
3519 "Expected manifest content in JSON"
3520 );
3521
3522 unsafe {
3523 c2pa_free(settings as *mut c_void);
3524 c2pa_free(context as *mut c_void);
3525 c2pa_free(configured_reader as *mut c_void);
3526 };
3527 }
3528
3529 #[test]
3530 fn test_c2pa_reader_with_manifest_data_and_stream() {
3531 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
3533 let mut source_stream = TestStream::new(source_image.to_vec());
3534 let mut dest_stream = TestStream::new(Vec::new());
3535
3536 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
3537
3538 let format = CString::new("image/jpeg").unwrap();
3539 let mut manifest_bytes_ptr = std::ptr::null();
3540
3541 let result = unsafe {
3542 c2pa_builder_sign(
3543 builder,
3544 format.as_ptr(),
3545 source_stream.as_ptr(),
3546 dest_stream.as_ptr(),
3547 signer,
3548 &mut manifest_bytes_ptr,
3549 )
3550 };
3551 assert!(result > 0, "Signing should succeed");
3552 assert!(
3553 !manifest_bytes_ptr.is_null(),
3554 "Manifest bytes should be returned"
3555 );
3556 let manifest_size = result as usize;
3557
3558 let context = unsafe { c2pa_context_new() };
3560 assert!(!context.is_null());
3561
3562 let reader = unsafe { c2pa_reader_from_context(context) };
3563 assert!(!reader.is_null());
3564
3565 let mut validation_stream = TestStream::new(source_image.to_vec());
3567 let configured_reader = unsafe {
3568 c2pa_reader_with_manifest_data_and_stream(
3569 reader,
3570 format.as_ptr(),
3571 validation_stream.as_ptr(),
3572 manifest_bytes_ptr,
3573 manifest_size,
3574 )
3575 };
3576 assert!(
3577 !configured_reader.is_null(),
3578 "Reader should be configured with manifest data and stream"
3579 );
3580
3581 let free_result = unsafe { c2pa_free(reader as *const c_void) };
3583 assert_eq!(free_result, -1);
3584
3585 let json = unsafe { c2pa_reader_json(configured_reader) };
3587 assert!(!json.is_null(), "Should be able to get JSON from reader");
3588
3589 unsafe {
3590 c2pa_free(json as *const c_void);
3591 c2pa_free(configured_reader as *const c_void);
3592 c2pa_free(manifest_bytes_ptr as *const c_void);
3593 c2pa_free(builder as *const c_void);
3594 c2pa_free(signer as *const c_void);
3595 c2pa_free(context as *const c_void);
3596 }
3597 }
3598
3599 #[test]
3600 fn test_c2pa_reader_json_null_reader() {
3601 let result = unsafe { c2pa_reader_json(std::ptr::null_mut()) };
3602 assert!(result.is_null());
3603 let error = unsafe { c2pa_error() };
3604 let error_str = unsafe { CString::from_raw(error) };
3605 assert_eq!(error_str.to_str().unwrap(), "NullParameter: reader_ptr");
3606 }
3607
3608 #[test]
3609 fn test_c2pa_builder_add_resource_null_uri() {
3610 let manifest_def = CString::new("{}").unwrap();
3611 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3612 assert!(!builder.is_null());
3613 let mut stream = TestStream::new(Vec::new());
3614 let result =
3615 unsafe { c2pa_builder_add_resource(builder, std::ptr::null(), stream.as_ptr()) };
3616 assert_eq!(result, -1);
3617 let error = unsafe { c2pa_error() };
3618 let error_str = unsafe { CString::from_raw(error) };
3619 assert_eq!(error_str.to_str().unwrap(), "NullParameter: uri");
3620 unsafe { c2pa_builder_free(builder) };
3621 }
3622
3623 #[test]
3624 fn test_c2pa_builder_to_archive_null_stream() {
3625 let manifest_def = CString::new("{}").unwrap();
3626 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3627 assert!(!builder.is_null());
3628 let result = unsafe { c2pa_builder_to_archive(builder, std::ptr::null_mut()) };
3629 assert_eq!(result, -1);
3630 let error = unsafe { c2pa_error() };
3631 let error_str = unsafe { CString::from_raw(error) };
3632 assert_eq!(error_str.to_str().unwrap(), "NullParameter: stream");
3633 unsafe { c2pa_builder_free(builder) };
3634 }
3635
3636 #[test]
3637 fn test_c2pa_builder_add_ingredient_from_archive_null_stream() {
3638 let manifest_def = CString::new("{}").unwrap();
3639 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3640 assert!(!builder.is_null());
3641 let result =
3642 unsafe { c2pa_builder_add_ingredient_from_archive(builder, std::ptr::null_mut()) };
3643 assert_eq!(result, -1);
3644 let error = unsafe { c2pa_error() };
3645 let error_str = unsafe { CString::from_raw(error) };
3646 assert_eq!(error_str.to_str().unwrap(), "NullParameter: stream");
3647 unsafe { c2pa_builder_free(builder) };
3648 }
3649
3650 #[test]
3651 fn test_c2pa_builder_add_ingredient_from_archive_null_builder() {
3652 let archive_bytes = include_bytes!(fixture_path!("cloud.jpg"));
3653 let mut stream = TestStream::new(archive_bytes.to_vec());
3654 let result = unsafe {
3655 c2pa_builder_add_ingredient_from_archive(std::ptr::null_mut(), stream.as_ptr())
3656 };
3657 assert_eq!(result, -1);
3658 }
3659
3660 #[test]
3661 fn test_c2pa_builder_write_ingredient_archive_null_stream() {
3662 let manifest_def = CString::new("{}").unwrap();
3663 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3664 assert!(!builder.is_null());
3665 let ingredient_id = CString::new("test-ingredient").unwrap();
3666 let result = unsafe {
3667 c2pa_builder_write_ingredient_archive(
3668 builder,
3669 ingredient_id.as_ptr(),
3670 std::ptr::null_mut(),
3671 )
3672 };
3673 assert_eq!(result, -1);
3674 let error = unsafe { c2pa_error() };
3675 let error_str = unsafe { CString::from_raw(error) };
3676 assert_eq!(error_str.to_str().unwrap(), "NullParameter: stream");
3677 unsafe { c2pa_builder_free(builder) };
3678 }
3679
3680 #[test]
3681 fn test_c2pa_builder_write_ingredient_archive_null_ingredient_id() {
3682 let manifest_def = CString::new("{}").unwrap();
3683 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3684 assert!(!builder.is_null());
3685 let archive_bytes = vec![0u8; 0];
3686 let mut stream = TestStream::new(archive_bytes);
3687 let result = unsafe {
3688 c2pa_builder_write_ingredient_archive(builder, std::ptr::null(), stream.as_ptr())
3689 };
3690 assert_eq!(result, -1);
3691 let error = unsafe { c2pa_error() };
3692 let error_str = unsafe { CString::from_raw(error) };
3693 assert_eq!(error_str.to_str().unwrap(), "NullParameter: ingredient_id");
3694 unsafe { c2pa_builder_free(builder) };
3695 }
3696
3697 #[test]
3698 fn test_c2pa_builder_read_supported_mime_types() {
3699 let mut count = 0;
3700 let mime_types = unsafe { c2pa_builder_supported_mime_types(&mut count) };
3701 assert!(!mime_types.is_null());
3702 assert_eq!(count, C2paBuilder::supported_mime_types().len());
3703 unsafe { c2pa_free_string_array(mime_types, count) };
3704 }
3705
3706 #[test]
3707 fn test_c2pa_reader_read_supported_mime_types() {
3708 let mut count = 0;
3709 let mime_types = unsafe { c2pa_reader_supported_mime_types(&mut count) };
3710 assert!(!mime_types.is_null());
3711 assert_eq!(count, C2paReader::supported_mime_types().len());
3712 unsafe { c2pa_free_string_array(mime_types, count) };
3713 }
3714
3715 #[test]
3716 fn test_c2pa_free_string_array_with_nullptr() {
3717 assert!(catch_unwind(|| {
3718 unsafe {
3719 c2pa_free_string_array(std::ptr::null_mut(), 0);
3720 }
3721 })
3722 .is_ok());
3723 }
3724
3725 #[test]
3726 fn test_c2pa_free_string_array_with_count_1() {
3727 let mut ptrs = vec![to_c_string("image/jpeg".to_string())];
3728 let count = ptrs.len();
3729 let ptr = ptrs.as_mut_ptr() as *const *const c_char;
3730 std::mem::forget(ptrs);
3731
3732 assert!(catch_unwind(|| {
3734 unsafe {
3735 c2pa_free_string_array(ptr, count);
3736 }
3737 })
3738 .is_ok());
3739 }
3740
3741 #[test]
3742 fn test_create_callback_signer() {
3743 extern "C" fn test_callback(
3744 _context: *const (),
3745 _data: *const c_uchar,
3746 _len: usize,
3747 _signed_bytes: *mut c_uchar,
3748 _signed_len: usize,
3749 ) -> isize {
3750 1
3752 }
3753
3754 let certs = include_str!(fixture_path!("certs/ed25519.pub"));
3755 let certs_cstr = CString::new(certs).unwrap();
3756
3757 let signer = unsafe {
3758 c2pa_signer_create(
3759 std::ptr::null(),
3760 test_callback,
3761 C2paSigningAlg::Ed25519,
3762 certs_cstr.as_ptr(),
3763 std::ptr::null(),
3764 )
3765 };
3766
3767 assert!(!signer.is_null());
3769
3770 unsafe { c2pa_signer_free(signer) };
3771 }
3772
3773 #[test]
3774 fn test_sign_with_callback_signer() {
3775 extern "C" fn test_callback(
3778 _context: *const (),
3779 data: *const c_uchar,
3780 len: usize,
3781 signed_bytes: *mut c_uchar,
3782 signed_len: usize,
3783 ) -> isize {
3784 let private_key = include_bytes!(fixture_path!("certs/ed25519.pem"));
3785 let private_key_cstr = match CString::new(private_key) {
3786 Ok(s) => s,
3787 Err(_) => return -1,
3788 };
3789
3790 let signature = unsafe { c2pa_ed25519_sign(data, len, private_key_cstr.as_ptr()) };
3791
3792 if signature.is_null() {
3794 return -1;
3795 }
3796
3797 let signature_len = 64;
3798 if signed_len < signature_len {
3799 unsafe { c2pa_signature_free(signature) };
3801 return -1;
3802 }
3803
3804 let signature_slice =
3806 match unsafe { safe_slice_from_raw_parts(signature, signature_len, "signature") } {
3807 Ok(slice) => slice,
3808 Err(_) => {
3809 unsafe { c2pa_signature_free(signature) };
3810 return -1;
3811 }
3812 };
3813
3814 if !unsafe { is_safe_buffer_size(signed_len, signed_bytes) } {
3816 unsafe { c2pa_signature_free(signature) };
3817 return -1;
3818 }
3819
3820 let signed_slice = unsafe { std::slice::from_raw_parts_mut(signed_bytes, signed_len) };
3821
3822 if signature_len <= signed_slice.len() {
3823 signed_slice[..signature_len].copy_from_slice(signature_slice);
3824 } else {
3825 unsafe { c2pa_signature_free(signature) };
3826 return -1;
3827 }
3828
3829 unsafe { c2pa_signature_free(signature) };
3830 signature_len as isize
3831 }
3832
3833 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
3834 let mut source_stream = TestStream::new(source_image.to_vec());
3835 let dest_vec = Vec::new();
3836 let mut dest_stream = TestStream::new(dest_vec);
3837
3838 let certs = include_str!(fixture_path!("certs/ed25519.pub"));
3839 let certs_cstr = CString::new(certs).unwrap();
3840
3841 let signer = unsafe {
3843 c2pa_signer_create(
3844 std::ptr::null(), test_callback,
3846 C2paSigningAlg::Ed25519,
3847 certs_cstr.as_ptr(),
3848 std::ptr::null(), )
3850 };
3851
3852 assert!(!signer.is_null());
3853
3854 let manifest_def = CString::new("{}").unwrap();
3855 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
3856 assert!(!builder.is_null());
3857
3858 let result = unsafe {
3859 c2pa_builder_set_intent(
3860 builder,
3861 C2paBuilderIntent::Create,
3862 C2paDigitalSourceType::Empty,
3863 )
3864 };
3865 assert_eq!(result, 0);
3866
3867 let format = CString::new("image/jpeg").unwrap();
3868 let mut manifest_bytes_ptr = std::ptr::null();
3869
3870 let result = unsafe {
3872 c2pa_builder_sign(
3873 builder,
3874 format.as_ptr(),
3875 source_stream.as_ptr(),
3876 dest_stream.as_ptr(),
3877 signer,
3878 &mut manifest_bytes_ptr,
3879 )
3880 };
3881 assert!(result > 0);
3882
3883 dest_stream.stream_mut().rewind().unwrap();
3885 let format = CString::new("image/jpeg").unwrap();
3886
3887 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), dest_stream.as_ptr()) };
3888 if reader.is_null() {
3889 if let Some(msg) = CimplError::last_message() {
3890 panic!("Reader creation failed: {}", msg);
3891 }
3892 }
3893 assert!(!reader.is_null());
3894
3895 let json = unsafe { c2pa_reader_json(reader) };
3896 assert!(!json.is_null());
3897 let json_str = unsafe { CString::from_raw(json) };
3898 let json_content = json_str.to_str().unwrap();
3899
3900 assert!(json_content.contains("manifest"));
3901
3902 unsafe {
3903 c2pa_manifest_bytes_free(manifest_bytes_ptr);
3904 c2pa_reader_free(reader);
3905 }
3906 unsafe { c2pa_builder_free(builder) };
3907 unsafe { c2pa_signer_free(signer) };
3908 }
3909
3910 #[test]
3911 #[cfg(feature = "file_io")]
3912 fn test_reader_from_file_cawg_identity() {
3913 let settings = CString::new(include_bytes!(
3914 "../../cli/tests/fixtures/trust/cawg_test_settings.toml"
3915 ))
3916 .unwrap();
3917 let format = CString::new("toml").unwrap();
3918 let result = unsafe { c2pa_load_settings(settings.as_ptr(), format.as_ptr()) };
3919 assert_eq!(result, 0);
3920
3921 let base = env!("CARGO_MANIFEST_DIR");
3922 let path =
3923 CString::new(format!("{base}/../sdk/tests/fixtures/C_with_CAWG_data.jpg")).unwrap();
3924 let reader = unsafe { c2pa_reader_from_file(path.as_ptr()) };
3925 if reader.is_null() {
3926 let error = unsafe { c2pa_error() };
3927 let error_str = unsafe { CString::from_raw(error) };
3928 panic!("Failed to create reader: {}", error_str.to_str().unwrap());
3929 }
3930 assert!(!reader.is_null());
3931 let json = unsafe { c2pa_reader_json(reader) };
3932 assert!(!json.is_null());
3933 let json_str = unsafe { CString::from_raw(json) };
3934 println!("JSON Report: {}", json_str.to_str().unwrap());
3935 let json_report = json_str.to_str().unwrap();
3936 assert!(json_report.contains("cawg.identity"));
3937 assert!(json_report.contains("cawg.identity.well-formed"));
3938 unsafe { c2pa_reader_free(reader) };
3939 }
3940
3941 #[test]
3942 fn test_c2pa_signer_from_settings() {
3943 const SETTINGS: &str = include_str!("../../sdk/tests/fixtures/test_settings.json");
3944 let settings = CString::new(SETTINGS).unwrap();
3945 let format = CString::new("json").unwrap();
3946 let result = unsafe { c2pa_load_settings(settings.as_ptr(), format.as_ptr()) };
3947 assert_eq!(result, 0);
3948 let signer = unsafe { c2pa_signer_from_settings() };
3949 assert!(!signer.is_null());
3950 unsafe { c2pa_signer_free(signer) };
3951 }
3952
3953 #[test]
3954 fn test_c2pa_settings_new() {
3955 let settings = unsafe { c2pa_settings_new() };
3956 assert!(!settings.is_null());
3957 unsafe { c2pa_free(settings as *mut c_void) };
3958 }
3959
3960 #[test]
3961 fn test_c2pa_settings_update_from_json_string() {
3962 let settings = unsafe { c2pa_settings_new() };
3963 assert!(!settings.is_null());
3964
3965 let json = CString::new(r#"{"verify": {"verify_after_sign": true}}"#).unwrap();
3966 let format = CString::new("json").unwrap();
3967
3968 let result =
3969 unsafe { c2pa_settings_update_from_string(settings, json.as_ptr(), format.as_ptr()) };
3970 assert_eq!(result, 0);
3971
3972 unsafe { c2pa_free(settings as *mut c_void) };
3973 }
3974
3975 #[test]
3976 fn test_c2pa_settings_update_from_toml_string() {
3977 let settings = unsafe { c2pa_settings_new() };
3978 assert!(!settings.is_null());
3979
3980 let toml = CString::new(
3981 r#"
3982[verify]
3983verify_after_sign = true
3984"#,
3985 )
3986 .unwrap();
3987 let format = CString::new("toml").unwrap();
3988
3989 let result =
3990 unsafe { c2pa_settings_update_from_string(settings, toml.as_ptr(), format.as_ptr()) };
3991 assert_eq!(result, 0);
3992
3993 unsafe { c2pa_free(settings as *mut c_void) };
3994 }
3995
3996 #[test]
3997 fn test_c2pa_settings_update_from_string_invalid() {
3998 let settings = unsafe { c2pa_settings_new() };
3999 assert!(!settings.is_null());
4000
4001 let invalid_json = CString::new(r#"{"verify": {"verify_after_sign": "#).unwrap();
4002 let format = CString::new("json").unwrap();
4003
4004 let result = unsafe {
4005 c2pa_settings_update_from_string(settings, invalid_json.as_ptr(), format.as_ptr())
4006 };
4007 assert_ne!(result, 0); unsafe { c2pa_free(settings as *mut c_void) };
4010 }
4011
4012 #[test]
4013 fn test_c2pa_settings_set_value() {
4014 let settings = unsafe { c2pa_settings_new() };
4015 assert!(!settings.is_null());
4016
4017 let path = CString::new("verify.verify_after_sign").unwrap();
4018 let value = CString::new("true").unwrap();
4019
4020 let result = unsafe { c2pa_settings_set_value(settings, path.as_ptr(), value.as_ptr()) };
4021 assert_eq!(result, 0);
4022
4023 unsafe { c2pa_free(settings as *mut c_void) };
4024 }
4025
4026 #[test]
4027 fn test_c2pa_settings_set_value_string() {
4028 let settings = unsafe { c2pa_settings_new() };
4029 assert!(!settings.is_null());
4030
4031 let path = CString::new("core.allowed_network_hosts").unwrap();
4033 let value = CString::new(r#"["example.com", "test.org"]"#).unwrap(); let result = unsafe { c2pa_settings_set_value(settings, path.as_ptr(), value.as_ptr()) };
4036 assert_eq!(result, 0);
4037
4038 unsafe { c2pa_free(settings as *mut c_void) };
4039 }
4040
4041 #[test]
4042 fn test_c2pa_settings_set_value_number() {
4043 let settings = unsafe { c2pa_settings_new() };
4044 assert!(!settings.is_null());
4045
4046 let path = CString::new("verify.max_memory_usage").unwrap();
4047 let value = CString::new("1000000").unwrap();
4048
4049 let result = unsafe { c2pa_settings_set_value(settings, path.as_ptr(), value.as_ptr()) };
4050 assert_eq!(result, 0);
4051
4052 unsafe { c2pa_free(settings as *mut c_void) };
4053 }
4054
4055 #[test]
4056 fn test_c2pa_settings_set_value_invalid_json() {
4057 let settings = unsafe { c2pa_settings_new() };
4058 assert!(!settings.is_null());
4059
4060 let path = CString::new("verify.verify_after_sign").unwrap();
4061 let invalid_value = CString::new("not valid json").unwrap(); let result =
4064 unsafe { c2pa_settings_set_value(settings, path.as_ptr(), invalid_value.as_ptr()) };
4065 assert_ne!(result, 0); unsafe { c2pa_free(settings as *mut c_void) };
4068 }
4069
4070 #[test]
4071 fn test_c2pa_context_new() {
4072 let context = unsafe { c2pa_context_new() };
4073 assert!(!context.is_null());
4074 unsafe { c2pa_free(context as *mut c_void) };
4075 }
4076
4077 #[test]
4078 fn test_c2pa_context_builder_new() {
4079 let builder = unsafe { c2pa_context_builder_new() };
4080 assert!(!builder.is_null());
4081 unsafe { c2pa_free(builder as *mut c_void) };
4082 }
4083
4084 #[test]
4085 fn test_c2pa_context_builder_set_settings() {
4086 let builder = unsafe { c2pa_context_builder_new() };
4087 assert!(!builder.is_null());
4088
4089 let settings = unsafe { c2pa_settings_new() };
4090 assert!(!settings.is_null());
4091
4092 let json = CString::new(r#"{"verify": {"verify_after_sign": true}}"#).unwrap();
4094 let format = CString::new("json").unwrap();
4095 let result =
4096 unsafe { c2pa_settings_update_from_string(settings, json.as_ptr(), format.as_ptr()) };
4097 assert_eq!(result, 0);
4098
4099 let result = unsafe { c2pa_context_builder_set_settings(builder, settings) };
4101 assert_eq!(result, 0);
4102
4103 unsafe {
4104 c2pa_free(settings as *mut c_void);
4105 c2pa_free(builder as *mut c_void);
4106 };
4107 }
4108
4109 #[test]
4110 fn test_c2pa_context_builder_build() {
4111 let builder = unsafe { c2pa_context_builder_new() };
4112 assert!(!builder.is_null());
4113
4114 let settings = unsafe { c2pa_settings_new() };
4115 assert!(!settings.is_null());
4116
4117 let json = CString::new(r#"{"verify": {"verify_after_sign": true}}"#).unwrap();
4118 let format = CString::new("json").unwrap();
4119 let result =
4120 unsafe { c2pa_settings_update_from_string(settings, json.as_ptr(), format.as_ptr()) };
4121 assert_eq!(result, 0);
4122
4123 let result = unsafe { c2pa_context_builder_set_settings(builder, settings) };
4124 assert_eq!(result, 0);
4125
4126 let context = unsafe { c2pa_context_builder_build(builder) };
4128 assert!(!context.is_null());
4129
4130 let free_result = unsafe { c2pa_free(builder as *const c_void) };
4132 assert_eq!(free_result, -1);
4133
4134 unsafe {
4135 c2pa_free(settings as *mut c_void);
4136 c2pa_free(context as *mut c_void);
4137 };
4138 }
4139
4140 #[test]
4141 fn test_c2pa_context_builder_set_settings_multiple_times() {
4142 let builder = unsafe { c2pa_context_builder_new() };
4143 assert!(!builder.is_null());
4144
4145 let settings1 = unsafe { c2pa_settings_new() };
4147 assert!(!settings1.is_null());
4148 let json1 = CString::new(r#"{"verify": {"verify_after_sign": true}}"#).unwrap();
4149 let format = CString::new("json").unwrap();
4150 let result =
4151 unsafe { c2pa_settings_update_from_string(settings1, json1.as_ptr(), format.as_ptr()) };
4152 assert_eq!(result, 0);
4153
4154 let result = unsafe { c2pa_context_builder_set_settings(builder, settings1) };
4155 assert_eq!(result, 0);
4156
4157 let settings2 = unsafe { c2pa_settings_new() };
4159 assert!(!settings2.is_null());
4160 let json2 = CString::new(r#"{"verify": {"verify_after_sign": false}}"#).unwrap();
4161 let result =
4162 unsafe { c2pa_settings_update_from_string(settings2, json2.as_ptr(), format.as_ptr()) };
4163 assert_eq!(result, 0);
4164
4165 let result = unsafe { c2pa_context_builder_set_settings(builder, settings2) };
4166 assert_eq!(result, 0);
4167
4168 let context = unsafe { c2pa_context_builder_build(builder) };
4170 assert!(!context.is_null());
4171
4172 unsafe {
4173 c2pa_free(settings1 as *mut c_void);
4174 c2pa_free(settings2 as *mut c_void);
4175 c2pa_free(context as *mut c_void);
4176 };
4177 }
4178
4179 #[test]
4180 fn test_c2pa_context_builder_with_full_config() {
4181 let builder = unsafe { c2pa_context_builder_new() };
4182 assert!(!builder.is_null());
4183
4184 let settings = unsafe { c2pa_settings_new() };
4185 assert!(!settings.is_null());
4186
4187 const SETTINGS: &str = include_str!("../../sdk/tests/fixtures/test_settings.json");
4189 let settings_str = CString::new(SETTINGS).unwrap();
4190 let format = CString::new("json").unwrap();
4191 let result = unsafe {
4192 c2pa_settings_update_from_string(settings, settings_str.as_ptr(), format.as_ptr())
4193 };
4194 assert_eq!(result, 0);
4195
4196 let result = unsafe { c2pa_context_builder_set_settings(builder, settings) };
4198 assert_eq!(result, 0);
4199
4200 let context = unsafe { c2pa_context_builder_build(builder) };
4202 assert!(!context.is_null());
4203
4204 unsafe {
4205 c2pa_free(settings as *mut c_void);
4206 c2pa_free(context as *mut c_void);
4207 };
4208 }
4209
4210 #[test]
4211 fn test_c2pa_context_can_be_shared() {
4212 let context = unsafe { c2pa_context_new() };
4214 assert!(!context.is_null());
4215
4216 let reader1 = unsafe { c2pa_reader_from_context(context) };
4218 assert!(!reader1.is_null());
4219
4220 let reader2 = unsafe { c2pa_reader_from_context(context) };
4221 assert!(!reader2.is_null());
4222
4223 unsafe {
4225 c2pa_free(reader1 as *mut c_void);
4226 c2pa_free(reader2 as *mut c_void);
4227 c2pa_free(context as *mut c_void);
4228 };
4229 }
4230
4231 #[test]
4232 fn test_c2pa_free_works_for_all_types() {
4233 let settings = unsafe { c2pa_settings_new() };
4235 assert!(!settings.is_null());
4236 let result = unsafe { c2pa_free(settings as *mut c_void) };
4237 assert_eq!(result, 0);
4238
4239 let context = unsafe { c2pa_context_new() };
4240 assert!(!context.is_null());
4241 let result = unsafe { c2pa_free(context as *mut c_void) };
4242 assert_eq!(result, 0);
4243
4244 let test_str = CString::new("test").unwrap();
4246 let c_str = to_c_string(test_str.to_str().unwrap().to_string());
4247 assert!(!c_str.is_null());
4248 let result = unsafe { c2pa_free(c_str as *mut c_void) };
4249 assert_eq!(result, 0);
4250 }
4251
4252 #[test]
4253 fn test_c2pa_reader_detailed_json() {
4254 use std::ffi::CStr;
4255
4256 let source_image = include_bytes!(fixture_path!("C.jpg"));
4257 let mut stream = TestStream::new(source_image.to_vec());
4258 let format = CString::new("image/jpeg").unwrap();
4259
4260 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4261 assert!(!reader.is_null());
4262
4263 let detailed_json = unsafe { c2pa_reader_detailed_json(reader) };
4265 assert!(!detailed_json.is_null());
4266
4267 let json_str = unsafe { CStr::from_ptr(detailed_json).to_str().unwrap() };
4269 assert!(!json_str.is_empty(), "Detailed JSON should not be empty");
4270
4271 let json_value: serde_json::Value = serde_json::from_str(json_str).unwrap();
4272 assert!(json_value.is_object(), "Detailed JSON should be an object");
4274
4275 unsafe {
4276 c2pa_free(detailed_json as *mut c_void);
4277 c2pa_free(reader as *mut c_void);
4278 }
4279 }
4280
4281 #[test]
4282 fn test_c2pa_reader_is_embedded() {
4283 let source_image = include_bytes!(fixture_path!("C.jpg"));
4285 let mut stream = TestStream::new(source_image.to_vec());
4286 let format = CString::new("image/jpeg").unwrap();
4287
4288 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4289 assert!(!reader.is_null());
4290
4291 let _is_embedded = unsafe { c2pa_reader_is_embedded(reader) };
4294
4295 unsafe {
4297 c2pa_free(reader as *mut c_void);
4298 }
4299 }
4300
4301 #[test]
4302 fn test_c2pa_builder_from_context() {
4303 let context = unsafe { c2pa_context_new() };
4305 assert!(!context.is_null());
4306
4307 let builder = unsafe { c2pa_builder_from_context(context) };
4309 assert!(!builder.is_null());
4310
4311 let manifest_json = CString::new(r#"{"claim_generator": "test"}"#).unwrap();
4313 let builder = unsafe { c2pa_builder_with_definition(builder, manifest_json.as_ptr()) };
4314 assert!(!builder.is_null());
4315
4316 unsafe {
4317 c2pa_free(builder as *mut c_void);
4318 c2pa_free(context as *mut c_void);
4319 }
4320 }
4321
4322 #[test]
4323 fn test_c2pa_format_embeddable() {
4324 let jpeg_format = CString::new("image/jpeg").unwrap();
4327 let placeholder_bytes = b"placeholder";
4328 let mut result_ptr: *const c_uchar = std::ptr::null();
4329
4330 let _result = unsafe {
4331 c2pa_format_embeddable(
4332 jpeg_format.as_ptr(),
4333 placeholder_bytes.as_ptr(),
4334 placeholder_bytes.len(),
4335 &mut result_ptr,
4336 )
4337 };
4338
4339 if !result_ptr.is_null() {
4342 unsafe { c2pa_free(result_ptr as *const c_void) };
4343 }
4344 }
4345
4346 #[test]
4347 fn test_c2pa_builder_add_ingredient_from_stream() {
4348 let manifest_def = CString::new("{}").unwrap();
4349 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
4350 assert!(!builder.is_null());
4351
4352 let ingredient_image = include_bytes!(fixture_path!("C.jpg"));
4354 let mut ingredient_stream = TestStream::new(ingredient_image.to_vec());
4355
4356 let ingredient_json = CString::new(r#"{"title": "Test Ingredient"}"#).unwrap();
4357 let format = CString::new("image/jpeg").unwrap();
4358
4359 let result = unsafe {
4361 c2pa_builder_add_ingredient_from_stream(
4362 builder,
4363 ingredient_json.as_ptr(),
4364 format.as_ptr(),
4365 ingredient_stream.as_ptr(),
4366 )
4367 };
4368 assert_eq!(result, 0, "Should successfully add ingredient");
4369
4370 unsafe {
4371 c2pa_free(builder as *mut c_void);
4372 }
4373 }
4374
4375 #[test]
4376 fn test_c2pa_builder_with_definition() {
4377 let manifest_def = CString::new("{}").unwrap();
4379 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
4380 assert!(!builder.is_null());
4381
4382 let new_manifest = CString::new(r#"{"claim_generator": "test_with_definition"}"#).unwrap();
4384 let new_builder = unsafe { c2pa_builder_with_definition(builder, new_manifest.as_ptr()) };
4385 assert!(!new_builder.is_null(), "Should return new builder");
4386
4387 let free_result = unsafe { c2pa_free(builder as *const c_void) };
4389 assert_eq!(free_result, -1);
4390
4391 unsafe {
4392 c2pa_free(new_builder as *mut c_void);
4393 }
4394 }
4395
4396 #[test]
4397 fn test_c2pa_builder_with_definition_null_json() {
4398 let manifest_def = CString::new("{}").unwrap();
4400 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
4401 assert!(!builder.is_null());
4402
4403 let new_builder = unsafe { c2pa_builder_with_definition(builder, std::ptr::null()) };
4405 assert!(
4406 new_builder.is_null(),
4407 "Should return null for invalid input"
4408 );
4409
4410 let free_result = unsafe { c2pa_free(builder as *mut c_void) };
4412 assert_eq!(free_result, 0);
4413 }
4414
4415 #[test]
4416 fn test_c2pa_builder_with_archive() {
4417 let manifest_def = CString::new("{}").unwrap();
4419 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
4420 assert!(!builder.is_null());
4421
4422 let archive_bytes = include_bytes!(fixture_path!("C.jpg"));
4424 let mut archive_stream = TestStream::new(archive_bytes.to_vec());
4425
4426 let new_builder = unsafe { c2pa_builder_with_archive(builder, archive_stream.as_ptr()) };
4428
4429 let free_result = unsafe { c2pa_free(builder as *const c_void) };
4431 assert_eq!(free_result, -1);
4432
4433 if !new_builder.is_null() {
4434 unsafe {
4435 c2pa_free(new_builder as *mut c_void);
4436 }
4437 }
4438 }
4439
4440 #[test]
4441 fn test_c2pa_builder_with_archive_null_stream() {
4442 let manifest_def = CString::new("{}").unwrap();
4444 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
4445 assert!(!builder.is_null());
4446
4447 let new_builder = unsafe { c2pa_builder_with_archive(builder, std::ptr::null_mut()) };
4449 assert!(
4450 new_builder.is_null(),
4451 "Should return null for invalid stream"
4452 );
4453
4454 let free_result = unsafe { c2pa_free(builder as *mut c_void) };
4456 assert_eq!(free_result, 0);
4457 }
4458
4459 #[test]
4460 fn test_c2pa_reader_with_fragment() {
4461 let source_image = include_bytes!(fixture_path!("C.jpg"));
4463 let mut stream = TestStream::new(source_image.to_vec());
4464 let format = CString::new("image/jpeg").unwrap();
4465
4466 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4467 assert!(!reader.is_null());
4468
4469 let fragment_bytes = include_bytes!(fixture_path!("C.jpg"));
4471 let mut fragment_stream = TestStream::new(fragment_bytes.to_vec());
4472 let mut main_stream = TestStream::new(source_image.to_vec());
4473
4474 let new_reader = unsafe {
4476 c2pa_reader_with_fragment(
4477 reader,
4478 format.as_ptr(),
4479 main_stream.as_ptr(),
4480 fragment_stream.as_ptr(),
4481 )
4482 };
4483
4484 let free_result = unsafe { c2pa_free(reader as *const c_void) };
4486 assert_eq!(free_result, -1);
4487
4488 if !new_reader.is_null() {
4489 unsafe {
4490 c2pa_free(new_reader as *mut c_void);
4491 }
4492 }
4493 }
4494
4495 #[test]
4496 fn test_c2pa_reader_with_fragment_null_format() {
4497 let source_image = include_bytes!(fixture_path!("C.jpg"));
4499 let mut stream = TestStream::new(source_image.to_vec());
4500 let format = CString::new("image/jpeg").unwrap();
4501
4502 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4503 assert!(!reader.is_null());
4504
4505 let mut fragment_stream = TestStream::new(source_image.to_vec());
4506 let mut main_stream = TestStream::new(source_image.to_vec());
4507
4508 let new_reader = unsafe {
4510 c2pa_reader_with_fragment(
4511 reader,
4512 std::ptr::null(),
4513 main_stream.as_ptr(),
4514 fragment_stream.as_ptr(),
4515 )
4516 };
4517 assert!(
4518 new_reader.is_null(),
4519 "Should return null for invalid format"
4520 );
4521
4522 let free_result = unsafe { c2pa_free(reader as *const c_void) };
4524 assert_eq!(free_result, 0);
4525 }
4526
4527 #[test]
4530 fn test_c2pa_reader_new() {
4531 let reader = unsafe { c2pa_reader_new() };
4532 assert!(!reader.is_null(), "Should create a default reader");
4533
4534 unsafe {
4535 c2pa_free(reader as *mut c_void);
4536 }
4537 }
4538
4539 #[test]
4540 fn test_c2pa_reader_is_embedded_null() {
4541 let result = unsafe { c2pa_reader_is_embedded(std::ptr::null_mut()) };
4543 assert!(!result, "Null reader should return false");
4544 }
4545
4546 #[test]
4547 fn test_c2pa_reader_remote_url_null() {
4548 let result = unsafe { c2pa_reader_remote_url(std::ptr::null_mut()) };
4550 assert!(result.is_null(), "Null reader should return null URL");
4551 }
4552
4553 #[test]
4554 fn test_c2pa_builder_set_intent_null() {
4555 let result = unsafe {
4557 c2pa_builder_set_intent(
4558 std::ptr::null_mut(),
4559 C2paBuilderIntent::Create,
4560 C2paDigitalSourceType::DigitalCapture,
4561 )
4562 };
4563 assert_eq!(result, -1, "Null builder should return -1");
4564 }
4565
4566 #[test]
4567 fn test_c2pa_builder_add_action_null_builder() {
4568 let action = CString::new(r#"{"action": "c2pa.edited"}"#).unwrap();
4569 let result = unsafe { c2pa_builder_add_action(std::ptr::null_mut(), action.as_ptr()) };
4570 assert_eq!(result, -1, "Null builder should return error");
4571 }
4572
4573 #[test]
4574 fn test_c2pa_builder_add_action_null_action() {
4575 let manifest_def = CString::new("{}").unwrap();
4576 let builder = unsafe { c2pa_builder_from_json(manifest_def.as_ptr()) };
4577 assert!(!builder.is_null());
4578
4579 let result = unsafe { c2pa_builder_add_action(builder, std::ptr::null()) };
4580 assert_eq!(result, -1, "Null action should return error");
4581
4582 unsafe {
4583 c2pa_free(builder as *mut c_void);
4584 }
4585 }
4586
4587 #[test]
4588 fn test_c2pa_context_builder_set_settings_null_settings() {
4589 let builder = unsafe { c2pa_context_builder_new() };
4590 assert!(!builder.is_null());
4591
4592 let result = unsafe { c2pa_context_builder_set_settings(builder, std::ptr::null_mut()) };
4594 assert_eq!(result, -1, "Null settings should return -1");
4595
4596 unsafe {
4597 c2pa_free(builder as *mut c_void);
4598 }
4599 }
4600
4601 #[test]
4602 fn test_c2pa_signer_reserve_size() {
4603 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
4604
4605 let size = unsafe { c2pa_signer_reserve_size(signer) };
4606 assert!(size > 0, "Reserve size should be positive");
4607
4608 unsafe {
4609 c2pa_free(signer as *mut c_void);
4610 c2pa_free(builder as *mut c_void);
4611 }
4612 }
4613
4614 #[test]
4615 fn test_c2pa_signer_reserve_size_null() {
4616 let size = unsafe { c2pa_signer_reserve_size(std::ptr::null_mut()) };
4617 assert_eq!(size, -1, "Null signer should return -1");
4618 }
4619
4620 #[test]
4621 fn test_c2pa_string_free_backward_compat() {
4622 let test_str = CString::new("test string").unwrap();
4624 let c_str = to_c_string(test_str.to_str().unwrap().to_string());
4625 assert!(!c_str.is_null());
4626
4627 unsafe {
4629 c2pa_string_free(c_str);
4630 }
4631 }
4632
4633 #[test]
4634 fn test_c2pa_string_free_null() {
4635 unsafe {
4637 c2pa_string_free(std::ptr::null_mut());
4638 }
4639 }
4641
4642 #[test]
4643 fn test_c2pa_release_string() {
4644 let test_str = CString::new("test string for release").unwrap();
4646 let c_str = to_c_string(test_str.to_str().unwrap().to_string());
4647 assert!(!c_str.is_null());
4648
4649 unsafe {
4651 c2pa_release_string(c_str);
4652 }
4653 }
4654
4655 #[test]
4656 fn test_c2pa_ed25519_sign_actually_calls_function() {
4657 let bytes = b"test data to sign";
4659 let private_key_pem = include_bytes!(fixture_path!("certs/ed25519.pem"));
4660 let private_key = CString::new(private_key_pem.as_slice()).unwrap();
4661
4662 let signature =
4663 unsafe { c2pa_ed25519_sign(bytes.as_ptr(), bytes.len(), private_key.as_ptr()) };
4664
4665 assert!(!signature.is_null(), "Should return signature");
4666
4667 unsafe {
4668 c2pa_signature_free(signature);
4669 }
4670 }
4671
4672 #[test]
4673 fn test_c2pa_ed25519_sign_null_bytes() {
4674 let private_key_path = CString::new(fixture_path!("certs/ed25519.pem")).unwrap();
4675
4676 let signature =
4677 unsafe { c2pa_ed25519_sign(std::ptr::null(), 10, private_key_path.as_ptr()) };
4678
4679 assert!(signature.is_null(), "Null bytes should return null");
4680 }
4681
4682 #[test]
4683 fn test_c2pa_ed25519_sign_null_key() {
4684 let bytes = b"test data";
4685
4686 let signature = unsafe { c2pa_ed25519_sign(bytes.as_ptr(), bytes.len(), std::ptr::null()) };
4687
4688 assert!(signature.is_null(), "Null key should return null");
4689
4690 let error = unsafe { c2pa_error() };
4692 assert!(!error.is_null());
4693 unsafe {
4694 c2pa_string_free(error);
4695 }
4696 }
4697
4698 #[test]
4699 fn test_c2pa_reader_detailed_json_null() {
4700 let result = unsafe { c2pa_reader_detailed_json(std::ptr::null_mut()) };
4701 assert!(result.is_null(), "Null reader should return null");
4702 }
4703
4704 #[test]
4705 fn test_c2pa_reader_json_better_coverage() {
4706 let source_image = include_bytes!(fixture_path!("C.jpg"));
4708 let mut stream = TestStream::new(source_image.to_vec());
4709 let format = CString::new("image/jpeg").unwrap();
4710
4711 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4712 assert!(!reader.is_null());
4713
4714 let json = unsafe { c2pa_reader_json(reader) };
4715 assert!(!json.is_null(), "Should return JSON");
4716
4717 use std::ffi::CStr;
4719 let json_str = unsafe { CStr::from_ptr(json).to_str().unwrap() };
4720 assert!(!json_str.is_empty());
4721 let _: serde_json::Value = serde_json::from_str(json_str).unwrap();
4722
4723 unsafe {
4724 c2pa_free(json as *mut c_void);
4725 c2pa_free(reader as *mut c_void);
4726 }
4727 }
4728
4729 #[test]
4730 fn test_c2pa_error_set_last() {
4731 let error_msg = CString::new("Custom error message").unwrap();
4732 let result = unsafe { c2pa_error_set_last(error_msg.as_ptr()) };
4733 assert_eq!(result, 0, "c2pa_error_set_last should return 0 on success");
4734
4735 let error = unsafe { c2pa_error() };
4737 assert!(
4738 !error.is_null(),
4739 "Error should be retrievable after set_last"
4740 );
4741 let error_str = unsafe { CString::from_raw(error) };
4742 assert_eq!(
4744 error_str.to_str().unwrap(),
4745 "Other: Custom error message",
4746 "Error message should match what was set"
4747 );
4748 }
4749
4750 #[test]
4751 fn test_c2pa_error_set_last_null() {
4752 let result = unsafe { c2pa_error_set_last(std::ptr::null()) };
4753 assert_eq!(
4754 result, -1,
4755 "c2pa_error_set_last should return -1 for null parameter"
4756 );
4757 }
4758
4759 #[test]
4760 fn test_c2pa_reader_from_manifest_data_and_stream() {
4761 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
4763 let mut source_stream = TestStream::new(source_image.to_vec());
4764 let dest_vec = Vec::new();
4765 let mut dest_stream = TestStream::new(dest_vec);
4766
4767 let (signer, builder) = setup_signer_and_builder_for_signing_tests();
4768
4769 let format = CString::new("image/jpeg").unwrap();
4770 let mut manifest_bytes_ptr = std::ptr::null();
4771
4772 let result = unsafe {
4774 c2pa_builder_sign(
4775 builder,
4776 format.as_ptr(),
4777 source_stream.as_ptr(),
4778 dest_stream.as_ptr(),
4779 signer,
4780 &mut manifest_bytes_ptr,
4781 )
4782 };
4783 assert!(result > 0, "Signing should succeed");
4784 assert!(
4785 !manifest_bytes_ptr.is_null(),
4786 "Manifest bytes should be returned"
4787 );
4788
4789 let manifest_size = result as usize;
4790
4791 let mut validation_stream = TestStream::new(source_image.to_vec());
4794
4795 let reader = unsafe {
4796 c2pa_reader_from_manifest_data_and_stream(
4797 format.as_ptr(),
4798 validation_stream.as_ptr(),
4799 manifest_bytes_ptr,
4800 manifest_size,
4801 )
4802 };
4803
4804 assert!(
4805 !reader.is_null(),
4806 "Reader should be created from manifest data and stream"
4807 );
4808
4809 let json = unsafe { c2pa_reader_json(reader) };
4811 assert!(!json.is_null(), "Should be able to get JSON from reader");
4812
4813 unsafe {
4815 c2pa_free(json as *const c_void);
4816 c2pa_free(reader as *const c_void);
4817 c2pa_free(manifest_bytes_ptr as *const c_void);
4818 c2pa_free(builder as *const c_void);
4819 c2pa_free(signer as *const c_void);
4820 }
4821 }
4822
4823 #[test]
4824 fn test_c2pa_reader_from_manifest_data_and_stream_null_format() {
4825 let source_image = include_bytes!(fixture_path!("C.jpg"));
4826 let mut stream = TestStream::new(source_image.to_vec());
4827 let manifest_data = [0u8; 100];
4828
4829 let reader = unsafe {
4830 c2pa_reader_from_manifest_data_and_stream(
4831 std::ptr::null(),
4832 stream.as_ptr(),
4833 manifest_data.as_ptr(),
4834 manifest_data.len(),
4835 )
4836 };
4837
4838 assert!(reader.is_null(), "Reader should be null for null format");
4839 let error = unsafe { c2pa_error() };
4840 let error_str = unsafe { CString::from_raw(error) };
4841 assert_eq!(error_str.to_str().unwrap(), "NullParameter: format");
4842 }
4843
4844 #[test]
4845 fn test_c2pa_reader_resource_to_stream() {
4846 let source_image = include_bytes!(fixture_path!("C.jpg"));
4848 let mut stream = TestStream::new(source_image.to_vec());
4849 let format = CString::new("image/jpeg").unwrap();
4850
4851 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4852 assert!(
4853 !reader.is_null(),
4854 "Reader should be created from C2PA image"
4855 );
4856
4857 let resource_uri =
4859 CString::new("self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg").unwrap();
4860 let mut output_stream = TestStream::new(Vec::new());
4861
4862 let result = unsafe {
4863 c2pa_reader_resource_to_stream(reader, resource_uri.as_ptr(), output_stream.as_ptr())
4864 };
4865
4866 assert!(result >= 0, "resource_to_stream should return >= 0");
4869
4870 unsafe {
4872 c2pa_free(reader as *const c_void);
4873 }
4874 }
4875
4876 #[test]
4877 fn test_c2pa_reader_resource_to_stream_null_reader() {
4878 let resource_uri = CString::new("some_uri").unwrap();
4879 let mut output_stream = TestStream::new(Vec::new());
4880
4881 let result = unsafe {
4882 c2pa_reader_resource_to_stream(
4883 std::ptr::null_mut(),
4884 resource_uri.as_ptr(),
4885 output_stream.as_ptr(),
4886 )
4887 };
4888
4889 assert_eq!(
4890 result, -1,
4891 "resource_to_stream should return -1 for null reader"
4892 );
4893 let error = unsafe { c2pa_error() };
4894 let error_str = unsafe { CString::from_raw(error) };
4895 assert_eq!(error_str.to_str().unwrap(), "NullParameter: reader_ptr");
4896 }
4897
4898 #[test]
4899 fn test_c2pa_reader_resource_to_stream_null_uri() {
4900 let source_image = include_bytes!(fixture_path!("C.jpg"));
4902 let mut stream = TestStream::new(source_image.to_vec());
4903 let format = CString::new("image/jpeg").unwrap();
4904
4905 let reader = unsafe { c2pa_reader_from_stream(format.as_ptr(), stream.as_ptr()) };
4906 assert!(!reader.is_null());
4907
4908 let mut output_stream = TestStream::new(Vec::new());
4909
4910 let result = unsafe {
4911 c2pa_reader_resource_to_stream(reader, std::ptr::null(), output_stream.as_ptr())
4912 };
4913
4914 assert_eq!(
4915 result, -1,
4916 "resource_to_stream should return -1 for null uri"
4917 );
4918
4919 unsafe {
4921 c2pa_free(reader as *const c_void);
4922 }
4923 }
4924
4925 #[test]
4926
4927 fn test_data_hash_embeddable_workflow() {
4928 const SETTINGS: &str = include_str!(fixture_path!("test_settings.json"));
4931
4932 let settings = unsafe { c2pa_settings_new() };
4933 assert!(!settings.is_null());
4934 let json_str = CString::new(SETTINGS).unwrap();
4935 let fmt = CString::new("json").unwrap();
4936 let result =
4937 unsafe { c2pa_settings_update_from_string(settings, json_str.as_ptr(), fmt.as_ptr()) };
4938 assert_eq!(result, 0);
4939
4940 let ctx_builder = unsafe { c2pa_context_builder_new() };
4941 assert!(!ctx_builder.is_null());
4942 let result = unsafe { c2pa_context_builder_set_settings(ctx_builder, settings) };
4943 assert_eq!(result, 0);
4944 let context = unsafe { c2pa_context_builder_build(ctx_builder) };
4945 assert!(!context.is_null());
4946
4947 let builder = unsafe { c2pa_builder_from_context(context) };
4949 assert!(!builder.is_null());
4950
4951 let format = CString::new("image/jpeg").unwrap();
4952 let needs = unsafe { c2pa_builder_needs_placeholder(builder, format.as_ptr()) };
4954 assert!(needs >= 0, "needs_placeholder should not error");
4955 assert!(needs <= 1, "needs_placeholder returns 0 or 1");
4956
4957 let source_image = include_bytes!(fixture_path!("IMG_0003.jpg"));
4959 let mut source_stream = TestStream::new(source_image.to_vec());
4960 let result = unsafe {
4961 c2pa_builder_update_hash_from_stream(builder, format.as_ptr(), source_stream.as_ptr())
4962 };
4963 assert_eq!(result, 0, "update_hash_from_stream failed");
4964
4965 let mut signed_bytes_ptr: *const c_uchar = std::ptr::null();
4967 let len = unsafe {
4968 c2pa_builder_sign_embeddable(builder, format.as_ptr(), &mut signed_bytes_ptr)
4969 };
4970 assert!(len > 0, "sign_embeddable should return positive length");
4971 assert!(!signed_bytes_ptr.is_null());
4972
4973 unsafe {
4974 c2pa_free(signed_bytes_ptr as *mut c_void);
4975 c2pa_free(settings as *mut c_void);
4976 c2pa_free(context as *mut c_void);
4977 c2pa_free(builder as *mut c_void);
4978 }
4979 }
4980
4981 #[test]
4982
4983 fn test_bmff_embeddable_workflow_with_mdat_hashes() {
4984 const SETTINGS: &str = include_str!(fixture_path!("test_settings.json"));
4986
4987 let settings = unsafe { c2pa_settings_new() };
4988 assert!(!settings.is_null());
4989 let json_str = CString::new(SETTINGS).unwrap();
4990 let fmt = CString::new("json").unwrap();
4991 let result =
4992 unsafe { c2pa_settings_update_from_string(settings, json_str.as_ptr(), fmt.as_ptr()) };
4993 assert_eq!(result, 0);
4994
4995 let ctx_builder = unsafe { c2pa_context_builder_new() };
4996 assert!(!ctx_builder.is_null());
4997 let result = unsafe { c2pa_context_builder_set_settings(ctx_builder, settings) };
4998 assert_eq!(result, 0);
4999 let context = unsafe { c2pa_context_builder_build(ctx_builder) };
5000 assert!(!context.is_null());
5001
5002 let builder = unsafe { c2pa_builder_from_context(context) };
5004 assert!(!builder.is_null());
5005
5006 let format = CString::new("video/mp4").unwrap();
5007 let needs = unsafe { c2pa_builder_needs_placeholder(builder, format.as_ptr()) };
5009 assert_eq!(
5010 needs, 1,
5011 "needs_placeholder should be 1 for video/mp4 before placeholder"
5012 );
5013
5014 let size_only =
5016 unsafe { c2pa_builder_placeholder(builder, format.as_ptr(), std::ptr::null_mut()) };
5017 assert!(
5018 size_only > 0,
5019 "placeholder with null output should return positive size"
5020 );
5021 assert_ne!(
5022 size_only, -1,
5023 "placeholder with null output should not error"
5024 );
5025
5026 let mut placeholder_ptr: *const c_uchar = std::ptr::null();
5028 let placeholder_len =
5029 unsafe { c2pa_builder_placeholder(builder, format.as_ptr(), &mut placeholder_ptr) };
5030 assert!(
5031 placeholder_len > 0,
5032 "placeholder should return non-empty bytes"
5033 );
5034 assert!(!placeholder_ptr.is_null());
5035
5036 unsafe {
5038 c2pa_builder_set_fixed_size_merkle(builder, 1);
5039 }
5040
5041 let leaf_data: [u8; 4096] = [0xab; 4096];
5045 let result = unsafe {
5046 c2pa_builder_hash_mdat_bytes(builder, 0, leaf_data.as_ptr(), leaf_data.len(), true)
5047 };
5048 assert_eq!(result, 0, "set_bmff_mdat_hashes failed");
5049
5050 let video = include_bytes!(fixture_path!("video1.mp4"));
5052 let mut video_stream = TestStream::new(video.to_vec());
5053 let result = unsafe {
5054 c2pa_builder_update_hash_from_stream(builder, format.as_ptr(), video_stream.as_ptr())
5055 };
5056 assert_eq!(result, 0, "update_hash_from_stream failed");
5057
5058 let mut signed_bytes_ptr: *const c_uchar = std::ptr::null();
5060 let len = unsafe {
5061 c2pa_builder_sign_embeddable(builder, format.as_ptr(), &mut signed_bytes_ptr)
5062 };
5063 assert!(len > 0, "sign_embeddable should return positive length");
5064 assert!(!signed_bytes_ptr.is_null());
5065
5066 unsafe {
5067 c2pa_free(placeholder_ptr as *mut c_void);
5068 c2pa_free(signed_bytes_ptr as *mut c_void);
5069 c2pa_free(settings as *mut c_void);
5070 c2pa_free(context as *mut c_void);
5071 c2pa_free(builder as *mut c_void);
5072 }
5073 }
5074
5075 #[test]
5076 fn test_context_builder_set_signer() {
5077 let certs = include_str!(fixture_path!("certs/ed25519.pub"));
5078 let private_key = include_bytes!(fixture_path!("certs/ed25519.pem"));
5079 let alg = CString::new("Ed25519").unwrap();
5080 let sign_cert = CString::new(certs).unwrap();
5081 let private_key = CString::new(private_key.as_slice()).unwrap();
5082 let signer_info = C2paSignerInfo {
5083 alg: alg.as_ptr(),
5084 sign_cert: sign_cert.as_ptr(),
5085 private_key: private_key.as_ptr(),
5086 ta_url: std::ptr::null(),
5087 };
5088 let signer = unsafe { c2pa_signer_from_info(&signer_info) };
5089 assert!(!signer.is_null());
5090
5091 let builder = unsafe { c2pa_context_builder_new() };
5092 assert!(!builder.is_null());
5093
5094 let result = unsafe { c2pa_context_builder_set_signer(builder, signer) };
5095 assert_eq!(result, 0);
5096
5097 let free_result = unsafe { c2pa_free(signer as *const c_void) };
5099 assert_eq!(free_result, -1);
5100
5101 let context = unsafe { c2pa_context_builder_build(builder) };
5102 assert!(!context.is_null());
5103
5104 let builder = unsafe { c2pa_builder_from_context(context) };
5105 assert!(!builder.is_null());
5106
5107 unsafe {
5108 c2pa_free(builder as *mut c_void);
5109 c2pa_free(context as *mut c_void);
5110 }
5111 }
5112
5113 #[test]
5114 fn test_context_builder_set_signer_null() {
5115 let builder = unsafe { c2pa_context_builder_new() };
5116 assert!(!builder.is_null());
5117
5118 let result = unsafe { c2pa_context_builder_set_signer(builder, std::ptr::null_mut()) };
5119 assert_eq!(result, -1, "Null signer should be rejected");
5120
5121 unsafe { c2pa_free(builder as *mut c_void) };
5122 }
5123
5124 #[test]
5125 fn test_c2pa_context_builder_set_progress_callback() {
5126 use std::sync::atomic::{AtomicU32, Ordering};
5127
5128 let call_count = Arc::new(AtomicU32::new(0));
5129 let raw_ptr = Arc::as_ptr(&call_count) as *const c_void;
5130
5131 unsafe extern "C" fn progress_cb(
5132 context: *const c_void,
5133 _phase: C2paProgressPhase,
5134 _step: u32,
5135 _total: u32,
5136 ) -> c_int {
5137 let counter = &*(context as *const AtomicU32);
5138 counter.fetch_add(1, Ordering::SeqCst);
5139 1
5140 }
5141
5142 let builder = unsafe { c2pa_context_builder_new() };
5143 assert!(!builder.is_null());
5144
5145 let result =
5146 unsafe { c2pa_context_builder_set_progress_callback(builder, raw_ptr, progress_cb) };
5147 assert_eq!(result, 0, "set_progress_callback should succeed");
5148
5149 let context = unsafe { c2pa_context_builder_build(builder) };
5150 assert!(!context.is_null());
5151
5152 unsafe { c2pa_free(context as *mut c_void) };
5153 }
5155
5156 #[test]
5157 fn test_c2pa_context_builder_set_progress_callback_null_user_data() {
5158 unsafe extern "C" fn progress_cb(
5159 _context: *const c_void,
5160 _phase: C2paProgressPhase,
5161 _step: u32,
5162 _total: u32,
5163 ) -> c_int {
5164 1
5165 }
5166
5167 let builder = unsafe { c2pa_context_builder_new() };
5168 assert!(!builder.is_null());
5169
5170 let result = unsafe {
5171 c2pa_context_builder_set_progress_callback(builder, std::ptr::null(), progress_cb)
5172 };
5173 assert_eq!(result, 0, "NULL user_data should be accepted");
5174
5175 let context = unsafe { c2pa_context_builder_build(builder) };
5176 assert!(!context.is_null());
5177
5178 unsafe { c2pa_free(context as *mut c_void) };
5179 }
5180
5181 #[test]
5182 fn test_c2pa_context_builder_set_progress_callback_null_builder() {
5183 unsafe extern "C" fn progress_cb(
5184 _context: *const c_void,
5185 _phase: C2paProgressPhase,
5186 _step: u32,
5187 _total: u32,
5188 ) -> c_int {
5189 1
5190 }
5191
5192 let result = unsafe {
5193 c2pa_context_builder_set_progress_callback(
5194 std::ptr::null_mut(),
5195 std::ptr::null(),
5196 progress_cb,
5197 )
5198 };
5199 assert_eq!(result, -1, "NULL builder should return error");
5200 }
5201
5202 #[test]
5203 fn test_progress_phase_to_c2pa_progress_phase() {
5204 let cases: &[(ProgressPhase, i32)] = &[
5205 (ProgressPhase::Reading, 0),
5206 (ProgressPhase::VerifyingManifest, 1),
5207 (ProgressPhase::VerifyingSignature, 2),
5208 (ProgressPhase::VerifyingIngredient, 3),
5209 (ProgressPhase::VerifyingAssetHash, 4),
5210 (ProgressPhase::AddingIngredient, 5),
5211 (ProgressPhase::Thumbnail, 6),
5212 (ProgressPhase::Hashing, 7),
5213 (ProgressPhase::Signing, 8),
5214 (ProgressPhase::Embedding, 9),
5215 (ProgressPhase::FetchingRemoteManifest, 10),
5216 (ProgressPhase::Writing, 11),
5217 (ProgressPhase::FetchingOCSP, 12),
5218 (ProgressPhase::FetchingTimestamp, 13),
5219 ];
5220 for (sdk_phase, expected) in cases {
5221 let c_phase = C2paProgressPhase::from(sdk_phase.clone());
5222 assert_eq!(c_phase as i32, *expected, "mismatch for {sdk_phase:?}");
5223 }
5224 }
5225
5226 #[test]
5227 fn test_c2pa_context_cancel() {
5228 let context = unsafe { c2pa_context_new() };
5229 assert!(!context.is_null());
5230
5231 let result = unsafe { c2pa_context_cancel(context) };
5232 assert_eq!(result, 0, "cancel should succeed on a valid context");
5233
5234 unsafe { c2pa_free(context as *mut c_void) };
5235 }
5236
5237 #[test]
5238 fn test_c2pa_context_cancel_null() {
5239 let result = unsafe { c2pa_context_cancel(std::ptr::null_mut()) };
5240 assert_eq!(result, -1, "NULL context should return error");
5241 }
5242
5243 #[test]
5244 fn test_c2pa_context_cancel_via_builder() {
5245 let builder = unsafe { c2pa_context_builder_new() };
5246 assert!(!builder.is_null());
5247
5248 let context = unsafe { c2pa_context_builder_build(builder) };
5249 assert!(!context.is_null());
5250
5251 let result = unsafe { c2pa_context_cancel(context) };
5252 assert_eq!(result, 0, "cancel should work on a built context");
5253
5254 unsafe { c2pa_free(context as *mut c_void) };
5255 }
5256}