diff options
Diffstat (limited to 'sha1.lua')
-rw-r--r-- | sha1.lua | 169 |
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 + |