Introduction
Module was originally written by Egor Skriptunoff and distributed under an MIT license.
It can be found here: pure_lua_SHA/sha2.lua at master · Egor-Skriptunoff/pure_lua_SHA · GitHub
That version was around 3000 lines long, and supported Lua versions 5.1, 5.2, 5.3, 5.4, and LuaJIT.
Although that is super cool, Roblox only uses Lua 5.1, so that was extreme overkill.
I worked (with @howmanysmaII’s guidance) to port it to Roblox in a way that doesn’t overcomplicate it with support of unreachable cases. then, howmanysmall came in to do some final optimizations that really squeeze out the most performance possible.
After quite a bit of work and benchmarking, this is what we were left with.
Enjoy!
Features
This module contains functions to calculate SHA digest:
- MD5, SHA-1,
- SHA-224, SHA-256, SHA-512/224, SHA-512/256, SHA-384, SHA-512,
- SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256,
- HMAC
Additionally, it has a few extra utility functions:
- hex_to_bin
- base64_to_bin
- bin_to_base64
Usage
Input data should be a string
Result (SHA digest) is returned in hexadecimal representation as a string of lowercase hex digits.
Simplest usage example:
local HashLib = require(script.HashLib)
local your_hash = HashLib.sha256("your string")
API
HashLib.md5
HashLib.sha1
SHA2 hash functions:
HashLib.sha224
HashLib.sha256
HashLib.sha512_224
HashLib.sha512_256
HashLib.sha384
HashLib.sha512
SHA3 hash functions:
HashLib.sha3_224
HashLib.sha3_256
HashLib.sha3_384
HashLib.sha3_512
HashLib.shake128
HashLib.shake256
Misc utilities:
HashLib.hmac (Applicable to any hash function from this module except SHAKE*)
HashLib.hex_to_bin
HashLib.base64_to_bin
HashLib.bin_to_base64
Benchmarking and Comparison
For the benchmarking test, I could only do 350 hashes per test, because with higher amounts the unoptimized version caused a script timeout. So, that’s a sign that I did a really good job. ![]()
Benchmark Script
--[=[
Messy code to run some tests
Note that CPU usage goes through the roof while doing this
--]=]
local HashLib = require(script.HashLib)
local HashLib_Unoptimized = require(script.HashLib_Unoptimized)
local ipairs = ipairs
local TestAmount = 350
-------------------------------------------------------------------------------------------------
local function TestCompare(FunctionName, Arg)
wait(0.1)
print("\n### Testing",FunctionName)
-- test my version
local Times = {}
for i=1, TestAmount do
local t = tick()
local hash = HashLib[FunctionName](Arg)
local TimeTaken = tick()-t
Times[#Times+1] = TimeTaken
end
local Total = 0
for _,Time in ipairs(Times) do
Total = Total+Time
end
print("* Optimized HashLib took `".. string.format("%.10f", Total).. "` to do ".. TestAmount .." "..FunctionName.." hashes with each one taking `".. string.format("%.10f", Total/#Times) .. "` on average")
-- test original
local UnoptTimes = {}
for i=1, TestAmount do
local t = tick()
local hash = HashLib_Unoptimized[FunctionName]("This is my input")
UnoptTimes[#UnoptTimes+1] = tick()-t
end
local UnoptTotal = 0
for _,UnoptTime in ipairs(UnoptTimes) do
UnoptTotal = UnoptTotal+UnoptTime
end
print("* Unoptimized HashLib took `".. string.format("%.10f", UnoptTotal).. "` to do ".. TestAmount .." "..FunctionName.." hashes with each one taking `".. string.format("%.10f", UnoptTotal/#UnoptTimes) .. "` on average")
end
wait(2) -- Let game state finish initializing before we eat CPU
TestCompare("md5", "hash this string please")
TestCompare("sha1", "hash this string please")
TestCompare("sha224", "hash this string please")
TestCompare("sha256", "hash this string please")
TestCompare("sha512_224", "hash this string please")
TestCompare("sha512_256", "hash this string please")
TestCompare("sha384", "hash this string please")
TestCompare("sha512", "hash this string please")
TestCompare("sha3_224", "hash this string please")
TestCompare("sha3_256", "hash this string please")
TestCompare("sha3_384", "hash this string please")
TestCompare("sha3_512", "hash this string please")
Benchmark Results
Testing md5
- Optimized HashLib took
0.0990054607to do 350 md5 hashes with each one taking0.0002828727on average - Unoptimized HashLib took
0.4256963730to do 350 md5 hashes with each one taking0.0012162754on average
Testing sha1
- Optimized HashLib took
0.1464135647to do 350 sha1 hashes with each one taking0.0004183245on average - Unoptimized HashLib took
1.1473040581to do 350 sha1 hashes with each one taking0.0032780116on average
Testing sha224
- Optimized HashLib took
0.2236335278to do 350 sha224 hashes with each one taking0.0006389529on average - Unoptimized HashLib took
2.3714523315to do 350 sha224 hashes with each one taking0.0067755781on average
Testing sha256
- Optimized HashLib took
0.2243692875to do 350 sha256 hashes with each one taking0.0006410551on average - Unoptimized HashLib took
2.3787267208to do 350 sha256 hashes with each one taking0.0067963621on average
Testing sha512_224
- Optimized HashLib took
0.7043654919to do 350 sha512_224 hashes with each one taking0.0020124728on average - Unoptimized HashLib took
6.3255631924to do 350 sha512_224 hashes with each one taking0.0180730377on average
Testing sha512_256
- Optimized HashLib took
0.7125756741to do 350 sha512_256 hashes with each one taking0.0020359305on average - Unoptimized HashLib took
6.3216791153to do 350 sha512_256 hashes with each one taking0.0180619403on average
Testing sha384
- Optimized HashLib took
0.7102406025to do 350 sha384 hashes with each one taking0.0020292589on average - Unoptimized HashLib took
6.3122458458to do 350 sha384 hashes with each one taking0.0180349881on average
Testing sha512
- Optimized HashLib took
0.7212450504to do 350 sha512 hashes with each one taking0.0020607001on average - Unoptimized HashLib took
6.3489837646to do 350 sha512 hashes with each one taking0.0181399536on average
Testing sha3_224
- Optimized HashLib took
0.8299937248to do 350 sha3_224 hashes with each one taking0.0023714106on average - Unoptimized HashLib took
13.0032684803to do 350 sha3_224 hashes with each one taking0.0371521957on average
Testing sha3_256
- Optimized HashLib took
0.8421859741to do 350 sha3_256 hashes with each one taking0.0024062456on average - Unoptimized HashLib took
12.9854648113to do 350 sha3_256 hashes with each one taking0.0371013280on average
Testing sha3_384
- Optimized HashLib took
0.8211791515to do 350 sha3_384 hashes with each one taking0.0023462261on average - Unoptimized HashLib took
12.9778954983to do 350 sha3_384 hashes with each one taking0.0370797014on average
Testing sha3_512
- Optimized HashLib took
0.8204553127to do 350 sha3_512 hashes with each one taking0.0023441580on average - Unoptimized HashLib took
12.8966257572to do 350 sha3_512 hashes with each one taking0.0368475022on average
The optimized version is many times faster in almost all cases. ![]()
