diff options
Diffstat (limited to 'platform/osx/export/codesign.cpp')
-rw-r--r-- | platform/osx/export/codesign.cpp | 1564 |
1 files changed, 0 insertions, 1564 deletions
diff --git a/platform/osx/export/codesign.cpp b/platform/osx/export/codesign.cpp deleted file mode 100644 index fd044c00cc..0000000000 --- a/platform/osx/export/codesign.cpp +++ /dev/null @@ -1,1564 +0,0 @@ -/*************************************************************************/ -/* codesign.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "codesign.h" - -#include "lipo.h" -#include "macho.h" -#include "plist.h" - -#include "core/os/os.h" -#include "editor/editor_paths.h" -#include "editor/editor_settings.h" - -#include "modules/modules_enabled.gen.h" // For regex. - -#include <ctime> - -#ifdef MODULE_REGEX_ENABLED - -/*************************************************************************/ -/* CodeSignCodeResources */ -/*************************************************************************/ - -String CodeSignCodeResources::hash_sha1_base64(const String &p_path) { - Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA1Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = fa->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - unsigned char hash[0x14]; - ctx.finish(hash); - - return CryptoCore::b64_encode_str(hash, 0x14); -} - -String CodeSignCodeResources::hash_sha256_base64(const String &p_path) { - Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA256Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = fa->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - unsigned char hash[0x20]; - ctx.finish(hash); - - return CryptoCore::b64_encode_str(hash, 0x20); -} - -void CodeSignCodeResources::add_rule1(const String &p_rule, const String &p_key, int p_weight, bool p_store) { - rules1.push_back(CRRule(p_rule, p_key, p_weight, p_store)); -} - -void CodeSignCodeResources::add_rule2(const String &p_rule, const String &p_key, int p_weight, bool p_store) { - rules2.push_back(CRRule(p_rule, p_key, p_weight, p_store)); -} - -CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules1(const String &p_path) const { - CRMatch found = CRMatch::CR_MATCH_NO; - int weight = 0; - for (int i = 0; i < rules1.size(); i++) { - RegEx regex = RegEx(rules1[i].file_pattern); - if (regex.search(p_path).is_valid()) { - if (rules1[i].key == "omit") { - return CRMatch::CR_MATCH_NO; - } else if (rules1[i].key == "nested") { - if (weight <= rules1[i].weight) { - found = CRMatch::CR_MATCH_NESTED; - weight = rules1[i].weight; - } - } else if (rules1[i].key == "optional") { - if (weight <= rules1[i].weight) { - found = CRMatch::CR_MATCH_OPTIONAL; - weight = rules1[i].weight; - } - } else { - if (weight <= rules1[i].weight) { - found = CRMatch::CR_MATCH_YES; - weight = rules1[i].weight; - } - } - } - } - return found; -} - -CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules2(const String &p_path) const { - CRMatch found = CRMatch::CR_MATCH_NO; - int weight = 0; - for (int i = 0; i < rules2.size(); i++) { - RegEx regex = RegEx(rules2[i].file_pattern); - if (regex.search(p_path).is_valid()) { - if (rules2[i].key == "omit") { - return CRMatch::CR_MATCH_NO; - } else if (rules2[i].key == "nested") { - if (weight <= rules2[i].weight) { - found = CRMatch::CR_MATCH_NESTED; - weight = rules2[i].weight; - } - } else if (rules2[i].key == "optional") { - if (weight <= rules2[i].weight) { - found = CRMatch::CR_MATCH_OPTIONAL; - weight = rules2[i].weight; - } - } else { - if (weight <= rules2[i].weight) { - found = CRMatch::CR_MATCH_YES; - weight = rules2[i].weight; - } - } - } - } - return found; -} - -bool CodeSignCodeResources::add_file1(const String &p_root, const String &p_path) { - CRMatch found = match_rules1(p_path); - if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) { - return true; // No match. - } - - CRFile f; - f.name = p_path; - f.optional = (found == CRMatch::CR_MATCH_OPTIONAL); - f.nested = false; - f.hash = hash_sha1_base64(p_root.plus_file(p_path)); - print_verbose(vformat("CodeSign/CodeResources: File(V1) %s hash1:%s", f.name, f.hash)); - - files1.push_back(f); - return true; -} - -bool CodeSignCodeResources::add_file2(const String &p_root, const String &p_path) { - CRMatch found = match_rules2(p_path); - if (found == CRMatch::CR_MATCH_NESTED) { - return add_nested_file(p_root, p_path, p_root.plus_file(p_path)); - } - if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) { - return true; // No match. - } - - CRFile f; - f.name = p_path; - f.optional = (found == CRMatch::CR_MATCH_OPTIONAL); - f.nested = false; - f.hash = hash_sha1_base64(p_root.plus_file(p_path)); - f.hash2 = hash_sha256_base64(p_root.plus_file(p_path)); - - print_verbose(vformat("CodeSign/CodeResources: File(V2) %s hash1:%s hash2:%s", f.name, f.hash, f.hash2)); - - files2.push_back(f); - return true; -} - -bool CodeSignCodeResources::add_nested_file(const String &p_root, const String &p_path, const String &p_exepath) { -#define CLEANUP() \ - if (files_to_add.size() > 1) { \ - for (int j = 0; j < files_to_add.size(); j++) { \ - da->remove(files_to_add[j]); \ - } \ - } - - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(da.is_null(), false); - - Vector<String> files_to_add; - if (LipO::is_lipo(p_exepath)) { - String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo"); - Error err = da->make_dir_recursive(tmp_path_name); - ERR_FAIL_COND_V_MSG(err != OK, false, vformat("CodeSign/CodeResources: Failed to create \"%s\" subfolder.", tmp_path_name)); - LipO lip; - if (lip.open_file(p_exepath)) { - for (int i = 0; i < lip.get_arch_count(); i++) { - if (!lip.extract_arch(i, tmp_path_name.plus_file("_rqexe_" + itos(i)))) { - CLEANUP(); - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Failed to extract thin binary."); - } - files_to_add.push_back(tmp_path_name.plus_file("_rqexe_" + itos(i))); - } - } - } else if (MachO::is_macho(p_exepath)) { - files_to_add.push_back(p_exepath); - } - - CRFile f; - f.name = p_path; - f.optional = false; - f.nested = true; - for (int i = 0; i < files_to_add.size(); i++) { - MachO mh; - if (!mh.open_file(files_to_add[i])) { - CLEANUP(); - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid executable file."); - } - PackedByteArray hash = mh.get_cdhash_sha256(); // Use SHA-256 variant, if available. - if (hash.size() != 0x20) { - hash = mh.get_cdhash_sha1(); // Use SHA-1 instead. - if (hash.size() != 0x14) { - CLEANUP(); - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Unsigned nested executable file."); - } - } - hash.resize(0x14); // Always clamp to 0x14 size. - f.hash = CryptoCore::b64_encode_str(hash.ptr(), hash.size()); - - PackedByteArray rq_blob = mh.get_requirements(); - String req_string; - if (rq_blob.size() > 8) { - CodeSignRequirements rq = CodeSignRequirements(rq_blob); - Vector<String> rqs = rq.parse_requirements(); - for (int j = 0; j < rqs.size(); j++) { - if (rqs[j].begins_with("designated => ")) { - req_string = rqs[j].replace("designated => ", ""); - } - } - } - if (req_string.is_empty()) { - req_string = "cdhash H\"" + String::hex_encode_buffer(hash.ptr(), hash.size()) + "\""; - } - print_verbose(vformat("CodeSign/CodeResources: Nested object %s (cputype: %d) cdhash:%s designated rq:%s", f.name, mh.get_cputype(), f.hash, req_string)); - if (f.requirements != req_string) { - if (i != 0) { - f.requirements += " or "; - } - f.requirements += req_string; - } - } - files2.push_back(f); - - CLEANUP(); - return true; - -#undef CLEANUP -} - -bool CodeSignCodeResources::add_folder_recursive(const String &p_root, const String &p_path, const String &p_main_exe_path) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(da.is_null(), false); - Error err = da->change_dir(p_root.plus_file(p_path)); - ERR_FAIL_COND_V(err != OK, false); - - bool ret = true; - da->list_dir_begin(); - String n = da->get_next(); - while (n != String()) { - if (n != "." && n != "..") { - String path = p_root.plus_file(p_path).plus_file(n); - if (path == p_main_exe_path) { - n = da->get_next(); - continue; // Skip main executable. - } - if (da->current_is_dir()) { - CRMatch found = match_rules2(p_path.plus_file(n)); - String fmw_ver = "Current"; // Framework version (default). - String info_path; - String main_exe; - bool bundle = false; - if (da->file_exists(path.plus_file("Contents/Info.plist"))) { - info_path = path.plus_file("Contents/Info.plist"); - main_exe = path.plus_file("Contents/MacOS"); - bundle = true; - } else if (da->file_exists(path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) { - info_path = path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)); - main_exe = path.plus_file(vformat("Versions/%s", fmw_ver)); - bundle = true; - } else if (da->file_exists(path.plus_file("Info.plist"))) { - info_path = path.plus_file("Info.plist"); - main_exe = path; - bundle = true; - } - if (bundle && found == CRMatch::CR_MATCH_NESTED && !info_path.is_empty()) { - // Read Info.plist. - PList info_plist; - if (info_plist.load_file(info_path)) { - if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) { - main_exe = main_exe.plus_file(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data())); - } else { - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, no exe name."); - } - } else { - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, can't load."); - } - ret = ret && add_nested_file(p_root, p_path.plus_file(n), main_exe); - } else { - ret = ret && add_folder_recursive(p_root, p_path.plus_file(n), p_main_exe_path); - } - } else { - ret = ret && add_file1(p_root, p_path.plus_file(n)); - ret = ret && add_file2(p_root, p_path.plus_file(n)); - } - } - - n = da->get_next(); - } - - da->list_dir_end(); - return ret; -} - -bool CodeSignCodeResources::save_to_file(const String &p_path) { - PList pl; - - print_verbose(vformat("CodeSign/CodeResources: Writing to file: %s", p_path)); - - // Write version 1 hashes. - Ref<PListNode> files1_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(files1_dict, "files"); - for (int i = 0; i < files1.size(); i++) { - if (files1[i].optional) { - Ref<PListNode> file_dict = PListNode::new_dict(); - files1_dict->push_subnode(file_dict, files1[i].name); - - file_dict->push_subnode(PListNode::new_data(files1[i].hash), "hash"); - file_dict->push_subnode(PListNode::new_bool(true), "optional"); - } else { - files1_dict->push_subnode(PListNode::new_data(files1[i].hash), files1[i].name); - } - } - - // Write version 2 hashes. - Ref<PListNode> files2_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(files2_dict, "files2"); - for (int i = 0; i < files2.size(); i++) { - Ref<PListNode> file_dict = PListNode::new_dict(); - files2_dict->push_subnode(file_dict, files2[i].name); - - if (files2[i].nested) { - file_dict->push_subnode(PListNode::new_data(files2[i].hash), "cdhash"); - file_dict->push_subnode(PListNode::new_string(files2[i].requirements), "requirement"); - } else { - file_dict->push_subnode(PListNode::new_data(files2[i].hash), "hash"); - file_dict->push_subnode(PListNode::new_data(files2[i].hash2), "hash2"); - if (files2[i].optional) { - file_dict->push_subnode(PListNode::new_bool(true), "optional"); - } - } - } - - // Write version 1 rules. - Ref<PListNode> rules1_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(rules1_dict, "rules"); - for (int i = 0; i < rules1.size(); i++) { - if (rules1[i].store) { - if (rules1[i].key.is_empty() && rules1[i].weight <= 0) { - rules1_dict->push_subnode(PListNode::new_bool(true), rules1[i].file_pattern); - } else { - Ref<PListNode> rule_dict = PListNode::new_dict(); - rules1_dict->push_subnode(rule_dict, rules1[i].file_pattern); - if (!rules1[i].key.is_empty()) { - rule_dict->push_subnode(PListNode::new_bool(true), rules1[i].key); - } - if (rules1[i].weight != 1) { - rule_dict->push_subnode(PListNode::new_real(rules1[i].weight), "weight"); - } - } - } - } - - // Write version 2 rules. - Ref<PListNode> rules2_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(rules2_dict, "rules2"); - for (int i = 0; i < rules2.size(); i++) { - if (rules2[i].store) { - if (rules2[i].key.is_empty() && rules2[i].weight <= 0) { - rules2_dict->push_subnode(PListNode::new_bool(true), rules2[i].file_pattern); - } else { - Ref<PListNode> rule_dict = PListNode::new_dict(); - rules2_dict->push_subnode(rule_dict, rules2[i].file_pattern); - if (!rules2[i].key.is_empty()) { - rule_dict->push_subnode(PListNode::new_bool(true), rules2[i].key); - } - if (rules2[i].weight != 1) { - rule_dict->push_subnode(PListNode::new_real(rules2[i].weight), "weight"); - } - } - } - } - String text = pl.save_text(); - ERR_FAIL_COND_V_MSG(text.is_empty(), false, "CodeSign/CodeResources: Generating resources PList failed."); - - Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path)); - - CharString cs = text.utf8(); - fa->store_buffer((const uint8_t *)cs.ptr(), cs.length()); - return true; -} - -/*************************************************************************/ -/* CodeSignRequirements */ -/*************************************************************************/ - -CodeSignRequirements::CodeSignRequirements() { - blob.append_array({ 0xFA, 0xDE, 0x0C, 0x01 }); // Requirement set magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x0C }); // Length of requirements set (12 bytes). - blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Empty. -} - -CodeSignRequirements::CodeSignRequirements(const PackedByteArray &p_data) { - blob = p_data; -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - r_out += "certificate "; - uint32_t tag_slot = _R(r_pos); - if (tag_slot == 0x00000000) { - r_out += "leaf"; - } else if (tag_slot == 0xffffffff) { - r_out += "root"; - } else { - r_out += itos((int32_t)tag_slot); - } - r_pos += 4; -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t key_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - CharString key; - key.resize(key_size); - memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size); - r_pos += 4 + key_size + PAD(key_size, 4); - r_out += "[" + String::utf8(key, key_size) + "]"; -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t key_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - r_out += "[field."; - r_out += itos(blob[r_pos + 4] / 40) + "."; - r_out += itos(blob[r_pos + 4] % 40); - uint32_t spos = r_pos + 5; - while (spos < r_pos + 4 + key_size) { - r_out += "."; - if (blob[spos] <= 127) { - r_out += itos(blob[spos]); - spos += 1; - } else { - uint32_t x = (0x7F & blob[spos]) << 7; - spos += 1; - while (blob[spos] > 127) { - x = (x + (0x7F & blob[spos])) << 7; - spos += 1; - } - x = (x + (0x7F & blob[spos])); - r_out += itos(x); - spos += 1; - } - } - r_out += "]"; - r_pos += 4 + key_size + PAD(key_size, 4); -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t tag_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + tag_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - PackedByteArray data; - data.resize(tag_size); - memcpy(data.ptrw(), blob.ptr() + r_pos + 4, tag_size); - r_out += "H\"" + String::hex_encode_buffer(data.ptr(), data.size()) + "\""; - r_pos += 4 + tag_size + PAD(tag_size, 4); -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t key_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - CharString key; - key.resize(key_size); - memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size); - r_pos += 4 + key_size + PAD(key_size, 4); - r_out += "\"" + String::utf8(key, key_size) + "\""; -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t date = _R(r_pos); - time_t t = 978307200 + date; - struct tm lt; -#ifdef WINDOWS_ENABLED - gmtime_s(<, &t); -#else - gmtime_r(&t, <); -#endif - 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)); -#undef _R -} - -_FORCE_INLINE_ bool CodeSignRequirements::_parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_V_MSG(r_pos >= p_rq_size, false, "CodeSign/Requirements: Out of bounds."); - uint32_t match = _R(r_pos); - r_pos += 4; - switch (match) { - case 0x00000000: { - r_out += "exists"; - } break; - case 0x00000001: { - r_out += "= "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000002: { - r_out += "~ "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000003: { - r_out += "= *"; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000004: { - r_out += "= "; - _parse_value(r_pos, r_out, p_rq_size); - r_out += "*"; - } break; - case 0x00000005: { - r_out += "< "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000006: { - r_out += "> "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000007: { - r_out += "<= "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000008: { - r_out += ">= "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000009: { - r_out += "= "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000A: { - r_out += "< "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000B: { - r_out += "> "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000C: { - r_out += "<= "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000D: { - r_out += ">= "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000E: { - r_out += "absent"; - } break; - default: { - return false; - } - } - return true; -#undef _R -} - -Vector<String> CodeSignRequirements::parse_requirements() const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - Vector<String> list; - - // Read requirements set header. - ERR_FAIL_COND_V_MSG(blob.size() < 12, list, "CodeSign/Requirements: Blob is too small."); - uint32_t magic = _R(0); - ERR_FAIL_COND_V_MSG(magic != 0xfade0c01, list, "CodeSign/Requirements: Invalid set magic."); - uint32_t size = _R(4); - ERR_FAIL_COND_V_MSG(size != (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid set size."); - uint32_t count = _R(8); - - for (uint32_t i = 0; i < count; i++) { - String out; - - // Read requirement header. - uint32_t rq_type = _R(12 + i * 8); - uint32_t rq_offset = _R(12 + i * 8 + 4); - ERR_FAIL_COND_V_MSG(rq_offset + 12 >= (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid requirement offset."); - switch (rq_type) { - case 0x00000001: { - out += "host => "; - } break; - case 0x00000002: { - out += "guest => "; - } break; - case 0x00000003: { - out += "designated => "; - } break; - case 0x00000004: { - out += "library => "; - } break; - case 0x00000005: { - out += "plugin => "; - } break; - default: { - ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement type."); - } - } - uint32_t rq_magic = _R(rq_offset); - uint32_t rq_size = _R(rq_offset + 4); - uint32_t rq_ver = _R(rq_offset + 8); - uint32_t pos = rq_offset + 12; - ERR_FAIL_COND_V_MSG(rq_magic != 0xfade0c00, list, "CodeSign/Requirements: Invalid requirement magic."); - ERR_FAIL_COND_V_MSG(rq_ver != 0x00000001, list, "CodeSign/Requirements: Invalid requirement version."); - - // Read requirement tokens. - List<String> tokens; - while (pos < rq_offset + rq_size) { - uint32_t rq_tag = _R(pos); - pos += 4; - String token; - switch (rq_tag) { - case 0x00000000: { - token = "false"; - } break; - case 0x00000001: { - token = "true"; - } break; - case 0x00000002: { - token = "identifier "; - _parse_value(pos, token, rq_offset + rq_size); - } break; - case 0x00000003: { - token = "anchor apple"; - } break; - case 0x00000004: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - token += " "; - _parse_hash_string(pos, token, rq_offset + rq_size); - } break; - case 0x00000005: { - token = "info"; - _parse_key(pos, token, rq_offset + rq_size); - token += " = "; - _parse_value(pos, token, rq_offset + rq_size); - } break; - case 0x00000006: { - token = "and"; - } break; - case 0x00000007: { - token = "or"; - } break; - case 0x00000008: { - token = "cdhash "; - _parse_hash_string(pos, token, rq_offset + rq_size); - } break; - case 0x00000009: { - token = "!"; - } break; - case 0x0000000A: { - token = "info"; - _parse_key(pos, token, rq_offset + rq_size); - token += " "; - ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix."); - } break; - case 0x0000000B: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - _parse_key(pos, token, rq_offset + rq_size); - token += " "; - ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix."); - } break; - case 0x0000000C: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - token += " trusted"; - } break; - case 0x0000000D: { - token = "anchor trusted"; - } break; - case 0x0000000E: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - _parse_oid_key(pos, token, rq_offset + rq_size); - token += " "; - ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix."); - } break; - case 0x0000000F: { - token = "anchor apple generic"; - } break; - default: { - ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement token."); - } break; - } - tokens.push_back(token); - } - - // Polish to infix notation (w/o bracket optimization). - for (List<String>::Element *E = tokens.back(); E; E = E->prev()) { - if (E->get() == "and") { - ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence."); - String token = "(" + E->next()->get() + " and " + E->next()->next()->get() + ")"; - tokens.erase(E->next()->next()); - tokens.erase(E->next()); - E->get() = token; - } else if (E->get() == "or") { - ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence."); - String token = "(" + E->next()->get() + " or " + E->next()->next()->get() + ")"; - tokens.erase(E->next()->next()); - tokens.erase(E->next()); - E->get() = token; - } - } - - if (tokens.size() == 1) { - list.push_back(out + tokens.front()->get()); - } else { - ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid token sequence."); - } - } - - return list; -#undef _R -} - -PackedByteArray CodeSignRequirements::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignRequirements::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignRequirements::get_size() const { - return blob.size(); -} - -void CodeSignRequirements::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Requirements: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignEntitlementsText */ -/*************************************************************************/ - -CodeSignEntitlementsText::CodeSignEntitlementsText() { - blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes). -} - -CodeSignEntitlementsText::CodeSignEntitlementsText(const String &p_string) { - CharString utf8 = p_string.utf8(); - blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic. - for (int i = 3; i >= 0; i--) { - uint8_t x = ((utf8.length() + 8) >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } - for (int i = 0; i < utf8.length(); i++) { // Write data. - blob.push_back(utf8[i]); - } -} - -PackedByteArray CodeSignEntitlementsText::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignEntitlementsText::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignEntitlementsText::get_size() const { - return blob.size(); -} - -void CodeSignEntitlementsText::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsText: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignEntitlementsBinary */ -/*************************************************************************/ - -CodeSignEntitlementsBinary::CodeSignEntitlementsBinary() { - blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes). -} - -CodeSignEntitlementsBinary::CodeSignEntitlementsBinary(const String &p_string) { - PList pl = PList(p_string); - - PackedByteArray asn1 = pl.save_asn1(); - blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic. - uint32_t size = asn1.size() + 8; - for (int i = 3; i >= 0; i--) { - uint8_t x = (size >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } - blob.append_array(asn1); // Write data. -} - -PackedByteArray CodeSignEntitlementsBinary::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignEntitlementsBinary::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignEntitlementsBinary::get_size() const { - return blob.size(); -} - -void CodeSignEntitlementsBinary::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsBinary: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignCodeDirectory */ -/*************************************************************************/ - -CodeSignCodeDirectory::CodeSignCodeDirectory() { - blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Size (8 bytes). -} - -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) { - pages = p_code_limit / (uint64_t(1) << p_page_size); - remain = p_code_limit % (uint64_t(1) << p_page_size); - code_slots = pages + (remain > 0 ? 1 : 0); - special_slots = 7; - - int cd_size = 8 + sizeof(CodeDirectoryHeader) + (code_slots + special_slots) * p_hash_size + p_id.size() + p_team_id.size(); - int cd_off = 8 + sizeof(CodeDirectoryHeader); - blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic. - for (int i = 3; i >= 0; i--) { - uint8_t x = (cd_size >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } - blob.resize(cd_size); - memset(blob.ptrw() + 8, 0x00, cd_size - 8); - CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8); - - bool is_64_cl = (p_code_limit >= std::numeric_limits<uint32_t>::max()); - - // Version and options. - cd->version = BSWAP32(0x20500); - cd->flags = BSWAP32(SIGNATURE_ADHOC | SIGNATURE_RUNTIME); - cd->special_slots = BSWAP32(special_slots); - cd->code_slots = BSWAP32(code_slots); - if (is_64_cl) { - cd->code_limit_64 = BSWAP64(p_code_limit); - } else { - cd->code_limit = BSWAP32(p_code_limit); - } - cd->hash_size = p_hash_size; - cd->hash_type = p_hash_type; - cd->page_size = p_page_size; - cd->exec_seg_base = 0x00; - cd->exec_seg_limit = BSWAP64(p_exe_limit); - cd->exec_seg_flags = 0; - if (p_main) { - cd->exec_seg_flags |= EXECSEG_MAIN_BINARY; - } - cd->exec_seg_flags = BSWAP64(cd->exec_seg_flags); - uint32_t version = (11 << 16) + (3 << 8) + 0; // Version 11.3.0 - cd->runtime = BSWAP32(version); - - // Copy ID. - cd->ident_offset = BSWAP32(cd_off); - memcpy(blob.ptrw() + cd_off, p_id.get_data(), p_id.size()); - cd_off += p_id.size(); - - // Copy Team ID. - if (p_team_id.length() > 0) { - cd->team_offset = BSWAP32(cd_off); - memcpy(blob.ptrw() + cd_off, p_team_id.get_data(), p_team_id.size()); - cd_off += p_team_id.size(); - } else { - cd->team_offset = 0; - } - - // Scatter vector. - cd->scatter_vector_offset = 0; // Not used. - - // Executable hashes offset. - cd->hash_offset = BSWAP32(cd_off + special_slots * cd->hash_size); -} - -bool CodeSignCodeDirectory::set_hash_in_slot(const PackedByteArray &p_hash, int p_slot) { - ERR_FAIL_COND_V_MSG((p_slot < -special_slots) || (p_slot >= code_slots), false, vformat("CodeSign/CodeDirectory: Invalid hash slot index: %d.", p_slot)); - CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8); - for (int i = 0; i < cd->hash_size; i++) { - blob.write[BSWAP32(cd->hash_offset) + p_slot * cd->hash_size + i] = p_hash[i]; - } - return true; -} - -int32_t CodeSignCodeDirectory::get_page_count() { - return pages; -} - -int32_t CodeSignCodeDirectory::get_page_remainder() { - return remain; -} - -PackedByteArray CodeSignCodeDirectory::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignCodeDirectory::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignCodeDirectory::get_size() const { - return blob.size(); -} - -void CodeSignCodeDirectory::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/CodeDirectory: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignSignature */ -/*************************************************************************/ - -CodeSignSignature::CodeSignSignature() { - blob.append_array({ 0xFA, 0xDE, 0x0B, 0x01 }); // Signature magic. - uint32_t sign_size = 8; // Ad-hoc signature is empty. - for (int i = 3; i >= 0; i--) { - uint8_t x = (sign_size >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } -} - -PackedByteArray CodeSignSignature::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignSignature::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignSignature::get_size() const { - return blob.size(); -} - -void CodeSignSignature::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Signature: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignSuperBlob */ -/*************************************************************************/ - -bool CodeSignSuperBlob::add_blob(const Ref<CodeSignBlob> &p_blob) { - if (p_blob.is_valid()) { - blobs.push_back(p_blob); - return true; - } else { - return false; - } -} - -int CodeSignSuperBlob::get_size() const { - int size = 12 + blobs.size() * 8; - for (int i = 0; i < blobs.size(); i++) { - if (blobs[i].is_null()) { - return 0; - } - size += blobs[i]->get_size(); - } - return size; -} - -void CodeSignSuperBlob::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/SuperBlob: Invalid file handle."); - uint32_t size = get_size(); - uint32_t data_offset = 12 + blobs.size() * 8; - - // Write header. - p_file->store_32(BSWAP32(0xfade0cc0)); - p_file->store_32(BSWAP32(size)); - p_file->store_32(BSWAP32(blobs.size())); - - // Write index. - for (int i = 0; i < blobs.size(); i++) { - if (blobs[i].is_null()) { - return; - } - p_file->store_32(BSWAP32(blobs[i]->get_index_type())); - p_file->store_32(BSWAP32(data_offset)); - data_offset += blobs[i]->get_size(); - } - - // Write blobs. - for (int i = 0; i < blobs.size(); i++) { - blobs[i]->write_to_file(p_file); - } -} - -/*************************************************************************/ -/* CodeSign */ -/*************************************************************************/ - -PackedByteArray CodeSign::file_hash_sha1(const String &p_path) { - PackedByteArray file_hash; - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA1Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = f->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - file_hash.resize(0x14); - ctx.finish(file_hash.ptrw()); - return file_hash; -} - -PackedByteArray CodeSign::file_hash_sha256(const String &p_path) { - PackedByteArray file_hash; - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA256Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = f->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - file_hash.resize(0x20); - ctx.finish(file_hash.ptrw()); - return file_hash; -} - -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) { -#define CLEANUP() \ - if (files_to_sign.size() > 1) { \ - for (int j = 0; j < files_to_sign.size(); j++) { \ - da->remove(files_to_sign[j]); \ - } \ - } - - print_verbose(vformat("CodeSign: Signing executable: %s, bundle: %s with entitlements %s", p_exe_path, p_bundle_path, p_ent_path)); - - PackedByteArray info_hash1, info_hash2; - PackedByteArray res_hash1, res_hash2; - String id; - String main_exe = p_exe_path; - - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da.is_null()) { - r_error_msg = TTR("Can't get filesystem access."); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access."); - } - - // Read Info.plist. - if (!p_info.is_empty()) { - print_verbose(vformat("CodeSign: Reading bundle info...")); - PList info_plist; - if (info_plist.load_file(p_info)) { - info_hash1 = file_hash_sha1(p_info); - info_hash2 = file_hash_sha256(p_info); - if (info_hash1.is_empty() || info_hash2.is_empty()) { - r_error_msg = TTR("Failed to get Info.plist hash."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get Info.plist hash."); - } - - if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) { - main_exe = p_exe_path.plus_file(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data())); - } else { - r_error_msg = TTR("Invalid Info.plist, no exe name."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no exe name."); - } - - if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleIdentifier")) { - id = info_plist.get_root()->data_dict["CFBundleIdentifier"]->data_string.get_data(); - } else { - r_error_msg = TTR("Invalid Info.plist, no bundle id."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no bundle id."); - } - } else { - r_error_msg = TTR("Invalid Info.plist, can't load."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, can't load."); - } - } - - // Extract fat binary. - Vector<String> files_to_sign; - if (LipO::is_lipo(main_exe)) { - print_verbose(vformat("CodeSign: Executable is fat, extracting...")); - String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo"); - Error err = da->make_dir_recursive(tmp_path_name); - if (err != OK) { - r_error_msg = vformat(TTR("Failed to create \"%s\" subfolder."), tmp_path_name); - ERR_FAIL_V_MSG(FAILED, vformat("CodeSign: Failed to create \"%s\" subfolder.", tmp_path_name)); - } - LipO lip; - if (lip.open_file(main_exe)) { - for (int i = 0; i < lip.get_arch_count(); i++) { - if (!lip.extract_arch(i, tmp_path_name.plus_file("_exe_" + itos(i)))) { - CLEANUP(); - r_error_msg = TTR("Failed to extract thin binary."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to extract thin binary."); - } - files_to_sign.push_back(tmp_path_name.plus_file("_exe_" + itos(i))); - } - } - } else if (MachO::is_macho(main_exe)) { - print_verbose(vformat("CodeSign: Executable is thin...")); - files_to_sign.push_back(main_exe); - } else { - r_error_msg = TTR("Invalid binary format."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid binary format."); - } - - // Check if it's already signed. - if (!p_force) { - for (int i = 0; i < files_to_sign.size(); i++) { - MachO mh; - mh.open_file(files_to_sign[i]); - if (mh.is_signed()) { - CLEANUP(); - r_error_msg = TTR("Already signed!"); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Already signed!"); - } - } - } - - // Generate core resources. - if (!p_bundle_path.is_empty()) { - print_verbose(vformat("CodeSign: Generating bundle CodeResources...")); - CodeSignCodeResources cr; - - if (p_ios_bundle) { - cr.add_rule1("^.*"); - cr.add_rule1("^.*\\.lproj/", "optional", 100); - cr.add_rule1("^.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule1("^Base\\.lproj/", "", 1010); - cr.add_rule1("^version.plist$"); - - cr.add_rule2(".*\\.dSYM($|/)", "", 11); - cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000); - cr.add_rule2("^.*"); - cr.add_rule2("^.*\\.lproj/", "optional", 1000); - cr.add_rule2("^.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule2("^Base\\.lproj/", "", 1010); - cr.add_rule2("^Info\\.plist$", "omit", 20); - cr.add_rule2("^PkgInfo$", "omit", 20); - cr.add_rule2("^embedded\\.provisionprofile$", "", 10); - cr.add_rule2("^version\\.plist$", "", 20); - - cr.add_rule2("^_MASReceipt", "omit", 2000, false); - cr.add_rule2("^_CodeSignature", "omit", 2000, false); - cr.add_rule2("^CodeResources", "omit", 2000, false); - } else { - cr.add_rule1("^Resources/"); - cr.add_rule1("^Resources/.*\\.lproj/", "optional", 1000); - cr.add_rule1("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule1("^Resources/Base\\.lproj/", "", 1010); - cr.add_rule1("^version.plist$"); - - cr.add_rule2(".*\\.dSYM($|/)", "", 11); - cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000); - cr.add_rule2("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", "nested", 10); - cr.add_rule2("^.*"); - cr.add_rule2("^Info\\.plist$", "omit", 20); - cr.add_rule2("^PkgInfo$", "omit", 20); - cr.add_rule2("^Resources/", "", 20); - cr.add_rule2("^Resources/.*\\.lproj/", "optional", 1000); - cr.add_rule2("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule2("^Resources/Base\\.lproj/", "", 1010); - cr.add_rule2("^[^/]+$", "nested", 10); - cr.add_rule2("^embedded\\.provisionprofile$", "", 10); - cr.add_rule2("^version\\.plist$", "", 20); - cr.add_rule2("^_MASReceipt", "omit", 2000, false); - cr.add_rule2("^_CodeSignature", "omit", 2000, false); - cr.add_rule2("^CodeResources", "omit", 2000, false); - } - - if (!cr.add_folder_recursive(p_bundle_path, "", main_exe)) { - CLEANUP(); - r_error_msg = TTR("Failed to process nested resources."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to process nested resources."); - } - Error err = da->make_dir_recursive(p_bundle_path.plus_file("_CodeSignature")); - if (err != OK) { - CLEANUP(); - r_error_msg = TTR("Failed to create _CodeSignature subfolder."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create _CodeSignature subfolder."); - } - cr.save_to_file(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources")); - res_hash1 = file_hash_sha1(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources")); - res_hash2 = file_hash_sha256(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources")); - if (res_hash1.is_empty() || res_hash2.is_empty()) { - CLEANUP(); - r_error_msg = TTR("Failed to get CodeResources hash."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get CodeResources hash."); - } - } - - // Generate common signature structures. - if (id.is_empty()) { - CryptoCore::RandomGenerator rng; - ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator."); - uint8_t uuid[16]; - Error err = rng.get_random_bytes(uuid, 16); - ERR_FAIL_COND_V_MSG(err, err, "Failed to generate UUID."); - id = (String("a-55554944") /*a-UUID*/ + String::hex_encode_buffer(uuid, 16)); - } - CharString uuid_str = id.utf8(); - print_verbose(vformat("CodeSign: Used bundle ID: %s", id)); - - print_verbose(vformat("CodeSign: Processing entitlements...")); - - Ref<CodeSignEntitlementsText> cet; - Ref<CodeSignEntitlementsBinary> ceb; - if (!p_ent_path.is_empty()) { - String entitlements = FileAccess::get_file_as_string(p_ent_path); - if (entitlements.is_empty()) { - CLEANUP(); - r_error_msg = TTR("Invalid entitlements file."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file."); - } - cet = Ref<CodeSignEntitlementsText>(memnew(CodeSignEntitlementsText(entitlements))); - ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements))); - } - - print_verbose(vformat("CodeSign: Generating requirements...")); - Ref<CodeSignRequirements> rq; - String team_id = ""; - rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements())); - - // Sign executables. - for (int i = 0; i < files_to_sign.size(); i++) { - MachO mh; - if (!mh.open_file(files_to_sign[i])) { - CLEANUP(); - r_error_msg = TTR("Invalid executable file."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid executable file."); - } - print_verbose(vformat("CodeSign: Signing executable for cputype: %d ...", mh.get_cputype())); - - print_verbose(vformat("CodeSign: Generating CodeDirectory...")); - Ref<CodeSignCodeDirectory> cd1 = memnew(CodeSignCodeDirectory(0x14, 0x01, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit())); - Ref<CodeSignCodeDirectory> cd2 = memnew(CodeSignCodeDirectory(0x20, 0x02, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit())); - print_verbose(vformat("CodeSign: Calculating special slot hashes...")); - if (info_hash2.size() == 0x20) { - cd2->set_hash_in_slot(info_hash2, CodeSignCodeDirectory::SLOT_INFO_PLIST); - } - if (info_hash1.size() == 0x14) { - cd1->set_hash_in_slot(info_hash1, CodeSignCodeDirectory::SLOT_INFO_PLIST); - } - cd1->set_hash_in_slot(rq->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS); - cd2->set_hash_in_slot(rq->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS); - if (res_hash2.size() == 0x20) { - cd2->set_hash_in_slot(res_hash2, CodeSignCodeDirectory::SLOT_RESOURCES); - } - if (res_hash1.size() == 0x14) { - cd1->set_hash_in_slot(res_hash1, CodeSignCodeDirectory::SLOT_RESOURCES); - } - if (cet.is_valid()) { - cd1->set_hash_in_slot(cet->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); //Text variant. - cd2->set_hash_in_slot(cet->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); - } - if (ceb.is_valid()) { - cd1->set_hash_in_slot(ceb->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); //ASN.1 variant. - cd2->set_hash_in_slot(ceb->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); - } - - // Calculate signature size. - int sign_size = 12; // SuperBlob header. - sign_size += cd1->get_size() + 8; - sign_size += cd2->get_size() + 8; - sign_size += rq->get_size() + 8; - if (cet.is_valid()) { - sign_size += cet->get_size() + 8; - } - if (ceb.is_valid()) { - sign_size += ceb->get_size() + 8; - } - sign_size += 16; // Empty signature size. - - // Alloc/resize signature load command. - print_verbose(vformat("CodeSign: Reallocating space for the signature superblob (%d)...", sign_size)); - if (!mh.set_signature_size(sign_size)) { - CLEANUP(); - r_error_msg = TTR("Can't resize signature load command."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Can't resize signature load command."); - } - - print_verbose(vformat("CodeSign: Calculating executable code hashes...")); - // Calculate executable code hashes. - PackedByteArray buffer; - PackedByteArray hash1, hash2; - hash1.resize(0x14); - hash2.resize(0x20); - buffer.resize(1 << 12); - mh.get_file()->seek(0); - for (int32_t j = 0; j < cd2->get_page_count(); j++) { - mh.get_file()->get_buffer(buffer.ptrw(), (1 << 12)); - CryptoCore::SHA256Context ctx2; - ctx2.start(); - ctx2.update(buffer.ptr(), (1 << 12)); - ctx2.finish(hash2.ptrw()); - cd2->set_hash_in_slot(hash2, j); - - CryptoCore::SHA1Context ctx1; - ctx1.start(); - ctx1.update(buffer.ptr(), (1 << 12)); - ctx1.finish(hash1.ptrw()); - cd1->set_hash_in_slot(hash1, j); - } - if (cd2->get_page_remainder() > 0) { - mh.get_file()->get_buffer(buffer.ptrw(), cd2->get_page_remainder()); - CryptoCore::SHA256Context ctx2; - ctx2.start(); - ctx2.update(buffer.ptr(), cd2->get_page_remainder()); - ctx2.finish(hash2.ptrw()); - cd2->set_hash_in_slot(hash2, cd2->get_page_count()); - - CryptoCore::SHA1Context ctx1; - ctx1.start(); - ctx1.update(buffer.ptr(), cd1->get_page_remainder()); - ctx1.finish(hash1.ptrw()); - cd1->set_hash_in_slot(hash1, cd1->get_page_count()); - } - - print_verbose(vformat("CodeSign: Generating signature...")); - Ref<CodeSignSignature> cs; - cs = Ref<CodeSignSignature>(memnew(CodeSignSignature())); - - print_verbose(vformat("CodeSign: Writing signature superblob...")); - // Write signature data to the executable. - CodeSignSuperBlob sb = CodeSignSuperBlob(); - sb.add_blob(cd2); - sb.add_blob(cd1); - sb.add_blob(rq); - if (cet.is_valid()) { - sb.add_blob(cet); - } - if (ceb.is_valid()) { - sb.add_blob(ceb); - } - sb.add_blob(cs); - mh.get_file()->seek(mh.get_signature_offset()); - sb.write_to_file(mh.get_file()); - } - if (files_to_sign.size() > 1) { - print_verbose(vformat("CodeSign: Rebuilding fat executable...")); - LipO lip; - if (!lip.create_file(main_exe, files_to_sign)) { - CLEANUP(); - r_error_msg = TTR("Failed to create fat binary."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create fat binary."); - } - CLEANUP(); - } - FileAccess::set_unix_permissions(main_exe, 0755); // Restore unix permissions. - return OK; -#undef CLEANUP -} - -Error CodeSign::codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da.is_null()) { - r_error_msg = TTR("Can't get filesystem access."); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access."); - } - - if (da->dir_exists(p_path)) { - String fmw_ver = "Current"; // Framework version (default). - String info_path; - String main_exe; - String bundle_path; - bool bundle = false; - bool ios_bundle = false; - if (da->file_exists(p_path.plus_file("Contents/Info.plist"))) { - info_path = p_path.plus_file("Contents/Info.plist"); - main_exe = p_path.plus_file("Contents/MacOS"); - bundle_path = p_path.plus_file("Contents"); - bundle = true; - } else if (da->file_exists(p_path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) { - info_path = p_path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)); - main_exe = p_path.plus_file(vformat("Versions/%s", fmw_ver)); - bundle_path = p_path.plus_file(vformat("Versions/%s", fmw_ver)); - bundle = true; - } else if (da->file_exists(p_path.plus_file("Info.plist"))) { - info_path = p_path.plus_file("Info.plist"); - main_exe = p_path; - bundle_path = p_path; - bundle = true; - ios_bundle = true; - } - if (bundle) { - return _codesign_file(p_use_hardened_runtime, p_force, info_path, main_exe, bundle_path, p_ent_path, ios_bundle, r_error_msg); - } else { - r_error_msg = TTR("Unknown bundle type."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown bundle type."); - } - } else if (da->file_exists(p_path)) { - return _codesign_file(p_use_hardened_runtime, p_force, "", p_path, "", p_ent_path, false, r_error_msg); - } else { - r_error_msg = TTR("Unknown object type."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown object type."); - } -} - -#endif // MODULE_REGEX_ENABLED |