summaryrefslogtreecommitdiff
path: root/sha1.lua
diff options
context:
space:
mode:
Diffstat (limited to 'sha1.lua')
-rw-r--r--sha1.lua169
1 files changed, 169 insertions, 0 deletions
diff --git a/sha1.lua b/sha1.lua
new file mode 100644
index 0000000..7451595
--- /dev/null
+++ b/sha1.lua
@@ -0,0 +1,169 @@
+-- from https://github.com/mpeterv/sha1
+-- by Enrique GarcĂ­a Cota, Eike Decker, Jeffrey Friedl, Peter Melnichenko
+-- (MIT license)
+
+local sha1 = {}
+
+-- Merges four bytes into a uint32 number.
+local function bytes_to_uint32(a, b, c, d)
+ return a * 0x1000000 + b * 0x10000 + c * 0x100 + d
+end
+
+-- Splits a uint32 number into four bytes.
+local function uint32_to_bytes(a)
+ local a4 = a % 256
+ a = (a - a4) / 256
+ local a3 = a % 256
+ a = (a - a3) / 256
+ local a2 = a % 256
+ local a1 = (a - a2) / 256
+ return a1, a2, a3, a4
+end
+
+local function uint32_lrot(a, bits)
+ return ((a << bits) & 0xFFFFFFFF) | (a >> (32 - bits))
+end
+
+local function uint32_ternary(a, b, c)
+ -- c ~ (a & (b ~ c)) has less bitwise operations than (a & b) | (~a & c).
+ return c ~ (a & (b ~ c))
+end
+
+local function uint32_majority(a, b, c)
+ -- (a & (b | c)) | (b & c) has less bitwise operations than (a & b) | (a & c) | (b & c).
+ return (a & (b | c)) | (b & c)
+end
+
+local sbyte = string.byte
+local schar = string.char
+local sformat = string.format
+local srep = string.rep
+
+local function hex_to_binary(hex)
+ return (hex:gsub("..", function(hexval)
+ return schar(tonumber(hexval, 16))
+ end))
+end
+
+-- Calculates SHA1 for a string, returns it encoded as 40 hexadecimal digits.
+function sha1.sha1(str)
+ -- Input preprocessing.
+ -- First, append a `1` bit and seven `0` bits.
+ local first_append = schar(0x80)
+
+ -- Next, append some zero bytes to make the length of the final message a multiple of 64.
+ -- Eight more bytes will be added next.
+ local non_zero_message_bytes = #str + 1 + 8
+ local second_append = srep(schar(0), -non_zero_message_bytes % 64)
+
+ -- Finally, append the length of the original message in bits as a 64-bit number.
+ -- Assume that it fits into the lower 32 bits.
+ local third_append = schar(0, 0, 0, 0, uint32_to_bytes(#str * 8))
+
+ str = str .. first_append .. second_append .. third_append
+ assert(#str % 64 == 0)
+
+ -- Initialize hash value.
+ local h0 = 0x67452301
+ local h1 = 0xEFCDAB89
+ local h2 = 0x98BADCFE
+ local h3 = 0x10325476
+ local h4 = 0xC3D2E1F0
+
+ local w = {}
+
+ -- Process the input in successive 64-byte chunks.
+ for chunk_start = 1, #str, 64 do
+ -- Load the chunk into W[0..15] as uint32 numbers.
+ local uint32_start = chunk_start
+
+ for i = 0, 15 do
+ w[i] = bytes_to_uint32(sbyte(str, uint32_start, uint32_start + 3))
+ uint32_start = uint32_start + 4
+ end
+
+ -- Extend the input vector.
+ for i = 16, 79 do
+ w[i] = uint32_lrot(w[i - 3] ~ w[i - 8] ~ w[i - 14] ~ w[i - 16], 1)
+ end
+
+ -- Initialize hash value for this chunk.
+ local a = h0
+ local b = h1
+ local c = h2
+ local d = h3
+ local e = h4
+
+ -- Main loop.
+ for i = 0, 79 do
+ local f
+ local k
+
+ if i <= 19 then
+ f = uint32_ternary(b, c, d)
+ k = 0x5A827999
+ elseif i <= 39 then
+ f = b ~ c ~ d
+ k = 0x6ED9EBA1
+ elseif i <= 59 then
+ f = uint32_majority(b, c, d)
+ k = 0x8F1BBCDC
+ else
+ f = b ~ c ~ d
+ k = 0xCA62C1D6
+ end
+
+ local temp = (uint32_lrot(a, 5) + f + e + k + w[i]) % 4294967296
+ e = d
+ d = c
+ c = uint32_lrot(b, 30)
+ b = a
+ a = temp
+ end
+
+ -- Add this chunk's hash to result so far.
+ h0 = (h0 + a) % 4294967296
+ h1 = (h1 + b) % 4294967296
+ h2 = (h2 + c) % 4294967296
+ h3 = (h3 + d) % 4294967296
+ h4 = (h4 + e) % 4294967296
+ end
+
+ return sformat("%08x%08x%08x%08x%08x", h0, h1, h2, h3, h4)
+end
+
+function sha1.binary(str)
+ return hex_to_binary(sha1.sha1(str))
+end
+
+-- Precalculate replacement tables.
+local xor_with_0x5c = {}
+local xor_with_0x36 = {}
+
+for i = 0, 0xff do
+ xor_with_0x5c[schar(i)] = schar(0x5c ~ i)
+ xor_with_0x36[schar(i)] = schar(0x36 ~ i)
+end
+
+-- 512 bits.
+local BLOCK_SIZE = 64
+
+function sha1.hmac(key, text)
+ if #key > BLOCK_SIZE then
+ key = sha1.binary(key)
+ end
+
+ local key_xord_with_0x36 = key:gsub('.', xor_with_0x36) .. srep(schar(0x36), BLOCK_SIZE - #key)
+ local key_xord_with_0x5c = key:gsub('.', xor_with_0x5c) .. srep(schar(0x5c), BLOCK_SIZE - #key)
+
+ return sha1.sha1(key_xord_with_0x5c .. sha1.binary(key_xord_with_0x36 .. text))
+end
+
+function sha1.hmac_binary(key, text)
+ return hex_to_binary(sha1.hmac(key, text))
+end
+
+setmetatable(sha1, {__call = function(_, str) return sha1.sha1(str) end})
+
+return sha1
+