SHOW:
|
|
- or go back to the newest paste.
1 | ------------------------------------------------------------------------------- | |
2 | -- SHA-1 secure hash computation, and HMAC-SHA1 signature computation, | |
3 | -- in pure Lua (tested on Lua 5.1) | |
4 | -- License: GPL | |
5 | -- | |
6 | -- Usage: | |
7 | -- local hash_as_hex = sha1(message) -- returns a hex string | |
8 | -- local hash_as_data = sha1_binary(message) -- returns raw bytes | |
9 | -- | |
10 | -- Pass sha1() a string, and it returns a hash as a 40-character hex string. | |
11 | -- | |
12 | -- The "_binary" version does the same, but returns the 20-byte string of raw | |
13 | -- data that the 40-byte hex string represents. | |
14 | -- | |
15 | ------------------------------------------------------------------------------- | |
16 | -- based on Zet's implementation (which I found too big) | |
17 | -- > http://cube3d.de/uploads/Main/sha1.txt | |
18 | -- > > based on Jeffrey Friedl's implementation (which I found a bit too slow) | |
19 | -- > > jfriedl@yahoo.com | |
20 | -- > > http://regex.info/blog/ | |
21 | -- > > Version 1 [May 28, 2009] | |
22 | -- Algorithm: http://www.itl.nist.gov/fipspubs/fip180-1.htm | |
23 | ||
24 | ------------------------------------------------------------------------------- | |
25 | ------------------------------------------------------------------------------- | |
26 | ||
27 | -- set this to false if you don't want to build several 64k sized tables when | |
28 | -- loading this file (takes a while but grants a boost of factor 13) | |
29 | local cfg_caching = false | |
30 | ||
31 | -- local storing of global functions (minor speedup) | |
32 | local floor,modf = math.floor,math.modf | |
33 | local char,format,rep = string.char,string.format,string.rep | |
34 | ||
35 | -- merge 4 bytes to an 32 bit word | |
36 | local function bytes_to_w32 (a,b,c,d) return a*0x1000000+b*0x10000+c*0x100+d end | |
37 | -- split a 32 bit word into four 8 bit numbers | |
38 | local function w32_to_bytes (i) | |
39 | return floor(i/0x1000000)%0x100,floor(i/0x10000)%0x100,floor(i/0x100)%0x100,i%0x100 | |
40 | end | |
41 | ||
42 | -- shift the bits of a 32 bit word. Don't use negative values for "bits" | |
43 | local function w32_rot (bits,a) | |
44 | local b2 = 2^(32-bits) | |
45 | local a,b = modf(a/b2) | |
46 | return a+b*b2*(2^(bits)) | |
47 | end | |
48 | ||
49 | -- caching function for functions that accept 2 arguments, both of values between | |
50 | -- 0 and 255. The function to be cached is passed, all values are calculated | |
51 | -- during loading and a function is returned that returns the cached values (only) | |
52 | local function cache2arg (fn) | |
53 | if not cfg_caching then return fn end | |
54 | local lut = {} | |
55 | for i=0,0xffff do | |
56 | local a,b = floor(i/0x100),i%0x100 | |
57 | lut[i] = fn(a,b) | |
58 | end | |
59 | return function (a,b) | |
60 | return lut[a*0x100+b] | |
61 | end | |
62 | end | |
63 | ||
64 | -- splits an 8-bit number into 8 bits, returning all 8 bits as booleans | |
65 | local function byte_to_bits (b) | |
66 | local b = function (n) | |
67 | local b = floor(b/n) | |
68 | return b%2==1 | |
69 | end | |
70 | return b(1),b(2),b(4),b(8),b(16),b(32),b(64),b(128) | |
71 | end | |
72 | ||
73 | -- builds an 8bit number from 8 booleans | |
74 | local function bits_to_byte (a,b,c,d,e,f,g,h) | |
75 | local function n(b,x) return b and x or 0 end | |
76 | return n(a,1)+n(b,2)+n(c,4)+n(d,8)+n(e,16)+n(f,32)+n(g,64)+n(h,128) | |
77 | end | |
78 | ||
79 | -- debug function for visualizing bits in a string | |
80 | local function bits_to_string (a,b,c,d,e,f,g,h) | |
81 | local function x(b) return b and "1" or "0" end | |
82 | return ("%s%s%s%s %s%s%s%s"):format(x(a),x(b),x(c),x(d),x(e),x(f),x(g),x(h)) | |
83 | end | |
84 | ||
85 | -- debug function for converting a 8-bit number as bit string | |
86 | local function byte_to_bit_string (b) | |
87 | return bits_to_string(byte_to_bits(b)) | |
88 | end | |
89 | ||
90 | -- debug function for converting a 32 bit number as bit string | |
91 | local function w32_to_bit_string(a) | |
92 | if type(a) == "string" then return a end | |
93 | local aa,ab,ac,ad = w32_to_bytes(a) | |
94 | local s = byte_to_bit_string | |
95 | return ("%s %s %s %s"):format(s(aa):reverse(),s(ab):reverse(),s(ac):reverse(),s(ad):reverse()):reverse() | |
96 | end | |
97 | ||
98 | -- bitwise "and" function for 2 8bit number | |
99 | local band = cache2arg (function(a,b) | |
100 | local A,B,C,D,E,F,G,H = byte_to_bits(b) | |
101 | local a,b,c,d,e,f,g,h = byte_to_bits(a) | |
102 | return bits_to_byte( | |
103 | A and a, B and b, C and c, D and d, | |
104 | E and e, F and f, G and g, H and h) | |
105 | end) | |
106 | ||
107 | -- bitwise "or" function for 2 8bit numbers | |
108 | local bor = cache2arg(function(a,b) | |
109 | local A,B,C,D,E,F,G,H = byte_to_bits(b) | |
110 | local a,b,c,d,e,f,g,h = byte_to_bits(a) | |
111 | return bits_to_byte( | |
112 | A or a, B or b, C or c, D or d, | |
113 | E or e, F or f, G or g, H or h) | |
114 | end) | |
115 | ||
116 | -- bitwise "xor" function for 2 8bit numbers | |
117 | local bxor = cache2arg(function(a,b) | |
118 | local A,B,C,D,E,F,G,H = byte_to_bits(b) | |
119 | local a,b,c,d,e,f,g,h = byte_to_bits(a) | |
120 | return bits_to_byte( | |
121 | A ~= a, B ~= b, C ~= c, D ~= d, | |
122 | E ~= e, F ~= f, G ~= g, H ~= h) | |
123 | end) | |
124 | ||
125 | -- bitwise complement for one 8bit number | |
126 | local function bnot (x) | |
127 | return 255-(x % 256) | |
128 | end | |
129 | ||
130 | -- creates a function to combine to 32bit numbers using an 8bit combination function | |
131 | local function w32_comb(fn) | |
132 | return function (a,b) | |
133 | local aa,ab,ac,ad = w32_to_bytes(a) | |
134 | local ba,bb,bc,bd = w32_to_bytes(b) | |
135 | return bytes_to_w32(fn(aa,ba),fn(ab,bb),fn(ac,bc),fn(ad,bd)) | |
136 | end | |
137 | end | |
138 | ||
139 | -- create functions for and, xor and or, all for 2 32bit numbers | |
140 | local w32_and = w32_comb(band) | |
141 | local w32_xor = w32_comb(bxor) | |
142 | local w32_or = w32_comb(bor) | |
143 | ||
144 | -- xor function that may receive a variable number of arguments | |
145 | local function w32_xor_n (a,...) | |
146 | local aa,ab,ac,ad = w32_to_bytes(a) | |
147 | for i=1,select('#',...) do | |
148 | local ba,bb,bc,bd = w32_to_bytes(select(i,...)) | |
149 | aa,ab,ac,ad = bxor(aa,ba),bxor(ab,bb),bxor(ac,bc),bxor(ad,bd) | |
150 | end | |
151 | return bytes_to_w32(aa,ab,ac,ad) | |
152 | end | |
153 | ||
154 | -- combining 3 32bit numbers through binary "or" operation | |
155 | local function w32_or3 (a,b,c) | |
156 | local aa,ab,ac,ad = w32_to_bytes(a) | |
157 | local ba,bb,bc,bd = w32_to_bytes(b) | |
158 | local ca,cb,cc,cd = w32_to_bytes(c) | |
159 | return bytes_to_w32( | |
160 | bor(aa,bor(ba,ca)), bor(ab,bor(bb,cb)), bor(ac,bor(bc,cc)), bor(ad,bor(bd,cd)) | |
161 | ) | |
162 | end | |
163 | ||
164 | -- binary complement for 32bit numbers | |
165 | local function w32_not (a) | |
166 | return 4294967295-(a % 4294967296) | |
167 | end | |
168 | ||
169 | -- adding 2 32bit numbers, cutting off the remainder on 33th bit | |
170 | local function w32_add (a,b) return (a+b) % 4294967296 end | |
171 | ||
172 | -- adding n 32bit numbers, cutting off the remainder (again) | |
173 | local function w32_add_n (a,...) | |
174 | for i=1,select('#',...) do | |
175 | a = (a+select(i,...)) % 4294967296 | |
176 | end | |
177 | return a | |
178 | end | |
179 | -- converting the number to a hexadecimal string | |
180 | local function w32_to_hexstring (w) return format("%08x",w) end | |
181 | ||
182 | -- calculating the SHA1 for some text | |
183 | function sha1(msg) | |
184 | local H0,H1,H2,H3,H4 = 0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0 | |
185 | local msg_len_in_bits = #msg * 8 | |
186 | ||
187 | local first_append = char(0x80) -- append a '1' bit plus seven '0' bits | |
188 | ||
189 | local non_zero_message_bytes = #msg +1 +8 -- the +1 is the appended bit 1, the +8 are for the final appended length | |
190 | local current_mod = non_zero_message_bytes % 64 | |
191 | local second_append = current_mod>0 and rep(char(0), 64 - current_mod) or "" | |
192 | ||
193 | -- now to append the length as a 64-bit number. | |
194 | local B1, R1 = modf(msg_len_in_bits / 0x01000000) | |
195 | local B2, R2 = modf( 0x01000000 * R1 / 0x00010000) | |
196 | local B3, R3 = modf( 0x00010000 * R2 / 0x00000100) | |
197 | local B4 = 0x00000100 * R3 | |
198 | ||
199 | local L64 = char( 0) .. char( 0) .. char( 0) .. char( 0) -- high 32 bits | |
200 | .. char(B1) .. char(B2) .. char(B3) .. char(B4) -- low 32 bits | |
201 | ||
202 | msg = msg .. first_append .. second_append .. L64 | |
203 | ||
204 | assert(#msg % 64 == 0) | |
205 | ||
206 | local chunks = #msg / 64 | |
207 | ||
208 | local W = { } | |
209 | local start, A, B, C, D, E, f, K, TEMP | |
210 | local chunk = 0 | |
211 | ||
212 | while chunk < chunks do | |
213 | -- | |
214 | -- break chunk up into W[0] through W[15] | |
215 | -- | |
216 | start,chunk = chunk * 64 + 1,chunk + 1 | |
217 | ||
218 | for t = 0, 15 do | |
219 | W[t] = bytes_to_w32(msg:byte(start, start + 3)) | |
220 | start = start + 4 | |
221 | end | |
222 | ||
223 | -- | |
224 | -- build W[16] through W[79] | |
225 | -- | |
226 | for t = 16, 79 do | |
227 | -- For t = 16 to 79 let Wt = S1(Wt-3 XOR Wt-8 XOR Wt-14 XOR Wt-16). | |
228 | W[t] = w32_rot(1, w32_xor_n(W[t-3], W[t-8], W[t-14], W[t-16])) | |
229 | end | |
230 | ||
231 | A,B,C,D,E = H0,H1,H2,H3,H4 | |
232 | ||
233 | for t = 0, 79 do | |
234 | if t <= 19 then | |
235 | -- (B AND C) OR ((NOT B) AND D) | |
236 | f = w32_or(w32_and(B, C), w32_and(w32_not(B), D)) | |
237 | K = 0x5A827999 | |
238 | elseif t <= 39 then | |
239 | -- B XOR C XOR D | |
240 | f = w32_xor_n(B, C, D) | |
241 | K = 0x6ED9EBA1 | |
242 | elseif t <= 59 then | |
243 | -- (B AND C) OR (B AND D) OR (C AND D | |
244 | f = w32_or3(w32_and(B, C), w32_and(B, D), w32_and(C, D)) | |
245 | K = 0x8F1BBCDC | |
246 | else | |
247 | -- B XOR C XOR D | |
248 | f = w32_xor_n(B, C, D) | |
249 | K = 0xCA62C1D6 | |
250 | end | |
251 | ||
252 | -- TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt; | |
253 | A,B,C,D,E = w32_add_n(w32_rot(5, A), f, E, W[t], K), | |
254 | A, w32_rot(30, B), C, D | |
255 | end | |
256 | -- Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. | |
257 | H0,H1,H2,H3,H4 = w32_add(H0, A),w32_add(H1, B),w32_add(H2, C),w32_add(H3, D),w32_add(H4, E) | |
258 | end | |
259 | local f = w32_to_hexstring | |
260 | return f(H0) .. f(H1) .. f(H2) .. f(H3) .. f(H4) | |
261 | end | |
262 | ||
263 | local function hex_to_binary(hex) | |
264 | return hex:gsub('..', function(hexval) | |
265 | return string.char(tonumber(hexval, 16)) | |
266 | end) | |
267 | end | |
268 | ||
269 | function sha1_binary(msg) | |
270 | return hex_to_binary(sha1(msg)) | |
271 | end | |
272 | ||
273 | local xor_with_0x5c = {} | |
274 | local xor_with_0x36 = {} | |
275 | -- building the lookuptables ahead of time (instead of littering the source code | |
276 | -- with precalculated values) | |
277 | for i=0,0xff do | |
278 | xor_with_0x5c[char(i)] = char(bxor(i,0x5c)) | |
279 | xor_with_0x36[char(i)] = char(bxor(i,0x36)) | |
280 | end | |
281 | ||
282 | -- String Randomizer API -- | |
283 | local chars = {} | |
284 | for loop = 0, 255 do | |
285 | chars[loop+1] = string.char(loop) | |
286 | end | |
287 | local string = table.concat(chars) | |
288 | ||
289 | local built = {['.'] = chars} | |
290 | ||
291 | local addLookup = function(charSet) | |
292 | local substitute = string.gsub(string, '[^'..charSet..']', '') | |
293 | local lookup = {} | |
294 | for loop = 1, string.len(substitute) do | |
295 | lookup[loop] = string.sub(substitute, loop, loop) | |
296 | end | |
297 | built[charSet] = lookup | |
298 | ||
299 | return lookup | |
300 | end | |
301 | ||
302 | function random(length, charSet) | |
303 | -- length (number) | |
304 | -- charSet (string, optional); e.g. %l%d for lower case letters and digits | |
305 | ||
306 | local charSet = charSet or '.' | |
307 | ||
308 | if charSet == '' then | |
309 | return '' | |
310 | else | |
311 | local result = {} | |
312 | local lookup = built[charSet] or addLookup(charSet) | |
313 | local range = table.getn(lookup) | |
314 | ||
315 | for loop = 1,length do | |
316 | result[loop] = lookup[math.random(1, range)] | |
317 | end | |
318 | ||
319 | return table.concat(result) | |
320 | end | |
321 | end | |
322 | ||
323 | function randomULN(length) | |
324 | return random(length, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789") | |
325 | end | |
326 | ||
327 | -- Hashing API -- | |
328 | function hashPassword(password) | |
329 | local salt = string.sub(sha1(randomULN(20)), 1, 12) | |
330 | local hash = sha1(salt .. password) | |
331 | local saltPos = string.len(password) | |
332 | if saltPos > string.len(hash) then | |
333 | saltPos = string.len(hash) | |
334 | end | |
335 | return string.sub(hash, 1, saltPos) .. salt .. string.sub(hash, saltPos + 1, string.len(hash)) | |
336 | end | |
337 | ||
338 | function checkPassword(input, correct) | |
339 | local saltPos = string.len(correct) | |
340 | if saltPos > string.len(input) - 12 then | |
341 | saltPos = string.len(input) - 12 | |
342 | end | |
343 | local salt = string.sub(input, saltPos + 1, saltPos + 12) | |
344 | local password = sha1(salt .. correct) | |
345 | return (password == (string.sub(input, 1, saltPos) .. string.sub(input, saltPos + 13, string.len(input)))) | |
346 | end | |
347 | ||
348 | -- crypt.checkPassword(crypt.hashPassword("password"), "password") |