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