GML [SOLVED] Getting incorrect results with Lintydude's AES library?

I'm currently working on a game in GM:S 2.2.5, using Lintydude's AES library found here. I'm using it to encrypt communication between the game client and a NodeJS server I built for the game (to store things like player scores, player collectibles, etc., especially since the collectibles will be from a common pool for all players). However, I'm getting a different output from the AES library than I am from JavaScript's Subtle Crypto API, making it impossible for the server to properly decrypt the requests.

I've verified that both the server and client have the exact same AES-256 key, which is this in hex:

9B13A3D0FA4F8DBDF752C3374F243642568D5BB41ECFBB6E832308C3C7387CB7

(On the server, I'm just importing the key as it was exported, in base64url encoding for JWK; on the client, I'm decoding it and converting it to hex. In either case, I've confirmed that the key is the same on both ends.)

However, when trying to test it with the message Hello, world in CBC mode, I get the following results:

SERVER:
6EE120C7ED50B339C5646108FDE79CD9

GML:
2825730989E85252B0BBC7C961714CE2
They're both encrypting the same "Hello, world" message with the same key (and the same initialization vector, which is just all hex 1's for testing), yet they produce different results, which is halting all my progress.

Using Lintydude's library, this is the code I'm using:

GML:
aes_init(256);
var iv = "11111111111111111111111111111111";
get_string("TEST ENCRYPTION", aes_string("Hello, world", hex_key, iv, 0,1,1,0,1,1));
Is the library implemented wrong, or am I missing something?
 

rytan451

Member
It is possible that Lintydude's implementation encrypts the string, including the terminating null character, while the njs implementation does not (or vice versa). What happens when you decrypt the server's encrypted output using Lintydude's implementation? What happens when you decrypt Lintydude's output using the server's implementation?
 
It is possible that Lintydude's implementation encrypts the string, including the terminating null character, while the njs implementation does not (or vice versa). What happens when you decrypt the server's encrypted output using Lintydude's implementation? What happens when you decrypt Lintydude's output using the server's implementation?
The server can't decrypt the value from the GML library; decryption fails completely. I haven't tried doing it the other way around (encrypting with the server and decrypting in GM), but I'll try that as soon as I get a chance and post the results.

EDIT So if I try to decrypt the server-generated message using this library, it decrypts mostly correctly, except with 4 extra bytes (of value 0x04) at the end. Looking at an ASCII chart, I see 0x04 means "end of transmission" -- but I'm certainly not including those in the string when I'm encrypting it.

In fact, if I append
Code:
chr(4) + chr(4) + chr(4) + chr(4)
to my payload in GM, the result comes out correctly matching the one from my server. I'd rather not have to worry about these (since I don't even know if it's always going to be four of them, etc.) so if anyone can help me figure out where they're coming from, I'd appreciate that.

EDIT 2 After more testing, it seems like it's even stranger than that. I noticed that the test string, "Hello, world", is 12 bytes, and 4 extra bytes aligns it to 16. So I decided to test with one fewer byte, "Hello, worl", and the decrypted result is still aligned to 16, but with five bytes of value 0x05 appended. I'm doing more tests now, but this seems like it could be really annoying to have to deal with...

EDIT 3 Yep, what it seems to be doing is aligning the message to the key size of 128 bits using a byte value that says how many bytes of padding there are. But apparently JS and this GML library seem to handle the padding differently. I don't know enough about AES to fix this myself, but if anyone knows how I should edit the aes_string script in the library to deal with this, please let me know!

EDIT 4 So I found this: https://stackoverflow.com/a/54751543/2444210 It seems like part of the WebCrypto standard is, in fact, to pad the message to a multiple of the 128-bit key size using byte values that indicate how many padding bytes there are. So I added a little intermediate script that does this, and everything seems to work fine now! Yay!

...I hope I'm done with encryption from now on, because this was a nightmare šŸ˜‚
 
Last edited:
Top