Is the expected behaviour of AES encryption to generate a random cipher each time?

Everytime aes128_encrypt and aes256_encrypt are used, a random cipher text is generated, even when using the same key. Upon looking at the source code and implementation in other languages, I found out that it might have something to do with IV (initialization vector), which is never passed as a parameter on those functions, but instead generated on the fly.

Is that supposed to happen? As it is right now, unless I’m missing something, it looks impossible to implement any kind of end-to-end encryption.

Here’s an example of aes128_encrypt being called a few times in succession with the same key, but with different encrypted results, although the decrypt functions are somehow able to work properly:

The code:

local nk = require("nakama")
local content = "hello world!"
local key = "goldenbridge_key"

function test_aes()
    print("------------------------------------")
    print("      original: "..content)
    print("           key: "..key)
    print("------------------------------------")
    local encrypted = nk.aes128_encrypt(content, key)
    local decrypted = nk.aes128_decrypt(encrypted, key)
    print("     encrypted: "..encrypted)
    print("     decrypted: "..decrypted)
    print("------------------------------------")
end

Thanks in advance!

It is not impossible to implement E2E encryption since the key is the only thing 2 parties need to know in order to encrypt/decrypt the message. The encryption output is different each time but it still is going to be decrypted with the “right key”. The only thing remaining is a secure key exchange protocol.

@luizsan What you’re seeing is the result of a randomised Initialization Vector being prepended to the ciphertext.

This is a standard approach for AES CFB: generate a random IV for every encryption call (even if using the same key), then transmit the IV along with the ciphertext. The AES key itself is expected to be exchanged some other way, the exact approach for this is entirely at your discretion.

Most AES encrypt/decrypt implementations I’m familiar with use this approach, so it should be portable across languages and libraries too.

Note that if you want to you can split the IV from the rest of the ciphertext, but the ciphertext will still be different for any given input+key combination since the IV is random each time. This is expected and desired, the aim is to mitigate various attacks against the encryption.

@zyro Thanks for the clarification! It makes perfect sense!
However, I’m still having trouble with the implementation. I want to send encrypted data from Unity using the Aes class so it can be decrypted by Nakama, and the other way around too. When I encrypt and decrypt things in both Unity and Nakama, they work fine, but when sending data from one to another I’m unable to decrypt the message despite using the same key.

I assume the problem is the IV because it has to be specified when using the Aes algorithm on C#, but as far as I know we don’t have access to it on Nakama yet it works perfectly fine on its own. So I’m not sure, am I doing something wrong here? Am I supposed to read the IV from the cipher text or what? And, if yes, how do I do it?

Thanks in advance!