Pārlūkot izejas kodu

Merge pull request #32556 from bruvzg/win_codesign

Code signing support for Windows exports
Rémi Verschelde 6 gadi atpakaļ
vecāks
revīzija
abd81dcb73

+ 7 - 0
editor/editor_export.cpp

@@ -1606,6 +1606,9 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr
 			da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 			da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 			for (int i = 0; i < so_files.size() && err == OK; i++) {
 			for (int i = 0; i < so_files.size() && err == OK; i++) {
 				err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
 				err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
+				if (err == OK) {
+					err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
+				}
 			}
 			}
 			memdelete(da);
 			memdelete(da);
 		}
 		}
@@ -1614,6 +1617,10 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr
 	return err;
 	return err;
 }
 }
 
 
+Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
+	return OK;
+}
+
 void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) {
 void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) {
 	extensions[p_feature_key] = p_extension;
 	extensions[p_feature_key] = p_extension;
 }
 }

+ 1 - 0
editor/editor_export.h

@@ -423,6 +423,7 @@ public:
 	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
 	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
 	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;
 	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
+	virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 
 
 	void set_extension(const String &p_extension, const String &p_feature_key = "default");
 	void set_extension(const String &p_extension, const String &p_feature_key = "default");
 	void set_name(const String &p_name);
 	void set_name(const String &p_name);

+ 18 - 5
platform/osx/export/export.cpp

@@ -132,10 +132,12 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
 
 
 #ifdef OSX_ENABLED
 #ifdef OSX_ENABLED
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));
 #endif
 #endif
 
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
@@ -375,16 +377,27 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
 		args.push_back("--entitlements");
 		args.push_back("--entitlements");
 		args.push_back(p_preset->get("codesign/entitlements"));
 		args.push_back(p_preset->get("codesign/entitlements"));
 	}
 	}
+
+	PoolStringArray user_args = p_preset->get("codesign/custom_options");
+	for (int i = 0; i < user_args.size(); i++) {
+		String user_arg = user_args[i].strip_edges();
+		if (!user_arg.empty()) {
+			args.push_back(user_arg);
+		}
+	}
+
 	args.push_back("-s");
 	args.push_back("-s");
 	args.push_back(p_preset->get("codesign/identity"));
 	args.push_back(p_preset->get("codesign/identity"));
+
 	args.push_back("-v"); /* provide some more feedback */
 	args.push_back("-v"); /* provide some more feedback */
+
 	args.push_back(p_path);
 	args.push_back(p_path);
 
 
 	String str;
 	String str;
 	Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);
 	Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);
 	ERR_FAIL_COND_V(err != OK, err);
 	ERR_FAIL_COND_V(err != OK, err);
 
 
-	print_line("codesign: " + str);
+	print_line("codesign (" + p_path + "): " + str);
 	if (str.find("no identity found") != -1) {
 	if (str.find("no identity found") != -1) {
 		EditorNode::add_io_error("codesign: no identity found");
 		EditorNode::add_io_error("codesign: no identity found");
 		return FAILED;
 		return FAILED;
@@ -663,20 +676,20 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 			err = save_pack(p_preset, pack_path, &shared_objects);
 			err = save_pack(p_preset, pack_path, &shared_objects);
 
 
 			// see if we can code sign our new package
 			// see if we can code sign our new package
-			String identity = p_preset->get("codesign/identity");
+			bool sign_enabled = p_preset->get("codesign/enable");
 
 
 			if (err == OK) {
 			if (err == OK) {
 				DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 				DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 				for (int i = 0; i < shared_objects.size(); i++) {
 				for (int i = 0; i < shared_objects.size(); i++) {
 					err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
 					err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
-					if (err == OK && identity != "") {
+					if (err == OK && sign_enabled) {
 						err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
 						err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
 					}
 					}
 				}
 				}
 				memdelete(da);
 				memdelete(da);
 			}
 			}
 
 
-			if (err == OK && identity != "") {
+			if (err == OK && sign_enabled) {
 				if (ep.step("Code signing bundle", 2)) {
 				if (ep.step("Code signing bundle", 2)) {
 					return ERR_SKIP;
 					return ERR_SKIP;
 				}
 				}

+ 183 - 2
platform/windows/export/export.cpp

@@ -31,6 +31,7 @@
 #include "core/os/file_access.h"
 #include "core/os/file_access.h"
 #include "core/os/os.h"
 #include "core/os/os.h"
 #include "editor/editor_export.h"
 #include "editor/editor_export.h"
+#include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "platform/windows/logo.gen.h"
 #include "platform/windows/logo.gen.h"
 
 
@@ -38,11 +39,22 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start,
 
 
 class EditorExportPlatformWindows : public EditorExportPlatformPC {
 class EditorExportPlatformWindows : public EditorExportPlatformPC {
 
 
+	Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+
 public:
 public:
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
+	virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
 	virtual void get_export_options(List<ExportOption> *r_options);
 	virtual void get_export_options(List<ExportOption> *r_options);
 };
 };
 
 
+Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
+	if (p_preset->get("codesign/enable")) {
+		return _code_sign(p_preset, p_path);
+	} else {
+		return OK;
+	}
+}
+
 Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
 Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
 	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
 
 
@@ -133,12 +145,28 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
 	OS::get_singleton()->execute(wine_path, args, true);
 	OS::get_singleton()->execute(wine_path, args, true);
 #endif
 #endif
 
 
-	return OK;
+	if (p_preset->get("codesign/enable") && err == OK) {
+		err = _code_sign(p_preset, p_path);
+	}
+
+	return err;
 }
 }
 
 
 void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
 void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
 	EditorExportPlatformPC::get_export_options(r_options);
 	EditorExportPlatformPC::get_export_options(r_options);
 
 
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
+#ifdef WINDOWS_ENABLED
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0));
+#endif
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), ""));
+	r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));
+
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
@@ -149,11 +177,164 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
 }
 }
 
 
+Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+	List<String> args;
+
+#ifdef WINDOWS_ENABLED
+	String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool");
+	if (signtool_path != String() && !FileAccess::exists(signtool_path)) {
+		ERR_PRINTS("Could not find signtool executable at " + signtool_path + ", aborting.");
+		return ERR_FILE_NOT_FOUND;
+	}
+	if (signtool_path == String()) {
+		signtool_path = "signtool"; // try to run signtool from PATH
+	}
+#else
+	String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode");
+	if (signtool_path != String() && !FileAccess::exists(signtool_path)) {
+		ERR_PRINTS("Could not find osslsigncode executable at " + signtool_path + ", aborting.");
+		return ERR_FILE_NOT_FOUND;
+	}
+	if (signtool_path == String()) {
+		signtool_path = "osslsigncode"; // try to run signtool from PATH
+	}
+#endif
+
+	args.push_back("sign");
+
+	//identity
+#ifdef WINDOWS_ENABLED
+	int id_type = p_preset->get("codesign/identity_type");
+	if (id_type == 0) { //auto select
+		args.push_back("/a");
+	} else if (id_type == 1) { //pkcs12
+		if (p_preset->get("codesign/identity") != "") {
+			args.push_back("/f");
+			args.push_back(p_preset->get("codesign/identity"));
+		} else {
+			EditorNode::add_io_error("codesign: no identity found");
+			return FAILED;
+		}
+	} else if (id_type == 2) { //Windows certificate store
+		if (p_preset->get("codesign/identity") != "") {
+			args.push_back("/sha1");
+			args.push_back(p_preset->get("codesign/identity"));
+		} else {
+			EditorNode::add_io_error("codesign: no identity found");
+			return FAILED;
+		}
+	} else {
+		EditorNode::add_io_error("codesign: invalid identity type");
+		return FAILED;
+	}
+#else
+	if (p_preset->get("codesign/identity") != "") {
+		args.push_back("-pkcs12");
+		args.push_back(p_preset->get("codesign/identity"));
+	} else {
+		EditorNode::add_io_error("codesign: no identity found");
+		return FAILED;
+	}
+#endif
+
+	//password
+	if (p_preset->get("codesign/password") != "") {
+#ifdef WINDOWS_ENABLED
+		args.push_back("/p");
+#else
+		args.push_back("-pass");
+#endif
+		args.push_back(p_preset->get("codesign/password"));
+	}
+
+	//timestamp
+	if (p_preset->get("codesign/timestamp")) {
+		if (p_preset->get("codesign/timestamp_server") != "") {
+#ifdef WINDOWS_ENABLED
+			args.push_back("/tr");
+			args.push_back(p_preset->get("codesign/timestamp_server_url"));
+			args.push_back("/td");
+			if ((int)p_preset->get("codesign/digest_algorithm") == 0) {
+				args.push_back("sha1");
+			} else {
+				args.push_back("sha256");
+			}
+#else
+			args.push_back("-ts");
+			args.push_back(p_preset->get("codesign/timestamp_server_url"));
+#endif
+		} else {
+			EditorNode::add_io_error("codesign: invalid timestamp server");
+			return FAILED;
+		}
+	}
+
+	//digest
+#ifdef WINDOWS_ENABLED
+	args.push_back("/fd");
+#else
+	args.push_back("-h");
+#endif
+	if ((int)p_preset->get("codesign/digest_algorithm") == 0) {
+		args.push_back("sha1");
+	} else {
+		args.push_back("sha256");
+	}
+
+	//description
+	if (p_preset->get("codesign/description") != "") {
+#ifdef WINDOWS_ENABLED
+		args.push_back("/d");
+#else
+		args.push_back("-n");
+#endif
+		args.push_back(p_preset->get("codesign/description"));
+	}
+
+	//user options
+	PoolStringArray user_args = p_preset->get("codesign/custom_options");
+	for (int i = 0; i < user_args.size(); i++) {
+		String user_arg = user_args[i].strip_edges();
+		if (!user_arg.empty()) {
+			args.push_back(user_arg);
+		}
+	}
+
+#ifndef WINDOWS_ENABLED
+	args.push_back("-in");
+#endif
+	args.push_back(p_path);
+#ifndef WINDOWS_ENABLED
+	args.push_back("-out");
+	args.push_back(p_path);
+#endif
+
+	String str;
+	Error err = OS::get_singleton()->execute(signtool_path, args, true, NULL, &str, NULL, true);
+	ERR_FAIL_COND_V(err != OK, err);
+
+	print_line("codesign (" + p_path + "): " + str);
+#ifndef WINDOWS_ENABLED
+	if (str.find("SignTool Error") != -1) {
+#else
+	if (str.find("Failed") != -1) {
+#endif
+		return FAILED;
+	}
+
+	return OK;
+}
+
 void register_windows_exporter() {
 void register_windows_exporter() {
 
 
 	EDITOR_DEF("export/windows/rcedit", "");
 	EDITOR_DEF("export/windows/rcedit", "");
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
-#ifndef WINDOWS_ENABLED
+#ifdef WINDOWS_ENABLED
+	EDITOR_DEF("export/windows/signtool", "");
+	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
+#else
+	EDITOR_DEF("export/windows/osslsigncode", "");
+	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/osslsigncode", PROPERTY_HINT_GLOBAL_FILE));
 	// On non-Windows we need WINE to run rcedit
 	// On non-Windows we need WINE to run rcedit
 	EDITOR_DEF("export/windows/wine", "");
 	EDITOR_DEF("export/windows/wine", "");
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE));
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE));