1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
|