Advanced Encryption Standard in Luau

Advanced Encryption Standard

An implementation of AES, a symetric-key algorithm to encrypt and decrypt files (buffers and strings) securely. It should provide good security against cryptographic attacks.


Includes

  • AES-128, AES-192 and AES-256.
  • ECB, CBC, PCBC, CFB, OFB and CTR cipher modes.
  • New buffers support and cast from strings.
  • New AesCipher object for various encryption/decryption methods.
  • New non-padding, ANSI X9.23, ISO 10126, PCKS#7, ISO/IEC 7816-4 and zero pads.

Implementation

In order to implement this asset, download the model or use require. Check if the asset is the original one and insert it to your place. Here is an example of a way to start using it:

local AES = require(11195079384) -- Or reference a ModuleScript
local cipher = AES.new("This is a sample", Aes.modes.CTR, Aes.pads.Pkcs7)
encrypted = cipher:Encrypt("Lorem ipsum dolor sit amet")
decrypted = cipher:Decrypt(encrypted)
print(buffer.tostring(decrypted)) --Expected output: Lorem ipsum dolor sit amet

A correct use of this algorithm is to keep the key secret and IV unpredictable, and all the confidential processes must be done in the server. This implementation might not resistant to side-channel attacks.

Information

This module includes 6 modes of block cipher operations:

Mode
Description
Parallelizable
ECB Message divided into blocks which are then encrypted. IV is not an input. Note: this mode is just trivial and it should not be used in practice. Yes
CBC Plaintext is divided and ther XORed with the previous one before being encrypted. Decryption only
PCBC Plaintext is divided and XORed with both the previous plaintext and ciphertext before being encrypted. No
CFB Plaintext is made into a self-synchronizing stream cipher. Segment size property is 16 by default. Decryption only
OFB Plaintext generates keystream blocks which are then XORed with the plaintext blocks. No
CTR Generates keystream blocks by encrypting succesive values of a counter. More information is related below. Yes

Plaintext or ciphertext is the main input and contains the data wanted to encrypt or decrypt respectively. Its length must be a multiple of 16 bytes. Note: OFB and CTR are symmetric.
Key is the most important information that lets the algorithm encode and decode data. Key sizes are 16 (AES-128), 24 (AES-192) and 32 (AES-256) bytes long.
Initialization vector is a block which is used to randomize the encryption. It is an input of the encryption/decryption methods, zeroes are by default. It is ignored by ECB and CTR modes. It must be 16 bytes long.
Counter is a function which produces a sequence that should not repeat for a long time. It should be 16 bytes long. Incrementing-by-one counter is used by default, other counter properties may be used for parallelization optimizations.

Parallelization can be done using Actors (see Parallel Luau) but only works for specific modes related above. CTR mode must be done organizedly in order to get safe encryption. Deeper documentation about custom modes or pads can be found in the main module.

Performance

The algorithm has been benchmarked with an Intel® Core™ i5-10300H CPU @ 2.50GHz processor with a 32 GB RAM, using ECB mode with a key size of 16 (AES-128) and Luau native code:

Plaintext size
Encryption av. time
Decryption av. time
16 bytes 0.0000061 0.0000086
32 bytes 0.0000067 0.0000097
80 bytes 0.0000088 0.0000124
1600 bytes (1.6 KB) 0.0000688 0.0001054
3200 bytes 0.0001337 0.0002072
160000 bytes (160 KB) 0.0061583 0.0090831
1000000 bytes (1 MB) 0.0373192 0.0568667
5000000 bytes (5 MB) 0.1924914 0.2852383
10000000 bytes (10 MB) 0.3832369 0.5708973

The module has been optimized for Luau, requiring string, buffer and bit32 libraries. As shown, cycles are really fast for any ammount of data. More can be encrypted without crashing by using parallelization and rest turns, but at the cost of sacrificing time.


How do you rate this asset?
  • Great and useful
  • Great
  • Good
  • Needs improvements

0 voters

I would appreciate the feedback, since this optimization took me a lot of research and time!

54 Likes

It would be nice if you could provide unit for the average time that you have measured. eg. “Encryption avg. time (ms)”.

5 Likes

I like encryption but what would be the use case for this in a Roblox game?

1 Like

This is not how parallelization in Luau works. Coroutines run sequentially on the same thread. See this:
https://create.roblox.com/docs/scripting/scripts/parallel-scripting

10 Likes

Well, for Roblox games you could use as an extra protection against attacks on the server. I am not sure if Roblox includes a default encryption system.

1 Like

Interesting. Seems I have misunderstood the concept. I’ll check it and then edit

1 Like

How long would encrypted strings be?
if they were not too long I would put it in remote events to confuse the hell out of exploiters

2 Likes

this should not be used for security, and if ur looking into encrypting remote events, you are doing something wrong

3 Likes

Not for security
for torturing exploit developers

2 Likes

Encrypting data that goes into server / client would confuse the frick out of most devs and ward off a bunch of them

2 Likes

Not always true, you cant get much info off the player so exploit devs just find it as a game so decrypt it

2 Likes

Sending encrypted stuff doesnt do anything, the player already knows its not sensitive information so why not mess around with it a bit?

Theyll experiment and of course if they get banned they can just make a new account anyway and repeat the process like nothing happened. Once he figures it out you obviously could change a few things but whats the fun in that when theyll just find out the new code in a few days thus resulting in a back and fourth game between the game developer and the exploiter.

1 Like

you are forgetting the fact that not every exploiter is amazing at coding most would be confused

this onto itself will ward of a lot of exploiter

3 Likes

Update

This is an update of the asset. Please change to latest version: if using require(id), no action is required; if using the asset make the corresponding changes.


Changes

  • Solved a bug when converting string to bytes.
    Changed code:
local function convertType	(a) -- LINE 202
	--[[local t = type(a)]]
	if type(a)--[[t]] == "string" then
		local r = {}

		for i = 1, string.len(a), 7997 do
			table.move(--[[t = ]]{string.byte(a, i--[[ * 7997 - 7996]], i --[[* 7997]]+ 7996)}, 1, 7997, i, r)
			--[[for j = 1, #t do
				table.insert(r, t[j])
			end]]
		end
		return r
	elseif type(a)--[[t]] == "table" then
		for _, i in ipairs(a) do
			assert(type(i) == "number" and math.floor(i) == i and 0 <= i and i < 256,
				"Unable to cast value to bytes")
		end
		return a
	else
		error("Unable to cast value to bytes")
	end
end

Please comment if you find any bugs or improvements.

1 Like

Hey, I’ve been waiting for something like this and this looks pretty good.

Did you forget to include a decrypt_CTR and decrypt_OFB method, as these are both totally missing from your module and cause the sample code to error stating that the decrypt CTR function doesn’t exist.

1 Like

If you look at the Information block you will see that some of the encryption modes are Parallelizable which I’m assuming means that the encrypt function can also decrypt the input too.

1 Like

Sorry for bumping. But this seems like a very good resource! However where’s decrypt_CTR? Because I have to use CFB as an alternative.

1 Like

It is merged with encrypt_CTR function. It’s the same algorithm for both encrypting and decrypting.

1 Like

I heard CTR encryption was more secure so how would I decrypt CTR encryption as it is an nil value. (Nevermind if you guys have the same problem as me just make decrypt_CTR in the example code encrypt_CTR hope this helps you out.)

1 Like

I had literally the same thought just now.

1 Like