godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.11k stars 20.2k forks source link

ConfigFile load_encrypted_pass not working in Godot 4 for file encrypted with Godot 3 #78815

Open IceTyp opened 1 year ago

IceTyp commented 1 year ago

Godot version

4.1.rc1

System information

Godot v4.1.rc1 - Ubuntu 22.04.2 LTS 22.04 - Vulkan (Forward+) - integrated Intel(R) Xe Graphics (TGL GT2) () - 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz (8 Threads)

Issue description

I use an encrypted ConfigFile to keep track of game progression in my game. After porting to Godot 4, the file couldn't be opened and I received the error:

E 0:00:00:0224   Node.gd:13 @ _ready(): Condition "p_base->get_length() < base + length" is true. Returning: ERR_FILE_CORRUPT
  <C++ Source>   core/io/file_access_encrypted.cpp:72 @ open_and_parse()
  <Stack Trace>  Node.gd:13 @ _ready()

Note that the issue applies to Godot 4.0.3 and Godot 4.1.rc1.

Steps to reproduce

  1. Save a ConfigFile with save_encrypted_pass and any password using Godot 3.
  2. Load the file with load_encrypted_pass and the same password using Godot 4.
  3. The error ERR_FILE_CORRUPT will appear.

For instance, you can use the script

extends Node

const FILE := "user://file.cfg"
const ENCRYPTION_PASSWORD := ""

func _ready() -> void:
    var file = ConfigFile.new()
    file.load_encrypted_pass(FILE, ENCRYPTION_PASSWORD)
    file.save_encrypted_pass(FILE, ENCRYPTION_PASSWORD)

to load and save a file in both Godot 3 and Godot 4. To reproduce, run Test_encrypt_Godot3 with e.g. Godot 3.5.2. Then, run Test_encrypt_Godot4 with e.g. Godot 4.0.3.

Minimal reproduction project

Test_encrypt_Godot3.zip Test_encrypt_Godot4.zip

Calinou commented 1 year ago

This likely means having to add a compatibility handler in Godot 4 to handle encrypted Godot 3 ConfigFiles, as the format was changed in a compatibility-breaking manner.

YuriSizov commented 1 year ago

While a compatibility method could be nice to have, we do not provide support for interoperability between major versions. While contributors do their best to allow converting projects from Godot 3 to Godot 4, runtime parsing of old formats is not supported.

AThousandShips commented 1 year ago

Would probably be good to clarify in documentation if not already present, compatibility methods are a lot to maintain and there's workarounds like just transferring the file in a format that's compatible like JSON and then encrypting again inside 4.x

bruvzg commented 1 year ago

It's incompatibility in FileAccessEncrypted, not specific to ConfigFile. Encryption mode was changed from ECB to CFB, and some other parts of the file format were changed.

AThousandShips commented 1 year ago

If ConfigFile is still compatible raw then you can simply store it unencrypted and then re-save it encrypted, but I'd suggest re-adding all the data via some secondary data to ensure it works correctly in Godot 4 as some details might have changed

But I'd say we should add a clear line in the upgrading docs from 3.x -> 4.x that file formats aren't compatible

IceTyp commented 1 year ago

Thanks for the clarifications. I fully agree that this should be mentioned in the documentation.

I use ConfigFile to keep track of game data. For debugging, I use no encryption and for my game it works fine between Godot 3 and 4. After successfully porting to Godot 4, I was about to publish a new version of my game when I realized this issue during testing. For my players, this basically means now that old saves are no longer supported. I am not sure how I should proceed here. I might offer my players to send me their old saves so that I can update those for them. This only affects maybe a handful of players and since my game is free and not that long, starting anew might be fine for most of them anyway.

Still I would appreciate some suggestions on what else I can do to resolve this issue.

AThousandShips commented 1 year ago

For general support I'd suggest turning to the other community channels as this is for bugs really and not general support, and you'll get more support there with people being available, though this should be kept open to track documentation updates.

bruvzg commented 1 year ago

You can parse the old file manually, using GDScript. Here's a quick draft (w/o any data validation):

    var filename = "user://file.cfg"
    var password = "pass"

    # read file
    var f = FileAccess.open(filename, FileAccess.READ)
    f.get_32() # magic, should be 0x43454447
    f.get_32() # mode, ignore
    f.get_buffer(16) # md5, you can check if decrypted data md5 hash match to verify it's ok
    var len = f.get_64() # decrypted data length
    var ds = len # encrypted data length, adjusted to block size
    if ds % 16:
        ds += 16 - (ds % 16)
    var data = f.get_buffer(ds)
    var aes = AESContext.new()
    aes.start(AESContext.MODE_ECB_DECRYPT, password.md5_text().to_ascii_buffer())
    var decrypted = aes.update(data)
    aes.finish()
    decrypted.resize(len)
    f.close()

    # parse config
    var cf = ConfigFile.new()
    cf.parse(decrypted.get_string_from_utf8())