|
1 | 1 | #!/usr/bin/env lua |
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 |
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) |
10 | | - 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 | | - local a, b, c, d = A, B, C, D |
44 | | - |
45 | | - for i = 1, 64 do -- Main loop: 64 rounds of transformations |
46 | | - 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 |
51 | | - f = F(b, c, d) |
52 | | - g = (i - 1) % 16 |
53 | | - elseif round == 2 then |
54 | | - f = G(b, c, d) |
55 | | - g = (5 * (i - 1) + 1) % 16 |
56 | | - elseif round == 3 then |
57 | | - f = H(b, c, d) |
58 | | - g = (3 * (i - 1) + 5) % 16 |
59 | | - elseif round == 4 then |
60 | | - f = I(b, c, d) |
61 | | - g = (7 * (i - 1)) % 16 |
62 | | - end |
63 | | - |
64 | | - local temp = d |
65 | | - d = c |
66 | | - c = b |
67 | | - b = (b + LShift32((a + f + words[g + 1] + |
68 | | - T[i]) & 0xFFFFFFFF, shift)) & 0xFFFFFFFF |
69 | | - a = temp |
70 | | - end |
71 | | - |
72 | | - -- Add chunk's hash to the result so far |
73 | | - A = (A + a) & 0xFFFFFFFF |
74 | | - B = (B + b) & 0xFFFFFFFF |
75 | | - C = (C + c) & 0xFFFFFFFF |
76 | | - D = (D + d) & 0xFFFFFFFF |
77 | | - |
78 | | - return A, B, C, D |
79 | | - end |
80 | | - |
81 | | - 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 |
98 | | - end |
99 | | - |
100 | | - local A, B, C, D, len = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0 |
101 | | - |
102 | | - 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 |
114 | | - end |
115 | | - |
116 | | - -- Format output as a hexadecimal string in little-endian order |
117 | | - local function to_hex(x) |
118 | | - return string.format("%02x%02x%02x%02x", |
119 | | - x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF) |
120 | | - end |
121 | | - |
122 | | - return to_hex(A) .. to_hex(B) .. to_hex(C) .. to_hex(D) |
123 | | -end |
124 | | - |
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 |
130 | | -end |
131 | | - |
132 | | -if LIB then PredefineConstants() return end |
133 | | - |
134 | | -if #arg < 1 then |
135 | | - -- Show accurate Lua binary and script location |
136 | | - print((arg[-1] or "?") .. " " .. (arg[0] or "?") .. " [FILE]...") |
137 | | - os.exit(1) |
138 | | -else |
139 | | - PredefineConstants() |
140 | | - |
141 | | - 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 | | - |
148 | | - if sum then |
149 | | - print(sum .. " " .. arg[i]) |
150 | | - else |
151 | | - print(arg[i] .. ": " .. "Unknown error") |
152 | | - os.exit(-1) |
153 | | - end |
154 | | - else |
155 | | - print(arg[i] .. ": " .. err) |
156 | | - os.exit(1) |
157 | | - end |
158 | | - end |
159 | | -end |
| 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 |
| 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) |
| 10 | + 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 | + local a, b, c, d = A, B, C, D |
| 44 | + |
| 45 | + for i = 1, 64 do -- Main loop: 64 rounds of transformations |
| 46 | + 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 |
| 51 | + f = F(b, c, d) |
| 52 | + g = (i - 1) % 16 |
| 53 | + elseif round == 2 then |
| 54 | + f = G(b, c, d) |
| 55 | + g = (5 * (i - 1) + 1) % 16 |
| 56 | + elseif round == 3 then |
| 57 | + f = H(b, c, d) |
| 58 | + g = (3 * (i - 1) + 5) % 16 |
| 59 | + elseif round == 4 then |
| 60 | + f = I(b, c, d) |
| 61 | + g = (7 * (i - 1)) % 16 |
| 62 | + end |
| 63 | + |
| 64 | + local temp = d |
| 65 | + d = c |
| 66 | + c = b |
| 67 | + b = (b + LShift32((a + f + words[g + 1] + |
| 68 | + T[i]) & 0xFFFFFFFF, shift)) & 0xFFFFFFFF |
| 69 | + a = temp |
| 70 | + end |
| 71 | + |
| 72 | + -- Add chunk's hash to the result so far |
| 73 | + A = (A + a) & 0xFFFFFFFF |
| 74 | + B = (B + b) & 0xFFFFFFFF |
| 75 | + C = (C + c) & 0xFFFFFFFF |
| 76 | + D = (D + d) & 0xFFFFFFFF |
| 77 | + |
| 78 | + return A, B, C, D |
| 79 | + end |
| 80 | + |
| 81 | + 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 |
| 98 | + end |
| 99 | + |
| 100 | + local A, B, C, D, len = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0 |
| 101 | + |
| 102 | + 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 |
| 114 | + end |
| 115 | + |
| 116 | + -- Format output as a hexadecimal string in little-endian order |
| 117 | + local function to_hex(x) |
| 118 | + return string.format("%02x%02x%02x%02x", |
| 119 | + x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF) |
| 120 | + end |
| 121 | + |
| 122 | + return to_hex(A) .. to_hex(B) .. to_hex(C) .. to_hex(D) |
| 123 | +end |
| 124 | + |
| 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 |
| 130 | +end |
| 131 | + |
| 132 | +if LIB then PredefineConstants() return end |
| 133 | + |
| 134 | +if #arg < 1 then |
| 135 | + -- Show accurate Lua binary and script location |
| 136 | + print((arg[-1] or "?") .. " " .. (arg[0] or "?") .. " [FILE]...") |
| 137 | + os.exit(1) |
| 138 | +else |
| 139 | + PredefineConstants() |
| 140 | + |
| 141 | + 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 | + |
| 148 | + if sum then |
| 149 | + print(sum .. " " .. arg[i]) |
| 150 | + else |
| 151 | + print(arg[i] .. ": " .. "Unknown error") |
| 152 | + os.exit(-1) |
| 153 | + end |
| 154 | + else |
| 155 | + print(arg[i] .. ": " .. err) |
| 156 | + os.exit(1) |
| 157 | + end |
| 158 | + end |
| 159 | +end |
0 commit comments