|
1 | 1 | #!/usr/bin/env lua |
2 | 2 |
|
3 | | ---- Read a files hash from it's current point to the end |
4 | | ---- @param file file The file to read to the end |
5 | | ---- @return string The md5 checksum result of the file |
| 3 | +--- Read file hash |
| 4 | +--- @param file file The file to read (to end) |
| 5 | +--- @return string MD5 checksum |
6 | 6 | function md5_file(file) |
7 | | - local function Md5Transform(chunk, A, B, C, D) -- Core MD5 transformation |
8 | | - local F = function(x, y, z) |
9 | | - return (x & y) | (~x & z) |
| 7 | + local function Md5Transform(chunk, A, B, C, D) |
| 8 | + local F = function(x, y, z) return (x & y) | (~x & z) end |
| 9 | + local G = function(x, y, z) return (x & z) | (y & ~z) end |
| 10 | + local H = function(x, y, z) return x ~ y ~ z end |
| 11 | + local I = function(x, y, z) return y ~ (x | ~z) end |
| 12 | + local function LS(x, n) return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF end |
| 13 | + local wo, sh = {}, {{ 7, 12, 17, 22 }, { 5, 9, 14, 20 }, { 4, 11, 16, 23 }, { 6, 10, 15, 21 }} |
| 14 | + for i = 0, 15 do |
| 15 | + local o = i * 4 + 1 |
| 16 | + wo[i + 1] = |
| 17 | + string.byte(chunk, o) | |
| 18 | + (string.byte(chunk, o + 1) << 8) | |
| 19 | + (string.byte(chunk, o + 2) << 16) | |
| 20 | + (string.byte(chunk, o + 3) << 24) |
10 | 21 | end |
11 | | - |
12 | | - local G = function(x, y, z) |
13 | | - return (x & z) | (y & ~z) |
14 | | - end |
15 | | - |
16 | | - local H = function(x, y, z) |
17 | | - return x ~ y ~ z |
18 | | - end |
19 | | - |
20 | | - local I = function(x, y, z) |
21 | | - return y ~ (x | ~z) |
22 | | - end |
23 | | - |
24 | | - local function LShift32(x, n) |
25 | | - return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF |
26 | | - end |
27 | | - |
28 | | - local words, shifts = {}, { |
29 | | - { 7, 12, 17, 22 }, |
30 | | - { 5, 9, 14, 20 }, |
31 | | - { 4, 11, 16, 23 }, |
32 | | - { 6, 10, 15, 21 }, |
33 | | - } |
34 | | - |
35 | | - for i = 0, 15 do -- Break the chunk into 16 little-endian 32-bit words |
36 | | - local offset = i * 4 + 1 |
37 | | - words[i + 1] = string.byte(chunk, offset) | |
38 | | - (string.byte(chunk, offset + 1) << 8) | |
39 | | - (string.byte(chunk, offset + 2) << 16) | |
40 | | - (string.byte(chunk, offset + 3) << 24) |
41 | | - end |
42 | | - |
43 | 22 | local a, b, c, d = A, B, C, D |
44 | | - |
45 | | - for i = 1, 64 do -- Main loop: 64 rounds of transformations |
| 23 | + for i = 1, 64 do |
46 | 24 | local f, g |
47 | | - local round = math.floor((i - 1) / 16) + 1 |
48 | | - local shift = shifts[round][(i - 1) % 4 + 1] |
49 | | - |
50 | | - if round == 1 then |
| 25 | + local r = math.floor((i - 1) / 16) + 1 |
| 26 | + local s = sh[r][(i - 1) % 4 + 1] |
| 27 | + if r == 1 then |
51 | 28 | f = F(b, c, d) |
52 | 29 | g = (i - 1) % 16 |
53 | | - elseif round == 2 then |
| 30 | + elseif r == 2 then |
54 | 31 | f = G(b, c, d) |
55 | 32 | g = (5 * (i - 1) + 1) % 16 |
56 | | - elseif round == 3 then |
| 33 | + elseif r == 3 then |
57 | 34 | f = H(b, c, d) |
58 | 35 | g = (3 * (i - 1) + 5) % 16 |
59 | | - elseif round == 4 then |
| 36 | + elseif r == 4 then |
60 | 37 | f = I(b, c, d) |
61 | 38 | g = (7 * (i - 1)) % 16 |
62 | 39 | end |
63 | | - |
64 | | - local temp = d |
| 40 | + local t = d |
65 | 41 | d = c |
66 | 42 | c = b |
67 | | - b = (b + LShift32((a + f + words[g + 1] + |
68 | | - T[i]) & 0xFFFFFFFF, shift)) & 0xFFFFFFFF |
69 | | - a = temp |
| 43 | + b = (b + LS((a + f + wo[g + 1] + T[i]) & 0xFFFFFFFF, s)) & 0xFFFFFFFF |
| 44 | + a = t |
70 | 45 | end |
71 | | - |
72 | | - -- Add chunk's hash to the result so far |
73 | 46 | A = (A + a) & 0xFFFFFFFF |
74 | 47 | B = (B + b) & 0xFFFFFFFF |
75 | 48 | C = (C + c) & 0xFFFFFFFF |
76 | 49 | D = (D + d) & 0xFFFFFFFF |
77 | | - |
78 | 50 | return A, B, C, D |
79 | 51 | end |
80 | | - |
81 | 52 | local function Md5Pad(messageLength) |
82 | | - local msg_len = messageLength * 8 -- Message length in bits |
83 | | - local padding = "\128" -- Initial padding |
84 | | - local pad_len = (56 - (messageLength % 64)) -- 448 mod 512 |
85 | | - |
86 | | - if pad_len <= 0 then |
87 | | - pad_len = pad_len + 64 |
88 | | - end |
89 | | - |
90 | | - padding = padding .. string.rep("\0", pad_len - 1) |
91 | | - |
92 | | - -- Append the original message length as a 64-bit little-endian integer |
93 | | - for i = 0, 7 do |
94 | | - padding = padding .. string.char((msg_len >> (8 * i)) & 0xFF) |
95 | | - end |
96 | | - |
97 | | - return padding |
| 53 | + local ml = messageLength * 8 |
| 54 | + local p = "\128" |
| 55 | + local pl = (56 - (messageLength % 64)) |
| 56 | + if pl <= 0 then pl = pl + 64 end |
| 57 | + p = p .. string.rep("\0", pl - 1) |
| 58 | + for i = 0, 7 do p = p .. string.char((ml >> (8 * i)) & 0xFF) end |
| 59 | + return p |
98 | 60 | end |
99 | | - |
100 | | - local A, B, C, D, len = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0 |
| 61 | + local A, B, C, D, l = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0 |
101 | 62 |
|
102 | 63 | while true do |
103 | | - local chunk = file:read(64) -- Read in chunks of 64 bytes (512 bits) |
104 | | - |
105 | | - if not chunk then break end |
106 | | - len = len + #chunk |
107 | | - |
108 | | - -- If it's the final chunk, apply padding |
109 | | - if #chunk < 64 then chunk = chunk .. Md5Pad(len) end |
110 | | - A, B, C, D = Md5Transform(chunk, A, B, C, D) |
111 | | - |
112 | | - -- Stop processing after the padded block |
113 | | - if #chunk > 64 then break end |
| 64 | + local c = file:read(64) |
| 65 | + if not c then break end |
| 66 | + l = l + #c |
| 67 | + if #c < 64 then c = c .. Md5Pad(l) end |
| 68 | + A, B, C, D = Md5Transform(c, A, B, C, D) |
| 69 | + if #c > 64 then break end |
114 | 70 | end |
115 | | - |
116 | | - -- Format output as a hexadecimal string in little-endian order |
117 | 71 | local function to_hex(x) |
118 | 72 | return string.format("%02x%02x%02x%02x", |
119 | 73 | x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF) |
120 | 74 | end |
121 | | - |
122 | 75 | return to_hex(A) .. to_hex(B) .. to_hex(C) .. to_hex(D) |
123 | 76 | end |
124 | 77 |
|
125 | | -local function PredefineConstants() |
126 | | - T = {} -- Predefined MD5 constants of sine-based shifts (T values) |
127 | | - for i = 1, 64 do |
128 | | - T[i] = math.floor(2 ^ 32 * math.abs(math.sin(i))) |
129 | | - end |
| 78 | +local function init() |
| 79 | + T = {} |
| 80 | + for i = 1, 64 do T[i] = math.floor(2 ^ 32 * math.abs(math.sin(i))) end |
130 | 81 | end |
131 | 82 |
|
132 | | -if LIB then PredefineConstants() return end |
133 | | - |
| 83 | +if LIB then init() return end |
134 | 84 | if #arg < 1 then |
135 | | - -- Show accurate Lua binary and script location |
136 | 85 | print((arg[-1] or "?") .. " " .. (arg[0] or "?") .. " [FILE]...") |
137 | 86 | os.exit(1) |
138 | 87 | else |
139 | | - PredefineConstants() |
140 | | - |
| 88 | + init() |
141 | 89 | for i = 1, #arg do |
142 | | - local file, err = io.open(arg[i], "rb") |
143 | | - |
144 | | - if file then |
145 | | - local sum = md5_file(file) |
146 | | - file:close() |
147 | | - |
| 90 | + local f, e = io.open(arg[i], "rb") |
| 91 | + if f then |
| 92 | + local sum = md5_file(f) f:close() |
148 | 93 | if sum then |
149 | 94 | print(sum .. " " .. arg[i]) |
150 | 95 | else |
151 | | - print(arg[i] .. ": " .. "Unknown error") |
152 | | - os.exit(-1) |
| 96 | + print(arg[i] .. ": " .. "Unknown error") os.exit(-1) |
153 | 97 | end |
154 | 98 | else |
155 | | - print(arg[i] .. ": " .. err) |
156 | | - os.exit(1) |
| 99 | + print(arg[i] .. ": " .. e) os.exit(1) |
157 | 100 | end |
158 | 101 | end |
159 | 102 | end |
0 commit comments