codesign.cpp 52 KB

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