Merge pull request #98918 from bruvzg/pck_enc_iv

Allow setting custom initialization vector for FileAccessEncrypted. Add export setting to set static seed for PCK encryption initialization vectors.
This commit is contained in:
Thaddeus Crews 2024-11-13 08:34:31 -06:00
commit 1627912d11
No known key found for this signature in database
GPG Key ID: 62181B86FE9E5D84
18 changed files with 200 additions and 52 deletions

View File

@ -0,0 +1,41 @@
/**************************************************************************/
/* file_access.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
Ref<FileAccess> FileAccess::_open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
return open_encrypted(p_path, p_mode_flags, p_key, Vector<uint8_t>());
}
void FileAccess::_bind_compatibility_methods() {
ClassDB::bind_compatibility_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::_open_encrypted_bind_compat_98918);
}
#endif // DISABLE_DEPRECATED

View File

@ -29,6 +29,7 @@
/**************************************************************************/
#include "file_access.h"
#include "file_access.compat.inc"
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
@ -124,7 +125,7 @@ Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags)
return fa;
}
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv) {
Ref<FileAccess> fa = _open(p_path, p_mode_flags);
if (fa.is_null()) {
return fa;
@ -132,7 +133,7 @@ Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mod
Ref<FileAccessEncrypted> fae;
fae.instantiate();
Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ);
Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ, true, p_iv);
last_file_open_error = err;
if (err) {
return Ref<FileAccess>();
@ -806,7 +807,7 @@ String FileAccess::get_sha256(const String &p_file) {
void FileAccess::_bind_methods() {
ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open);
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::open_encrypted);
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key", "iv"), &FileAccess::open_encrypted, DEFVAL(Vector<uint8_t>()));
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass);
ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0));
ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error);

View File

@ -109,6 +109,12 @@ protected:
static FileCloseFailNotify close_fail_notify;
#ifndef DISABLE_DEPRECATED
static Ref<FileAccess> _open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
static void _bind_compatibility_methods();
#endif
private:
static bool backup_save;
thread_local static Error last_file_open_error;
@ -199,7 +205,7 @@ public:
static Ref<FileAccess> create_for_path(const String &p_path);
static Ref<FileAccess> open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv = Vector<uint8_t>());
static Ref<FileAccess> open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass);
static Ref<FileAccess> open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ);
static Error get_open_error();

View File

@ -36,7 +36,7 @@
#include <stdio.h>
Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) {
Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic, const Vector<uint8_t> &p_iv) {
ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, vformat("Can't open file while another file from path '%s' is open.", file->get_path_absolute()));
ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER);
@ -49,6 +49,16 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
writing = true;
file = p_base;
key = p_key;
if (p_iv.is_empty()) {
iv.resize(16);
CryptoCore::RandomGenerator rng;
ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
Error err = rng.get_random_bytes(iv.ptrw(), 16);
ERR_FAIL_COND_V(err != OK, err);
} else {
ERR_FAIL_COND_V(p_iv.size() != 16, ERR_INVALID_PARAMETER);
iv = p_iv;
}
} else if (p_mode == MODE_READ) {
writing = false;
@ -63,10 +73,8 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
p_base->get_buffer(md5d, 16);
length = p_base->get_64();
unsigned char iv[16];
for (int i = 0; i < 16; i++) {
iv[i] = p_base->get_8();
}
iv.resize(16);
p_base->get_buffer(iv.ptrw(), 16);
base = p_base->get_position();
ERR_FAIL_COND_V(p_base->get_length() < base + length, ERR_FILE_CORRUPT);
@ -83,7 +91,7 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
CryptoCore::AESContext ctx;
ctx.set_encode_key(key.ptrw(), 256); // Due to the nature of CFB, same key schedule is used for both encryption and decryption!
ctx.decrypt_cfb(ds, iv, data.ptrw(), data.ptrw());
ctx.decrypt_cfb(ds, iv.ptrw(), data.ptrw(), data.ptrw());
}
data.resize(length);
@ -145,14 +153,9 @@ void FileAccessEncrypted::_close() {
file->store_buffer(hash, 16);
file->store_64(data.size());
file->store_buffer(iv.ptr(), 16);
unsigned char iv[16];
for (int i = 0; i < 16; i++) {
iv[i] = Math::rand() % 256;
file->store_8(iv[i]);
}
ctx.encrypt_cfb(len, iv, compressed.ptrw(), compressed.ptrw());
ctx.encrypt_cfb(len, iv.ptrw(), compressed.ptrw(), compressed.ptrw());
file->store_buffer(compressed.ptr(), compressed.size());
data.clear();

View File

@ -44,6 +44,7 @@ public:
};
private:
Vector<uint8_t> iv;
Vector<uint8_t> key;
bool writing = false;
Ref<FileAccess> file;
@ -57,9 +58,11 @@ private:
void _close();
public:
Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true);
Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true, const Vector<uint8_t> &p_iv = Vector<uint8_t>());
Error open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode);
Vector<uint8_t> get_iv() const { return iv; }
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open

View File

@ -308,6 +308,7 @@
<param index="0" name="path" type="String" />
<param index="1" name="mode_flags" type="int" enum="FileAccess.ModeFlags" />
<param index="2" name="key" type="PackedByteArray" />
<param index="3" name="iv" type="PackedByteArray" default="PackedByteArray()" />
<description>
Creates a new [FileAccess] object and opens an encrypted file in write or read mode. You need to pass a binary key to encrypt/decrypt it.
[b]Note:[/b] The provided key must be 32 bytes long.

View File

@ -87,6 +87,8 @@ void EditorExport::_save() {
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, "seed", preset->get_seed());
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());
@ -307,6 +309,9 @@ void EditorExport::load_config() {
preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED));
preset->set_patches(config->get_value(section, "patches", Vector<String>()));
if (config->has_section_key(section, "seed")) {
preset->set_seed(config->get_value(section, "seed"));
}
if (config->has_section_key(section, "encrypt_pck")) {
preset->set_enc_pck(config->get_value(section, "encrypt_pck"));
}

View File

@ -216,7 +216,7 @@ void EditorExportPlatform::_unload_patches() {
PackedData::get_singleton()->clear();
}
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) {
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, uint64_t p_seed) {
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
PackData *pd = (PackData *)p_userdata;
@ -247,10 +247,27 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
Ref<FileAccess> ftmp = pd->f;
if (sd.encrypted) {
Vector<uint8_t> iv;
if (p_seed != 0) {
uint64_t seed = p_seed;
const uint8_t *ptr = p_data.ptr();
int64_t len = p_data.size();
for (int64_t i = 0; i < len; i++) {
seed = ((seed << 5) + seed) ^ ptr[i];
}
RandomPCG rng = RandomPCG(seed, RandomPCG::DEFAULT_INC);
iv.resize(16);
for (int i = 0; i < 16; i++) {
iv.write[i] = rng.rand() % 256;
}
}
fae.instantiate();
ERR_FAIL_COND_V(fae.is_null(), ERR_SKIP);
Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false);
Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv);
ERR_FAIL_COND_V(err != OK, ERR_SKIP);
ftmp = fae;
}
@ -288,15 +305,15 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
return OK;
}
Error EditorExportPlatform::_save_pack_patch_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) {
Error EditorExportPlatform::_save_pack_patch_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, uint64_t p_seed) {
if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
return OK;
}
return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key);
return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed);
}
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) {
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, uint64_t p_seed) {
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
String path = p_path.replace_first("res://", "");
@ -328,12 +345,12 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat
return OK;
}
Error EditorExportPlatform::_save_zip_patch_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) {
Error EditorExportPlatform::_save_zip_patch_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, uint64_t p_seed) {
if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
return OK;
}
return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key);
return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed);
}
Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const {
@ -932,7 +949,7 @@ Vector<String> EditorExportPlatform::get_forced_export_files() {
return files;
}
Error EditorExportPlatform::_script_save_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) {
Error EditorExportPlatform::_script_save_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, uint64_t p_seed) {
Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb;
ERR_FAIL_COND_V(!cb.is_valid(), FAILED);
@ -1043,8 +1060,10 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
Vector<String> enc_in_filters;
Vector<String> enc_ex_filters;
Vector<uint8_t> key;
uint64_t seed = 0;
if (enc_pck) {
seed = p_preset->get_seed();
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();
@ -1116,7 +1135,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
}
for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
err = p_save_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);
err = p_save_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, seed);
if (err != OK) {
return err;
}
@ -1234,7 +1253,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
err = p_save_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);
err = p_save_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, seed);
if (err != OK) {
return err;
}
@ -1266,7 +1285,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
if (importer_type == "keep") {
// Just keep file as-is.
Vector<uint8_t> array = FileAccess::get_file_as_bytes(path);
err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
@ -1309,13 +1328,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
sarr.resize(cs.size());
memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
}
// Now actual remapped file:
sarr = FileAccess::get_file_as_bytes(export_path);
err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
}
@ -1345,14 +1364,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_bytes(remapped_path);
err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
} 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_bytes(remapped_path);
err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
} else {
// Remove paths if feature not enabled.
config->erase_section_key("remap", remap);
@ -1378,7 +1397,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
sarr.resize(cs.size());
memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
@ -1399,7 +1418,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path);
err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
}
@ -1463,7 +1482,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
new_file.write[j] = utf8[j];
}
err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
}
@ -1477,7 +1496,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
Vector<String> forced_export = get_forced_export_files();
for (int i = 0; i < forced_export.size(); i++) {
Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]);
err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
}
@ -1489,7 +1508,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb);
DirAccess::remove_file_or_error(engine_cfb);
err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key);
err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed);
if (err != OK) {
return err;
}
@ -1872,6 +1891,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
Ref<FileAccess> fhead = f;
if (enc_pck && enc_directory) {
uint64_t seed = p_preset->get_seed();
String script_key = _get_script_encryption_key(p_preset);
Vector<uint8_t> key;
key.resize(32);
@ -1906,7 +1926,27 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
return ERR_CANT_CREATE;
}
err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false);
Vector<uint8_t> iv;
if (seed != 0) {
for (int i = 0; i < pd.file_ofs.size(); i++) {
for (int64_t j = 0; j < pd.file_ofs[i].path_utf8.length(); j++) {
seed = ((seed << 5) + seed) ^ pd.file_ofs[i].path_utf8.get_data()[j];
}
for (int64_t j = 0; j < pd.file_ofs[i].md5.size(); j++) {
seed = ((seed << 5) + seed) ^ pd.file_ofs[i].md5[j];
}
seed = ((seed << 5) + seed) ^ pd.file_ofs[i].ofs;
seed = ((seed << 5) + seed) ^ pd.file_ofs[i].size;
}
RandomPCG rng = RandomPCG(seed, RandomPCG::DEFAULT_INC);
iv.resize(16);
for (int i = 0; i < 16; i++) {
iv.write[i] = rng.rand() % 256;
}
}
err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv);
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't open encrypted file to write."));
return ERR_CANT_CREATE;

View File

@ -53,7 +53,7 @@ protected:
static void _bind_methods();
public:
typedef Error (*EditorExportSaveFunction)(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);
typedef Error (*EditorExportSaveFunction)(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, uint64_t p_seed);
typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path);
typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
@ -114,14 +114,14 @@ private:
static bool _check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data);
static Error _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);
static Error _save_pack_patch_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);
static Error _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, uint64_t p_seed);
static Error _save_pack_patch_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, uint64_t p_seed);
static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so);
static Error _remove_pack_file(void *p_userdata, const String &p_path);
static Error _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);
static Error _save_zip_patch_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);
static Error _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, uint64_t p_seed);
static Error _save_zip_patch_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, uint64_t p_seed);
static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
struct ScriptCallbackData {
@ -129,7 +129,7 @@ private:
Callable so_cb;
};
static Error _script_save_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);
static Error _script_save_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, uint64_t p_seed);
static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so);
void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude);

View File

@ -451,6 +451,15 @@ String EditorExportPreset::get_enc_ex_filter() const {
return enc_ex_filters;
}
void EditorExportPreset::set_seed(uint64_t p_seed) {
seed = p_seed;
EditorExport::singleton->save_presets();
}
uint64_t EditorExportPreset::get_seed() const {
return seed;
}
void EditorExportPreset::set_enc_pck(bool p_enabled) {
enc_pck = p_enabled;
EditorExport::singleton->save_presets();

View File

@ -92,6 +92,7 @@ private:
String enc_ex_filters;
bool enc_pck = false;
bool enc_directory = false;
uint64_t seed = 0;
String script_key;
int script_mode = MODE_SCRIPT_BINARY_TOKENS_COMPRESSED;
@ -165,6 +166,9 @@ public:
void set_enc_ex_filter(const String &p_filter);
String get_enc_ex_filter() const;
void set_seed(uint64_t p_seed);
uint64_t get_seed() const;
void set_enc_pck(bool p_enabled);
bool get_enc_pck() const;

View File

@ -382,10 +382,16 @@ void ProjectExportDialog::_edit_preset(int p_index) {
bool enc_pck_mode = current->get_enc_pck();
enc_pck->set_pressed(enc_pck_mode);
uint64_t seed = current->get_seed();
if (!updating_seed) {
seed_input->set_text(itos(seed));
}
enc_directory->set_disabled(!enc_pck_mode);
enc_in_filters->set_editable(enc_pck_mode);
enc_ex_filters->set_editable(enc_pck_mode);
script_key->set_editable(enc_pck_mode);
seed_input->set_editable(enc_pck_mode);
bool enc_directory_mode = current->get_enc_directory();
enc_directory->set_pressed(enc_directory_mode);
@ -591,6 +597,21 @@ void ProjectExportDialog::_enc_pck_changed(bool p_pressed) {
_update_current_preset();
}
void ProjectExportDialog::_seed_input_changed(const String &p_text) {
if (updating) {
return;
}
Ref<EditorExportPreset> current = get_current_preset();
ERR_FAIL_COND(current.is_null());
current->set_seed(seed_input->get_text().to_int());
updating_seed = true;
_update_current_preset();
updating_seed = false;
}
void ProjectExportDialog::_enc_directory_changed(bool p_pressed) {
if (updating) {
return;
@ -1623,6 +1644,10 @@ ProjectExportDialog::ProjectExportDialog() {
sec_vb->add_child(script_key_error);
sections->add_child(sec_scroll_container);
seed_input = memnew(LineEdit);
seed_input->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_seed_input_changed));
sec_vb->add_margin_child(TTR("Initialization vector seed"), seed_input);
Label *sec_info = memnew(Label);
sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source."));
sec_vb->add_child(sec_info);

View File

@ -172,6 +172,7 @@ class ProjectExportDialog : public ConfirmationDialog {
CheckButton *enc_directory = nullptr;
LineEdit *enc_in_filters = nullptr;
LineEdit *enc_ex_filters = nullptr;
LineEdit *seed_input = nullptr;
OptionButton *script_mode = nullptr;
@ -192,9 +193,11 @@ class ProjectExportDialog : public ConfirmationDialog {
bool updating_script_key = false;
bool updating_enc_filters = false;
bool updating_seed = false;
void _enc_pck_changed(bool p_pressed);
void _enc_directory_changed(bool p_pressed);
void _enc_filters_changed(const String &p_text);
void _seed_input_changed(const String &p_text);
void _script_encryption_key_changed(const String &p_key);
bool _validate_script_encryption_key(const String &p_key);

View File

@ -115,3 +115,10 @@ GH-91201
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/OS/methods/read_string_from_stdin': arguments
Added optional argument. Compatibility method registered.
GH-98918
--------
Validate extension JSON: Error: Field 'classes/FileAccess/methods/open_encrypted/arguments': size changed value in new API, from 3 to 4.
Optional argument added to allow setting initialization vector. Compatibility method registered.

View File

@ -788,7 +788,7 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
return OK;
}
Error EditorExportPlatformAndroid::save_apk_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) {
Error EditorExportPlatformAndroid::save_apk_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, uint64_t p_seed) {
APKExportData *ed = static_cast<APKExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", "assets/");
@ -796,7 +796,7 @@ Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String
return OK;
}
Error EditorExportPlatformAndroid::ignore_apk_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) {
Error EditorExportPlatformAndroid::ignore_apk_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, uint64_t p_seed) {
return OK;
}

View File

@ -142,9 +142,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
static Error save_apk_so(void *p_userdata, const SharedObject &p_so);
static Error save_apk_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);
static Error save_apk_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, uint64_t p_seed);
static Error ignore_apk_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);
static Error ignore_apk_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, uint64_t p_seed);
static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so);
@ -186,7 +186,7 @@ protected:
void _notification(int p_what);
public:
typedef Error (*EditorExportSaveFunction)(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);
typedef Error (*EditorExportSaveFunction)(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, uint64_t p_seed);
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;

View File

@ -169,7 +169,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
// It is used by the export_project_files method to save all the asset files into the gradle project.
// It's functionality mirrors that of the method save_apk_file.
// This method will be called ONLY when gradle build is enabled.
Error rename_and_store_file_in_gradle_project(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) {
Error rename_and_store_file_in_gradle_project(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, uint64_t p_seed) {
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
print_verbose("Saving project files from " + p_path + " into " + dst_path);

View File

@ -93,7 +93,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
// It is used by the export_project_files method to save all the asset files into the gradle project.
// It's functionality mirrors that of the method save_apk_file.
// This method will be called ONLY when gradle build is enabled.
Error rename_and_store_file_in_gradle_project(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);
Error rename_and_store_file_in_gradle_project(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, uint64_t p_seed);
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir);