codesign.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  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. const 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. r_pos += 4 + key_size + PAD(key_size, 4);
  423. r_out += "[" + String::utf8((const char *)blob.ptr() + r_pos + 4, key_size) + "]";
  424. #undef _R
  425. }
  426. _FORCE_INLINE_ void CodeSignRequirements::_parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  427. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  428. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  429. uint32_t key_size = _R(r_pos);
  430. ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  431. r_out += "[field.";
  432. r_out += itos(blob[r_pos + 4] / 40) + ".";
  433. r_out += itos(blob[r_pos + 4] % 40);
  434. uint32_t spos = r_pos + 5;
  435. while (spos < r_pos + 4 + key_size) {
  436. r_out += ".";
  437. if (blob[spos] <= 127) {
  438. r_out += itos(blob[spos]);
  439. spos += 1;
  440. } else {
  441. uint32_t x = (0x7F & blob[spos]) << 7;
  442. spos += 1;
  443. while (blob[spos] > 127) {
  444. x = (x + (0x7F & blob[spos])) << 7;
  445. spos += 1;
  446. }
  447. x = (x + (0x7F & blob[spos]));
  448. r_out += itos(x);
  449. spos += 1;
  450. }
  451. }
  452. r_out += "]";
  453. r_pos += 4 + key_size + PAD(key_size, 4);
  454. #undef _R
  455. }
  456. _FORCE_INLINE_ void CodeSignRequirements::_parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  457. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  458. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  459. uint32_t tag_size = _R(r_pos);
  460. ERR_FAIL_COND_MSG(r_pos + tag_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  461. r_out += "H\"" + String::hex_encode_buffer(blob.ptr() + r_pos + 4, tag_size) + "\"";
  462. r_pos += 4 + tag_size + PAD(tag_size, 4);
  463. #undef _R
  464. }
  465. _FORCE_INLINE_ void CodeSignRequirements::_parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  466. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  467. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  468. const uint32_t key_size = _R(r_pos);
  469. ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
  470. r_pos += 4 + key_size + PAD(key_size, 4);
  471. r_out += "\"" + String::utf8((const char *)blob.ptr() + r_pos + 4, key_size) + "\"";
  472. #undef _R
  473. }
  474. _FORCE_INLINE_ void CodeSignRequirements::_parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  475. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  476. ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
  477. uint32_t date = _R(r_pos);
  478. time_t t = 978307200 + date;
  479. struct tm lt;
  480. #ifdef WINDOWS_ENABLED
  481. gmtime_s(&lt, &t);
  482. #else
  483. gmtime_r(&t, &lt);
  484. #endif
  485. 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));
  486. #undef _R
  487. }
  488. _FORCE_INLINE_ bool CodeSignRequirements::_parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
  489. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  490. ERR_FAIL_COND_V_MSG(r_pos >= p_rq_size, false, "CodeSign/Requirements: Out of bounds.");
  491. uint32_t match = _R(r_pos);
  492. r_pos += 4;
  493. switch (match) {
  494. case 0x00000000: {
  495. r_out += "exists";
  496. } break;
  497. case 0x00000001: {
  498. r_out += "= ";
  499. _parse_value(r_pos, r_out, p_rq_size);
  500. } break;
  501. case 0x00000002: {
  502. r_out += "~ ";
  503. _parse_value(r_pos, r_out, p_rq_size);
  504. } break;
  505. case 0x00000003: {
  506. r_out += "= *";
  507. _parse_value(r_pos, r_out, p_rq_size);
  508. } break;
  509. case 0x00000004: {
  510. r_out += "= ";
  511. _parse_value(r_pos, r_out, p_rq_size);
  512. r_out += "*";
  513. } break;
  514. case 0x00000005: {
  515. r_out += "< ";
  516. _parse_value(r_pos, r_out, p_rq_size);
  517. } break;
  518. case 0x00000006: {
  519. r_out += "> ";
  520. _parse_value(r_pos, r_out, p_rq_size);
  521. } break;
  522. case 0x00000007: {
  523. r_out += "<= ";
  524. _parse_value(r_pos, r_out, p_rq_size);
  525. } break;
  526. case 0x00000008: {
  527. r_out += ">= ";
  528. _parse_value(r_pos, r_out, p_rq_size);
  529. } break;
  530. case 0x00000009: {
  531. r_out += "= ";
  532. _parse_date(r_pos, r_out, p_rq_size);
  533. } break;
  534. case 0x0000000A: {
  535. r_out += "< ";
  536. _parse_date(r_pos, r_out, p_rq_size);
  537. } break;
  538. case 0x0000000B: {
  539. r_out += "> ";
  540. _parse_date(r_pos, r_out, p_rq_size);
  541. } break;
  542. case 0x0000000C: {
  543. r_out += "<= ";
  544. _parse_date(r_pos, r_out, p_rq_size);
  545. } break;
  546. case 0x0000000D: {
  547. r_out += ">= ";
  548. _parse_date(r_pos, r_out, p_rq_size);
  549. } break;
  550. case 0x0000000E: {
  551. r_out += "absent";
  552. } break;
  553. default: {
  554. return false;
  555. }
  556. }
  557. return true;
  558. #undef _R
  559. }
  560. Vector<String> CodeSignRequirements::parse_requirements() const {
  561. #define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
  562. Vector<String> list;
  563. // Read requirements set header.
  564. ERR_FAIL_COND_V_MSG(blob.size() < 12, list, "CodeSign/Requirements: Blob is too small.");
  565. uint32_t magic = _R(0);
  566. ERR_FAIL_COND_V_MSG(magic != 0xfade0c01, list, "CodeSign/Requirements: Invalid set magic.");
  567. uint32_t size = _R(4);
  568. ERR_FAIL_COND_V_MSG(size != (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid set size.");
  569. uint32_t count = _R(8);
  570. for (uint32_t i = 0; i < count; i++) {
  571. String out;
  572. // Read requirement header.
  573. uint32_t rq_type = _R(12 + i * 8);
  574. uint32_t rq_offset = _R(12 + i * 8 + 4);
  575. ERR_FAIL_COND_V_MSG(rq_offset + 12 >= (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid requirement offset.");
  576. switch (rq_type) {
  577. case 0x00000001: {
  578. out += "host => ";
  579. } break;
  580. case 0x00000002: {
  581. out += "guest => ";
  582. } break;
  583. case 0x00000003: {
  584. out += "designated => ";
  585. } break;
  586. case 0x00000004: {
  587. out += "library => ";
  588. } break;
  589. case 0x00000005: {
  590. out += "plugin => ";
  591. } break;
  592. default: {
  593. ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement type.");
  594. }
  595. }
  596. uint32_t rq_magic = _R(rq_offset);
  597. uint32_t rq_size = _R(rq_offset + 4);
  598. uint32_t rq_ver = _R(rq_offset + 8);
  599. uint32_t pos = rq_offset + 12;
  600. ERR_FAIL_COND_V_MSG(rq_magic != 0xfade0c00, list, "CodeSign/Requirements: Invalid requirement magic.");
  601. ERR_FAIL_COND_V_MSG(rq_ver != 0x00000001, list, "CodeSign/Requirements: Invalid requirement version.");
  602. // Read requirement tokens.
  603. List<String> tokens;
  604. while (pos < rq_offset + rq_size) {
  605. uint32_t rq_tag = _R(pos);
  606. pos += 4;
  607. String token;
  608. switch (rq_tag) {
  609. case 0x00000000: {
  610. token = "false";
  611. } break;
  612. case 0x00000001: {
  613. token = "true";
  614. } break;
  615. case 0x00000002: {
  616. token = "identifier ";
  617. _parse_value(pos, token, rq_offset + rq_size);
  618. } break;
  619. case 0x00000003: {
  620. token = "anchor apple";
  621. } break;
  622. case 0x00000004: {
  623. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  624. token += " ";
  625. _parse_hash_string(pos, token, rq_offset + rq_size);
  626. } break;
  627. case 0x00000005: {
  628. token = "info";
  629. _parse_key(pos, token, rq_offset + rq_size);
  630. token += " = ";
  631. _parse_value(pos, token, rq_offset + rq_size);
  632. } break;
  633. case 0x00000006: {
  634. token = "and";
  635. } break;
  636. case 0x00000007: {
  637. token = "or";
  638. } break;
  639. case 0x00000008: {
  640. token = "cdhash ";
  641. _parse_hash_string(pos, token, rq_offset + rq_size);
  642. } break;
  643. case 0x00000009: {
  644. token = "!";
  645. } break;
  646. case 0x0000000A: {
  647. token = "info";
  648. _parse_key(pos, token, rq_offset + rq_size);
  649. token += " ";
  650. ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
  651. } break;
  652. case 0x0000000B: {
  653. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  654. _parse_key(pos, token, rq_offset + rq_size);
  655. token += " ";
  656. ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
  657. } break;
  658. case 0x0000000C: {
  659. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  660. token += " trusted";
  661. } break;
  662. case 0x0000000D: {
  663. token = "anchor trusted";
  664. } break;
  665. case 0x0000000E: {
  666. _parse_certificate_slot(pos, token, rq_offset + rq_size);
  667. _parse_oid_key(pos, token, rq_offset + rq_size);
  668. token += " ";
  669. ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
  670. } break;
  671. case 0x0000000F: {
  672. token = "anchor apple generic";
  673. } break;
  674. default: {
  675. ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement token.");
  676. } break;
  677. }
  678. tokens.push_back(token);
  679. }
  680. // Polish to infix notation (w/o bracket optimization).
  681. for (List<String>::Element *E = tokens.back(); E; E = E->prev()) {
  682. if (E->get() == "and") {
  683. ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence.");
  684. String token = "(" + E->next()->get() + " and " + E->next()->next()->get() + ")";
  685. tokens.erase(E->next()->next());
  686. tokens.erase(E->next());
  687. E->get() = token;
  688. } else if (E->get() == "or") {
  689. ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence.");
  690. String token = "(" + E->next()->get() + " or " + E->next()->next()->get() + ")";
  691. tokens.erase(E->next()->next());
  692. tokens.erase(E->next());
  693. E->get() = token;
  694. }
  695. }
  696. if (tokens.size() == 1) {
  697. list.push_back(out + tokens.front()->get());
  698. } else {
  699. ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid token sequence.");
  700. }
  701. }
  702. return list;
  703. #undef _R
  704. }
  705. PackedByteArray CodeSignRequirements::get_hash_sha1() const {
  706. PackedByteArray hash;
  707. hash.resize(0x14);
  708. CryptoCore::SHA1Context ctx;
  709. ctx.start();
  710. ctx.update(blob.ptr(), blob.size());
  711. ctx.finish(hash.ptrw());
  712. return hash;
  713. }
  714. PackedByteArray CodeSignRequirements::get_hash_sha256() const {
  715. PackedByteArray hash;
  716. hash.resize(0x20);
  717. CryptoCore::SHA256Context ctx;
  718. ctx.start();
  719. ctx.update(blob.ptr(), blob.size());
  720. ctx.finish(hash.ptrw());
  721. return hash;
  722. }
  723. int CodeSignRequirements::get_size() const {
  724. return blob.size();
  725. }
  726. void CodeSignRequirements::write_to_file(Ref<FileAccess> p_file) const {
  727. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Requirements: Invalid file handle.");
  728. p_file->store_buffer(blob.ptr(), blob.size());
  729. }
  730. /*************************************************************************/
  731. /* CodeSignEntitlementsText */
  732. /*************************************************************************/
  733. CodeSignEntitlementsText::CodeSignEntitlementsText() {
  734. blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic.
  735. blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes).
  736. }
  737. CodeSignEntitlementsText::CodeSignEntitlementsText(const String &p_string) {
  738. CharString utf8 = p_string.utf8();
  739. blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic.
  740. for (int i = 3; i >= 0; i--) {
  741. uint8_t x = ((utf8.length() + 8) >> i * 8) & 0xFF; // Size.
  742. blob.push_back(x);
  743. }
  744. for (int i = 0; i < utf8.length(); i++) { // Write data.
  745. blob.push_back(utf8[i]);
  746. }
  747. }
  748. PackedByteArray CodeSignEntitlementsText::get_hash_sha1() const {
  749. PackedByteArray hash;
  750. hash.resize(0x14);
  751. CryptoCore::SHA1Context ctx;
  752. ctx.start();
  753. ctx.update(blob.ptr(), blob.size());
  754. ctx.finish(hash.ptrw());
  755. return hash;
  756. }
  757. PackedByteArray CodeSignEntitlementsText::get_hash_sha256() const {
  758. PackedByteArray hash;
  759. hash.resize(0x20);
  760. CryptoCore::SHA256Context ctx;
  761. ctx.start();
  762. ctx.update(blob.ptr(), blob.size());
  763. ctx.finish(hash.ptrw());
  764. return hash;
  765. }
  766. int CodeSignEntitlementsText::get_size() const {
  767. return blob.size();
  768. }
  769. void CodeSignEntitlementsText::write_to_file(Ref<FileAccess> p_file) const {
  770. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsText: Invalid file handle.");
  771. p_file->store_buffer(blob.ptr(), blob.size());
  772. }
  773. /*************************************************************************/
  774. /* CodeSignEntitlementsBinary */
  775. /*************************************************************************/
  776. CodeSignEntitlementsBinary::CodeSignEntitlementsBinary() {
  777. blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic.
  778. blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes).
  779. }
  780. CodeSignEntitlementsBinary::CodeSignEntitlementsBinary(const String &p_string) {
  781. PList pl = PList(p_string);
  782. PackedByteArray asn1 = pl.save_asn1();
  783. blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic.
  784. uint32_t size = asn1.size() + 8;
  785. for (int i = 3; i >= 0; i--) {
  786. uint8_t x = (size >> i * 8) & 0xFF; // Size.
  787. blob.push_back(x);
  788. }
  789. blob.append_array(asn1); // Write data.
  790. }
  791. PackedByteArray CodeSignEntitlementsBinary::get_hash_sha1() const {
  792. PackedByteArray hash;
  793. hash.resize(0x14);
  794. CryptoCore::SHA1Context ctx;
  795. ctx.start();
  796. ctx.update(blob.ptr(), blob.size());
  797. ctx.finish(hash.ptrw());
  798. return hash;
  799. }
  800. PackedByteArray CodeSignEntitlementsBinary::get_hash_sha256() const {
  801. PackedByteArray hash;
  802. hash.resize(0x20);
  803. CryptoCore::SHA256Context ctx;
  804. ctx.start();
  805. ctx.update(blob.ptr(), blob.size());
  806. ctx.finish(hash.ptrw());
  807. return hash;
  808. }
  809. int CodeSignEntitlementsBinary::get_size() const {
  810. return blob.size();
  811. }
  812. void CodeSignEntitlementsBinary::write_to_file(Ref<FileAccess> p_file) const {
  813. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsBinary: Invalid file handle.");
  814. p_file->store_buffer(blob.ptr(), blob.size());
  815. }
  816. /*************************************************************************/
  817. /* CodeSignCodeDirectory */
  818. /*************************************************************************/
  819. CodeSignCodeDirectory::CodeSignCodeDirectory() {
  820. blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic.
  821. blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Size (8 bytes).
  822. }
  823. 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) {
  824. pages = p_code_limit / (uint64_t(1) << p_page_size);
  825. remain = p_code_limit % (uint64_t(1) << p_page_size);
  826. code_slots = pages + (remain > 0 ? 1 : 0);
  827. special_slots = 7;
  828. int cd_size = 8 + sizeof(CodeDirectoryHeader) + (code_slots + special_slots) * p_hash_size + p_id.size() + p_team_id.size();
  829. int cd_off = 8 + sizeof(CodeDirectoryHeader);
  830. blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic.
  831. for (int i = 3; i >= 0; i--) {
  832. uint8_t x = (cd_size >> i * 8) & 0xFF; // Size.
  833. blob.push_back(x);
  834. }
  835. blob.resize(cd_size);
  836. memset(blob.ptrw() + 8, 0x00, cd_size - 8);
  837. CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8);
  838. bool is_64_cl = (p_code_limit >= std::numeric_limits<uint32_t>::max());
  839. // Version and options.
  840. cd->version = BSWAP32(0x20500);
  841. cd->flags = BSWAP32(SIGNATURE_ADHOC | SIGNATURE_RUNTIME);
  842. cd->special_slots = BSWAP32(special_slots);
  843. cd->code_slots = BSWAP32(code_slots);
  844. if (is_64_cl) {
  845. cd->code_limit_64 = BSWAP64(p_code_limit);
  846. } else {
  847. cd->code_limit = BSWAP32(p_code_limit);
  848. }
  849. cd->hash_size = p_hash_size;
  850. cd->hash_type = p_hash_type;
  851. cd->page_size = p_page_size;
  852. cd->exec_seg_base = 0x00;
  853. cd->exec_seg_limit = BSWAP64(p_exe_limit);
  854. cd->exec_seg_flags = 0;
  855. if (p_main) {
  856. cd->exec_seg_flags |= EXECSEG_MAIN_BINARY;
  857. }
  858. cd->exec_seg_flags = BSWAP64(cd->exec_seg_flags);
  859. uint32_t version = (11 << 16) + (3 << 8) + 0; // Version 11.3.0
  860. cd->runtime = BSWAP32(version);
  861. // Copy ID.
  862. cd->ident_offset = BSWAP32(cd_off);
  863. memcpy(blob.ptrw() + cd_off, p_id.get_data(), p_id.size());
  864. cd_off += p_id.size();
  865. // Copy Team ID.
  866. if (p_team_id.length() > 0) {
  867. cd->team_offset = BSWAP32(cd_off);
  868. memcpy(blob.ptrw() + cd_off, p_team_id.get_data(), p_team_id.size());
  869. cd_off += p_team_id.size();
  870. } else {
  871. cd->team_offset = 0;
  872. }
  873. // Scatter vector.
  874. cd->scatter_vector_offset = 0; // Not used.
  875. // Executable hashes offset.
  876. cd->hash_offset = BSWAP32(cd_off + special_slots * cd->hash_size);
  877. }
  878. bool CodeSignCodeDirectory::set_hash_in_slot(const PackedByteArray &p_hash, int p_slot) {
  879. ERR_FAIL_COND_V_MSG((p_slot < -special_slots) || (p_slot >= code_slots), false, vformat("CodeSign/CodeDirectory: Invalid hash slot index: %d.", p_slot));
  880. CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8);
  881. for (int i = 0; i < cd->hash_size; i++) {
  882. blob.write[BSWAP32(cd->hash_offset) + p_slot * cd->hash_size + i] = p_hash[i];
  883. }
  884. return true;
  885. }
  886. int32_t CodeSignCodeDirectory::get_page_count() {
  887. return pages;
  888. }
  889. int32_t CodeSignCodeDirectory::get_page_remainder() {
  890. return remain;
  891. }
  892. PackedByteArray CodeSignCodeDirectory::get_hash_sha1() const {
  893. PackedByteArray hash;
  894. hash.resize(0x14);
  895. CryptoCore::SHA1Context ctx;
  896. ctx.start();
  897. ctx.update(blob.ptr(), blob.size());
  898. ctx.finish(hash.ptrw());
  899. return hash;
  900. }
  901. PackedByteArray CodeSignCodeDirectory::get_hash_sha256() const {
  902. PackedByteArray hash;
  903. hash.resize(0x20);
  904. CryptoCore::SHA256Context ctx;
  905. ctx.start();
  906. ctx.update(blob.ptr(), blob.size());
  907. ctx.finish(hash.ptrw());
  908. return hash;
  909. }
  910. int CodeSignCodeDirectory::get_size() const {
  911. return blob.size();
  912. }
  913. void CodeSignCodeDirectory::write_to_file(Ref<FileAccess> p_file) const {
  914. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/CodeDirectory: Invalid file handle.");
  915. p_file->store_buffer(blob.ptr(), blob.size());
  916. }
  917. /*************************************************************************/
  918. /* CodeSignSignature */
  919. /*************************************************************************/
  920. CodeSignSignature::CodeSignSignature() {
  921. blob.append_array({ 0xFA, 0xDE, 0x0B, 0x01 }); // Signature magic.
  922. uint32_t sign_size = 8; // Ad-hoc signature is empty.
  923. for (int i = 3; i >= 0; i--) {
  924. uint8_t x = (sign_size >> i * 8) & 0xFF; // Size.
  925. blob.push_back(x);
  926. }
  927. }
  928. PackedByteArray CodeSignSignature::get_hash_sha1() const {
  929. PackedByteArray hash;
  930. hash.resize(0x14);
  931. CryptoCore::SHA1Context ctx;
  932. ctx.start();
  933. ctx.update(blob.ptr(), blob.size());
  934. ctx.finish(hash.ptrw());
  935. return hash;
  936. }
  937. PackedByteArray CodeSignSignature::get_hash_sha256() const {
  938. PackedByteArray hash;
  939. hash.resize(0x20);
  940. CryptoCore::SHA256Context ctx;
  941. ctx.start();
  942. ctx.update(blob.ptr(), blob.size());
  943. ctx.finish(hash.ptrw());
  944. return hash;
  945. }
  946. int CodeSignSignature::get_size() const {
  947. return blob.size();
  948. }
  949. void CodeSignSignature::write_to_file(Ref<FileAccess> p_file) const {
  950. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Signature: Invalid file handle.");
  951. p_file->store_buffer(blob.ptr(), blob.size());
  952. }
  953. /*************************************************************************/
  954. /* CodeSignSuperBlob */
  955. /*************************************************************************/
  956. bool CodeSignSuperBlob::add_blob(const Ref<CodeSignBlob> &p_blob) {
  957. if (p_blob.is_valid()) {
  958. blobs.push_back(p_blob);
  959. return true;
  960. } else {
  961. return false;
  962. }
  963. }
  964. int CodeSignSuperBlob::get_size() const {
  965. int size = 12 + blobs.size() * 8;
  966. for (int i = 0; i < blobs.size(); i++) {
  967. if (blobs[i].is_null()) {
  968. return 0;
  969. }
  970. size += blobs[i]->get_size();
  971. }
  972. return size;
  973. }
  974. void CodeSignSuperBlob::write_to_file(Ref<FileAccess> p_file) const {
  975. ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/SuperBlob: Invalid file handle.");
  976. uint32_t size = get_size();
  977. uint32_t data_offset = 12 + blobs.size() * 8;
  978. // Write header.
  979. p_file->store_32(BSWAP32(0xfade0cc0));
  980. p_file->store_32(BSWAP32(size));
  981. p_file->store_32(BSWAP32(blobs.size()));
  982. // Write index.
  983. for (int i = 0; i < blobs.size(); i++) {
  984. if (blobs[i].is_null()) {
  985. return;
  986. }
  987. p_file->store_32(BSWAP32(blobs[i]->get_index_type()));
  988. p_file->store_32(BSWAP32(data_offset));
  989. data_offset += blobs[i]->get_size();
  990. }
  991. // Write blobs.
  992. for (int i = 0; i < blobs.size(); i++) {
  993. blobs[i]->write_to_file(p_file);
  994. }
  995. }
  996. /*************************************************************************/
  997. /* CodeSign */
  998. /*************************************************************************/
  999. PackedByteArray CodeSign::file_hash_sha1(const String &p_path) {
  1000. PackedByteArray file_hash;
  1001. Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
  1002. ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
  1003. CryptoCore::SHA1Context ctx;
  1004. ctx.start();
  1005. unsigned char step[4096];
  1006. while (true) {
  1007. uint64_t br = f->get_buffer(step, 4096);
  1008. if (br > 0) {
  1009. ctx.update(step, br);
  1010. }
  1011. if (br < 4096) {
  1012. break;
  1013. }
  1014. }
  1015. file_hash.resize(0x14);
  1016. ctx.finish(file_hash.ptrw());
  1017. return file_hash;
  1018. }
  1019. PackedByteArray CodeSign::file_hash_sha256(const String &p_path) {
  1020. PackedByteArray file_hash;
  1021. Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
  1022. ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
  1023. CryptoCore::SHA256Context ctx;
  1024. ctx.start();
  1025. unsigned char step[4096];
  1026. while (true) {
  1027. uint64_t br = f->get_buffer(step, 4096);
  1028. if (br > 0) {
  1029. ctx.update(step, br);
  1030. }
  1031. if (br < 4096) {
  1032. break;
  1033. }
  1034. }
  1035. file_hash.resize(0x20);
  1036. ctx.finish(file_hash.ptrw());
  1037. return file_hash;
  1038. }
  1039. 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) {
  1040. #define CLEANUP() \
  1041. if (files_to_sign.size() > 1) { \
  1042. for (int j = 0; j < files_to_sign.size(); j++) { \
  1043. da->remove(files_to_sign[j]); \
  1044. } \
  1045. }
  1046. print_verbose(vformat("CodeSign: Signing executable: %s, bundle: %s with entitlements %s", p_exe_path, p_bundle_path, p_ent_path));
  1047. PackedByteArray info_hash1, info_hash2;
  1048. PackedByteArray res_hash1, res_hash2;
  1049. String id;
  1050. String main_exe = p_exe_path;
  1051. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1052. if (da.is_null()) {
  1053. r_error_msg = TTR("Can't get filesystem access.");
  1054. ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
  1055. }
  1056. // Read Info.plist.
  1057. if (!p_info.is_empty()) {
  1058. print_verbose("CodeSign: Reading bundle info...");
  1059. PList info_plist;
  1060. if (info_plist.load_file(p_info)) {
  1061. info_hash1 = file_hash_sha1(p_info);
  1062. info_hash2 = file_hash_sha256(p_info);
  1063. if (info_hash1.is_empty() || info_hash2.is_empty()) {
  1064. r_error_msg = TTR("Failed to get Info.plist hash.");
  1065. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get Info.plist hash.");
  1066. }
  1067. if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) {
  1068. main_exe = p_exe_path.path_join(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data()));
  1069. } else {
  1070. r_error_msg = TTR("Invalid Info.plist, no exe name.");
  1071. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no exe name.");
  1072. }
  1073. if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleIdentifier")) {
  1074. id = info_plist.get_root()->data_dict["CFBundleIdentifier"]->data_string.get_data();
  1075. } else {
  1076. r_error_msg = TTR("Invalid Info.plist, no bundle id.");
  1077. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no bundle id.");
  1078. }
  1079. } else {
  1080. r_error_msg = TTR("Invalid Info.plist, can't load.");
  1081. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, can't load.");
  1082. }
  1083. }
  1084. // Extract fat binary.
  1085. Vector<String> files_to_sign;
  1086. if (LipO::is_lipo(main_exe)) {
  1087. print_verbose(vformat("CodeSign: Executable is fat, extracting..."));
  1088. String tmp_path_name = EditorPaths::get_singleton()->get_temp_dir().path_join("_lipo");
  1089. Error err = da->make_dir_recursive(tmp_path_name);
  1090. if (err != OK) {
  1091. r_error_msg = vformat(TTR("Failed to create \"%s\" subfolder."), tmp_path_name);
  1092. ERR_FAIL_V_MSG(FAILED, vformat("CodeSign: Failed to create \"%s\" subfolder.", tmp_path_name));
  1093. }
  1094. LipO lip;
  1095. if (lip.open_file(main_exe)) {
  1096. for (int i = 0; i < lip.get_arch_count(); i++) {
  1097. if (!lip.extract_arch(i, tmp_path_name.path_join("_exe_" + itos(i)))) {
  1098. CLEANUP();
  1099. r_error_msg = TTR("Failed to extract thin binary.");
  1100. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to extract thin binary.");
  1101. }
  1102. files_to_sign.push_back(tmp_path_name.path_join("_exe_" + itos(i)));
  1103. }
  1104. }
  1105. } else if (MachO::is_macho(main_exe)) {
  1106. print_verbose("CodeSign: Executable is thin...");
  1107. files_to_sign.push_back(main_exe);
  1108. } else {
  1109. r_error_msg = TTR("Invalid binary format.");
  1110. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid binary format.");
  1111. }
  1112. // Check if it's already signed.
  1113. if (!p_force) {
  1114. for (int i = 0; i < files_to_sign.size(); i++) {
  1115. MachO mh;
  1116. mh.open_file(files_to_sign[i]);
  1117. if (mh.is_signed()) {
  1118. CLEANUP();
  1119. r_error_msg = TTR("Already signed!");
  1120. ERR_FAIL_V_MSG(FAILED, "CodeSign: Already signed!");
  1121. }
  1122. }
  1123. }
  1124. // Generate core resources.
  1125. if (!p_bundle_path.is_empty()) {
  1126. print_verbose("CodeSign: Generating bundle CodeResources...");
  1127. CodeSignCodeResources cr;
  1128. if (p_ios_bundle) {
  1129. cr.add_rule1("^.*");
  1130. cr.add_rule1("^.*\\.lproj/", "optional", 100);
  1131. cr.add_rule1("^.*\\.lproj/locversion.plist$", "omit", 1100);
  1132. cr.add_rule1("^Base\\.lproj/", "", 1010);
  1133. cr.add_rule1("^version.plist$");
  1134. cr.add_rule2(".*\\.dSYM($|/)", "", 11);
  1135. cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000);
  1136. cr.add_rule2("^.*");
  1137. cr.add_rule2("^.*\\.lproj/", "optional", 1000);
  1138. cr.add_rule2("^.*\\.lproj/locversion.plist$", "omit", 1100);
  1139. cr.add_rule2("^Base\\.lproj/", "", 1010);
  1140. cr.add_rule2("^Info\\.plist$", "omit", 20);
  1141. cr.add_rule2("^PkgInfo$", "omit", 20);
  1142. cr.add_rule2("^embedded\\.provisionprofile$", "", 10);
  1143. cr.add_rule2("^version\\.plist$", "", 20);
  1144. cr.add_rule2("^_MASReceipt", "omit", 2000, false);
  1145. cr.add_rule2("^_CodeSignature", "omit", 2000, false);
  1146. cr.add_rule2("^CodeResources", "omit", 2000, false);
  1147. } else {
  1148. cr.add_rule1("^Resources($|/)");
  1149. cr.add_rule1("^Resources/.*\\.lproj/", "optional", 1000);
  1150. cr.add_rule1("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100);
  1151. cr.add_rule1("^Resources/Base\\.lproj/", "", 1010);
  1152. cr.add_rule1("^version.plist$");
  1153. cr.add_rule2(".*\\.dSYM($|/)", "", 11);
  1154. cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000);
  1155. cr.add_rule2("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", "nested", 10);
  1156. cr.add_rule2("^.*");
  1157. cr.add_rule2("^Info\\.plist$", "omit", 20);
  1158. cr.add_rule2("^PkgInfo$", "omit", 20);
  1159. cr.add_rule2("^Resources($|/)", "", 20);
  1160. cr.add_rule2("^Resources/.*\\.lproj/", "optional", 1000);
  1161. cr.add_rule2("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100);
  1162. cr.add_rule2("^Resources/Base\\.lproj/", "", 1010);
  1163. cr.add_rule2("^[^/]+$", "nested", 10);
  1164. cr.add_rule2("^embedded\\.provisionprofile$", "", 10);
  1165. cr.add_rule2("^version\\.plist$", "", 20);
  1166. cr.add_rule2("^_MASReceipt", "omit", 2000, false);
  1167. cr.add_rule2("^_CodeSignature", "omit", 2000, false);
  1168. cr.add_rule2("^CodeResources", "omit", 2000, false);
  1169. }
  1170. if (!cr.add_folder_recursive(p_bundle_path, "", main_exe)) {
  1171. CLEANUP();
  1172. r_error_msg = TTR("Failed to process nested resources.");
  1173. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to process nested resources.");
  1174. }
  1175. Error err = da->make_dir_recursive(p_bundle_path.path_join("_CodeSignature"));
  1176. if (err != OK) {
  1177. CLEANUP();
  1178. r_error_msg = TTR("Failed to create _CodeSignature subfolder.");
  1179. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create _CodeSignature subfolder.");
  1180. }
  1181. cr.save_to_file(p_bundle_path.path_join("_CodeSignature").path_join("CodeResources"));
  1182. res_hash1 = file_hash_sha1(p_bundle_path.path_join("_CodeSignature").path_join("CodeResources"));
  1183. res_hash2 = file_hash_sha256(p_bundle_path.path_join("_CodeSignature").path_join("CodeResources"));
  1184. if (res_hash1.is_empty() || res_hash2.is_empty()) {
  1185. CLEANUP();
  1186. r_error_msg = TTR("Failed to get CodeResources hash.");
  1187. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get CodeResources hash.");
  1188. }
  1189. }
  1190. // Generate common signature structures.
  1191. if (id.is_empty()) {
  1192. CryptoCore::RandomGenerator rng;
  1193. ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
  1194. uint8_t uuid[16];
  1195. Error err = rng.get_random_bytes(uuid, 16);
  1196. ERR_FAIL_COND_V_MSG(err, err, "Failed to generate UUID.");
  1197. id = (String("a-55554944") /*a-UUID*/ + String::hex_encode_buffer(uuid, 16));
  1198. }
  1199. CharString uuid_str = id.utf8();
  1200. print_verbose(vformat("CodeSign: Used bundle ID: %s", id));
  1201. print_verbose("CodeSign: Processing entitlements...");
  1202. Ref<CodeSignEntitlementsText> cet;
  1203. Ref<CodeSignEntitlementsBinary> ceb;
  1204. if (!p_ent_path.is_empty()) {
  1205. String entitlements = FileAccess::get_file_as_string(p_ent_path);
  1206. if (entitlements.is_empty()) {
  1207. CLEANUP();
  1208. r_error_msg = TTR("Invalid entitlements file.");
  1209. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file.");
  1210. }
  1211. cet.instantiate(entitlements);
  1212. ceb.instantiate(entitlements);
  1213. }
  1214. print_verbose("CodeSign: Generating requirements...");
  1215. Ref<CodeSignRequirements> rq;
  1216. String team_id = "";
  1217. rq.instantiate();
  1218. // Sign executables.
  1219. for (int i = 0; i < files_to_sign.size(); i++) {
  1220. MachO mh;
  1221. if (!mh.open_file(files_to_sign[i])) {
  1222. CLEANUP();
  1223. r_error_msg = TTR("Invalid executable file.");
  1224. ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid executable file.");
  1225. }
  1226. print_verbose(vformat("CodeSign: Signing executable for cputype: %d ...", mh.get_cputype()));
  1227. print_verbose("CodeSign: Generating CodeDirectory...");
  1228. Ref<CodeSignCodeDirectory> cd1 = memnew(CodeSignCodeDirectory(0x14, 0x01, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
  1229. Ref<CodeSignCodeDirectory> cd2 = memnew(CodeSignCodeDirectory(0x20, 0x02, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
  1230. print_verbose("CodeSign: Calculating special slot hashes...");
  1231. if (info_hash2.size() == 0x20) {
  1232. cd2->set_hash_in_slot(info_hash2, CodeSignCodeDirectory::SLOT_INFO_PLIST);
  1233. }
  1234. if (info_hash1.size() == 0x14) {
  1235. cd1->set_hash_in_slot(info_hash1, CodeSignCodeDirectory::SLOT_INFO_PLIST);
  1236. }
  1237. cd1->set_hash_in_slot(rq->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS);
  1238. cd2->set_hash_in_slot(rq->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS);
  1239. if (res_hash2.size() == 0x20) {
  1240. cd2->set_hash_in_slot(res_hash2, CodeSignCodeDirectory::SLOT_RESOURCES);
  1241. }
  1242. if (res_hash1.size() == 0x14) {
  1243. cd1->set_hash_in_slot(res_hash1, CodeSignCodeDirectory::SLOT_RESOURCES);
  1244. }
  1245. if (cet.is_valid()) {
  1246. cd1->set_hash_in_slot(cet->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); //Text variant.
  1247. cd2->set_hash_in_slot(cet->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS);
  1248. }
  1249. if (ceb.is_valid()) {
  1250. cd1->set_hash_in_slot(ceb->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); //ASN.1 variant.
  1251. cd2->set_hash_in_slot(ceb->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS);
  1252. }
  1253. // Calculate signature size.
  1254. int sign_size = 12; // SuperBlob header.
  1255. sign_size += cd1->get_size() + 8;
  1256. sign_size += cd2->get_size() + 8;
  1257. sign_size += rq->get_size() + 8;
  1258. if (cet.is_valid()) {
  1259. sign_size += cet->get_size() + 8;
  1260. }
  1261. if (ceb.is_valid()) {
  1262. sign_size += ceb->get_size() + 8;
  1263. }
  1264. sign_size += 16; // Empty signature size.
  1265. // Alloc/resize signature load command.
  1266. print_verbose(vformat("CodeSign: Reallocating space for the signature superblob (%d)...", sign_size));
  1267. if (!mh.set_signature_size(sign_size)) {
  1268. CLEANUP();
  1269. r_error_msg = TTR("Can't resize signature load command.");
  1270. ERR_FAIL_V_MSG(FAILED, "CodeSign: Can't resize signature load command.");
  1271. }
  1272. print_verbose("CodeSign: Calculating executable code hashes...");
  1273. // Calculate executable code hashes.
  1274. PackedByteArray buffer;
  1275. PackedByteArray hash1, hash2;
  1276. hash1.resize(0x14);
  1277. hash2.resize(0x20);
  1278. buffer.resize(1 << 12);
  1279. mh.get_file()->seek(0);
  1280. for (int32_t j = 0; j < cd2->get_page_count(); j++) {
  1281. mh.get_file()->get_buffer(buffer.ptrw(), (1 << 12));
  1282. CryptoCore::SHA256Context ctx2;
  1283. ctx2.start();
  1284. ctx2.update(buffer.ptr(), (1 << 12));
  1285. ctx2.finish(hash2.ptrw());
  1286. cd2->set_hash_in_slot(hash2, j);
  1287. CryptoCore::SHA1Context ctx1;
  1288. ctx1.start();
  1289. ctx1.update(buffer.ptr(), (1 << 12));
  1290. ctx1.finish(hash1.ptrw());
  1291. cd1->set_hash_in_slot(hash1, j);
  1292. }
  1293. if (cd2->get_page_remainder() > 0) {
  1294. mh.get_file()->get_buffer(buffer.ptrw(), cd2->get_page_remainder());
  1295. CryptoCore::SHA256Context ctx2;
  1296. ctx2.start();
  1297. ctx2.update(buffer.ptr(), cd2->get_page_remainder());
  1298. ctx2.finish(hash2.ptrw());
  1299. cd2->set_hash_in_slot(hash2, cd2->get_page_count());
  1300. CryptoCore::SHA1Context ctx1;
  1301. ctx1.start();
  1302. ctx1.update(buffer.ptr(), cd1->get_page_remainder());
  1303. ctx1.finish(hash1.ptrw());
  1304. cd1->set_hash_in_slot(hash1, cd1->get_page_count());
  1305. }
  1306. print_verbose("CodeSign: Generating signature...");
  1307. Ref<CodeSignSignature> cs;
  1308. cs.instantiate();
  1309. print_verbose("CodeSign: Writing signature superblob...");
  1310. // Write signature data to the executable.
  1311. CodeSignSuperBlob sb = CodeSignSuperBlob();
  1312. sb.add_blob(cd2);
  1313. sb.add_blob(cd1);
  1314. sb.add_blob(rq);
  1315. if (cet.is_valid()) {
  1316. sb.add_blob(cet);
  1317. }
  1318. if (ceb.is_valid()) {
  1319. sb.add_blob(ceb);
  1320. }
  1321. sb.add_blob(cs);
  1322. mh.get_file()->seek(mh.get_signature_offset());
  1323. sb.write_to_file(mh.get_file());
  1324. }
  1325. if (files_to_sign.size() > 1) {
  1326. print_verbose("CodeSign: Rebuilding fat executable...");
  1327. LipO lip;
  1328. if (!lip.create_file(main_exe, files_to_sign)) {
  1329. CLEANUP();
  1330. r_error_msg = TTR("Failed to create fat binary.");
  1331. ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create fat binary.");
  1332. }
  1333. CLEANUP();
  1334. }
  1335. FileAccess::set_unix_permissions(main_exe, 0755); // Restore unix permissions.
  1336. return OK;
  1337. #undef CLEANUP
  1338. }
  1339. Error CodeSign::codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg) {
  1340. Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  1341. if (da.is_null()) {
  1342. r_error_msg = TTR("Can't get filesystem access.");
  1343. ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
  1344. }
  1345. if (da->dir_exists(p_path)) {
  1346. String fmw_ver = "Current"; // Framework version (default).
  1347. String info_path;
  1348. String main_exe;
  1349. String bundle_path;
  1350. bool bundle = false;
  1351. bool ios_bundle = false;
  1352. if (da->file_exists(p_path.path_join("Contents/Info.plist"))) {
  1353. info_path = p_path.path_join("Contents/Info.plist");
  1354. main_exe = p_path.path_join("Contents/MacOS");
  1355. bundle_path = p_path.path_join("Contents");
  1356. bundle = true;
  1357. } else if (da->file_exists(p_path.path_join(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) {
  1358. info_path = p_path.path_join(vformat("Versions/%s/Resources/Info.plist", fmw_ver));
  1359. main_exe = p_path.path_join(vformat("Versions/%s", fmw_ver));
  1360. bundle_path = p_path.path_join(vformat("Versions/%s", fmw_ver));
  1361. bundle = true;
  1362. } else if (da->file_exists(p_path.path_join("Resources/Info.plist"))) {
  1363. info_path = p_path.path_join("Resources/Info.plist");
  1364. main_exe = p_path;
  1365. bundle_path = p_path;
  1366. bundle = true;
  1367. } else if (da->file_exists(p_path.path_join("Info.plist"))) {
  1368. info_path = p_path.path_join("Info.plist");
  1369. main_exe = p_path;
  1370. bundle_path = p_path;
  1371. bundle = true;
  1372. ios_bundle = true;
  1373. }
  1374. if (bundle) {
  1375. return _codesign_file(p_use_hardened_runtime, p_force, info_path, main_exe, bundle_path, p_ent_path, ios_bundle, r_error_msg);
  1376. } else {
  1377. r_error_msg = TTR("Unknown bundle type.");
  1378. ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown bundle type.");
  1379. }
  1380. } else if (da->file_exists(p_path)) {
  1381. return _codesign_file(p_use_hardened_runtime, p_force, "", p_path, "", p_ent_path, false, r_error_msg);
  1382. } else {
  1383. r_error_msg = TTR("Unknown object type.");
  1384. ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown object type.");
  1385. }
  1386. }