Skip to main content

zsign_rust/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5use std::ffi::{CStr, CString};
6use std::os::raw::c_char;
7use std::ptr;
8
9use thiserror::Error;
10
11include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
12
13#[derive(Debug, Default)]
14pub struct ZSignOptions {
15    pub input_path: String,
16
17    pub cert_file: Option<String>,
18    pub pkey_file: Option<String>,
19    pub prov_file: Option<String>,
20    pub password: Option<String>,
21    pub adhoc: bool,
22    pub sha256_only: bool,
23
24    pub bundle_id: Option<String>,
25    pub bundle_name: Option<String>,
26    pub bundle_version: Option<String>,
27    pub entitlements_file: Option<String>,
28
29    pub dylib_files: Vec<String>,
30    pub weak_inject: bool,
31
32    pub force: bool,
33    pub check_signature: bool,
34    pub temp_folder: Option<String>,
35
36    pub debug: bool,
37    pub quiet: bool,
38    pub enable_cache: bool,
39}
40
41#[derive(Debug, Clone, Error)]
42pub enum ZSignError {
43    #[error("Invalid input parameters")]
44    InvalidInput,
45    #[error("Signing operation failed")]
46    SigningFailed,
47    #[error("File is not signed")]
48    NotSigned,
49    #[error("Unknown error, code {0}")]
50    Unknown(i32),
51}
52
53impl ZSignOptions {
54    pub fn new<S: Into<String>>(input_path: S) -> Self {
55        Self {
56            input_path: input_path.into(),
57            enable_cache: true,
58            ..Default::default()
59        }
60    }
61
62    pub fn with_adhoc_signing(mut self) -> Self {
63        self.adhoc = true;
64        self
65    }
66
67    pub fn with_cert_file<S: Into<String>>(mut self, cert_file: S) -> Self {
68        self.cert_file = Some(cert_file.into());
69        self
70    }
71
72    pub fn with_pkey_file<S: Into<String>>(mut self, pkey_file: S) -> Self {
73        self.pkey_file = Some(pkey_file.into());
74        self
75    }
76
77    pub fn with_prov_file<S: Into<String>>(mut self, prov_file: S) -> Self {
78        self.prov_file = Some(prov_file.into());
79        self
80    }
81
82    pub fn with_password<S: Into<String>>(mut self, password: S) -> Self {
83        self.password = Some(password.into());
84        self
85    }
86
87    pub fn with_bundle_id<S: Into<String>>(mut self, bundle_id: S) -> Self {
88        self.bundle_id = Some(bundle_id.into());
89        self
90    }
91
92    pub fn with_bundle_name<S: Into<String>>(mut self, bundle_name: S) -> Self {
93        self.bundle_name = Some(bundle_name.into());
94        self
95    }
96
97    pub fn with_bundle_version<S: Into<String>>(mut self, bundle_version: S) -> Self {
98        self.bundle_version = Some(bundle_version.into());
99        self
100    }
101
102    pub fn with_entitlements_file<S: Into<String>>(mut self, entitlements_file: S) -> Self {
103        self.entitlements_file = Some(entitlements_file.into());
104        self
105    }
106
107    pub fn with_temp_folder<S: Into<String>>(mut self, temp_folder: S) -> Self {
108        self.temp_folder = Some(temp_folder.into());
109        self
110    }
111
112    pub fn with_weak_inject(mut self) -> Self {
113        self.weak_inject = true;
114        self
115    }
116
117    pub fn with_force(mut self) -> Self {
118        self.force = true;
119        self
120    }
121
122    pub fn with_check_signature(mut self) -> Self {
123        self.check_signature = true;
124        self
125    }
126
127    pub fn with_quiet(mut self) -> Self {
128        self.quiet = true;
129        self
130    }
131
132    pub fn with_debug(mut self) -> Self {
133        self.debug = true;
134        self
135    }
136
137    pub fn with_disable_cache(mut self) -> Self {
138        self.enable_cache = false;
139        self
140    }
141
142    pub fn add_dylib<S: Into<String>>(mut self, dylib_path: S) -> Self {
143        self.dylib_files.push(dylib_path.into());
144        self
145    }
146
147    pub fn sign(&self) -> Result<(), ZSignError> {
148        let input_path = CString::new(self.input_path.clone()).unwrap();
149        let cert_file = self
150            .cert_file
151            .as_ref()
152            .map(|s| CString::new(s.clone()).unwrap());
153        let pkey_file = self
154            .pkey_file
155            .as_ref()
156            .map(|s| CString::new(s.clone()).unwrap());
157        let prov_file = self
158            .prov_file
159            .as_ref()
160            .map(|s| CString::new(s.clone()).unwrap());
161        let password = self
162            .password
163            .as_ref()
164            .map(|s| CString::new(s.clone()).unwrap());
165        let bundle_id = self
166            .bundle_id
167            .as_ref()
168            .map(|s| CString::new(s.clone()).unwrap());
169        let bundle_name = self
170            .bundle_name
171            .as_ref()
172            .map(|s| CString::new(s.clone()).unwrap());
173        let bundle_version = self
174            .bundle_version
175            .as_ref()
176            .map(|s| CString::new(s.clone()).unwrap());
177        let entitlements_file = self
178            .entitlements_file
179            .as_ref()
180            .map(|s| CString::new(s.clone()).unwrap());
181        let temp_folder = self
182            .temp_folder
183            .as_ref()
184            .map(|s| CString::new(s.clone()).unwrap());
185
186        let dylib_cstrings: Vec<CString> = self
187            .dylib_files
188            .iter()
189            .map(|s| CString::new(s.clone()).unwrap())
190            .collect();
191        let mut dylib_ptrs: Vec<*const c_char> =
192            dylib_cstrings.iter().map(|cs| cs.as_ptr()).collect();
193
194        unsafe {
195            let result = sign_ipa(
196                input_path.as_ptr(),
197                cert_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
198                pkey_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
199                prov_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
200                password.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
201                if self.adhoc { 1 } else { 0 },
202                if self.sha256_only { 1 } else { 0 },
203                bundle_id.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
204                bundle_name.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
205                bundle_version.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
206                entitlements_file
207                    .as_ref()
208                    .map_or(ptr::null(), |s| s.as_ptr()),
209                if dylib_ptrs.is_empty() {
210                    ptr::null_mut()
211                } else {
212                    dylib_ptrs.as_mut_ptr()
213                },
214                dylib_ptrs.len() as i32,
215                if self.weak_inject { 1 } else { 0 },
216                if self.force { 1 } else { 0 },
217                if self.check_signature { 1 } else { 0 },
218                temp_folder.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
219                if self.debug { 1 } else { 0 },
220                if self.quiet { 1 } else { 0 },
221                if self.enable_cache { 1 } else { 0 },
222            );
223
224            match result {
225                0 => Ok(()),
226                -1 => Err(ZSignError::SigningFailed),
227                -2 => Err(ZSignError::NotSigned),
228                code => Err(ZSignError::Unknown(code)),
229            }
230        }
231    }
232}
233
234pub fn get_version() -> String {
235    unsafe {
236        let version_ptr = get_zsign_version();
237        let version_cstr = CStr::from_ptr(version_ptr);
238        version_cstr.to_str().unwrap().to_string()
239    }
240}