44--- @param file file The file
55--- @return string SHA-256 checksum
66function sha256_file (file )
7- -- M = Maximum value of number (32-bits)
8- -- K = SHA256 round constants
9- local M , K = 0xFFFFFFFF , {
7+
8+ -- M = Maximum value of number (32-bits)
9+ -- K = SHA256 round constants
10+ local M , K = 0xFFFFFFFF , {
1011 0x428a2f98 , 0x71374491 , 0xb5c0fbcf , 0xe9b5dba5 , 0x3956c25b , 0x59f111f1 ,
1112 0x923f82a4 , 0xab1c5ed5 , 0xd807aa98 , 0x12835b01 , 0x243185be , 0x550c7dc3 ,
1213 0x72be5d74 , 0x80deb1fe , 0x9bdc06a7 , 0xc19bf174 , 0xe49b69c1 , 0xefbe4786 ,
@@ -20,110 +21,168 @@ function sha256_file(file)
2021 0x90befffa , 0xa4506ceb , 0xbef9a3f7 , 0xc67178f2
2122 }
2223
23- --- Right rotate a 32-bit value
24- --- @param x number The 32-bit value to be rotated
25- --- @param n number The number of rotations to the right that should be done
26- --- @return number The right shifted value
27- local function RR (x , n )
28- return (x >> n ) | ((x & M ) << (32 - n ))
29- end
30-
31- --- SHA-256 transformation function
32- --- @param k string A 64-byte chunk of the message
33- --- @param H table The current hash state (8 32-bit values )
34- local function TF (k , H )
35- local W = {}
36- for i = 0 , 15 do -- Convert chunk into 32-bit list
37- local o = i * 4 + 1
38- W [i ] = string.byte (k , o ) << 24 | string.byte (k , o + 1 ) << 16 | string.byte (k , o + 2 ) << 8 | string.byte (k , o + 3 )
39- end
40- for i = 16 , 63 do -- Message expansion
41- local s0 , s1 = RR (W [i - 15 ], 7 ) ~ RR (W [i - 15 ], 18 ) ~ (W [i - 15 ] >> 3 ), RR (W [i - 2 ], 17 ) ~ RR (W [i - 2 ], 19 ) ~ (W [i - 2 ] >> 10 )
42- W [i ] = (W [i - 16 ] + s0 + W [i - 7 ] + s1 ) & M
43- end
44- local a , b , c , d , e , f , g , h = table.unpack (H )
45- for i = 0 , 63 do
46- local S0 , S1 , ch , maj = RR (a , 2 ) ~ RR (a , 13 ) ~ RR (a , 22 ), RR (e , 6 ) ~ RR (e , 11 ) ~ RR (e , 25 ), (e & f ) ~ ((~e ) & g ), (a & b ) ~ (a & c ) ~ (b & c )
47- local t1 , t2 = (h + S1 + ch + K [i + 1 ] + W [i ]) & M , (S0 + maj ) & M
48- h = g
49- g = f
50- f = e
51- e = (d + t1 ) & M
52- d = c
53- c = b
54- b = a
55- a = (t1 + t2 ) & M
56- end
57- H [1 ] = (H [1 ] + a ) & M
58- H [2 ] = (H [2 ] + b ) & M
59- H [3 ] = (H [3 ] + c ) & M
60- H [4 ] = (H [4 ] + d ) & M
61- H [5 ] = (H [5 ] + e ) & M
62- H [6 ] = (H [6 ] + f ) & M
63- H [7 ] = (H [7 ] + g ) & M
64- H [8 ] = (H [8 ] + h ) & M
65- end
66-
67- --- Create SHA-256 padding for message
68- --- @param l number The length of the message in bytes
69- --- @return string The padding string to append to the message
70- local function P (l )
71- local ml , p , pl = l * 8 , " \128 " , 56 - (l % 64 )
72- if pl <= 0 then
73- pl = pl + 64
74- end
75- p = p .. string.rep (" \0 " , pl - 1 )
76- for i = 7 , 0 , - 1 do
77- p = p .. string.char ((ml >> (i * 8 )) & 0xFF )
78- end
79- return p
80- end
81-
82- local H , l = { 0x6a09e667 , 0xbb67ae85 , 0x3c6ef372 , 0xa54ff53a , 0x510e527f , 0x9b05688c , 0x1f83d9ab , 0x5be0cd19 }, 0
83- while 1 do
84- local c = file :read (64 )
85- if not c then
86- break
87- end
88- l = l + # c
89- if # c < 64 then
90- c = c .. P (l )
91- TF (c , H )
92- break
93- end
94- TF (c , H )
95- end
96- local function h (x )
97- return string.format (" %08x" , x )
98- end
99- l = {}
100- for _ , v in ipairs (H ) do
101- table.insert (l , h (v ))
102- end
103- return table.concat (l )
24+ --- Right rotate a 32-bit value
25+ --- @param x number The 32-bit value to be rotated
26+ --- @param n number The number of rotations to the right that should be done
27+ --- @return number The right shifted value
28+ local function RR (x , n )
29+ return (x >> n ) | ((x & M ) << (32 - n ))
30+ end
31+
32+ --- SHA-256 transformation function
33+ --- @param k string A 64-byte chunk of the message
34+ --- @param H table The current hash state (8 32-bit values )
35+ local function TF (k , H )
36+
37+ -- W = list of 32-bit words
38+ local W = {}
39+
40+ for i = 0 , 15 do -- Convert chunk into 32-bit list
41+
42+ -- o = Offset calculation
43+ local o = i * 4 + 1
44+
45+ W [i ] = string.byte (k , o ) << 24 | string.byte (k , o + 1 ) << 16 | string.byte (k , o + 2 ) << 8 | string.byte (k , o + 3 )
46+ end
47+ for i = 16 , 63 do -- Message expansion
48+
49+ -- s0 = Message schedule shift 0
50+ -- s1 = Message schedule shift 1
51+ local s0 , s1 = RR (W [i - 15 ], 7 ) ~ RR (W [i - 15 ], 18 ) ~ (W [i - 15 ] >> 3 ), RR (W [i - 2 ], 17 ) ~ RR (W [i - 2 ], 19 ) ~ (W [i - 2 ] >> 10 )
52+
53+ W [i ] = (W [i - 16 ] + s0 + W [i - 7 ] + s1 ) & M
54+ end
55+
56+ -- 32-bit working variables initialized with the current hash values of H
57+ local a , b , c , d , e , f , g , h = table.unpack (H )
58+
59+ for i = 0 , 63 do
60+
61+ -- S0 = Compression rotation variable 0
62+ -- S1 = Compression rotation variable 1
63+ -- ch = Choice function
64+ -- ma = Majority function
65+ local S0 , S1 , ch , ma = RR (a , 2 ) ~ RR (a , 13 ) ~ RR (a , 22 ), RR (e , 6 ) ~ RR (e , 11 ) ~ RR (e , 25 ), (e & f ) ~ ((~e ) & g ), (a & b ) ~ (a & c ) ~ (b & c )
66+
67+ -- t1 = Temporary variable 1
68+ -- t2 = Temporary variable 2
69+ local t1 , t2 = (h + S1 + ch + K [i + 1 ] + W [i ]) & M , (S0 + ma ) & M
70+
71+ -- Working variable update
72+ h = g
73+ g = f
74+ f = e
75+ e = (d + t1 ) & M
76+ d = c
77+ c = b
78+ b = a
79+ a = (t1 + t2 ) & M
80+ end
81+
82+ -- Update hash state with compressed block values
83+ H [1 ] = (H [1 ] + a ) & M
84+ H [2 ] = (H [2 ] + b ) & M
85+ H [3 ] = (H [3 ] + c ) & M
86+ H [4 ] = (H [4 ] + d ) & M
87+ H [5 ] = (H [5 ] + e ) & M
88+ H [6 ] = (H [6 ] + f ) & M
89+ H [7 ] = (H [7 ] + g ) & M
90+ H [8 ] = (H [8 ] + h ) & M
91+ end
92+
93+ --- Create SHA-256 padding for message
94+ --- @param l number The length of the message in bytes
95+ --- @return string The padding string to append to the message
96+ local function P (l )
97+
98+ -- ml = Message length (in bits)
99+ -- p = Padding string
100+ -- pl = Padding length (in bytes)
101+ local ml , p , pl = l * 8 , " \128 " , 56 - (l % 64 )
102+
103+ -- Add enough room for padding if required
104+ if pl <= 0 then
105+ pl = pl + 64
106+ end
107+
108+ -- Zero out padding
109+ p = p .. string.rep (" \0 " , pl - 1 )
110+
111+ -- Append original message length
112+ for i = 7 , 0 , - 1 do
113+ p = p .. string.char ((ml >> (i * 8 )) & 0xFF )
114+ end
115+
116+ return p
117+ end
118+
119+ -- H = Initial Hash values
120+ -- l = Total bytes of file processed
121+ local H , l = { 0x6a09e667 , 0xbb67ae85 , 0x3c6ef372 , 0xa54ff53a , 0x510e527f , 0x9b05688c , 0x1f83d9ab , 0x5be0cd19 }, 0
122+
123+ while 1 do
124+
125+ -- c = 64 byte chunk of file
126+ local c = file :read (64 )
127+
128+ if not c then
129+ break
130+ end
131+
132+ l = l + # c
133+
134+ if # c < 64 then
135+ c = c .. P (l )
136+ TF (c , H )
137+ break
138+ end
139+
140+ TF (c , H )
141+ end
142+
143+ local function h (x )
144+ return string.format (" %08x" , x )
145+ end
146+
147+ -- l = Final hash string
148+ l = {}
149+
150+ for _ , v in ipairs (H ) do
151+ table.insert (l , h (v ))
152+ end
153+
154+ return table.concat (l )
104155end
105156
106157if LIB then
107- return -- Don't run below code if brought in as a library
158+ return -- Don't run below code if brought in as a library
108159end
109160
110- if # arg < 1 then
111- print ((arg [- 1 ] or " ?" ) .. " " .. (arg [0 ] or " ?" ) .. " [FILE]..." )
112- os.exit (1 )
161+ if # arg < 1 then -- Print help and exit if no arguments
162+ print ((arg [- 1 ] or " ?" ) .. " " .. (arg [0 ] or " ?" ) .. " [FILE]..." )
163+ os.exit (1 )
113164else
114- for i = 1 , # arg do
115- local f , e = io.open (arg [i ], " rb" )
116- if not f then
117- print (e )
118- os.exit (1 )
119- end
120- local sum = sha256_file (f )
121- f :close ()
122- if sum then
123- print (sum .. " " .. arg [i ])
124- else
125- print (arg [i ] .. " : " .. " Unknown error" )
126- os.exit (- 1 )
127- end
128- end
165+ for i = 1 , # arg do
166+
167+ -- f = File to read
168+ -- e = Error message on failure to open
169+ local f , e = io.open (arg [i ], " rb" )
170+
171+ if not f then
172+ print (e )
173+ os.exit (1 )
174+ end
175+
176+ -- s = SHA256 string of file
177+ local s = sha256_file (f )
178+
179+ f :close ()
180+
181+ if s then -- Print hash and filename
182+ print (s .. " " .. arg [i ])
183+ else
184+ print (arg [i ] .. " : " .. " Unknown error" )
185+ os.exit (- 1 )
186+ end
187+ end
129188end
0 commit comments