You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
4.4 KiB
Lua

--[[lit-meta
name = "creationix/sha1"
version = "1.0.4"
homepage = "https://github.com/luvit/lit/blob/master/deps/sha1.lua"
description = "Pure Lua implementation of SHA1 using bitop"
authors = {
"Tim Caswell"
}
]]
-- http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
local bit = require('bit')
local band = bit.band
local bor = bit.bor
local bxor = bit.bxor
local lshift = bit.lshift
local rshift = bit.rshift
local rol = bit.rol
local tobit = bit.tobit
local tohex = bit.tohex
local byte = string.byte
local concat = table.concat
local floor = table.floor
local hasFFi, ffi = pcall(require, "ffi")
local newBlock = hasFFi and function ()
return ffi.new("uint32_t[80]")
end or function ()
local t = {}
for i = 0, 79 do
t[i] = 0
end
return t
end
local shared = newBlock()
local function unsigned(n)
return n < 0 and (n + 0x100000000) or n
end
local function create(sync)
local h0 = 0x67452301
local h1 = 0xEFCDAB89
local h2 = 0x98BADCFE
local h3 = 0x10325476
local h4 = 0xC3D2E1F0
-- The first 64 bytes (16 words) is the data chunk
local W = sync and shared or newBlock()
local offset = 0
local shift = 24
local totalLength = 0
local update, write, processBlock, digest
-- The user gave us more data. Store it!
function update(chunk)
local length = #chunk
totalLength = totalLength + length * 8
for i = 1, length do
write(byte(chunk, i))
end
end
function write(data)
W[offset] = bor(W[offset], lshift(band(data, 0xff), shift))
if shift > 0 then
shift = shift - 8
else
offset = offset + 1
shift = 24
end
if offset == 16 then
return processBlock()
end
end
-- No more data will come, pad the block, process and return the result.
function digest()
-- Pad
write(0x80)
if offset > 14 or (offset == 14 and shift < 24) then
processBlock()
end
offset = 14
shift = 24
-- 64-bit length big-endian
write(0x00) -- numbers this big aren't accurate in lua anyway
write(0x00) -- ..So just hard-code to zero.
write(totalLength > 0xffffffffff and floor(totalLength / 0x10000000000) or 0x00)
write(totalLength > 0xffffffff and floor(totalLength / 0x100000000) or 0x00)
for s = 24, 0, -8 do
write(rshift(totalLength, s))
end
-- At this point one last processBlock() should trigger and we can pull out the result.
return concat {
tohex(h0),
tohex(h1),
tohex(h2),
tohex(h3),
tohex(h4)
}
end
-- We have a full block to process. Let's do it!
function processBlock()
-- Extend the sixteen 32-bit words into eighty 32-bit words:
for i = 16, 79, 1 do
W[i] =
rol(bxor(W[i - 3], W[i - 8], W[i - 14], W[i - 16]), 1)
end
-- print("Block Contents:")
-- for i = 0, 15 do
-- print(string.format(" W[%d] = %s", i, tohex(W[i])))
-- end
-- print()
-- Initialize hash value for this chunk:
local a = h0
local b = h1
local c = h2
local d = h3
local e = h4
local f, k
-- print(" A B C D E")
-- local format =
-- "t=%02d: %s %s %s %s %s"
-- Main loop:
for t = 0, 79 do
if t < 20 then
f = bxor(d, band(b, bxor(c, d)))
k = 0x5A827999
elseif t < 40 then
f = bxor(b, c, d)
k = 0x6ED9EBA1
elseif t < 60 then
f = bor(band(b, c), (band(d, bor(b, c))))
k = 0x8F1BBCDC
else
f = bxor(b, c, d)
k = 0xCA62C1D6
end
e, d, c, b, a =
d,
c,
rol(b, 30),
a,
tobit(
unsigned(rol(a, 5)) +
unsigned(f) +
unsigned(e) +
unsigned(k) +
W[t]
)
-- print(string.format(format, t, tohex(a), tohex(b), tohex(c), tohex(d), tohex(e)))
end
-- Add this chunk's hash to result so far:
h0 = tobit(unsigned(h0) + a)
h1 = tobit(unsigned(h1) + b)
h2 = tobit(unsigned(h2) + c)
h3 = tobit(unsigned(h3) + d)
h4 = tobit(unsigned(h4) + e)
-- The block is now reusable.
offset = 0
for i = 0, 15 do
W[i] = 0
end
end
return {
update = update,
digest = digest
}
end
return function (buffer)
-- Pass in false or nil to get a streaming interface.
if not buffer then
return create(false)
end
local shasum = create(true)
shasum.update(buffer)
return shasum.digest()
end