Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store sensitive export options in dedicated credentials file #76165

Merged
merged 1 commit into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/core_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ void register_global_constants() {
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_BASIC_SETTING);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_READ_ONLY);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SECRET);

BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFAULT);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_EDITOR);
Expand Down
1 change: 1 addition & 0 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected.
PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector.
PROPERTY_USAGE_SECRET = 1 << 29, // Export preset credentials that should be stored separately from the rest of the export config.
and-rad marked this conversation as resolved.
Show resolved Hide resolved

PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2842,6 +2842,9 @@
<constant name="PROPERTY_USAGE_READ_ONLY" value="268435456" enum="PropertyUsageFlags" is_bitfield="true">
The property is read-only in the [EditorInspector].
</constant>
<constant name="PROPERTY_USAGE_SECRET" value="536870912" enum="PropertyUsageFlags" is_bitfield="true">
An export preset property with this flag contains confidential information and is stored separately from the rest of the export preset configuration.
</constant>
<constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags" is_bitfield="true">
Default usage (storage, editor and network).
</constant>
Expand Down
34 changes: 28 additions & 6 deletions editor/export/editor_export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ EditorExport *EditorExport::singleton = nullptr;

void EditorExport::_save() {
Ref<ConfigFile> config;
Ref<ConfigFile> credentials;
config.instantiate();
credentials.instantiate();
for (int i = 0; i < export_presets.size(); i++) {
Ref<EditorExportPreset> preset = export_presets[i];
String section = "preset." + itos(i);
Expand Down Expand Up @@ -83,16 +85,21 @@ void EditorExport::_save() {
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
config->set_value(section, "encrypt_pck", preset->get_enc_pck());
config->set_value(section, "encrypt_directory", preset->get_enc_directory());
config->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
credentials->set_value(section, "script_encryption_key", preset->get_script_encryption_key());

String option_section = "preset." + itos(i) + ".options";

for (const PropertyInfo &E : preset->get_properties()) {
config->set_value(option_section, E.name, preset->get(E.name));
if (E.usage & PROPERTY_USAGE_SECRET) {
credentials->set_value(option_section, E.name, preset->get(E.name));
} else {
config->set_value(option_section, E.name, preset->get(E.name));
}
}
}

config->save("res://export_presets.cfg");
credentials->save("res://.godot/export_credentials.cfg");
}

void EditorExport::save_presets() {
Expand Down Expand Up @@ -202,6 +209,13 @@ void EditorExport::load_config() {
return;
}

Ref<ConfigFile> credentials;
credentials.instantiate();
err = credentials->load("res://.godot/export_credentials.cfg");
if (!(err == OK || err == ERR_FILE_NOT_FOUND)) {
return;
}

block_save = true;

int index = 0;
Expand Down Expand Up @@ -284,22 +298,30 @@ void EditorExport::load_config() {
if (config->has_section_key(section, "encryption_exclude_filters")) {
preset->set_enc_ex_filter(config->get_value(section, "encryption_exclude_filters"));
}
if (config->has_section_key(section, "script_encryption_key")) {
preset->set_script_encryption_key(config->get_value(section, "script_encryption_key"));
if (credentials->has_section_key(section, "script_encryption_key")) {
preset->set_script_encryption_key(credentials->get_value(section, "script_encryption_key"));
}

String option_section = "preset." + itos(index) + ".options";

List<String> options;

config->get_section_keys(option_section, &options);

for (const String &E : options) {
Variant value = config->get_value(option_section, E);

preset->set(E, value);
}

if (credentials->has_section(option_section)) {
options.clear();
credentials->get_section_keys(option_section, &options);

for (const String &E : options) {
Variant value = credentials->get_value(option_section, E);
preset->set(E, value);
}
}

add_export_preset(preset);
index++;
}
Expand Down
12 changes: 10 additions & 2 deletions editor/export/editor_export_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,14 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector
return save_path.is_empty() ? p_path : save_path;
}

String EditorExportPlatform::_get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const {
const String from_env = OS::get_singleton()->get_environment(ENV_SCRIPT_ENCRYPTION_KEY);
if (!from_env.is_empty()) {
return from_env.to_lower();
}
return p_preset->get_script_encryption_key().to_lower();
}

Vector<String> EditorExportPlatform::get_forced_export_files() {
Vector<String> files;

Expand Down Expand Up @@ -946,7 +954,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}

// Get encryption key.
String script_key = p_preset->get_script_encryption_key().to_lower();
String script_key = _get_script_encryption_key(p_preset);
key.resize(32);
if (script_key.length() == 64) {
for (int i = 0; i < 32; i++) {
Expand Down Expand Up @@ -1577,7 +1585,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
Ref<FileAccess> fhead = f;

if (enc_pck && enc_directory) {
String script_key = p_preset->get_script_encryption_key().to_lower();
String script_key = _get_script_encryption_key(p_preset);
Vector<uint8_t> key;
key.resize(32);
if (script_key.length() == 64) {
Expand Down
3 changes: 3 additions & 0 deletions editor/export/editor_export_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ struct EditorProgress;

class EditorExportPlugin;

const String ENV_SCRIPT_ENCRYPTION_KEY = "GODOT_SCRIPT_ENCRYPTION_KEY";

class EditorExportPlatform : public RefCounted {
GDCLASS(EditorExportPlatform, RefCounted);

Expand Down Expand Up @@ -116,6 +118,7 @@ class EditorExportPlatform : public RefCounted {
bool _is_editable_ancestor(Node *p_root, Node *p_node);

String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save);
String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const;

protected:
struct ExportNotifier {
Expand Down
11 changes: 11 additions & 0 deletions editor/export/editor_export_preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,15 @@ String EditorExportPreset::get_script_encryption_key() const {
return script_key;
}

Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid) const {
const String from_env = OS::get_singleton()->get_environment(p_env_var);
if (!from_env.is_empty()) {
if (r_valid) {
*r_valid = true;
}
return from_env;
}
return get(p_name, r_valid);
}

EditorExportPreset::EditorExportPreset() {}
2 changes: 2 additions & 0 deletions editor/export/editor_export_preset.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ class EditorExportPreset : public RefCounted {
void set_script_encryption_key(const String &p_key);
String get_script_encryption_key() const;

Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;

const List<PropertyInfo> &get_properties() const { return properties; }

EditorExportPreset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,27 @@
</member>
<member name="keystore/debug" type="String" setter="" getter="">
Path of the debug keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PATH[/code].
</member>
<member name="keystore/debug_password" type="String" setter="" getter="">
Password for the debug keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD[/code].
</member>
<member name="keystore/debug_user" type="String" setter="" getter="">
User name for the debug keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_USER[/code].
</member>
<member name="keystore/release" type="String" setter="" getter="">
Path of the release keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PATH[/code].
</member>
<member name="keystore/release_password" type="String" setter="" getter="">
Password for the release keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD[/code].
</member>
<member name="keystore/release_user" type="String" setter="" getter="">
User name for the release keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_USER[/code].
</member>
<member name="launcher_icons/adaptive_background_432x432" type="String" setter="" getter="">
Background layer of the application adaptive icon file.
Expand Down
48 changes: 24 additions & 24 deletions platform/android/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1825,12 +1825,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), abi)), is_default));
}

r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));

r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
Expand Down Expand Up @@ -2277,9 +2277,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito

// Validate the rest of the export configuration.

String dk = p_preset->get("keystore/debug");
String dk_user = p_preset->get("keystore/debug_user");
String dk_password = p_preset->get("keystore/debug_password");
String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);

if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) {
valid = false;
Expand All @@ -2294,9 +2294,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
}

String rk = p_preset->get("keystore/release");
String rk_user = p_preset->get("keystore/release_user");
String rk_password = p_preset->get("keystore/release_password");
String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);

if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) {
valid = false;
Expand Down Expand Up @@ -2507,9 +2507,9 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
String release_keystore = p_preset->get("keystore/release");
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
Expand All @@ -2529,9 +2529,9 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
String password;
String user;
if (p_debug) {
keystore = p_preset->get("keystore/debug");
password = p_preset->get("keystore/debug_password");
user = p_preset->get("keystore/debug_user");
keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);

if (keystore.is_empty()) {
keystore = EDITOR_GET("export/android/debug_keystore");
Expand Down Expand Up @@ -2886,9 +2886,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP

if (should_sign) {
if (p_debug) {
String debug_keystore = p_preset->get("keystore/debug");
String debug_password = p_preset->get("keystore/debug_password");
String debug_user = p_preset->get("keystore/debug_user");
String debug_keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
String debug_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
String debug_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);

if (debug_keystore.is_empty()) {
debug_keystore = EDITOR_GET("export/android/debug_keystore");
Expand All @@ -2908,9 +2908,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password.
} else {
// Pass the release keystore info as well
String release_keystore = p_preset->get("keystore/release");
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if (release_keystore.is_relative_path()) {
release_keystore = OS::get_singleton()->get_resource_dir().path_join(release_keystore).simplify_path();
}
Expand Down
9 changes: 9 additions & 0 deletions platform/android/export/export_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="
</layer-list>
)SPLASH";

// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_ANDROID_KEYSTORE_DEBUG_PATH = "GODOT_ANDROID_KEYSTORE_DEBUG_PATH";
const String ENV_ANDROID_KEYSTORE_DEBUG_USER = "GODOT_ANDROID_KEYSTORE_DEBUG_USER";
const String ENV_ANDROID_KEYSTORE_DEBUG_PASS = "GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD";
const String ENV_ANDROID_KEYSTORE_RELEASE_PATH = "GODOT_ANDROID_KEYSTORE_RELEASE_PATH";
const String ENV_ANDROID_KEYSTORE_RELEASE_USER = "GODOT_ANDROID_KEYSTORE_RELEASE_USER";
const String ENV_ANDROID_KEYSTORE_RELEASE_PASS = "GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD";

struct LauncherIcon {
const char *export_path;
int dimensions = 0;
Expand Down
2 changes: 2 additions & 0 deletions platform/ios/doc_classes/EditorExportPlatformIOS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
</member>
<member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG[/code].
</member>
<member name="application/provisioning_profile_uuid_release" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code].
</member>
<member name="application/short_version" type="String" setter="" getter="">
Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
Expand Down
Loading