Hello,

Please add content warnings or spoiler block(s) as necessary, thank you.

As per the title, how could one decrypt and encrypt files using readily available, lightweight, libre, open source, audited, cross platform *nix command line utilities - preferably either gpg or openssl (and md5sum , coreutils, et. al.) - that were created by / for use with Godot's File.open_encrypted_with_pass / FileAccess.open_encrypted_with_pass? Explanations or guidance in any programming language or puesdocode are also welcome insofar as the crypto API is simple and understandable.

Additionally, focus on decryption would be preferable over encryption - I can handle the rest once given a few pointers. Likewise, checksum verification for decryption is not required.

Here are some links to relevant sections of code I found. Unfortunately I do not understand C++ thoroughly enough to be able to implement this myself.

Thank you for your valuable time and assistance.

Edit: Please re-tag / move this topic if necessary.

  • Here's the format for encrypted files from all versions of Godot from 2014 to 3.5.2. Endianness is configured using File.endian_swap.

    Each file type has its own format. Below describes encrypted files only:


    Here is some BASH code that can successfully open encrypted files (where the "savegame.bin" is a file encrypted by Godot, and "PA55W0RD" is the passphase):

    < savegame.bin tail -c+33 | openssl aes-256-ecb -d -nosalt -nopad -K $(printf PA55W0RD | md5sum | head -c32 | xxd -c32 -p)

    This can be made into a POSIX function:

    godot_decrypt_with_pass () {
      tail -c+33 | openssl aes-256-ecb -d -nosalt -nopad -K $(printf "$1" | md5sum | head -c32 | xxd -c32 -p)
    }

    Which can then be used as such (for example):

    < savegame.bin godot_decrypt_with_pass PA55W0RD

    Doing so will print the decrypted contents to stdout. Note that these implementations return extraneous null characters at the end of stream. See my follow up post for an updated version that handles this correctly.

    Note(s) to future programmers: This is not an example of secure encryption. Do not use this as an example for critical security components. Godot is an excellent F(L)OSS game engine and only provides these features for casual obfuscation. Iff security is desired:

    • Do not use ECB ever, for anything (other than education purposes).
    • Always use an IV.
    • Always use a valid padding scheme (such as PKCS#7 or ISO/IEC 9797-1.2) to avoid corrupting the tail end of your data.
    • Furthermore, never throw away entropy from the passphrase and instead consider using a standard password based key derivation function (such as PBKDF2 or HKDF).

    Other exercises include writing back encrypted files, and reading and writing files compressed with Godot. Let me know if there are any issues, or if implementations in other programming languages are desired. Do not contact me via direct message - this is a burner account that I no longer have access to.

    Thank you.

You can call shell commands in Godot, assuming those programs are installed (usually they would be on Linux, maybe macOS, but not Windows).

OS.execute()
OS.shell_open()

But what you probably want to do is integrate a crypto library into Godot as an add-on. This way it will work cross-platform, and you know the specific version that is installed, etc.

In Godot 3.x this would be done with GDNative: https://docs.godotengine.org/en/stable/tutorials/scripting/gdnative/index.html

For Godot 4.0, you would use GDExtension, but there is not much documentation yet: https://github.com/godotengine/godot-cpp/tree/master/test

Either way, you will need to know C/C++ fairly well.

    Hi cybereality, thanks for the quick response!

    Unfortunately, I'm not looking to implement crypto in Godot, or execute shell programs. Instead I'd like to know how one can read encrypted files written with File.open_encrypted_with_pass or FileAccess.open_encrypted_with_pass on systems without Godot.

    Godot uses AES256, but you will have to look at the C++ source code to understand exactly how it is parsed. I took a quick look and I'm not sure.

    Oh definitely! That's why I had linked to the C++ source code involved. Hoping somebody come along that can explain it a little bit 😃

    After a little bit of experimenting, it appears that the format for encrypted files for Godot 3.5.1 and 4.0 is slightly different (can provide link(s) to GitHub diffs if desired). Not ideal as I was hoping to target all Godot versions, but if it's only 2 or a few different formats then they can all be attempted using some kind of heuristic.

    Right now I'll be focusing on understanding the format for 3.5.1, as I believe that's what most end users are using right now.

    I figured it out.

    The secret was in the password based key derivation function. I had incorrectly assumed that Godot used MD5 in the same manner that openssl , GNU, and others do. Instead Godot uses a slightly different approach. It appears that the password function used is the same for Godot 3.5.x and Godot 4.

    According to git blame it was added in commit 162d2ebe4f1a6da2da62ad45c4cbfb161157d31d diff c1849500c36978f9b0ae57b54c704cda629c25f3b91516c6808974e1c57cc333R29 "Added google play services (needed for some stuff)" by Juan Linietsky. Of course I'm not trying to judge, I love Godot so much and I'm only (perhaps awkwardly) trying to provide a record for others in an attempt to facilitate communication with those who are interested. I am minimally curious however where that code came from and why that method was chosen over the MD5(Passphrase + Salt) + MD5(MD5(Passphrase + Salt) + Passphrase + Salt) method.

    I can post some working code solutions if anyone desires.

    Here's the format for encrypted files from all versions of Godot from 2014 to 3.5.2. Endianness is configured using File.endian_swap.

    Each file type has its own format. Below describes encrypted files only:


    Here is some BASH code that can successfully open encrypted files (where the "savegame.bin" is a file encrypted by Godot, and "PA55W0RD" is the passphase):

    < savegame.bin tail -c+33 | openssl aes-256-ecb -d -nosalt -nopad -K $(printf PA55W0RD | md5sum | head -c32 | xxd -c32 -p)

    This can be made into a POSIX function:

    godot_decrypt_with_pass () {
      tail -c+33 | openssl aes-256-ecb -d -nosalt -nopad -K $(printf "$1" | md5sum | head -c32 | xxd -c32 -p)
    }

    Which can then be used as such (for example):

    < savegame.bin godot_decrypt_with_pass PA55W0RD

    Doing so will print the decrypted contents to stdout. Note that these implementations return extraneous null characters at the end of stream. See my follow up post for an updated version that handles this correctly.

    Note(s) to future programmers: This is not an example of secure encryption. Do not use this as an example for critical security components. Godot is an excellent F(L)OSS game engine and only provides these features for casual obfuscation. Iff security is desired:

    • Do not use ECB ever, for anything (other than education purposes).
    • Always use an IV.
    • Always use a valid padding scheme (such as PKCS#7 or ISO/IEC 9797-1.2) to avoid corrupting the tail end of your data.
    • Furthermore, never throw away entropy from the passphrase and instead consider using a standard password based key derivation function (such as PBKDF2 or HKDF).

    Other exercises include writing back encrypted files, and reading and writing files compressed with Godot. Let me know if there are any issues, or if implementations in other programming languages are desired. Do not contact me via direct message - this is a burner account that I no longer have access to.

    Thank you.

      I just tried that BASH command, and it worked on a file created with ConfigFile.save_encrypted_pass(). (Although were ten ASCII 0x00 characters at the end.)

        Hello, DaveTheCoder

        Thanks for the like and response. I just made an edit about that a few minutes ago! 😃

        Godot uses a non-standard padding technique. Inside the file header is the correct length of the unencrypted data without the null bytes. Using this length is the only way of trimming the extraneous nulls. Otherwise there will be between 0 to 15 null bytes.

        An updated bash command / function to handle this is incoming (unless I suddenly disappear off the planet)...

        Here is the updated function. Use as before.

        Note that this only works on little endian files! For big endian, simply remove the | xxd -p -c1 | tac | tr -d '\n' part in the middle there (which reverses the length bytes since BASH is natively big-endian). I do not have any big endian machines that can run games at the moment however and it appears that Godot 4 dropped support for big endian files anyway.

        godot_decrypt_with_pass () {
          head -c24 > /dev/null
          len=$((16#$(head -c8 | xxd -p -c1 | tac | tr -d '\n')))
          openssl aes-256-ecb -d -nosalt -nopad -K $(printf "$1" | md5sum | head -c32 | xxd -c32 -p) | head -c$len
        }

        This will now correctly return the unencrypted data without the extraneous null characters.

        I guess you used a burner account for security reasons, but you might consider making a PR to update the crypto code on the source code. I bet the developers are just game devs and didn't know what they were doing.

          cybereality Will consider it! Unfortunately I'm not positive I'm qualified to make such a PR (or perhaps just imposter syndrome who knows), or even if such a PR is desired.

          I am absolutely thrilled about Godot and appreciate everything Juan Linietsky, Ariel Manzur, and +1880 others have done to make such an amazing FLOSS game engine and definitely believe they know how to do game development.

          Of course. I just meant that they are not security experts AFAIK. And other users may be relying on those functions for critical things that could pose a problem if they are not really secure.

          EarlHeidelberg this is a burner account that I no longer have access to.

          If you don't have access to it, how are you using it to make posts? Just curious.

            DaveTheCoder If you don't have access to it, how are you using it to make posts? Just curious.

            When the session expires no more login, registered with a burner mail account so no recovery possible.