Fastest Cryptography Library for Roblox

So my friend daily3014thegod and I started working on some security stuff for our games and realized that the existing crypto libraries on Roblox were either super slow or really hard to understand. So we decided to build our own cryptography library that’s both fast and actually readable.

Currently used in:
The Mimic
Adonis

Repository

GitHub - daily3014/rbx-cryptography: Luau implementations of popular hashing/encryption algorithms
(All important information is in the readme, just so I don’t have to keep updating this post)

Why Another Crypto Library?

Well, mainly because of speed but we tested some popular alternatives like HashLib and the fastest alternative library I found was by Dekkonot which is fast but could be better. Also many libraries fail many basic tests while this library passes all standard test vectors and edge cases.

Important Notes

Just to be clear this library is meant as a learning tool and for educational purposes. Don’t expect enterprise level support or updates. We built this for our own projects and figured the community might find it useful too.
If you find any bugs or issues, feel free to report them, but keep in mind this is more of a “use at your own risk” situation. We’re sharing the code so others can learn from it and maybe improve it.

Let me know what use cases you have for it.


P.S. - If you’re using this or working on security stuff, always do your own testing and validation. Crypto is a nightmare to debug

41 Likes

This is a very valuable resource. Thanks!

1 Like

I just tested it and it works fine for SHA256. This is a great resource, thanks!

1 Like

Proof this is the fastest one?

The benchmarks against the fastest alternatives I could find

1 Like

It would be SUPER awesome if you could demonstrate some real-world examples with this. I have never really dived into securing my games, so it would be helpful if you could show why/how to do it with this cryptography library.

I’ve used hashing in various ways across games. For anti-cheat, I validate tables by comparing client and server hashes any tampering changes the hash completely, making detection easy.

My messaging system chunks large messages over 1kb, then hashes before and after reassembly to verify that everything was sent. If hashes don’t match when reconstructed, the message gets dropped. This is needed since Roblox networking is unreliable and you need to catch broken messages for server-server.

I’ve also seen it used for caching systems where you hash file contents to create unique cache keys. If the same data gets requested again, you can instantly check if your cached version is still valid by comparing hashes instead of re-processing everything.

Before streaming enabled, hashing powered hash tables for O(1) lookups instead of O(n) linear searches. The hash function maps keys to array indices for direct data access.

For encryption, I wanted TLS but HttpService doesn’t expose certificates. So I encrypt remotes and use HMAC validation to catch mid call tampering. Encryption alone doesn’t guarantee integrity because exploiters can mess with the ciphertext and cause weird behavior when processed.

Another common use is for detecting duplicate content. You can hash user generated content like custom maps or models, then quickly quick if someones trying to upload the same thing multiple times without having to compare the actual data byte by byte.

There’s definitely more use cases, plus this library could be converted to pure Lua for non Roblox projects.

4 Likes

Update Log - v1.0.0
Added Blake3
Added Poly1305 + AEAD(authenticated encryption with associated data)
Optimized MD5
Optimized Blake2b (128,256,384,512)
Optimized SHA384
Optimized SHA512
Optimized EdDSA

Type checking fixes
Updated require method
New testing system (Sythivo)
General code cleanup

Full change log: Many stuff · daily3014/rbx-cryptography@7c5e3a7 · GitHub

Update Log - v1.1.3

  • Changed EdDSA.RandomBytes to always return a Curve25519 valid buffer
  • Reverted bad optimizations in Curve25519 that would cause SqrtDiv to fail 70% of the time
    Added argument type checks for AES, AEAD (ChaCha20, Poly1305 included), Blake2b/3, EdDSA and MaskedX25519
  • Made RandomBytes in EdDSA accessible: EdDSA.RandomBytes.Random(Length: number) → buffer
  • Optimized Blake3 by ~20%
  • Updated pesde target
  • Made AesCipher accessible in AES (Encryption.AES.AesCipher)
  • Changed AESCipher:Encrypt/Decrypt argument type to “buffer | string”
  • Updated README with more examples
  • Updated RandomString to optionally return a buffer
  • Optimized Blake2b by 20%
  • ChaCha20 is now accessible through AEAD: Encryption.AEAD.ChaCha20(…)
  • Added EdDSA.Mask25519 - Algorithm for exchanging keys
2 Likes

I use hashing a lot in my projects for remote names and cache to detect changes if hash is different. I appreciate SHA-1 for the shorter length and that all libraries are optimized. Great library

1 Like

Update Log - v1.1.4
Decoupled scalar clamping from random bytes generation in EdDSA. This means you can apply the scalar clamping to your own random string generator with EdDSA.RandomBytes.Clamp(Input: buffer)

You can still generate clamped random bytes with EdDSA.RandomBytes.Random()

1 Like

I’ve done testing on my own but your benchmark seems slightly incorrect as you’ve compared it to RobloxGamerPro200007’s module without enabling native codegen on his module

which brings your AES benchmark from this:
image
to this:
image

Benchmark code & details

details:
my benchmark was done only with CBC, sample size of 20K chars and 10k iterations

added “–!native” + “–!optimize 2” to RobloxGamerPro200007 modules
without those it would give:
image

benchmark code:

local key = "01234567890123456789012345678912"
local data = ("X"):rep(20000)

print("RobloxGamerPro200007")
do
	local AES = require(script.module)
	local encryptor = AES.new(key, AES.modes.CBC, AES.pads.Pkcs7)

	local x = {}
	local t1 = tick()
	for i=1,10000 do
		table.insert(x, encryptor:Encrypt("hello world" .. i))
	end
	print(tick() - t1)
end

print("daily3014")
do
	local AES = require(script.n2)
	local encryptor = AES.New(key, AES.Modes.CBC, AES.Pads.Pkcs7)

	local x = {}
	local t1 = tick()
	for i=1,10000 do
		table.insert(x, encryptor:Encrypt("hello world" .. i))
	end
	print(tick() - t1)
end

:person_facepalming: Why didn’t he include those flags, thanks for pointing that out, this still beats it but the margin is less, was actually messing around with it a few days ago to get more of the code into native, 2.744ms down to 0.711ms but its failing tests rn
image

Also your only encrypting 12 bytes so most of the time comes from calling functions etc

1 Like

We just finished optimizing AES, however due to some problems with codegen we couldn’t optimize it fully

2 Likes

oh my, i forgot to change the “hello world” to the data variable, my fault!
still very nice to see the optimizations you are bringing

1 Like