Skip to content

Commit

Permalink
Merge pull request #76165 from and-rad/safe-credentials
Browse files Browse the repository at this point in the history
Store sensitive export options in dedicated credentials file
  • Loading branch information
akien-mga committed May 10, 2023
2 parents 5271186 + fab160c commit 74e5ad5
Show file tree
Hide file tree
Showing 22 changed files with 211 additions and 105 deletions.
1 change: 1 addition & 0 deletions core/core_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,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 @@ -119,6 +119,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.

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 @@ -2845,6 +2845,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
6 changes: 6 additions & 0 deletions platform/android/doc_classes/EditorExportPlatformAndroid.xml
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

0 comments on commit 74e5ad5

Please sign in to comment.