summaryrefslogtreecommitdiff
path: root/editor/editor_export.cpp
diff options
context:
space:
mode:
authorbruvzg <7645683+bruvzg@users.noreply.github.com>2020-04-28 20:51:29 +0300
committerbruvzg <7645683+bruvzg@users.noreply.github.com>2020-09-05 14:53:39 +0300
commitf043eabdd84a509abf5266bb444d19af3e26b7c6 (patch)
treeb919d337e8edce9be6b1403b29b58707eee478be /editor/editor_export.cpp
parent52f6ac81be14fe3f7dacb2e2b75d9bf9a668b628 (diff)
Adds PCK encryption support (using script encryption key for export).
Change default encryption mode from ECB to CFB.
Diffstat (limited to 'editor/editor_export.cpp')
-rw-r--r--editor/editor_export.cpp262
1 files changed, 229 insertions, 33 deletions
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 16e69734d3..1d7429eb64 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -32,6 +32,7 @@
#include "core/crypto/crypto_core.h"
#include "core/io/config_file.h"
+#include "core/io/file_access_encrypted.h"
#include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
@@ -222,6 +223,42 @@ String EditorExportPreset::get_custom_features() const {
return custom_features;
}
+void EditorExportPreset::set_enc_in_filter(const String &p_filter) {
+ enc_in_filters = p_filter;
+ EditorExport::singleton->save_presets();
+}
+
+String EditorExportPreset::get_enc_in_filter() const {
+ return enc_in_filters;
+}
+
+void EditorExportPreset::set_enc_ex_filter(const String &p_filter) {
+ enc_ex_filters = p_filter;
+ EditorExport::singleton->save_presets();
+}
+
+String EditorExportPreset::get_enc_ex_filter() const {
+ return enc_ex_filters;
+}
+
+void EditorExportPreset::set_enc_pck(bool p_enabled) {
+ enc_pck = p_enabled;
+ EditorExport::singleton->save_presets();
+}
+
+bool EditorExportPreset::get_enc_pck() const {
+ return enc_pck;
+}
+
+void EditorExportPreset::set_enc_directory(bool p_enabled) {
+ enc_directory = p_enabled;
+ EditorExport::singleton->save_presets();
+}
+
+bool EditorExportPreset::get_enc_directory() const {
+ return enc_directory;
+}
+
void EditorExportPreset::set_script_export_mode(int p_mode) {
script_mode = p_mode;
EditorExport::singleton->save_presets();
@@ -292,20 +329,55 @@ void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags)
}
}
-Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
PackData *pd = (PackData *)p_userdata;
SavedData sd;
sd.path_utf8 = p_path.utf8();
sd.ofs = pd->f->get_position();
sd.size = p_data.size();
+ sd.encrypted = false;
+
+ for (int i = 0; i < p_enc_in_filters.size(); ++i) {
+ if (p_path.matchn(p_enc_in_filters[i]) || p_path.replace("res://", "").matchn(p_enc_in_filters[i])) {
+ sd.encrypted = true;
+ break;
+ }
+ }
+
+ for (int i = 0; i < p_enc_ex_filters.size(); ++i) {
+ if (p_path.matchn(p_enc_ex_filters[i]) || p_path.replace("res://", "").matchn(p_enc_ex_filters[i])) {
+ sd.encrypted = false;
+ break;
+ }
+ }
- pd->f->store_buffer(p_data.ptr(), p_data.size());
- int pad = _get_pad(PCK_PADDING, sd.size);
+ FileAccessEncrypted *fae = nullptr;
+ FileAccess *ftmp = pd->f;
+
+ if (sd.encrypted) {
+ fae = memnew(FileAccessEncrypted);
+ ERR_FAIL_COND_V(!fae, ERR_SKIP);
+
+ Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false);
+ ERR_FAIL_COND_V(err != OK, ERR_SKIP);
+ ftmp = fae;
+ }
+
+ // Store file content.
+ ftmp->store_buffer(p_data.ptr(), p_data.size());
+
+ if (fae) {
+ fae->release();
+ memdelete(fae);
+ }
+
+ int pad = _get_pad(PCK_PADDING, pd->f->get_position());
for (int i = 0; i < pad; i++) {
- pd->f->store_8(0);
+ pd->f->store_8(Math::rand() % 256);
}
+ // Store MD5 of original file.
{
unsigned char hash[16];
CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
@@ -324,7 +396,7 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
return OK;
}
-Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
String path = p_path.replace_first("res://", "");
ZipData *zd = (ZipData *)p_userdata;
@@ -694,6 +766,61 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
_edit_filter_list(paths, p_preset->get_include_filter(), false);
_edit_filter_list(paths, p_preset->get_exclude_filter(), true);
+ // Get encryption filters.
+ bool enc_pck = p_preset->get_enc_pck();
+ Vector<String> enc_in_filters;
+ Vector<String> enc_ex_filters;
+ Vector<uint8_t> key;
+
+ if (enc_pck) {
+ Vector<String> enc_in_split = p_preset->get_enc_in_filter().split(",");
+ for (int i = 0; i < enc_in_split.size(); i++) {
+ String f = enc_in_split[i].strip_edges();
+ if (f.empty()) {
+ continue;
+ }
+ enc_in_filters.push_back(f);
+ }
+
+ Vector<String> enc_ex_split = p_preset->get_enc_ex_filter().split(",");
+ for (int i = 0; i < enc_ex_split.size(); i++) {
+ String f = enc_ex_split[i].strip_edges();
+ if (f.empty()) {
+ continue;
+ }
+ enc_ex_filters.push_back(f);
+ }
+
+ // Get encryption key.
+ String script_key = p_preset->get_script_encryption_key().to_lower();
+ key.resize(32);
+ if (script_key.length() == 64) {
+ for (int i = 0; i < 32; i++) {
+ int v = 0;
+ if (i * 2 < script_key.length()) {
+ char32_t ct = script_key[i * 2];
+ if (ct >= '0' && ct <= '9') {
+ ct = ct - '0';
+ } else if (ct >= 'a' && ct <= 'f') {
+ ct = 10 + ct - 'a';
+ }
+ v |= ct << 4;
+ }
+
+ if (i * 2 + 1 < script_key.length()) {
+ char32_t ct = script_key[i * 2 + 1];
+ if (ct >= '0' && ct <= '9') {
+ ct = ct - '0';
+ } else if (ct >= 'a' && ct <= 'f') {
+ ct = 10 + ct - 'a';
+ }
+ v |= ct;
+ }
+ key.write[i] = v;
+ }
+ }
+ }
+
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
for (int i = 0; i < export_plugins.size(); i++) {
export_plugins.write[i]->set_export_preset(p_preset);
@@ -704,7 +831,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
}
for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
- p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size());
+ p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key);
}
export_plugins.write[i]->_clear();
@@ -756,14 +883,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
if (remap == "path") {
String remapped_path = config->get_value("remap", remap);
Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path);
- err = p_func(p_udata, remapped_path, array, idx, total);
+ err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
} else if (remap.begins_with("path.")) {
String feature = remap.get_slice(".", 1);
if (remap_features.has(feature)) {
String remapped_path = config->get_value("remap", remap);
Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path);
- err = p_func(p_udata, remapped_path, array, idx, total);
+ err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
}
}
}
@@ -774,7 +901,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
//also save the .import file
Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import");
- err = p_func(p_udata, path + ".import", array, idx, total);
+ err = p_func(p_udata, path + ".import", array, idx, total, enc_in_filters, enc_ex_filters, key);
if (err != OK) {
return err;
@@ -795,7 +922,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
- p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total);
+ p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key);
if (export_plugins[i]->extra_files[j].remap) {
do_export = false; //if remap, do not
path_remaps.push_back(path);
@@ -815,7 +942,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
//just store it as it comes
if (do_export) {
Vector<uint8_t> array = FileAccess::get_file_as_array(path);
- p_func(p_udata, path, array, idx, total);
+ p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
}
}
@@ -851,7 +978,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
new_file.write[j] = utf8[j];
}
- p_func(p_udata, from + ".remap", new_file, idx, total);
+ p_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key);
}
} else {
//old remap mode, will still work, but it's unused because it's not multiple pck export friendly
@@ -864,11 +991,11 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
String splash = ProjectSettings::get_singleton()->get("application/boot_splash/image");
if (icon != String() && FileAccess::exists(icon)) {
Vector<uint8_t> array = FileAccess::get_file_as_array(icon);
- p_func(p_udata, icon, array, idx, total);
+ p_func(p_udata, icon, array, idx, total, enc_in_filters, enc_ex_filters, key);
}
if (splash != String() && FileAccess::exists(splash) && icon != splash) {
Vector<uint8_t> array = FileAccess::get_file_as_array(splash);
- p_func(p_udata, splash, array, idx, total);
+ p_func(p_udata, splash, array, idx, total, enc_in_filters, enc_ex_filters, key);
}
String config_file = "project.binary";
@@ -877,7 +1004,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
Vector<uint8_t> data = FileAccess::get_file_as_array(engine_cfb);
DirAccess::remove_file_or_error(engine_cfb);
- p_func(p_udata, "res://" + config_file, data, idx, total);
+ p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key);
return OK;
}
@@ -953,6 +1080,17 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
f->store_32(VERSION_MINOR);
f->store_32(VERSION_PATCH);
+ uint32_t pack_flags = 0;
+ bool enc_pck = p_preset->get_enc_pck();
+ bool enc_directory = p_preset->get_enc_directory();
+ if (enc_pck && enc_directory) {
+ pack_flags |= PACK_DIR_ENCRYPTED;
+ }
+ f->store_32(pack_flags); // flags
+
+ uint64_t file_base_ofs = f->get_position();
+ f->store_64(0); // files base
+
for (int i = 0; i < 16; i++) {
//reserved
f->store_32(0);
@@ -960,40 +1098,82 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
f->store_32(pd.file_ofs.size()); //amount of files
- int64_t header_size = f->get_position();
+ FileAccessEncrypted *fae = nullptr;
+ FileAccess *fhead = f;
+
+ if (enc_pck && enc_directory) {
+ String script_key = p_preset->get_script_encryption_key().to_lower();
+ Vector<uint8_t> key;
+ key.resize(32);
+ if (script_key.length() == 64) {
+ for (int i = 0; i < 32; i++) {
+ int v = 0;
+ if (i * 2 < script_key.length()) {
+ char32_t ct = script_key[i * 2];
+ if (ct >= '0' && ct <= '9') {
+ ct = ct - '0';
+ } else if (ct >= 'a' && ct <= 'f') {
+ ct = 10 + ct - 'a';
+ }
+ v |= ct << 4;
+ }
- //precalculate header size
+ if (i * 2 + 1 < script_key.length()) {
+ char32_t ct = script_key[i * 2 + 1];
+ if (ct >= '0' && ct <= '9') {
+ ct = ct - '0';
+ } else if (ct >= 'a' && ct <= 'f') {
+ ct = 10 + ct - 'a';
+ }
+ v |= ct;
+ }
+ key.write[i] = v;
+ }
+ }
+ fae = memnew(FileAccessEncrypted);
+ ERR_FAIL_COND_V(!fae, ERR_SKIP);
- for (int i = 0; i < pd.file_ofs.size(); i++) {
- header_size += 4; // size of path string (32 bits is enough)
- int string_len = pd.file_ofs[i].path_utf8.length();
- header_size += string_len + _get_pad(4, string_len); ///size of path string
- header_size += 8; // offset to file _with_ header size included
- header_size += 8; // size of file
- header_size += 16; // md5
- }
+ err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false);
+ ERR_FAIL_COND_V(err != OK, ERR_SKIP);
- int header_padding = _get_pad(PCK_PADDING, header_size);
+ fhead = fae;
+ }
for (int i = 0; i < pd.file_ofs.size(); i++) {
int string_len = pd.file_ofs[i].path_utf8.length();
int pad = _get_pad(4, string_len);
- f->store_32(string_len + pad);
- f->store_buffer((const uint8_t *)pd.file_ofs[i].path_utf8.get_data(), string_len);
+ fhead->store_32(string_len + pad);
+ fhead->store_buffer((const uint8_t *)pd.file_ofs[i].path_utf8.get_data(), string_len);
for (int j = 0; j < pad; j++) {
- f->store_8(0);
+ fhead->store_8(0);
}
- f->store_64(pd.file_ofs[i].ofs + header_padding + header_size);
- f->store_64(pd.file_ofs[i].size); // pay attention here, this is where file is
- f->store_buffer(pd.file_ofs[i].md5.ptr(), 16); //also save md5 for file
+ fhead->store_64(pd.file_ofs[i].ofs);
+ fhead->store_64(pd.file_ofs[i].size); // pay attention here, this is where file is
+ fhead->store_buffer(pd.file_ofs[i].md5.ptr(), 16); //also save md5 for file
+ uint32_t flags = 0;
+ if (pd.file_ofs[i].encrypted) {
+ flags |= PACK_FILE_ENCRYPTED;
+ }
+ fhead->store_32(flags);
}
+ if (fae) {
+ fae->release();
+ memdelete(fae);
+ }
+
+ int header_padding = _get_pad(PCK_PADDING, f->get_position());
for (int i = 0; i < header_padding; i++) {
- f->store_8(0);
+ f->store_8(Math::rand() % 256);
}
+ uint64_t file_base = f->get_position();
+ f->seek(file_base_ofs);
+ f->store_64(file_base); // update files base
+ f->seek(file_base);
+
// Save the rest of the data.
ftmp = FileAccess::open(tmppath, FileAccess::READ);
@@ -1162,6 +1342,10 @@ void EditorExport::_save() {
config->set_value(section, "exclude_filter", preset->get_exclude_filter());
config->set_value(section, "export_path", preset->get_export_path());
config->set_value(section, "patch_list", preset->get_patches());
+ config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter());
+ 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_export_mode", preset->get_script_export_mode());
config->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
@@ -1337,6 +1521,18 @@ void EditorExport::load_config() {
preset->add_patch(patch_list[i]);
}
+ if (config->has_section_key(section, "encrypt_pck")) {
+ preset->set_enc_pck(config->get_value(section, "encrypt_pck"));
+ }
+ if (config->has_section_key(section, "encrypt_directory")) {
+ preset->set_enc_directory(config->get_value(section, "encrypt_directory"));
+ }
+ if (config->has_section_key(section, "encryption_include_filters")) {
+ preset->set_enc_in_filter(config->get_value(section, "encryption_include_filters"));
+ }
+ 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_export_mode")) {
preset->set_script_export_mode(config->get_value(section, "script_export_mode"));
}