codesign.cpp 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
  1. /**************************************************************************/
  2. /* codesign.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "codesign.h"
  31. #include "core/crypto/crypto_core.h"
  32. #include "core/io/dir_access.h"
  33. #include "core/io/plist.h"
  34. #include "editor/editor_paths.h"
  35. #include "lipo.h"
  36. #include "macho.h"
  37. #include "modules/regex/regex.h"
  38. #include <ctime>
  39. /*************************************************************************/
  40. /* CodeSignCodeResources */
  41. /*************************************************************************/
  42. String CodeSignCodeResources::hash_sha1_base64(const String &p_path) {
  43. Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
  44. ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
  45. CryptoCore::SHA1Context ctx;
  46. ctx.start();
  47. unsigned char step[4096];
  48. while (true) {
  49. uint64_t br = fa->get_buffer(step, 4096);
  50. if (br > 0) {
  51. ctx.update(step, br);
  52. }
  53. if (br < 4096) {
  54. break;
  55. }
  56. }
  57. unsigned char hash[0x14];
  58. ctx.finish(hash);
  59. return CryptoCore::b64_encode_str(hash, 0x14);
  60. }
  61. String CodeSignCodeResources::hash_sha256_base64(const String &p_path) {
  62. Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
  63. ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
  64. CryptoCore::SHA256Context ctx;
  65. ctx.start();
  66. unsigned char step[4096];
  67. while (true) {
  68. uint64_t br = fa->get_buffer(step, 4096);
  69. if (br > 0) {
  70. ctx.update(step, br);
  71. }
  72. if (br < 4096) {
  73. break;
  74. }
  75. }
  76. unsigned char hash[0x20];
  77. ctx.finish(hash);
  78. return CryptoCore::b64_encode_str(hash, 0x20);
  79. }
  80. void CodeSignCodeResources::add_rule1(const String &p_rule, const String &p_key, int p_weight, bool p_store) {
  81. rules1.push_back(CRRule(p_rule, p_key, p_weight, p_store));
  82. }
  83. void CodeSignCodeResources::add_rule2(const String &p_rule, const String &p_key, int p_weight, bool p_store) {
  84. rules2.push_back(CRRule(p_rule, p_key, p_weight, p_store));
  85. }
  86. CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules1(const String &p_path) const {
  87. CRMatch found = CRMatch::CR_MATCH_NO;
  88. int weight = 0;
  89. for (int i = 0; i < rules1.size(); i++) {
  90. RegEx regex = RegEx(rules1[i].file_pattern);
  91. if (regex.search(p_path).is_valid()) {
  92. if (rules1[i].key == "omit") {
  93. return CRMatch::CR_MATCH_NO;
  94. } else if (rules1[i].key == "nested") {
  95. if (weight <= rules1[i].weight) {
  96. found = CRMatch::CR_MATCH_NESTED;
  97. weight = rules1[i].weight;
  98. }
  99. } else if (rules1[i].key == "optional") {
  100. if (weight <= rules1[i].weight) {
  101. found = CRMatch::CR_MATCH_OPTIONAL;
  102. weight = rules1[i].weight;
  103. }
  104. } else {
  105. if (weight <= rules1[i].weight) {
  106. found = CRMatch::CR_MATCH_YES;
  107. weight = rules1[i].weight;
  108. }
  109. }
  110. }
  111. }
  112. return found;
  113. }
  114. CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules2(const String &p_path) const {
  115. CRMatch found = CRMatch::CR_MATCH_NO;
  116. int weight = 0;
  117. for (int i = 0; i < rules2.size(); i++) {
  118. RegEx regex = RegEx(rules2[i].file_pattern);
  119. if (regex.search(p_path).is_valid()) {
  120. if (rules2[i].key == "omit") {
  121. return CRMatch::CR_MATCH_NO;
  122. } else if (rules2[i].key == "nested") {
  123. if (weight <= rules2[i].weight) {
  124. found = CRMatch::CR_MATCH_NESTED;
  125. weight = rules2[i].weight;
  126. }
  127. } else if (rules2[i].key == "optional") {
  128. if (weight <= rules2[i].weight) {
  129. found = CRMatch::CR_MATCH_OPTIONAL;
  130. weight = rules2[i].weight;
  131. }
  132. } else {
  133. if (weight <= rules2[i].weight) {
  134. found = CRMatch::CR_MATCH_YES;
  135. weight = rules2[i].weight;
  136. }
  137. }
  138. }
  139. }
  140. return found;
  141. }
  142. bool CodeSignCodeResources::add_file1(const String &p_root, const String &p_path) {
  143. CRMatch found = match_rules1(p_path);
  144. if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) {
  145. return true; // No match.
  146. }
  147. CRFile f;
  148. f.name = p_path;
  149. f.optional = (found == CRMatch::CR_MATCH_OPTIONAL);
  150. f.nested = false;
  151. f.hash = hash_sha1_base64(p_root.path_join(p_path));
  152. print_verbose(vformat("CodeSign/CodeResources: File(V1) %s hash1:%s", f.name, f.hash));
  153. files1.push_back(f);
  154. return true;
  155. }
  156. bool CodeSignCodeResources::add_file2(const String &p_root, const String &p_path) {
  157. CRMatch found = match_rules2(p_path);
  158. if (found == CRMatch::CR_MATCH_NESTED) {
  159. return add_nested_file(p_root, p_path, p_root.path_join(p_path));
  160. }
  161. if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) {
  162. return true; // No match.
  163. }
  164. CRFile f;
  165. f.name = p_path;
  166. f.optional = (found == CRMatch::CR_MATCH_OPTIONAL);
  167. f.nested = false;
  168. f.hash = hash_sha1_base64(p_root.path_join(p_path));
  169. f.hash2 = hash_sha256_base64(p_root.path_join(p_path));
  170. print_verbose(vformat("CodeSign/CodeResources: File(V2) %s hash1:%s hash2:%s", f.name, f.hash, f.hash2));
  171. files2.push_back(f);
  172. return true;
  173. }
  174. bool CodeSignCodeResources::add_nested_file(const String &p_root, const String &p_path, const String &p_exepath) {
  175. #define CLEANUP() \
  176. if (files_to_add.size() > 1) { \
  177. for (int j = 0; j < files_to_add.size(); j++) { \
  178. da->remove(files_to_add[j]); \
  179. } \
  180. }
  181. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  182. ERR_FAIL_COND_V(da.is_null(), false);
  183. Vector<String> files_to_add;
  184. if (LipO::is_lipo(p_exepath)) {
  185. String tmp_path_name = EditorPaths::get_singleton()->get_temp_dir().path_join("_lipo");
  186. Error err = da->make_dir_recursive(tmp_path_name);
  187. ERR_FAIL_COND_V_MSG(err != OK, false, vformat("CodeSign/CodeResources: Failed to create \"%s\" subfolder.", tmp_path_name));
  188. LipO lip;
  189. if (lip.open_file(p_exepath)) {
  190. for (int i = 0; i < lip.get_arch_count(); i++) {
  191. if (!lip.extract_arch(i, tmp_path_name.path_join("_rqexe_" + itos(i)))) {
  192. CLEANUP();
  193. ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Failed to extract thin binary.");
  194. }
  195. files_to_add.push_back(tmp_path_name.path_join("_rqexe_" + itos(i)));
  196. }
  197. }
  198. } else if (MachO::is_macho(p_exepath)) {
  199. files_to_add.push_back(p_exepath);
  200. }
  201. CRFile f;
  202. f.name = p_path;
  203. f.optional = false;
  204. f.nested = true;
  205. for (int i = 0; i < files_to_add.size(); i++) {
  206. MachO mh;
  207. if (!mh.open_file(files_to_add[i])) {
  208. CLEANUP();
  209. ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid executable file.");
  210. }
  211. PackedByteArray hash = mh.get_cdhash_sha256(); // Use SHA-256 variant, if available.
  212. if (hash.size() != 0x20) {
  213. hash = mh.get_cdhash_sha1(); // Use SHA-1 instead.
  214. if (hash.size() != 0x14) {
  215. CLEANUP();
  216. ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Unsigned nested executable file.");
  217. }
  218. }
  219. hash.resize(0x14); // Always clamp to 0x14 size.
  220. f.hash = CryptoCore::b64_encode_str(hash.ptr(), hash.size());
  221. PackedByteArray rq_blob = mh.get_requirements();
  222. String req_string;
  223. if (rq_blob.size() > 8) {
  224. CodeSignRequirements rq = CodeSignRequirements(rq_blob);
  225. Vector<String> rqs = rq.parse_requirements();
  226. for (int j = 0; j < rqs.size(); j++) {
  227. if (rqs[j].begins_with("designated => ")) {
  228. req_string = rqs[j].replace("designated => ", "");
  229. }
  230. }
  231. }
  232. if (req_string.is_empty()) {
  233. req_string = "cdhash H\"" + String::hex_encode_buffer(hash.ptr(), hash.size()) + "\"";
  234. }
  235. print_verbose(vformat("CodeSign/CodeResources: Nested object %s (cputype: %d) cdhash:%s designated rq:%s", f.name, mh.get_cputype(), f.hash, req_string));
  236. if (f.requirements != req_string) {
  237. if (i != 0) {
  238. f.requirements += " or ";
  239. }
  240. f.requirements += req_string;
  241. }
  242. }
  243. files2.push_back(f);
  244. CLEANUP();
  245. return true;
  246. #undef CLEANUP
  247. }
  248. bool CodeSignCodeResources::add_folder_recursive(const String &p_root, const String &p_path, const String &p_main_exe_path) {
  249. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  250. ERR_FAIL_COND_V(da.is_null(), false);
  251. Error err = da->change_dir(p_root.path_join(p_path));
  252. ERR_FAIL_COND_V(err != OK, false);
  253. bool ret = true;
  254. da->list_dir_begin();
  255. String n = da->get_next();
  256. while (n != String()) {
  257. if (n != "." && n != "..") {
  258. String path = p_root.path_join(p_path).path_join(n);
  259. if (path == p_main_exe_path) {
  260. n = da->get_next();
  261. continue; // Skip main executable.
  262. }
  263. if (da->current_is_dir()) {
  264. CRMatch found = match_rules2(p_path.path_join(n));
  265. String fmw_ver = "Current"; // Framework version (default).
  266. String info_path;
  267. String main_exe;
  268. bool bundle = false;
  269. if (da->file_exists(path.path_join("Contents/Info.plist"))) {
  270. info_path = path.path_join("Contents/Info.plist");
  271. main_exe = path.path_join("Contents/MacOS");
  272. bundle = true;
  273. } else if (da->file_exists(path.path_join(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) {
  274. info_path = path.path_join(vformat("Versions/%s/Resources/Info.plist", fmw_ver));
  275. main_exe = path.path_join(vformat("Versions/%s", fmw_ver));
  276. bundle = true;
  277. } else if (da->file_exists(path.path_join("Resources/Info.plist"))) {
  278. info_path = path.path_join("Resources/Info.plist");
  279. main_exe = path;
  280. bundle = true;
  281. } else if (da->file_exists(path.path_join("Info.plist"))) {
  282. info_path = path.path_join("Info.plist");
  283. main_exe = path;
  284. bundle = true;
  285. }
  286. if (bundle && found == CRMatch::CR_MATCH_NESTED && !info_path.is_empty()) {
  287. // Read Info.plist.
  288. PList info_plist;
  289. if (info_plist.load_file(info_path)) {
  290. if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) {
  291. main_exe = main_exe.path_join(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data()));
  292. } else {
  293. ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, no exe name.");
  294. }
  295. } else {
  296. ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, can't load.");
  297. }
  298. ret = ret && add_nested_file(p_root, p_path.path_join(n), main_exe);
  299. } else {
  300. ret = ret && add_folder_recursive(p_root, p_path.path_join(n), p_main_exe_path);
  301. }
  302. } else {
  303. ret = ret && add_file1(p_root, p_path.path_join(n));
  304. ret = ret && add_file2(p_root, p_path.path_join(n));
  305. }
  306. }
  307. n = da->get_next();
  308. }
  309. da->list_dir_end();
  310. return ret;
  311. }
  312. bool CodeSignCodeResources::save_to_file(const String &p_path) {
  313. PList pl;
  314. print_verbose(vformat("CodeSign/CodeResources: Writing to file: %s", p_path));
  315. // Write version 1 hashes.
  316. Ref<PListNode> files1_dict = PListNode::new_dict();
  317. pl.get_root()->push_subnode(files1_dict, "files");
  318. for (int i = 0; i < files1.size(); i++) {
  319. if (files1[i].optional) {
  320. Ref<PListNode> file_dict = PListNode::new_dict();
  321. files1_dict->push_subnode(file_dict, files1[i].name);
  322. file_dict->push_subnode(PListNode::new_data(files1[i].hash), "hash");
  323. file_dict->push_subnode(PListNode::new_bool(true), "optional");
  324. } else {
  325. files1_dict->push_subnode(PListNode::new_data(files1[i].hash), files1[i].name);
  326. }
  327. }
  328. // Write version 2 hashes.
  329. Ref<PListNode> files2_dict = PListNode::new_dict();
  330. pl.get_root()->push_subnode(files2_dict, "files2");
  331. for (int i = 0; i < files2.size(); i++) {
  332. Ref<PListNode> file_dict = PListNode::new_dict();
  333. files2_dict->push_subnode(file_dict, files2[i].name);
  334. if (files2[i].nested) {
  335. file_dict->push_subnode(PListNode::new_data(files2[i].hash), "cdhash");
  336. file_dict->push_subnode(PListNode::new_string(files2[i].requirements), "requirement");
  337. } else {
  338. file_dict->push_subnode(PListNode::new_data(files2[i].hash), "hash");
  339. file_dict->push_subnode(PListNode::new_data(files2[i].hash2), "hash2");
  340. if (files2[i].optional) {
  341. file_dict->push_subnode(PListNode::new_bool(true), "optional");
  342. }
  343. }
  344. }
  345. // Write version 1 rules.
  346. Ref<PListNode> rules1_dict = PListNode::new_dict();
  347. pl.get_root()->push_subnode(rules1_dict, "rules");
  348. for (int i = 0; i < rules1.size(); i++) {
  349. if (rules1[i].store) {
  350. if (rules1[i].key.is_empty() && rules1[i].weight <= 0) {
  351. rules1_dict->push_subnode(PListNode::new_bool(true), rules1[i].file_pattern);
  352. } else {
  353. Ref<PListNode> rule_dict = PListNode::new_dict();
  354. rules1_dict->push_subnode(rule_dict, rules1[i].file_pattern);
  355. if (!rules1[i].key.is_empty()) {
  356. rule_dict->push_subnode(PListNode::new_bool(true), rules1[i].key);
  357. }
  358. if (rules1[i].weight != 1) {
  359. rule_dict->push_subnode(PListNode::new_real(rules1[i].weight), "weight");
  360. }
  361. }
  362. }
  363. }
  364. // Write version 2 rules.
  365. Ref<PListNode> rules2_dict = PListNode::new_dict();
  366. pl.get_root()->push_subnode(rules2_dict, "rules2");
  367. for (int i = 0; i < rules2.size(); i++) {
  368. if (rules2[i].store) {
  369. if (rules2[i].key.is_empty() && rules2[i].weight <= 0) {
  370. rules2_dict->push_subnode(PListNode::new_bool(true), rules2[i].file_pattern);
  371. } else {
  372. Ref<PListNode> rule_dict = PListNode::new_dict();
  373. rules2_dict->push_subnode(rule_dict, rules2[i].file_pattern);
  374. if (!rules2[i].key.is_empty()) {
  375. rule_dict->push_subnode(PListNode::new_bool(true), rules2[i].key);
  376. }
  377. if (rules2[i].weight != 1) {
  378. rule_dict->push_subnode(PListNode::new_real(rules2[i].weight), "weight");
  379. }
  380. }
  381. }
  382. }
  383. String text = pl.save_text();
  384. ERR_FAIL_COND_V_MSG(text.is_empty(), false, "CodeSign/CodeResources: Generating resources PList failed.");
  385. Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
  386. ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
  387. CharString cs = text.utf8();
  388. fa->store_buffer((const uint8_t *)cs.ptr(), cs.length());
  389. return true;
  390. }
  391. /*************************************************************************/
  392. /* CodeSignRequirements */
  393. /*************************************************************************/
  394. CodeSignRequirements::CodeSignRequirements() {
  395. blob.append_array({ 0xFA, 0xDE, 0x0C, 0x01 }); // Requirement set magic.
  396. blob.append_array({ 0x00, 0x00, 0x00, 0x0C }); // Length of requirements set (12 bytes).
  397. blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Empty.
  398. }
  399. CodeSignRequirements::CodeSignRequirements(const PackedByteArray &p_data) {
  400. blob = p_data;
  401. }
  402. _FORCE_INLINE_ void CodeSignRequirements::_parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  403. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  404. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  405. r_out += "certificate ";
  406. uint32_t tag_slot = _R(r_pos);
  407. if (tag_slot == 0x00000000) {
  408. r_out += "leaf";
  409. } else if (tag_slot == 0xffffffff) {
  410. r_out += "root";
  411. } else {
  412. r_out += itos((int32_t)tag_slot);
  413. }
  414. r_pos += 4;
  415. #undef _R
  416. }
  417. _FORCE_INLINE_ void CodeSignRequirements::_parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  418. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  419. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  420. uint32_t key_size = _R(r_pos);
  421. ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  422. CharString key;
  423. key.resize(key_size);
  424. memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size);
  425. r_pos += 4 + key_size + PAD(key_size, 4);
  426. r_out += "[" + String::utf8(key, key_size) + "]";
  427. #undef _R
  428. }
  429. _FORCE_INLINE_ void CodeSignRequirements::_parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  430. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  431. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  432. uint32_t key_size = _R(r_pos);
  433. ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  434. r_out += "[field.";
  435. r_out += itos(blob[r_pos + 4] / 40) + ".";
  436. r_out += itos(blob[r_pos + 4] % 40);
  437. uint32_t spos = r_pos + 5;
  438. while (spos < r_pos + 4 + key_size) {
  439. r_out += ".";
  440. if (blob[spos] <= 127) {
  441. r_out += itos(blob[spos]);
  442. spos += 1;
  443. } else {
  444. uint32_t x = (0x7F & blob[spos]) << 7;
  445. spos += 1;
  446. while (blob[spos] > 127) {
  447. x = (x + (0x7F & blob[spos])) << 7;
  448. spos += 1;
  449. }
  450. x = (x + (0x7F & blob[spos]));
  451. r_out += itos(x);
  452. spos += 1;
  453. }
  454. }
  455. r_out += "]";
  456. r_pos += 4 + key_size + PAD(key_size, 4);
  457. #undef _R
  458. }
  459. _FORCE_INLINE_ void CodeSignRequirements::_parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  460. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  461. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  462. uint32_t tag_size = _R(r_pos);
  463. ERR_FAIL_COND_MSG(r_pos + tag_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  464. PackedByteArray data;
  465. data.resize(tag_size);
  466. memcpy(data.ptrw(), blob.ptr() + r_pos + 4, tag_size);
  467. r_out += "H\"" + String::hex_encode_buffer(data.ptr(), data.size()) + "\"";
  468. r_pos += 4 + tag_size + PAD(tag_size, 4);
  469. #undef _R
  470. }
  471. _FORCE_INLINE_ void CodeSignRequirements::_parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  472. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  473. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  474. uint32_t key_size = _R(r_pos);
  475. ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  476. CharString key;
  477. key.resize(key_size);
  478. memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size);
  479. r_pos += 4 + key_size + PAD(key_size, 4);
  480. r_out += "\"" + String::utf8(key, key_size) + "\"";
  481. #undef _R
  482. }
  483. _FORCE_INLINE_ void CodeSignRequirements::_parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  484. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  485. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  486. uint32_t date = _R(r_pos);
  487. time_t t = 978307200 + date;
  488. struct tm lt;
  489. #ifdef WINDOWS_ENABLED
  490. gmtime_s(&lt, &t);
  491. #else
  492. gmtime_r(&t, &lt);
  493. #endif
  494. r_out += vformat("<%04d-%02d-%02d ", (int)(1900 + lt.tm_year), (int)(lt.tm_mon + 1), (int)(lt.tm_mday)) + vformat("%02d:%02d:%02d +0000>", (int)(lt.tm_hour), (int)(lt.tm_min), (int)(lt.tm_sec));
  495. #undef _R
  496. }
  497. _FORCE_INLINE_ bool CodeSignRequirements::_parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  498. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  499. ERR_FAIL_COND_V_MSG(r_pos >= p_rq_size, false, "CodeSign/Requirements: Out of bounds.");
  500. uint32_t match = _R(r_pos);
  501. r_pos += 4;
  502. switch (match) {
  503. case 0x00000000: {
  504. r_out += "exists";
  505. } break;
  506. case 0x00000001: {
  507. r_out += "= ";
  508. _parse_value(r_pos, r_out, p_rq_size);
  509. } break;
  510. case 0x00000002: {
  511. r_out += "~ ";
  512. _parse_value(r_pos, r_out, p_rq_size);
  513. } break;
  514. case 0x00000003: {
  515. r_out += "= *";
  516. _parse_value(r_pos, r_out, p_rq_size);
  517. } break;
  518. case 0x00000004: {
  519. r_out += "= ";
  520. _parse_value(r_pos, r_out, p_rq_size);
  521. r_out += "*";
  522. } break;
  523. case 0x00000005: {
  524. r_out += "< ";
  525. _parse_value(r_pos, r_out, p_rq_size);
  526. } break;
  527. case 0x00000006: {
  528. r_out += "> ";
  529. _parse_value(r_pos, r_out, p_rq_size);
  530. } break;
  531. case 0x00000007: {
  532. r_out += "<= ";
  533. _parse_value(r_pos, r_out, p_rq_size);
  534. } break;
  535. case 0x00000008: {
  536. r_out += ">= ";
  537. _parse_value(r_pos, r_out, p_rq_size);
  538. } break;
  539. case 0x00000009: {
  540. r_out += "= ";
  541. _parse_date(r_pos, r_out, p_rq_size);
  542. } break;
  543. case 0x0000000A: {
  544. r_out += "< ";
  545. _parse_date(r_pos, r_out, p_rq_size);
  546. } break;
  547. case 0x0000000B: {
  548. r_out += "> ";
  549. _parse_date(r_pos, r_out, p_rq_size);
  550. } break;
  551. case 0x0000000C: {
  552. r_out += "<= ";
  553. _parse_date(r_pos, r_out, p_rq_size);
  554. } break;
  555. case 0x0000000D: {
  556. r_out += ">= ";
  557. _parse_date(r_pos, r_out, p_rq_size);
  558. } break;
  559. case 0x0000000E: {
  560. r_out += "absent";
  561. } break;
  562. default: {
  563. return false;
  564. }
  565. }
  566. return true;
  567. #undef _R
  568. }
  569. Vector<String> CodeSignRequirements::parse_requirements() const {
  570. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  571. Vector<String> list;
  572. // Read requirements set header.
  573. ERR_FAIL_COND_V_MSG(blob.size() < 12, list, "CodeSign/Requirements: Blob is too small.");
  574. uint32_t magic = _R(0);
  575. ERR_FAIL_COND_V_MSG(magic != 0xfade0c01, list, "CodeSign/Requirements: Invalid set magic.");
  576. uint32_t size = _R(4);
  577. ERR_FAIL_COND_V_MSG(size != (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid set size.");
  578. uint32_t count = _R(8);
  579. for (uint32_t i = 0; i < count; i++) {
  580. String out;
  581. // Read requirement header.
  582. uint32_t rq_type = _R(12 + i * 8);
  583. uint32_t rq_offset = _R(12 + i * 8 + 4);
  584. ERR_FAIL_COND_V_MSG(rq_offset + 12 >= (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid requirement offset.");
  585. switch (rq_type) {
  586. case 0x00000001: {
  587. out += "host => ";
  588. } break;
  589. case 0x00000002: {
  590. out += "guest => ";
  591. } break;
  592. case 0x00000003: {
  593. out += "designated => ";
  594. } break;
  595. case 0x00000004: {
  596. out += "library => ";
  597. } break;
  598. case 0x00000005: {
  599. out += "plugin => ";
  600. } break;
  601. default: {
  602. ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement type.");
  603. }
  604. }
  605. uint32_t rq_magic = _R(rq_offset);
  606. uint32_t rq_size = _R(rq_offset + 4);
  607. uint32_t rq_ver = _R(rq_offset + 8);
  608. uint32_t pos = rq_offset + 12;
  609. ERR_FAIL_COND_V_MSG(rq_magic != 0xfade0c00, list, "CodeSign/Requirements: Invalid requirement magic.");
  610. ERR_FAIL_COND_V_MSG(rq_ver != 0x00000001, list, "CodeSign/Requirements: Invalid requirement version.");
  611. // Read requirement tokens.
  612. List<String> tokens;
  613. while (pos < rq_offset + rq_size) {
  614. uint32_t rq_tag = _R(pos);
  615. pos += 4;
  616. String token;
  617. switch (rq_tag) {
  618. case 0x00000000: {
  619. token = "false";
  620. } break;
  621. case 0x00000001: {
  622. token = "true";
  623. } break;
  624. case 0x00000002: {
  625. token = "identifier ";
  626. _parse_value(pos, token, rq_offset + rq_size);
  627. } break;
  628. case 0x00000003: {
  629. token = "anchor apple";
  630. } break;
  631. case 0x00000004: {
  632. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  633. token += " ";
  634. _parse_hash_string(pos, token, rq_offset + rq_size);
  635. } break;
  636. case 0x00000005: {
  637. token = "info";
  638. _parse_key(pos, token, rq_offset + rq_size);
  639. token += " = ";
  640. _parse_value(pos, token, rq_offset + rq_size);
  641. } break;
  642. case 0x00000006: {
  643. token = "and";
  644. } break;
  645. case 0x00000007: {
  646. token = "or";
  647. } break;
  648. case 0x00000008: {
  649. token = "cdhash ";
  650. _parse_hash_string(pos, token, rq_offset + rq_size);
  651. } break;
  652. case 0x00000009: {
  653. token = "!";
  654. } break;
  655. case 0x0000000A: {
  656. token = "info";
  657. _parse_key(pos, token, rq_offset + rq_size);
  658. token += " ";
  659. ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
  660. } break;
  661. case 0x0000000B: {
  662. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  663. _parse_key(pos, token, rq_offset + rq_size);
  664. token += " ";
  665. ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
  666. } break;
  667. case 0x0000000C: {
  668. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  669. token += " trusted";
  670. } break;
  671. case 0x0000000D: {
  672. token = "anchor trusted";
  673. } break;
  674. case 0x0000000E: {
  675. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  676. _parse_oid_key(pos, token, rq_offset + rq_size);
  677. token += " ";
  678. ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
  679. } break;
  680. case 0x0000000F: {
  681. token = "anchor apple generic";
  682. } break;
  683. default: {
  684. ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement token.");
  685. } break;
  686. }
  687. tokens.push_back(token);
  688. }
  689. // Polish to infix notation (w/o bracket optimization).
  690. for (List<String>::Element *E = tokens.back(); E; E = E->prev()) {
  691. if (E->get() == "and") {
  692. ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence.");
  693. String token = "(" + E->next()->get() + " and " + E->next()->next()->get() + ")";
  694. tokens.erase(E->next()->next());
  695. tokens.erase(E->next());
  696. E->get() = token;
  697. } else if (E->get() == "or") {
  698. ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence.");
  699. String token = "(" + E->next()->get() + " or " + E->next()->next()->get() + ")";
  700. tokens.erase(E->next()->next());
  701. tokens.erase(E->next());
  702. E->get() = token;
  703. }
  704. }
  705. if (tokens.size() == 1) {
  706. list.push_back(out + tokens.front()->get());
  707. } else {
  708. ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid token sequence.");
  709. }
  710. }
  711. return list;
  712. #undef _R
  713. }
  714. PackedByteArray CodeSignRequirements::get_hash_sha1() const {
  715. PackedByteArray hash;
  716. hash.resize(0x14);
  717. CryptoCore::SHA1Context ctx;
  718. ctx.start();
  719. ctx.update(blob.ptr(), blob.size());
  720. ctx.finish(hash.ptrw());
  721. return hash;
  722. }
  723. PackedByteArray CodeSignRequirements::get_hash_sha256() const {
  724. PackedByteArray hash;
  725. hash.resize(0x20);
  726. CryptoCore::SHA256Context ctx;
  727. ctx.start();
  728. ctx.update(blob.ptr(), blob.size());
  729. ctx.finish(hash.ptrw());
  730. return hash;
  731. }
  732. int CodeSignRequirements::get_size() const {
  733. return blob.size();
  734. }
  735. void CodeSignRequirements::write_to_file(Ref<FileAccess> p_file) const {
  736. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Requirements: Invalid file handle.");
  737. p_file->store_buffer(blob.ptr(), blob.size());
  738. }
  739. /*************************************************************************/
  740. /* CodeSignEntitlementsText */
  741. /*************************************************************************/
  742. CodeSignEntitlementsText::CodeSignEntitlementsText() {
  743. blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic.
  744. blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes).
  745. }
  746. CodeSignEntitlementsText::CodeSignEntitlementsText(const String &p_string) {
  747. CharString utf8 = p_string.utf8();
  748. blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic.
  749. for (int i = 3; i >= 0; i--) {
  750. uint8_t x = ((utf8.length() + 8) >> i * 8) & 0xFF; // Size.
  751. blob.push_back(x);
  752. }
  753. for (int i = 0; i < utf8.length(); i++) { // Write data.
  754. blob.push_back(utf8[i]);
  755. }
  756. }
  757. PackedByteArray CodeSignEntitlementsText::get_hash_sha1() const {
  758. PackedByteArray hash;
  759. hash.resize(0x14);
  760. CryptoCore::SHA1Context ctx;
  761. ctx.start();
  762. ctx.update(blob.ptr(), blob.size());
  763. ctx.finish(hash.ptrw());
  764. return hash;
  765. }
  766. PackedByteArray CodeSignEntitlementsText::get_hash_sha256() const {
  767. PackedByteArray hash;
  768. hash.resize(0x20);
  769. CryptoCore::SHA256Context ctx;
  770. ctx.start();
  771. ctx.update(blob.ptr(), blob.size());
  772. ctx.finish(hash.ptrw());
  773. return hash;
  774. }
  775. int CodeSignEntitlementsText::get_size() const {
  776. return blob.size();
  777. }
  778. void CodeSignEntitlementsText::write_to_file(Ref<FileAccess> p_file) const {
  779. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsText: Invalid file handle.");
  780. p_file->store_buffer(blob.ptr(), blob.size());
  781. }
  782. /*************************************************************************/
  783. /* CodeSignEntitlementsBinary */
  784. /*************************************************************************/
  785. CodeSignEntitlementsBinary::CodeSignEntitlementsBinary() {
  786. blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic.
  787. blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes).
  788. }
  789. CodeSignEntitlementsBinary::CodeSignEntitlementsBinary(const String &p_string) {
  790. PList pl = PList(p_string);
  791. PackedByteArray asn1 = pl.save_asn1();
  792. blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic.
  793. uint32_t size = asn1.size() + 8;
  794. for (int i = 3; i >= 0; i--) {
  795. uint8_t x = (size >> i * 8) & 0xFF; // Size.
  796. blob.push_back(x);
  797. }
  798. blob.append_array(asn1); // Write data.
  799. }
  800. PackedByteArray CodeSignEntitlementsBinary::get_hash_sha1() const {
  801. PackedByteArray hash;
  802. hash.resize(0x14);
  803. CryptoCore::SHA1Context ctx;
  804. ctx.start();
  805. ctx.update(blob.ptr(), blob.size());
  806. ctx.finish(hash.ptrw());
  807. return hash;
  808. }
  809. PackedByteArray CodeSignEntitlementsBinary::get_hash_sha256() const {
  810. PackedByteArray hash;
  811. hash.resize(0x20);
  812. CryptoCore::SHA256Context ctx;
  813. ctx.start();
  814. ctx.update(blob.ptr(), blob.size());
  815. ctx.finish(hash.ptrw());
  816. return hash;
  817. }
  818. int CodeSignEntitlementsBinary::get_size() const {
  819. return blob.size();
  820. }
  821. void CodeSignEntitlementsBinary::write_to_file(Ref<FileAccess> p_file) const {
  822. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsBinary: Invalid file handle.");
  823. p_file->store_buffer(blob.ptr(), blob.size());
  824. }
  825. /*************************************************************************/
  826. /* CodeSignCodeDirectory */
  827. /*************************************************************************/
  828. CodeSignCodeDirectory::CodeSignCodeDirectory() {
  829. blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic.
  830. blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Size (8 bytes).
  831. }
  832. CodeSignCodeDirectory::CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash_type, bool p_main, const CharString &p_id, const CharString &p_team_id, uint32_t p_page_size, uint64_t p_exe_limit, uint64_t p_code_limit) {
  833. pages = p_code_limit / (uint64_t(1) << p_page_size);
  834. remain = p_code_limit % (uint64_t(1) << p_page_size);
  835. code_slots = pages + (remain > 0 ? 1 : 0);
  836. special_slots = 7;
  837. int cd_size = 8 + sizeof(CodeDirectoryHeader) + (code_slots + special_slots) * p_hash_size + p_id.size() + p_team_id.size();
  838. int cd_off = 8 + sizeof(CodeDirectoryHeader);
  839. blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic.
  840. for (int i = 3; i >= 0; i--) {
  841. uint8_t x = (cd_size >> i * 8) & 0xFF; // Size.
  842. blob.push_back(x);
  843. }
  844. blob.resize(cd_size);
  845. memset(blob.ptrw() + 8, 0x00, cd_size - 8);
  846. CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8);
  847. bool is_64_cl = (p_code_limit >= std::numeric_limits<uint32_t>::max());
  848. // Version and options.
  849. cd->version = BSWAP32(0x20500);
  850. cd->flags = BSWAP32(SIGNATURE_ADHOC | SIGNATURE_RUNTIME);
  851. cd->special_slots = BSWAP32(special_slots);
  852. cd->code_slots = BSWAP32(code_slots);
  853. if (is_64_cl) {
  854. cd->code_limit_64 = BSWAP64(p_code_limit);
  855. } else {
  856. cd->code_limit = BSWAP32(p_code_limit);
  857. }
  858. cd->hash_size = p_hash_size;
  859. cd->hash_type = p_hash_type;
  860. cd->page_size = p_page_size;
  861. cd->exec_seg_base = 0x00;
  862. cd->exec_seg_limit = BSWAP64(p_exe_limit);
  863. cd->exec_seg_flags = 0;
  864. if (p_main) {
  865. cd->exec_seg_flags |= EXECSEG_MAIN_BINARY;
  866. }
  867. cd->exec_seg_flags = BSWAP64(cd->exec_seg_flags);
  868. uint32_t version = (11 << 16) + (3 << 8) + 0; // Version 11.3.0
  869. cd->runtime = BSWAP32(version);
  870. // Copy ID.
  871. cd->ident_offset = BSWAP32(cd_off);
  872. memcpy(blob.ptrw() + cd_off, p_id.get_data(), p_id.size());
  873. cd_off += p_id.size();
  874. // Copy Team ID.
  875. if (p_team_id.length() > 0) {
  876. cd->team_offset = BSWAP32(cd_off);
  877. memcpy(blob.ptrw() + cd_off, p_team_id.get_data(), p_team_id.size());
  878. cd_off += p_team_id.size();
  879. } else {
  880. cd->team_offset = 0;
  881. }
  882. // Scatter vector.
  883. cd->scatter_vector_offset = 0; // Not used.
  884. // Executable hashes offset.
  885. cd->hash_offset = BSWAP32(cd_off + special_slots * cd->hash_size);
  886. }
  887. bool CodeSignCodeDirectory::set_hash_in_slot(const PackedByteArray &p_hash, int p_slot) {
  888. ERR_FAIL_COND_V_MSG((p_slot < -special_slots) || (p_slot >= code_slots), false, vformat("CodeSign/CodeDirectory: Invalid hash slot index: %d.", p_slot));
  889. CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8);
  890. for (int i = 0; i < cd->hash_size; i++) {
  891. blob.write[BSWAP32(cd->hash_offset) + p_slot * cd->hash_size + i] = p_hash[i];
  892. }
  893. return true;
  894. }
  895. int32_t CodeSignCodeDirectory::get_page_count() {
  896. return pages;
  897. }
  898. int32_t CodeSignCodeDirectory::get_page_remainder() {
  899. return remain;
  900. }
  901. PackedByteArray CodeSignCodeDirectory::get_hash_sha1() const {
  902. PackedByteArray hash;
  903. hash.resize(0x14);
  904. CryptoCore::SHA1Context ctx;
  905. ctx.start();
  906. ctx.update(blob.ptr(), blob.size());
  907. ctx.finish(hash.ptrw());
  908. return hash;
  909. }
  910. PackedByteArray CodeSignCodeDirectory::get_hash_sha256() const {
  911. PackedByteArray hash;
  912. hash.resize(0x20);
  913. CryptoCore::SHA256Context ctx;
  914. ctx.start();
  915. ctx.update(blob.ptr(), blob.size());
  916. ctx.finish(hash.ptrw());
  917. return hash;
  918. }
  919. int CodeSignCodeDirectory::get_size() const {
  920. return blob.size();
  921. }
  922. void CodeSignCodeDirectory::write_to_file(Ref<FileAccess> p_file) const {
  923. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/CodeDirectory: Invalid file handle.");
  924. p_file->store_buffer(blob.ptr(), blob.size());
  925. }
  926. /*************************************************************************/
  927. /* CodeSignSignature */
  928. /*************************************************************************/
  929. CodeSignSignature::CodeSignSignature() {
  930. blob.append_array({ 0xFA, 0xDE, 0x0B, 0x01 }); // Signature magic.
  931. uint32_t sign_size = 8; // Ad-hoc signature is empty.
  932. for (int i = 3; i >= 0; i--) {
  933. uint8_t x = (sign_size >> i * 8) & 0xFF; // Size.
  934. blob.push_back(x);
  935. }
  936. }
  937. PackedByteArray CodeSignSignature::get_hash_sha1() const {
  938. PackedByteArray hash;
  939. hash.resize(0x14);
  940. CryptoCore::SHA1Context ctx;
  941. ctx.start();
  942. ctx.update(blob.ptr(), blob.size());
  943. ctx.finish(hash.ptrw());
  944. return hash;
  945. }
  946. PackedByteArray CodeSignSignature::get_hash_sha256() const {
  947. PackedByteArray hash;
  948. hash.resize(0x20);
  949. CryptoCore::SHA256Context ctx;
  950. ctx.start();
  951. ctx.update(blob.ptr(), blob.size());
  952. ctx.finish(hash.ptrw());
  953. return hash;
  954. }
  955. int CodeSignSignature::get_size() const {
  956. return blob.size();
  957. }
  958. void CodeSignSignature::write_to_file(Ref<FileAccess> p_file) const {
  959. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Signature: Invalid file handle.");
  960. p_file->store_buffer(blob.ptr(), blob.size());
  961. }
  962. /*************************************************************************/
  963. /* CodeSignSuperBlob */
  964. /*************************************************************************/
  965. bool CodeSignSuperBlob::add_blob(const Ref<CodeSignBlob> &p_blob) {
  966. if (p_blob.is_valid()) {
  967. blobs.push_back(p_blob);
  968. return true;
  969. } else {
  970. return false;
  971. }
  972. }
  973. int CodeSignSuperBlob::get_size() const {
  974. int size = 12 + blobs.size() * 8;
  975. for (int i = 0; i < blobs.size(); i++) {
  976. if (blobs[i].is_null()) {
  977. return 0;
  978. }
  979. size += blobs[i]->get_size();
  980. }
  981. return size;
  982. }
  983. void CodeSignSuperBlob::write_to_file(Ref<FileAccess> p_file) const {
  984. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/SuperBlob: Invalid file handle.");
  985. uint32_t size = get_size();
  986. uint32_t data_offset = 12 + blobs.size() * 8;
  987. // Write header.
  988. p_file->store_32(BSWAP32(0xfade0cc0));
  989. p_file->store_32(BSWAP32(size));
  990. p_file->store_32(BSWAP32(blobs.size()));
  991. // Write index.
  992. for (int i = 0; i < blobs.size(); i++) {
  993. if (blobs[i].is_null()) {
  994. return;
  995. }
  996. p_file->store_32(BSWAP32(blobs[i]->get_index_type()));
  997. p_file->store_32(BSWAP32(data_offset));
  998. data_offset += blobs[i]->get_size();
  999. }
  1000. // Write blobs.
  1001. for (int i = 0; i < blobs.size(); i++) {
  1002. blobs[i]->write_to_file(p_file);
  1003. }
  1004. }
  1005. /*************************************************************************/
  1006. /* CodeSign */
  1007. /*************************************************************************/
  1008. PackedByteArray CodeSign::file_hash_sha1(const String &p_path) {
  1009. PackedByteArray file_hash;
  1010. Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
  1011. ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
  1012. CryptoCore::SHA1Context ctx;
  1013. ctx.start();
  1014. unsigned char step[4096];
  1015. while (true) {
  1016. uint64_t br = f->get_buffer(step, 4096);
  1017. if (br > 0) {
  1018. ctx.update(step, br);
  1019. }
  1020. if (br < 4096) {
  1021. break;
  1022. }
  1023. }
  1024. file_hash.resize(0x14);
  1025. ctx.finish(file_hash.ptrw());
  1026. return file_hash;
  1027. }
  1028. PackedByteArray CodeSign::file_hash_sha256(const String &p_path) {
  1029. PackedByteArray file_hash;
  1030. Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
  1031. ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
  1032. CryptoCore::SHA256Context ctx;
  1033. ctx.start();
  1034. unsigned char step[4096];
  1035. while (true) {
  1036. uint64_t br = f->get_buffer(step, 4096);
  1037. if (br > 0) {
  1038. ctx.update(step, br);
  1039. }
  1040. if (br < 4096) {
  1041. break;
  1042. }
  1043. }
  1044. file_hash.resize(0x20);
  1045. ctx.finish(file_hash.ptrw());
  1046. return file_hash;
  1047. }
  1048. Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const String &p_info, const String &p_exe_path, const String &p_bundle_path, const String &p_ent_path, bool p_ios_bundle, String &r_error_msg) {
  1049. #define CLEANUP() \
  1050. if (files_to_sign.size() > 1) { \
  1051. for (int j = 0; j < files_to_sign.size(); j++) { \
  1052. da->remove(files_to_sign[j]); \
  1053. } \
  1054. }
  1055. print_verbose(vformat("CodeSign: Signing executable: %s, bundle: %s with entitlements %s", p_exe_path, p_bundle_path, p_ent_path));
  1056. PackedByteArray info_hash1, info_hash2;
  1057. PackedByteArray res_hash1, res_hash2;
  1058. String id;
  1059. String main_exe = p_exe_path;
  1060. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1061. if (da.is_null()) {
  1062. r_error_msg = TTR("Can't get filesystem access.");
  1063. ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
  1064. }
  1065. // Read Info.plist.
  1066. if (!p_info.is_empty()) {
  1067. print_verbose("CodeSign: Reading bundle info...");
  1068. PList info_plist;
  1069. if (info_plist.load_file(p_info)) {
  1070. info_hash1 = file_hash_sha1(p_info);
  1071. info_hash2 = file_hash_sha256(p_info);
  1072. if (info_hash1.is_empty() || info_hash2.is_empty()) {
  1073. r_error_msg = TTR("Failed to get Info.plist hash.");
  1074. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get Info.plist hash.");
  1075. }
  1076. if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) {
  1077. main_exe = p_exe_path.path_join(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data()));
  1078. } else {
  1079. r_error_msg = TTR("Invalid Info.plist, no exe name.");
  1080. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no exe name.");
  1081. }
  1082. if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleIdentifier")) {
  1083. id = info_plist.get_root()->data_dict["CFBundleIdentifier"]->data_string.get_data();
  1084. } else {
  1085. r_error_msg = TTR("Invalid Info.plist, no bundle id.");
  1086. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no bundle id.");
  1087. }
  1088. } else {
  1089. r_error_msg = TTR("Invalid Info.plist, can't load.");
  1090. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, can't load.");
  1091. }
  1092. }
  1093. // Extract fat binary.
  1094. Vector<String> files_to_sign;
  1095. if (LipO::is_lipo(main_exe)) {
  1096. print_verbose(vformat("CodeSign: Executable is fat, extracting..."));
  1097. String tmp_path_name = EditorPaths::get_singleton()->get_temp_dir().path_join("_lipo");
  1098. Error err = da->make_dir_recursive(tmp_path_name);
  1099. if (err != OK) {
  1100. r_error_msg = vformat(TTR("Failed to create \"%s\" subfolder."), tmp_path_name);
  1101. ERR_FAIL_V_MSG(FAILED, vformat("CodeSign: Failed to create \"%s\" subfolder.", tmp_path_name));
  1102. }
  1103. LipO lip;
  1104. if (lip.open_file(main_exe)) {
  1105. for (int i = 0; i < lip.get_arch_count(); i++) {
  1106. if (!lip.extract_arch(i, tmp_path_name.path_join("_exe_" + itos(i)))) {
  1107. CLEANUP();
  1108. r_error_msg = TTR("Failed to extract thin binary.");
  1109. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to extract thin binary.");
  1110. }
  1111. files_to_sign.push_back(tmp_path_name.path_join("_exe_" + itos(i)));
  1112. }
  1113. }
  1114. } else if (MachO::is_macho(main_exe)) {
  1115. print_verbose("CodeSign: Executable is thin...");
  1116. files_to_sign.push_back(main_exe);
  1117. } else {
  1118. r_error_msg = TTR("Invalid binary format.");
  1119. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid binary format.");
  1120. }
  1121. // Check if it's already signed.
  1122. if (!p_force) {
  1123. for (int i = 0; i < files_to_sign.size(); i++) {
  1124. MachO mh;
  1125. mh.open_file(files_to_sign[i]);
  1126. if (mh.is_signed()) {
  1127. CLEANUP();
  1128. r_error_msg = TTR("Already signed!");
  1129. ERR_FAIL_V_MSG(FAILED, "CodeSign: Already signed!");
  1130. }
  1131. }
  1132. }
  1133. // Generate core resources.
  1134. if (!p_bundle_path.is_empty()) {
  1135. print_verbose("CodeSign: Generating bundle CodeResources...");
  1136. CodeSignCodeResources cr;
  1137. if (p_ios_bundle) {
  1138. cr.add_rule1("^.*");
  1139. cr.add_rule1("^.*\\.lproj/", "optional", 100);
  1140. cr.add_rule1("^.*\\.lproj/locversion.plist$", "omit", 1100);
  1141. cr.add_rule1("^Base\\.lproj/", "", 1010);
  1142. cr.add_rule1("^version.plist$");
  1143. cr.add_rule2(".*\\.dSYM($|/)", "", 11);
  1144. cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000);
  1145. cr.add_rule2("^.*");
  1146. cr.add_rule2("^.*\\.lproj/", "optional", 1000);
  1147. cr.add_rule2("^.*\\.lproj/locversion.plist$", "omit", 1100);
  1148. cr.add_rule2("^Base\\.lproj/", "", 1010);
  1149. cr.add_rule2("^Info\\.plist$", "omit", 20);
  1150. cr.add_rule2("^PkgInfo$", "omit", 20);
  1151. cr.add_rule2("^embedded\\.provisionprofile$", "", 10);
  1152. cr.add_rule2("^version\\.plist$", "", 20);
  1153. cr.add_rule2("^_MASReceipt", "omit", 2000, false);
  1154. cr.add_rule2("^_CodeSignature", "omit", 2000, false);
  1155. cr.add_rule2("^CodeResources", "omit", 2000, false);
  1156. } else {
  1157. cr.add_rule1("^Resources($|/)");
  1158. cr.add_rule1("^Resources/.*\\.lproj/", "optional", 1000);
  1159. cr.add_rule1("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100);
  1160. cr.add_rule1("^Resources/Base\\.lproj/", "", 1010);
  1161. cr.add_rule1("^version.plist$");
  1162. cr.add_rule2(".*\\.dSYM($|/)", "", 11);
  1163. cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000);
  1164. cr.add_rule2("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", "nested", 10);
  1165. cr.add_rule2("^.*");
  1166. cr.add_rule2("^Info\\.plist$", "omit", 20);
  1167. cr.add_rule2("^PkgInfo$", "omit", 20);
  1168. cr.add_rule2("^Resources($|/)", "", 20);
  1169. cr.add_rule2("^Resources/.*\\.lproj/", "optional", 1000);
  1170. cr.add_rule2("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100);
  1171. cr.add_rule2("^Resources/Base\\.lproj/", "", 1010);
  1172. cr.add_rule2("^[^/]+$", "nested", 10);
  1173. cr.add_rule2("^embedded\\.provisionprofile$", "", 10);
  1174. cr.add_rule2("^version\\.plist$", "", 20);
  1175. cr.add_rule2("^_MASReceipt", "omit", 2000, false);
  1176. cr.add_rule2("^_CodeSignature", "omit", 2000, false);
  1177. cr.add_rule2("^CodeResources", "omit", 2000, false);
  1178. }
  1179. if (!cr.add_folder_recursive(p_bundle_path, "", main_exe)) {
  1180. CLEANUP();
  1181. r_error_msg = TTR("Failed to process nested resources.");
  1182. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to process nested resources.");
  1183. }
  1184. Error err = da->make_dir_recursive(p_bundle_path.path_join("_CodeSignature"));
  1185. if (err != OK) {
  1186. CLEANUP();
  1187. r_error_msg = TTR("Failed to create _CodeSignature subfolder.");
  1188. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create _CodeSignature subfolder.");
  1189. }
  1190. cr.save_to_file(p_bundle_path.path_join("_CodeSignature").path_join("CodeResources"));
  1191. res_hash1 = file_hash_sha1(p_bundle_path.path_join("_CodeSignature").path_join("CodeResources"));
  1192. res_hash2 = file_hash_sha256(p_bundle_path.path_join("_CodeSignature").path_join("CodeResources"));
  1193. if (res_hash1.is_empty() || res_hash2.is_empty()) {
  1194. CLEANUP();
  1195. r_error_msg = TTR("Failed to get CodeResources hash.");
  1196. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get CodeResources hash.");
  1197. }
  1198. }
  1199. // Generate common signature structures.
  1200. if (id.is_empty()) {
  1201. CryptoCore::RandomGenerator rng;
  1202. ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
  1203. uint8_t uuid[16];
  1204. Error err = rng.get_random_bytes(uuid, 16);
  1205. ERR_FAIL_COND_V_MSG(err, err, "Failed to generate UUID.");
  1206. id = (String("a-55554944") /*a-UUID*/ + String::hex_encode_buffer(uuid, 16));
  1207. }
  1208. CharString uuid_str = id.utf8();
  1209. print_verbose(vformat("CodeSign: Used bundle ID: %s", id));
  1210. print_verbose("CodeSign: Processing entitlements...");
  1211. Ref<CodeSignEntitlementsText> cet;
  1212. Ref<CodeSignEntitlementsBinary> ceb;
  1213. if (!p_ent_path.is_empty()) {
  1214. String entitlements = FileAccess::get_file_as_string(p_ent_path);
  1215. if (entitlements.is_empty()) {
  1216. CLEANUP();
  1217. r_error_msg = TTR("Invalid entitlements file.");
  1218. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file.");
  1219. }
  1220. cet.instantiate(entitlements);
  1221. ceb.instantiate(entitlements);
  1222. }
  1223. print_verbose("CodeSign: Generating requirements...");
  1224. Ref<CodeSignRequirements> rq;
  1225. String team_id = "";
  1226. rq.instantiate();
  1227. // Sign executables.
  1228. for (int i = 0; i < files_to_sign.size(); i++) {
  1229. MachO mh;
  1230. if (!mh.open_file(files_to_sign[i])) {
  1231. CLEANUP();
  1232. r_error_msg = TTR("Invalid executable file.");
  1233. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid executable file.");
  1234. }
  1235. print_verbose(vformat("CodeSign: Signing executable for cputype: %d ...", mh.get_cputype()));
  1236. print_verbose("CodeSign: Generating CodeDirectory...");
  1237. Ref<CodeSignCodeDirectory> cd1 = memnew(CodeSignCodeDirectory(0x14, 0x01, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
  1238. Ref<CodeSignCodeDirectory> cd2 = memnew(CodeSignCodeDirectory(0x20, 0x02, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
  1239. print_verbose("CodeSign: Calculating special slot hashes...");
  1240. if (info_hash2.size() == 0x20) {
  1241. cd2->set_hash_in_slot(info_hash2, CodeSignCodeDirectory::SLOT_INFO_PLIST);
  1242. }
  1243. if (info_hash1.size() == 0x14) {
  1244. cd1->set_hash_in_slot(info_hash1, CodeSignCodeDirectory::SLOT_INFO_PLIST);
  1245. }
  1246. cd1->set_hash_in_slot(rq->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS);
  1247. cd2->set_hash_in_slot(rq->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS);
  1248. if (res_hash2.size() == 0x20) {
  1249. cd2->set_hash_in_slot(res_hash2, CodeSignCodeDirectory::SLOT_RESOURCES);
  1250. }
  1251. if (res_hash1.size() == 0x14) {
  1252. cd1->set_hash_in_slot(res_hash1, CodeSignCodeDirectory::SLOT_RESOURCES);
  1253. }
  1254. if (cet.is_valid()) {
  1255. cd1->set_hash_in_slot(cet->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); //Text variant.
  1256. cd2->set_hash_in_slot(cet->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS);
  1257. }
  1258. if (ceb.is_valid()) {
  1259. cd1->set_hash_in_slot(ceb->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); //ASN.1 variant.
  1260. cd2->set_hash_in_slot(ceb->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS);
  1261. }
  1262. // Calculate signature size.
  1263. int sign_size = 12; // SuperBlob header.
  1264. sign_size += cd1->get_size() + 8;
  1265. sign_size += cd2->get_size() + 8;
  1266. sign_size += rq->get_size() + 8;
  1267. if (cet.is_valid()) {
  1268. sign_size += cet->get_size() + 8;
  1269. }
  1270. if (ceb.is_valid()) {
  1271. sign_size += ceb->get_size() + 8;
  1272. }
  1273. sign_size += 16; // Empty signature size.
  1274. // Alloc/resize signature load command.
  1275. print_verbose(vformat("CodeSign: Reallocating space for the signature superblob (%d)...", sign_size));
  1276. if (!mh.set_signature_size(sign_size)) {
  1277. CLEANUP();
  1278. r_error_msg = TTR("Can't resize signature load command.");
  1279. ERR_FAIL_V_MSG(FAILED, "CodeSign: Can't resize signature load command.");
  1280. }
  1281. print_verbose("CodeSign: Calculating executable code hashes...");
  1282. // Calculate executable code hashes.
  1283. PackedByteArray buffer;
  1284. PackedByteArray hash1, hash2;
  1285. hash1.resize(0x14);
  1286. hash2.resize(0x20);
  1287. buffer.resize(1 << 12);
  1288. mh.get_file()->seek(0);
  1289. for (int32_t j = 0; j < cd2->get_page_count(); j++) {
  1290. mh.get_file()->get_buffer(buffer.ptrw(), (1 << 12));
  1291. CryptoCore::SHA256Context ctx2;
  1292. ctx2.start();
  1293. ctx2.update(buffer.ptr(), (1 << 12));
  1294. ctx2.finish(hash2.ptrw());
  1295. cd2->set_hash_in_slot(hash2, j);
  1296. CryptoCore::SHA1Context ctx1;
  1297. ctx1.start();
  1298. ctx1.update(buffer.ptr(), (1 << 12));
  1299. ctx1.finish(hash1.ptrw());
  1300. cd1->set_hash_in_slot(hash1, j);
  1301. }
  1302. if (cd2->get_page_remainder() > 0) {
  1303. mh.get_file()->get_buffer(buffer.ptrw(), cd2->get_page_remainder());
  1304. CryptoCore::SHA256Context ctx2;
  1305. ctx2.start();
  1306. ctx2.update(buffer.ptr(), cd2->get_page_remainder());
  1307. ctx2.finish(hash2.ptrw());
  1308. cd2->set_hash_in_slot(hash2, cd2->get_page_count());
  1309. CryptoCore::SHA1Context ctx1;
  1310. ctx1.start();
  1311. ctx1.update(buffer.ptr(), cd1->get_page_remainder());
  1312. ctx1.finish(hash1.ptrw());
  1313. cd1->set_hash_in_slot(hash1, cd1->get_page_count());
  1314. }
  1315. print_verbose("CodeSign: Generating signature...");
  1316. Ref<CodeSignSignature> cs;
  1317. cs.instantiate();
  1318. print_verbose("CodeSign: Writing signature superblob...");
  1319. // Write signature data to the executable.
  1320. CodeSignSuperBlob sb = CodeSignSuperBlob();
  1321. sb.add_blob(cd2);
  1322. sb.add_blob(cd1);
  1323. sb.add_blob(rq);
  1324. if (cet.is_valid()) {
  1325. sb.add_blob(cet);
  1326. }
  1327. if (ceb.is_valid()) {
  1328. sb.add_blob(ceb);
  1329. }
  1330. sb.add_blob(cs);
  1331. mh.get_file()->seek(mh.get_signature_offset());
  1332. sb.write_to_file(mh.get_file());
  1333. }
  1334. if (files_to_sign.size() > 1) {
  1335. print_verbose("CodeSign: Rebuilding fat executable...");
  1336. LipO lip;
  1337. if (!lip.create_file(main_exe, files_to_sign)) {
  1338. CLEANUP();
  1339. r_error_msg = TTR("Failed to create fat binary.");
  1340. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create fat binary.");
  1341. }
  1342. CLEANUP();
  1343. }
  1344. FileAccess::set_unix_permissions(main_exe, 0755); // Restore unix permissions.
  1345. return OK;
  1346. #undef CLEANUP
  1347. }
  1348. Error CodeSign::codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg) {
  1349. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1350. if (da.is_null()) {
  1351. r_error_msg = TTR("Can't get filesystem access.");
  1352. ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
  1353. }
  1354. if (da->dir_exists(p_path)) {
  1355. String fmw_ver = "Current"; // Framework version (default).
  1356. String info_path;
  1357. String main_exe;
  1358. String bundle_path;
  1359. bool bundle = false;
  1360. bool ios_bundle = false;
  1361. if (da->file_exists(p_path.path_join("Contents/Info.plist"))) {
  1362. info_path = p_path.path_join("Contents/Info.plist");
  1363. main_exe = p_path.path_join("Contents/MacOS");
  1364. bundle_path = p_path.path_join("Contents");
  1365. bundle = true;
  1366. } else if (da->file_exists(p_path.path_join(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) {
  1367. info_path = p_path.path_join(vformat("Versions/%s/Resources/Info.plist", fmw_ver));
  1368. main_exe = p_path.path_join(vformat("Versions/%s", fmw_ver));
  1369. bundle_path = p_path.path_join(vformat("Versions/%s", fmw_ver));
  1370. bundle = true;
  1371. } else if (da->file_exists(p_path.path_join("Resources/Info.plist"))) {
  1372. info_path = p_path.path_join("Resources/Info.plist");
  1373. main_exe = p_path;
  1374. bundle_path = p_path;
  1375. bundle = true;
  1376. } else if (da->file_exists(p_path.path_join("Info.plist"))) {
  1377. info_path = p_path.path_join("Info.plist");
  1378. main_exe = p_path;
  1379. bundle_path = p_path;
  1380. bundle = true;
  1381. ios_bundle = true;
  1382. }
  1383. if (bundle) {
  1384. return _codesign_file(p_use_hardened_runtime, p_force, info_path, main_exe, bundle_path, p_ent_path, ios_bundle, r_error_msg);
  1385. } else {
  1386. r_error_msg = TTR("Unknown bundle type.");
  1387. ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown bundle type.");
  1388. }
  1389. } else if (da->file_exists(p_path)) {
  1390. return _codesign_file(p_use_hardened_runtime, p_force, "", p_path, "", p_ent_path, false, r_error_msg);
  1391. } else {
  1392. r_error_msg = TTR("Unknown object type.");
  1393. ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown object type.");
  1394. }
  1395. }