|
1
|
|
|
#!/usr/bin/env python3 |
|
2
|
4 |
|
"""This module is used to generate file hashes/checksums and PGP signatures.""" |
|
3
|
|
|
|
|
4
|
4 |
|
import zlib # crc32/adler32 |
|
5
|
4 |
|
import hashlib # all other hashes |
|
6
|
4 |
|
import hmac # escreens is a hmac, news at 11 |
|
7
|
4 |
|
import os # path work |
|
8
|
4 |
|
import concurrent.futures # parallelization |
|
9
|
4 |
|
import gnupg # interface b/w Python, GPG |
|
10
|
4 |
|
from bbarchivist import bbconstants # premade stuff |
|
11
|
4 |
|
from bbarchivist import exceptions # exceptions |
|
12
|
4 |
|
from bbarchivist import utilities # cores |
|
13
|
4 |
|
from bbarchivist import iniconfig # config parsing |
|
14
|
|
|
|
|
15
|
4 |
|
__author__ = "Thurask" |
|
16
|
4 |
|
__license__ = "WTFPL v2" |
|
17
|
4 |
|
__copyright__ = "Copyright 2015-2016 Thurask" |
|
18
|
|
|
|
|
19
|
|
|
|
|
20
|
4 |
|
def hc32(filepath, blocksize=16 * 1024 * 1024): |
|
21
|
|
|
""" |
|
22
|
|
|
Return CRC32 checksum of a file. |
|
23
|
|
|
|
|
24
|
|
|
:param filepath: File you wish to verify. |
|
25
|
|
|
:type filepath: str |
|
26
|
|
|
|
|
27
|
|
|
:param blocksize: How much of file to read at once. |
|
28
|
|
|
:type blocksize: int |
|
29
|
|
|
""" |
|
30
|
4 |
|
seed = 0 |
|
31
|
4 |
|
with open(filepath, 'rb') as file: |
|
32
|
4 |
|
for chunk in iter(lambda: file.read(blocksize), b''): |
|
33
|
4 |
|
seed = zlib.crc32(chunk, seed) |
|
34
|
4 |
|
final = format(seed & 0xFFFFFFFF, "08x") |
|
35
|
4 |
|
return final |
|
36
|
|
|
|
|
37
|
|
|
|
|
38
|
4 |
|
def ha32(filepath, blocksize=16 * 1024 * 1024): |
|
39
|
|
|
""" |
|
40
|
|
|
Return Adler32 checksum of a file. |
|
41
|
|
|
|
|
42
|
|
|
:param filepath: File you wish to verify. |
|
43
|
|
|
:type filepath: str |
|
44
|
|
|
|
|
45
|
|
|
:param blocksize: How much of file to read at once. |
|
46
|
|
|
:type blocksize: int |
|
47
|
|
|
""" |
|
48
|
4 |
|
asum = 1 |
|
49
|
4 |
|
with open(filepath, 'rb') as file: |
|
50
|
4 |
|
while True: |
|
51
|
4 |
|
data = file.read(blocksize) |
|
52
|
4 |
|
if not data: |
|
53
|
4 |
|
break |
|
54
|
4 |
|
asum = zlib.adler32(data, asum) |
|
55
|
4 |
|
if asum < 0: |
|
56
|
|
|
asum += 2 ** 32 |
|
57
|
4 |
|
final = format(asum & 0xFFFFFFFF, "08x") |
|
58
|
4 |
|
return final |
|
59
|
|
|
|
|
60
|
|
|
|
|
61
|
4 |
|
def hs1(filepath, blocksize=16 * 1024 * 1024): |
|
62
|
|
|
""" |
|
63
|
|
|
Return SHA-1 hash of a file. |
|
64
|
|
|
|
|
65
|
|
|
:param filepath: File you wish to verify. |
|
66
|
|
|
:type filepath: str |
|
67
|
|
|
|
|
68
|
|
|
:param blocksize: How much of file to read at once. |
|
69
|
|
|
:type blocksize: int |
|
70
|
|
|
""" |
|
71
|
4 |
|
sha1 = hashlib.sha1() |
|
72
|
4 |
|
hashfunc_reader(filepath, sha1, blocksize) |
|
73
|
4 |
|
return sha1.hexdigest() |
|
74
|
|
|
|
|
75
|
|
|
|
|
76
|
4 |
|
def hs224(filepath, blocksize=16 * 1024 * 1024): |
|
77
|
|
|
""" |
|
78
|
|
|
Return SHA-224 hash of a file. |
|
79
|
|
|
|
|
80
|
|
|
:param filepath: File you wish to verify. |
|
81
|
|
|
:type filepath: str |
|
82
|
|
|
|
|
83
|
|
|
:param blocksize: How much of file to read at once. |
|
84
|
|
|
:type blocksize: int |
|
85
|
|
|
""" |
|
86
|
4 |
|
sha224 = hashlib.sha224() |
|
87
|
4 |
|
hashfunc_reader(filepath, sha224, blocksize) |
|
88
|
4 |
|
return sha224.hexdigest() |
|
89
|
|
|
|
|
90
|
|
|
|
|
91
|
4 |
|
def hs256(filepath, blocksize=16 * 1024 * 1024): |
|
92
|
|
|
""" |
|
93
|
|
|
Return SHA-256 hash of a file. |
|
94
|
|
|
|
|
95
|
|
|
:param filepath: File you wish to verify. |
|
96
|
|
|
:type filepath: str |
|
97
|
|
|
|
|
98
|
|
|
:param blocksize: How much of file to read at once. |
|
99
|
|
|
:type blocksize: int |
|
100
|
|
|
""" |
|
101
|
4 |
|
sha256 = hashlib.sha256() |
|
102
|
4 |
|
hashfunc_reader(filepath, sha256, blocksize) |
|
103
|
4 |
|
return sha256.hexdigest() |
|
104
|
|
|
|
|
105
|
|
|
|
|
106
|
4 |
|
def hs384(filepath, blocksize=16 * 1024 * 1024): |
|
107
|
|
|
""" |
|
108
|
|
|
Return SHA-384 hash of a file. |
|
109
|
|
|
|
|
110
|
|
|
:param filepath: File you wish to verify. |
|
111
|
|
|
:type filepath: str |
|
112
|
|
|
|
|
113
|
|
|
:param blocksize: How much of file to read at once. |
|
114
|
|
|
:type blocksize: int |
|
115
|
|
|
""" |
|
116
|
4 |
|
sha384 = hashlib.sha384() |
|
117
|
4 |
|
hashfunc_reader(filepath, sha384, blocksize) |
|
118
|
4 |
|
return sha384.hexdigest() |
|
119
|
|
|
|
|
120
|
|
|
|
|
121
|
4 |
|
def hs512(filepath, blocksize=16 * 1024 * 1024): |
|
122
|
|
|
""" |
|
123
|
|
|
Return SHA-512 hash of a file. |
|
124
|
|
|
|
|
125
|
|
|
:param filepath: File you wish to verify. |
|
126
|
|
|
:type filepath: str |
|
127
|
|
|
|
|
128
|
|
|
:param blocksize: How much of file to read at once. |
|
129
|
|
|
:type blocksize: int |
|
130
|
|
|
""" |
|
131
|
4 |
|
sha512 = hashlib.sha512() |
|
132
|
4 |
|
hashfunc_reader(filepath, sha512, blocksize) |
|
133
|
4 |
|
return sha512.hexdigest() |
|
134
|
|
|
|
|
135
|
|
|
|
|
136
|
4 |
|
def hs3224(filepath, blocksize=16 * 1024 * 1024): |
|
137
|
|
|
""" |
|
138
|
|
|
Return SHA3-224 hash of a file. |
|
139
|
|
|
|
|
140
|
|
|
:param filepath: File you wish to verify. |
|
141
|
|
|
:type filepath: str |
|
142
|
|
|
|
|
143
|
|
|
:param blocksize: How much of file to read at once. |
|
144
|
|
|
:type blocksize: int |
|
145
|
|
|
""" |
|
146
|
4 |
|
try: |
|
147
|
4 |
|
sha3224 = hashlib.sha3_224() |
|
148
|
4 |
|
except AttributeError: |
|
149
|
4 |
|
print("REQUIRES PYTHON 3.6+") |
|
150
|
|
|
else: |
|
151
|
|
|
hashfunc_reader(filepath, sha3224, blocksize) |
|
152
|
|
|
return sha3224.hexdigest() |
|
153
|
|
|
|
|
154
|
|
|
|
|
155
|
4 |
|
def hs3256(filepath, blocksize=16 * 1024 * 1024): |
|
156
|
|
|
""" |
|
157
|
|
|
Return SHA3-256 hash of a file. |
|
158
|
|
|
|
|
159
|
|
|
:param filepath: File you wish to verify. |
|
160
|
|
|
:type filepath: str |
|
161
|
|
|
|
|
162
|
|
|
:param blocksize: How much of file to read at once. |
|
163
|
|
|
:type blocksize: int |
|
164
|
|
|
""" |
|
165
|
4 |
|
try: |
|
166
|
4 |
|
sha3256 = hashlib.sha3_256() |
|
167
|
4 |
|
except AttributeError: |
|
168
|
4 |
|
print("REQUIRES PYTHON 3.6+") |
|
169
|
|
|
else: |
|
170
|
|
|
hashfunc_reader(filepath, sha3256, blocksize) |
|
171
|
|
|
return sha3256.hexdigest() |
|
172
|
|
|
|
|
173
|
|
|
|
|
174
|
4 |
|
def hs3384(filepath, blocksize=16 * 1024 * 1024): |
|
175
|
|
|
""" |
|
176
|
|
|
Return SHA3-384 hash of a file. |
|
177
|
|
|
|
|
178
|
|
|
:param filepath: File you wish to verify. |
|
179
|
|
|
:type filepath: str |
|
180
|
|
|
|
|
181
|
|
|
:param blocksize: How much of file to read at once. |
|
182
|
|
|
:type blocksize: int |
|
183
|
|
|
""" |
|
184
|
4 |
|
try: |
|
185
|
4 |
|
sha3384 = hashlib.sha3_384() |
|
186
|
4 |
|
except AttributeError: |
|
187
|
4 |
|
print("REQUIRES PYTHON 3.6+") |
|
188
|
|
|
else: |
|
189
|
|
|
hashfunc_reader(filepath, sha3384, blocksize) |
|
190
|
|
|
return sha3384.hexdigest() |
|
191
|
|
|
|
|
192
|
|
|
|
|
193
|
4 |
|
def hs3512(filepath, blocksize=16 * 1024 * 1024): |
|
194
|
|
|
""" |
|
195
|
|
|
Return SHA3-512 hash of a file. |
|
196
|
|
|
|
|
197
|
|
|
:param filepath: File you wish to verify. |
|
198
|
|
|
:type filepath: str |
|
199
|
|
|
|
|
200
|
|
|
:param blocksize: How much of file to read at once. |
|
201
|
|
|
:type blocksize: int |
|
202
|
|
|
""" |
|
203
|
4 |
|
try: |
|
204
|
4 |
|
sha3512 = hashlib.sha3_512() |
|
205
|
4 |
|
except AttributeError: |
|
206
|
4 |
|
print("REQUIRES PYTHON 3.6+") |
|
207
|
|
|
else: |
|
208
|
|
|
hashfunc_reader(filepath, sha3512, blocksize) |
|
209
|
|
|
return sha3512.hexdigest() |
|
210
|
|
|
|
|
211
|
|
|
|
|
212
|
4 |
|
def hm5(filepath, blocksize=16 * 1024 * 1024): |
|
213
|
|
|
""" |
|
214
|
|
|
Return MD5 hash of a file. |
|
215
|
|
|
|
|
216
|
|
|
:param filepath: File you wish to verify. |
|
217
|
|
|
:type filepath: str |
|
218
|
|
|
|
|
219
|
|
|
:param blocksize: How much of file to read at once. |
|
220
|
|
|
:type blocksize: int |
|
221
|
|
|
""" |
|
222
|
4 |
|
md5 = hashlib.md5() |
|
223
|
4 |
|
hashfunc_reader(filepath, md5, blocksize) |
|
224
|
4 |
|
return md5.hexdigest() |
|
225
|
|
|
|
|
226
|
|
|
|
|
227
|
4 |
|
def hashfunc_reader(filepath, engine, blocksize=16 * 1024 * 1024): |
|
228
|
|
|
""" |
|
229
|
|
|
Generate hash from file contents. |
|
230
|
|
|
|
|
231
|
|
|
:param filepath: File you wish to verify. |
|
232
|
|
|
:type filepath: str |
|
233
|
|
|
|
|
234
|
|
|
:param engine: Hash object to update with file contents. |
|
235
|
|
|
:type engine: _hashlib.HASH |
|
236
|
|
|
|
|
237
|
|
|
:param blocksize: How much of file to read at once. |
|
238
|
|
|
:type blocksize: int |
|
239
|
|
|
""" |
|
240
|
4 |
|
with open(filepath, 'rb') as file: |
|
241
|
4 |
|
while True: |
|
242
|
4 |
|
data = file.read(blocksize) |
|
243
|
4 |
|
if not data: |
|
244
|
4 |
|
break |
|
245
|
4 |
|
engine.update(data) |
|
246
|
|
|
|
|
247
|
|
|
|
|
248
|
4 |
|
def ssl_hash(filepath, method, blocksize=16 * 1024 * 1024): |
|
249
|
|
|
""" |
|
250
|
|
|
Return SSL-library dependent hash of a file. |
|
251
|
|
|
|
|
252
|
|
|
:param filepath: File you wish to verify. |
|
253
|
|
|
:type filepath: str |
|
254
|
|
|
|
|
255
|
|
|
:param method: Method to use: algorithms in hashlib that are not guaranteed. |
|
256
|
|
|
:type method: str |
|
257
|
|
|
|
|
258
|
|
|
:param blocksize: How much of file to read at once. |
|
259
|
|
|
:type blocksize: int |
|
260
|
|
|
""" |
|
261
|
4 |
|
try: |
|
262
|
4 |
|
engine = hashlib.new(method) |
|
263
|
4 |
|
hashfunc_reader(filepath, engine, blocksize) |
|
264
|
4 |
|
return engine.hexdigest() |
|
265
|
4 |
|
except ValueError as exc: |
|
266
|
4 |
|
msg = "{0} HASH FAILED".format(method.upper()) |
|
267
|
4 |
|
exceptions.handle_exception(exc, msg, None) |
|
268
|
|
|
|
|
269
|
|
|
|
|
270
|
4 |
|
def hm4(filepath, blocksize=16 * 1024 * 1024): |
|
271
|
|
|
""" |
|
272
|
|
|
Return MD4 hash of a file; depends on system SSL library. |
|
273
|
|
|
|
|
274
|
|
|
:param filepath: File you wish to verify. |
|
275
|
|
|
:type filepath: str |
|
276
|
|
|
|
|
277
|
|
|
:param blocksize: How much of file to read at once. |
|
278
|
|
|
:type blocksize: int |
|
279
|
|
|
""" |
|
280
|
4 |
|
return ssl_hash(filepath, "md4", blocksize) |
|
281
|
|
|
|
|
282
|
|
|
|
|
283
|
4 |
|
def hr160(filepath, blocksize=16 * 1024 * 1024): |
|
284
|
|
|
""" |
|
285
|
|
|
Return RIPEMD160 hash of a file; depends on system SSL library. |
|
286
|
|
|
|
|
287
|
|
|
:param filepath: File you wish to verify. |
|
288
|
|
|
:type filepath: str |
|
289
|
|
|
|
|
290
|
|
|
:param blocksize: How much of file to read at once. |
|
291
|
|
|
:type blocksize: int |
|
292
|
|
|
""" |
|
293
|
4 |
|
return ssl_hash(filepath, "ripemd160", blocksize) |
|
294
|
|
|
|
|
295
|
|
|
|
|
296
|
4 |
|
def hwp(filepath, blocksize=16 * 1024 * 1024): |
|
297
|
|
|
""" |
|
298
|
|
|
Return Whirlpool hash of a file; depends on system SSL library. |
|
299
|
|
|
|
|
300
|
|
|
:param filepath: File you wish to verify. |
|
301
|
|
|
:type filepath: str |
|
302
|
|
|
|
|
303
|
|
|
:param blocksize: How much of file to read at once. |
|
304
|
|
|
:type blocksize: int |
|
305
|
|
|
""" |
|
306
|
4 |
|
return ssl_hash(filepath, "whirlpool", blocksize) |
|
307
|
|
|
|
|
308
|
|
|
|
|
309
|
4 |
|
def hs0(filepath, blocksize=16 * 1024 * 1024): |
|
310
|
|
|
""" |
|
311
|
|
|
Return SHA-0 hash of a file; depends on system SSL library. |
|
312
|
|
|
|
|
313
|
|
|
:param filepath: File you wish to verify. |
|
314
|
|
|
:type filepath: str |
|
315
|
|
|
|
|
316
|
|
|
:param blocksize: How much of file to read at once. |
|
317
|
|
|
:type blocksize: int |
|
318
|
|
|
""" |
|
319
|
4 |
|
return ssl_hash(filepath, "sha", blocksize) |
|
320
|
|
|
|
|
321
|
|
|
|
|
322
|
4 |
|
def gpgfile(filepath, gpginst, key=None, pword=None): |
|
323
|
|
|
""" |
|
324
|
|
|
Make ASCII-armored signature files with a given private key. |
|
325
|
|
|
Takes an instance of gnupg.GPG(). |
|
326
|
|
|
|
|
327
|
|
|
:param filepath: File you wish to verify. |
|
328
|
|
|
:type filepath: str |
|
329
|
|
|
|
|
330
|
|
|
:param gpginst: Instance of Python GnuPG executable. |
|
331
|
|
|
:type gpginst: gnupg.GPG() |
|
332
|
|
|
|
|
333
|
|
|
:param key: Key ID. 0xABCDEF01 |
|
334
|
|
|
:type key: str |
|
335
|
|
|
|
|
336
|
|
|
:param pword: Passphrase for key. |
|
337
|
|
|
:type pword: str |
|
338
|
|
|
""" |
|
339
|
4 |
|
with open(filepath, "rb") as file: |
|
340
|
4 |
|
fname = file.name + ".asc" |
|
341
|
4 |
|
gpginst.sign_file(file, detach=True, keyid=key, passphrase=pword, output=fname) |
|
342
|
|
|
|
|
343
|
|
|
|
|
344
|
4 |
|
def calculate_escreens(pin, app, uptime, duration=30): |
|
345
|
|
|
""" |
|
346
|
|
|
Calculate key for the Engineering Screens based on input. |
|
347
|
|
|
|
|
348
|
|
|
:param pin: PIN to check. 8 character hexadecimal, lowercase. |
|
349
|
|
|
:type pin: str |
|
350
|
|
|
|
|
351
|
|
|
:param app: App version. 10.x.y.zzzz. |
|
352
|
|
|
:type app: str |
|
353
|
|
|
|
|
354
|
|
|
:param uptime: Uptime in ms. |
|
355
|
|
|
:type uptime: str |
|
356
|
|
|
|
|
357
|
|
|
:param duration: 1, 3, 6, 15, 30 (days). |
|
358
|
|
|
:type duration: str |
|
359
|
|
|
""" |
|
360
|
|
|
#: Somehow, values for lifetimes for escreens. |
|
361
|
4 |
|
lifetimes = { |
|
362
|
|
|
1: "", |
|
363
|
|
|
3: "Hello my baby, hello my honey, hello my rag time gal", |
|
364
|
|
|
7: "He was a boy, and she was a girl, can I make it any more obvious?", |
|
365
|
|
|
15: "So am I, still waiting, for this world to stop hating?", |
|
366
|
|
|
30: "I love myself today, not like yesterday. " |
|
367
|
|
|
} |
|
368
|
4 |
|
lifetimes[30] += "I'm cool, I'm calm, I'm gonna be okay" |
|
369
|
|
|
#: Escreens magic HMAC secret. |
|
370
|
4 |
|
secret = 'Up the time stream without a TARDIS' |
|
371
|
4 |
|
duration = int(duration) |
|
372
|
4 |
|
if duration not in [1, 3, 6, 15, 30]: |
|
373
|
4 |
|
duration = 1 |
|
374
|
4 |
|
data = pin.lower() + app + str(uptime) + lifetimes[duration] |
|
375
|
4 |
|
newhmac = hmac.new(secret.encode(), data.encode(), digestmod=hashlib.sha1) |
|
376
|
4 |
|
key = newhmac.hexdigest()[:8] |
|
377
|
4 |
|
return key.upper() |
|
378
|
|
|
|
|
379
|
|
|
|
|
380
|
4 |
|
def hash_get(filename, hashfunc, workingdir, blocksize=16777216): |
|
381
|
|
|
""" |
|
382
|
|
|
Generate and pretty format the hash result for a file. |
|
383
|
|
|
|
|
384
|
|
|
:param filename: File to hash. |
|
385
|
|
|
:type filename: str |
|
386
|
|
|
|
|
387
|
|
|
:param hashfunc: Hash function to use. |
|
388
|
|
|
:type hashfunc: function |
|
389
|
|
|
|
|
390
|
|
|
:param workingdir: Working directory. |
|
391
|
|
|
:type workingdir: str |
|
392
|
|
|
|
|
393
|
|
|
:param blocksize: Block size. Default is 16MB. |
|
394
|
|
|
:type blocksize: int |
|
395
|
|
|
""" |
|
396
|
4 |
|
result = hashfunc(os.path.join(workingdir, filename), blocksize) |
|
397
|
4 |
|
return "{0} {1}\n".format(result.upper(), os.path.basename(filename)) |
|
398
|
|
|
|
|
399
|
|
|
|
|
400
|
4 |
|
def base_hash(hashtype, source, workingdir, block, hashfunc, target, kwargs=None): |
|
401
|
|
|
""" |
|
402
|
|
|
Generic hash function; get hash, write to file. |
|
403
|
|
|
|
|
404
|
|
|
:param hashtype: Hash type. |
|
405
|
|
|
:type hashtype: str |
|
406
|
|
|
|
|
407
|
|
|
:param source: File to be hashed; foobar.ext |
|
408
|
|
|
:type source: str |
|
409
|
|
|
|
|
410
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
411
|
|
|
:type workingdir: str |
|
412
|
|
|
|
|
413
|
|
|
:param block: Blocksize, in bytes. |
|
414
|
|
|
:type block: int |
|
415
|
|
|
|
|
416
|
|
|
:param target: File to write to. |
|
417
|
|
|
:type target: file |
|
418
|
|
|
|
|
419
|
|
|
:param kwargs: Values. Refer to `:func:verifier_config_loader`. |
|
420
|
|
|
:type kwargs: dict |
|
421
|
|
|
""" |
|
422
|
4 |
|
if kwargs[hashtype]: |
|
423
|
4 |
|
hash_generic = [hashtype.upper()] |
|
424
|
4 |
|
hash_generic.append(hash_get(source, hashfunc, workingdir, block)) |
|
425
|
4 |
|
target.write("\n".join(hash_generic)) |
|
426
|
|
|
|
|
427
|
|
|
|
|
428
|
4 |
|
def hash_writer(source, dest, workingdir, kwargs=None): |
|
429
|
|
|
""" |
|
430
|
|
|
Write per-file hashes. |
|
431
|
|
|
|
|
432
|
|
|
:param source: File to be hashed; foobar.ext |
|
433
|
|
|
:type source: str |
|
434
|
|
|
|
|
435
|
|
|
:param dest: Destination file; foobar.ext.cksum |
|
436
|
|
|
:type dest: str |
|
437
|
|
|
|
|
438
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
439
|
|
|
:type workingdir: str |
|
440
|
|
|
|
|
441
|
|
|
:param kwargs: Values. Refer to `:func:verifier_config_loader`. |
|
442
|
|
|
:type kwargs: dict |
|
443
|
|
|
""" |
|
444
|
4 |
|
block = int(kwargs['blocksize']) |
|
445
|
4 |
|
with open(dest, 'w') as target: |
|
446
|
4 |
|
base_hash("adler32", source, workingdir, block, ha32, target, kwargs) |
|
447
|
4 |
|
base_hash("crc32", source, workingdir, block, hc32, target, kwargs) |
|
448
|
4 |
|
base_hash("md4", source, workingdir, block, hm4, target, kwargs) |
|
449
|
4 |
|
base_hash("md5", source, workingdir, block, hm5, target, kwargs) |
|
450
|
4 |
|
base_hash("sha0", source, workingdir, block, hs0, target, kwargs) |
|
451
|
4 |
|
base_hash("sha1", source, workingdir, block, hs1, target, kwargs) |
|
452
|
4 |
|
base_hash("sha224", source, workingdir, block, hs224, target, kwargs) |
|
453
|
4 |
|
base_hash("sha256", source, workingdir, block, hs256, target, kwargs) |
|
454
|
4 |
|
base_hash("sha384", source, workingdir, block, hs384, target, kwargs) |
|
455
|
4 |
|
base_hash("sha512", source, workingdir, block, hs512, target, kwargs) |
|
456
|
4 |
|
base_hash("ripemd160", source, workingdir, block, hr160, target, kwargs) |
|
457
|
4 |
|
base_hash("whirlpool", source, workingdir, block, hwp, target, kwargs) |
|
458
|
4 |
|
base_hash("sha3224", source, workingdir, block, hs3224, target, kwargs) |
|
459
|
4 |
|
base_hash("sha3256", source, workingdir, block, hs3256, target, kwargs) |
|
460
|
4 |
|
base_hash("sha3384", source, workingdir, block, hs3384, target, kwargs) |
|
461
|
4 |
|
base_hash("sha3512", source, workingdir, block, hs3512, target, kwargs) |
|
462
|
|
|
|
|
463
|
|
|
|
|
464
|
4 |
|
def filefilter(file, workingdir, extras=()): |
|
465
|
|
|
""" |
|
466
|
|
|
Check if file in folder is a folder, or if it's got a forbidden extension. |
|
467
|
|
|
|
|
468
|
|
|
:param file: File to be hashed. |
|
469
|
|
|
:type file: str |
|
470
|
|
|
|
|
471
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
472
|
|
|
:type workingdir: str |
|
473
|
|
|
|
|
474
|
|
|
:param extras: Tuple of extra extensions. |
|
475
|
|
|
:type extras: tuple |
|
476
|
|
|
""" |
|
477
|
4 |
|
return not (os.path.isdir(os.path.join(workingdir, file)) or file.endswith(bbconstants.SUPPS + extras)) |
|
478
|
|
|
|
|
479
|
|
|
|
|
480
|
4 |
|
def prep_verifier(ldir, selective=False): |
|
481
|
|
|
""" |
|
482
|
|
|
Prepare files for verifier function. |
|
483
|
|
|
|
|
484
|
|
|
:param ldir: Path containing files you wish to verify. |
|
485
|
|
|
:type ldir: str |
|
486
|
|
|
|
|
487
|
|
|
:param selective: Filtering filenames/extensions. Default is false. |
|
488
|
|
|
:type selective: bool |
|
489
|
|
|
""" |
|
490
|
4 |
|
exts = (".txt",) if selective else () |
|
491
|
4 |
|
fxs = [os.path.join(ldir, afx) for afx in os.listdir(ldir) if filefilter(afx, ldir, exts)] |
|
492
|
4 |
|
return fxs |
|
493
|
|
|
|
|
494
|
|
|
|
|
495
|
4 |
|
def verifier(ldir, kwargs=None, selective=False): |
|
496
|
|
|
""" |
|
497
|
|
|
For all files in a directory, perform various hash/checksum functions. |
|
498
|
|
|
Take dict to define hashes, write output to a/individual .cksum file(s). |
|
499
|
|
|
|
|
500
|
|
|
:param ldir: Path containing files you wish to verify. |
|
501
|
|
|
:type ldir: str |
|
502
|
|
|
|
|
503
|
|
|
:param kwargs: Values. Refer to `:func:verifier_config_loader`. |
|
504
|
|
|
:type kwargs: dict |
|
505
|
|
|
|
|
506
|
|
|
:param selective: Filtering filenames/extensions. Default is false. |
|
507
|
|
|
:type selective: bool |
|
508
|
|
|
""" |
|
509
|
4 |
|
kwargs = verifier_config_loader() if kwargs is None else kwargs |
|
510
|
4 |
|
fxs = prep_verifier(ldir, selective) |
|
511
|
4 |
|
with concurrent.futures.ThreadPoolExecutor(max_workers=utilities.workers(fxs)) as xec: |
|
512
|
4 |
|
for file in fxs: |
|
513
|
4 |
|
verifier_individual(xec, ldir, file, kwargs) |
|
514
|
|
|
|
|
515
|
|
|
|
|
516
|
4 |
|
def verifier_individual(xec, ldir, file, kwargs): |
|
517
|
|
|
""" |
|
518
|
|
|
Individually verify files through a ThreadPoolExecutor. |
|
519
|
|
|
|
|
520
|
|
|
:param xec: ThreadPoolExecutor instance. |
|
521
|
|
|
:type xec: concurrent.futures.ThreadPoolExecutor |
|
522
|
|
|
|
|
523
|
|
|
:param ldir: Path containing files you wish to verify. |
|
524
|
|
|
:type ldir: str |
|
525
|
|
|
|
|
526
|
|
|
:param file: Filename. |
|
527
|
|
|
:type file: str |
|
528
|
|
|
|
|
529
|
|
|
:param kwargs: Values. Refer to `:func:verifier_config_loader`. |
|
530
|
|
|
:type kwargs: dict |
|
531
|
|
|
""" |
|
532
|
4 |
|
print("HASHING:", os.path.basename(file)) |
|
533
|
4 |
|
basename = file + ".cksum" |
|
534
|
4 |
|
targetname = os.path.join(ldir, basename) |
|
535
|
4 |
|
try: |
|
536
|
4 |
|
xec.submit(hash_writer, file, targetname, ldir, kwargs) |
|
537
|
4 |
|
except Exception as exc: |
|
|
|
|
|
|
538
|
4 |
|
exceptions.handle_exception(exc) |
|
539
|
|
|
|
|
540
|
|
|
|
|
541
|
4 |
|
def gpgrunner(workingdir, keyid=None, pword=None, selective=False): |
|
542
|
|
|
""" |
|
543
|
|
|
Create ASCII-armored PGP signatures for all files in a given directory, in parallel. |
|
544
|
|
|
|
|
545
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
546
|
|
|
:type workingdir: str |
|
547
|
|
|
|
|
548
|
|
|
:param keyid: Key to use. 8-character hexadecimal, with or without 0x. |
|
549
|
|
|
:type keyid: str |
|
550
|
|
|
|
|
551
|
|
|
:param pword: Passphrase for given key. |
|
552
|
|
|
:type pword: str |
|
553
|
|
|
|
|
554
|
|
|
:param selective: Filtering filenames/extensions. Default is false. |
|
555
|
|
|
:type selective: bool |
|
556
|
|
|
""" |
|
557
|
4 |
|
try: |
|
558
|
4 |
|
gpg = gnupg.GPG() |
|
559
|
4 |
|
except ValueError: |
|
560
|
4 |
|
print("COULD NOT FIND GnuPG!") |
|
561
|
4 |
|
raise SystemExit |
|
562
|
|
|
else: |
|
563
|
4 |
|
gpgrunner_clean(gpg, workingdir, keyid, pword, selective) |
|
564
|
|
|
|
|
565
|
|
|
|
|
566
|
4 |
|
def prep_gpgkeyid(keyid=None): |
|
567
|
|
|
""" |
|
568
|
|
|
Prepare GPG key ID. |
|
569
|
|
|
|
|
570
|
|
|
:param keyid: Key to use. 8-character hexadecimal, with or without 0x. |
|
571
|
|
|
:type keyid: str |
|
572
|
|
|
""" |
|
573
|
4 |
|
return "0x" + keyid.upper() if not keyid.startswith("0x") else keyid.upper().replace("X", "x") |
|
574
|
|
|
|
|
575
|
|
|
|
|
576
|
4 |
|
def prep_gpgrunner(workingdir, keyid=None): |
|
577
|
|
|
""" |
|
578
|
|
|
Prepare key and files for gpgrunner function. |
|
579
|
|
|
|
|
580
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
581
|
|
|
:type workingdir: str |
|
582
|
|
|
|
|
583
|
|
|
:param keyid: Key to use. 8-character hexadecimal, with or without 0x. |
|
584
|
|
|
:type keyid: str |
|
585
|
|
|
""" |
|
586
|
4 |
|
keyid = prep_gpgkeyid(keyid) |
|
587
|
4 |
|
dirlist = os.listdir(workingdir) |
|
588
|
4 |
|
files = [file for file in dirlist if filefilter(file, workingdir)] |
|
589
|
4 |
|
return keyid, files |
|
590
|
|
|
|
|
591
|
|
|
|
|
592
|
4 |
|
def gpgrunner_clean(gpg, workingdir, keyid=None, pword=None, selective=False): |
|
593
|
|
|
""" |
|
594
|
|
|
Run GPG signature generation after filtering out errors. |
|
595
|
|
|
|
|
596
|
|
|
:param gpg: Instance of Python GnuPG executable. |
|
597
|
|
|
:type gpg: gnupg.GPG() |
|
598
|
|
|
|
|
599
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
600
|
|
|
:type workingdir: str |
|
601
|
|
|
|
|
602
|
|
|
:param keyid: Key to use. 8-character hexadecimal, with or without 0x. |
|
603
|
|
|
:type keyid: str |
|
604
|
|
|
|
|
605
|
|
|
:param pword: Passphrase for given key. |
|
606
|
|
|
:type pword: str |
|
607
|
|
|
|
|
608
|
|
|
:param selective: Filtering filenames/extensions. Default is false. |
|
609
|
|
|
:type selective: bool |
|
610
|
|
|
""" |
|
611
|
4 |
|
keyid, files = prep_gpgrunner(workingdir, keyid) |
|
|
|
|
|
|
612
|
4 |
|
with concurrent.futures.ThreadPoolExecutor(max_workers=utilities.workers(files)) as xec: |
|
613
|
4 |
|
for file in files: |
|
614
|
4 |
|
gpgwriter(gpg, xec, file, workingdir, selective, keyid, pword) |
|
615
|
|
|
|
|
616
|
|
|
|
|
617
|
4 |
|
def gpgwriter(gpg, xec, file, workingdir, selective=False, keyid=None, pword=None): |
|
618
|
|
|
""" |
|
619
|
|
|
Write individual GPG signatures. |
|
620
|
|
|
|
|
621
|
|
|
:param gpg: Instance of Python GnuPG executable. |
|
622
|
|
|
:type gpg: gnupg.GPG() |
|
623
|
|
|
|
|
624
|
|
|
:param xec: ThreadPoolExecutor instance. |
|
625
|
|
|
:type xec: concurrent.futures.ThreadPoolExecutor |
|
626
|
|
|
|
|
627
|
|
|
:param file: File inside workingdir that is being verified. |
|
628
|
|
|
:type file: str |
|
629
|
|
|
|
|
630
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
631
|
|
|
:type workingdir: str |
|
632
|
|
|
|
|
633
|
|
|
:param selective: Filtering filenames/extensions. Default is false. |
|
634
|
|
|
:type selective: bool |
|
635
|
|
|
|
|
636
|
|
|
:param keyid: Key to use. 8-character hexadecimal, with or without 0x. |
|
637
|
|
|
:type keyid: str |
|
638
|
|
|
|
|
639
|
|
|
:param pword: Passphrase for given key. |
|
640
|
|
|
:type pword: str |
|
641
|
|
|
""" |
|
642
|
4 |
|
sup = bbconstants.SUPPS + (".txt",) if selective else bbconstants.SUPPS |
|
643
|
4 |
|
if not file.endswith(sup): |
|
644
|
4 |
|
aps = bbconstants.ARCSPLUS |
|
645
|
4 |
|
pfx = bbconstants.PREFIXES |
|
646
|
4 |
|
if (utilities.prepends(file, pfx, aps)) if selective else True: |
|
647
|
4 |
|
gpgwriter_clean(gpg, xec, file, workingdir, keyid, pword) |
|
648
|
|
|
|
|
649
|
|
|
|
|
650
|
4 |
|
def gpgwriter_clean(gpg, xec, file, workingdir, keyid=None, pword=None): |
|
651
|
|
|
""" |
|
652
|
|
|
Write individual GPG signatures after filtering file list. |
|
653
|
|
|
|
|
654
|
|
|
:param gpg: Instance of Python GnuPG executable. |
|
655
|
|
|
:type gpg: gnupg.GPG() |
|
656
|
|
|
|
|
657
|
|
|
:param xec: ThreadPoolExecutor instance. |
|
658
|
|
|
:type xec: concurrent.futures.ThreadPoolExecutor |
|
659
|
|
|
|
|
660
|
|
|
:param file: File inside workingdir that is being verified. |
|
661
|
|
|
:type file: str |
|
662
|
|
|
|
|
663
|
|
|
:param workingdir: Path containing files you wish to verify. |
|
664
|
|
|
:type workingdir: str |
|
665
|
|
|
|
|
666
|
|
|
:param keyid: Key to use. 8-character hexadecimal, with or without 0x. |
|
667
|
|
|
:type keyid: str |
|
668
|
|
|
|
|
669
|
|
|
:param pword: Passphrase for given key. |
|
670
|
|
|
:type pword: str |
|
671
|
|
|
""" |
|
672
|
4 |
|
print("VERIFYING:", os.path.basename(file)) |
|
673
|
4 |
|
thepath = os.path.join(workingdir, file) |
|
674
|
4 |
|
try: |
|
675
|
4 |
|
xec.submit(gpgfile, thepath, gpg, keyid, pword) |
|
676
|
4 |
|
except Exception as exc: |
|
|
|
|
|
|
677
|
4 |
|
exceptions.handle_exception(exc) |
|
678
|
|
|
|
|
679
|
|
|
|
|
680
|
4 |
|
def gpg_config_loader(homepath=None): |
|
681
|
|
|
""" |
|
682
|
|
|
Read a ConfigParser file to get PGP key, password (optional) |
|
683
|
|
|
|
|
684
|
|
|
:param homepath: Folder containing ini file. Default is user directory. |
|
685
|
|
|
:type homepath: str |
|
686
|
|
|
""" |
|
687
|
4 |
|
config = iniconfig.generic_loader('gpgrunner', homepath) |
|
688
|
4 |
|
gpgkey = config.get('key', fallback=None) |
|
689
|
4 |
|
gpgpass = config.get('pass', fallback=None) |
|
690
|
4 |
|
return gpgkey, gpgpass |
|
691
|
|
|
|
|
692
|
|
|
|
|
693
|
4 |
|
def gpg_config_writer(key=None, password=None, homepath=None): |
|
694
|
|
|
""" |
|
695
|
|
|
Write a ConfigParser file to store PGP key, password (optional) |
|
696
|
|
|
|
|
697
|
|
|
:param key: Key ID, leave as None to not write. |
|
698
|
|
|
:type key: str |
|
699
|
|
|
|
|
700
|
|
|
:param password: Key password, leave as None to not write. |
|
701
|
|
|
:type password: str |
|
702
|
|
|
|
|
703
|
|
|
:param homepath: Folder containing ini file. Default is user directory. |
|
704
|
|
|
:type homepath: str |
|
705
|
|
|
""" |
|
706
|
4 |
|
results = {} |
|
707
|
4 |
|
if key is not None: |
|
708
|
4 |
|
results["key"] = key |
|
709
|
4 |
|
if password is not None: |
|
710
|
4 |
|
results["pass"] = password |
|
711
|
4 |
|
iniconfig.generic_writer("gpgrunner", results, homepath) |
|
712
|
|
|
|
|
713
|
|
|
|
|
714
|
4 |
|
def verifier_config_loader(homepath=None): |
|
715
|
|
|
""" |
|
716
|
|
|
Read a ConfigParser file to get hash preferences. |
|
717
|
|
|
|
|
718
|
|
|
:param homepath: Folder containing ini file. Default is user directory. |
|
719
|
|
|
:type homepath: str |
|
720
|
|
|
""" |
|
721
|
4 |
|
ini = iniconfig.generic_loader("hashmodes", homepath) |
|
722
|
4 |
|
results = {} |
|
723
|
4 |
|
results['crc32'] = bool(ini.getboolean('crc32', fallback=False)) |
|
724
|
4 |
|
results['adler32'] = bool(ini.getboolean('adler32', fallback=False)) |
|
725
|
4 |
|
results['sha0'] = bool(ini.getboolean('sha0', fallback=False)) |
|
726
|
4 |
|
results['sha1'] = bool(ini.getboolean('sha1', fallback=True)) |
|
727
|
4 |
|
results['sha224'] = bool(ini.getboolean('sha224', fallback=False)) |
|
728
|
4 |
|
results['sha256'] = bool(ini.getboolean('sha256', fallback=True)) |
|
729
|
4 |
|
results['sha384'] = bool(ini.getboolean('sha384', fallback=False)) |
|
730
|
4 |
|
results['sha512'] = bool(ini.getboolean('sha512', fallback=False)) |
|
731
|
4 |
|
results['md5'] = bool(ini.getboolean('md5', fallback=True)) |
|
732
|
4 |
|
results['md4'] = bool(ini.getboolean('md4', fallback=False)) |
|
733
|
4 |
|
results['ripemd160'] = bool(ini.getboolean('ripemd160', fallback=False)) |
|
734
|
4 |
|
results['whirlpool'] = bool(ini.getboolean('whirlpool', fallback=False)) |
|
735
|
4 |
|
results['blocksize'] = int(ini.getint('blocksize', fallback=16777216)) |
|
736
|
4 |
|
results['sha3224'] = bool(ini.getboolean('sha3224', fallback=False)) |
|
737
|
4 |
|
results['sha3256'] = bool(ini.getboolean('sha3256', fallback=False)) |
|
738
|
4 |
|
results['sha3384'] = bool(ini.getboolean('sha3384', fallback=False)) |
|
739
|
4 |
|
results['sha3512'] = bool(ini.getboolean('sha3512', fallback=False)) |
|
740
|
4 |
|
return results |
|
741
|
|
|
|
|
742
|
|
|
|
|
743
|
4 |
|
def verifier_config_writer(resultdict=None, homepath=None): |
|
744
|
|
|
""" |
|
745
|
|
|
Write a ConfigParser file to store hash preferences. |
|
746
|
|
|
|
|
747
|
|
|
:param resultdict: Dictionary of results: {method, bool} |
|
748
|
|
|
:type resultdict: dict({str, bool}) |
|
749
|
|
|
|
|
750
|
|
|
:param homepath: Folder containing ini file. Default is user directory. |
|
751
|
|
|
:type homepath: str |
|
752
|
|
|
""" |
|
753
|
4 |
|
if resultdict is None: |
|
754
|
4 |
|
resultdict = verifier_config_loader() |
|
755
|
4 |
|
results = {method: str(flag).lower() for method, flag in resultdict.items()} |
|
756
|
|
|
iniconfig.generic_writer("hashmodes", results, homepath) |
|
757
|
|
|
|
Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.
So, unless you specifically plan to handle any error, consider adding a more specific exception.