Completed
Push — master ( 37160e...09023d )
by John
01:04
created

verifier_individual()   A

Complexity

Conditions 2

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
dl 0
loc 23
rs 9.0856
c 1
b 0
f 0
1
#!/usr/bin/env python3
2
"""This module is used to generate file hashes/checksums and PGP signatures."""
3
4
import zlib  # crc32/adler32
5
import hashlib  # all other hashes
6
import hmac  # escreens is a hmac, news at 11
7
import os  # path work
8
import concurrent.futures  # parallelization
9
import gnupg  # interface b/w Python, GPG
10
from bbarchivist import bbconstants  # premade stuff
11
from bbarchivist import exceptions  # exceptions
12
from bbarchivist import utilities  # cores
13
from bbarchivist import iniconfig  # config parsing
14
15
__author__ = "Thurask"
16
__license__ = "WTFPL v2"
17
__copyright__ = "Copyright 2015-2016 Thurask"
18
19
20
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
    seed = 0
31
    with open(filepath, 'rb') as file:
32
        for chunk in iter(lambda: file.read(blocksize), b''):
33
            seed = zlib.crc32(chunk, seed)
34
    final = format(seed & 0xFFFFFFFF, "08x")
35
    return final
36
37
38
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
    asum = 1
49
    with open(filepath, 'rb') as file:
50
        while True:
51
            data = file.read(blocksize)
52
            if not data:
53
                break
54
            asum = zlib.adler32(data, asum)
55
            if asum < 0:
56
                asum += 2 ** 32
57
    final = format(asum & 0xFFFFFFFF, "08x")
58
    return final
59
60
61
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
    sha1 = hashlib.sha1()
72
    hashfunc_reader(filepath, sha1, blocksize)
73
    return sha1.hexdigest()
74
75
76
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
    sha224 = hashlib.sha224()
87
    hashfunc_reader(filepath, sha224, blocksize)
88
    return sha224.hexdigest()
89
90
91
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
    sha256 = hashlib.sha256()
102
    hashfunc_reader(filepath, sha256, blocksize)
103
    return sha256.hexdigest()
104
105
106
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
    sha384 = hashlib.sha384()
117
    hashfunc_reader(filepath, sha384, blocksize)
118
    return sha384.hexdigest()
119
120
121
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
    sha512 = hashlib.sha512()
132
    hashfunc_reader(filepath, sha512, blocksize)
133
    return sha512.hexdigest()
134
135
136
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
    try:
147
        sha3224 = hashlib.sha3_224()
148
    except AttributeError:
149
        print("REQUIRES PYTHON 3.6+")
150
    else:
151
        hashfunc_reader(filepath, sha3224, blocksize)
152
        return sha3224.hexdigest()
153
154
155
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
    try:
166
        sha3256 = hashlib.sha3_256()
167
    except AttributeError:
168
        print("REQUIRES PYTHON 3.6+")
169
    else:
170
        hashfunc_reader(filepath, sha3256, blocksize)
171
        return sha3256.hexdigest()
172
173
174
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
    try:
185
        sha3384 = hashlib.sha3_384()
186
    except AttributeError:
187
        print("REQUIRES PYTHON 3.6+")
188
    else:
189
        hashfunc_reader(filepath, sha3384, blocksize)
190
        return sha3384.hexdigest()
191
192
193
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
    try:
204
        sha3512 = hashlib.sha3_512()
205
    except AttributeError:
206
        print("REQUIRES PYTHON 3.6+")
207
    else:
208
        hashfunc_reader(filepath, sha3512, blocksize)
209
        return sha3512.hexdigest()
210
211
212
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
    md5 = hashlib.md5()
223
    hashfunc_reader(filepath, md5, blocksize)
224
    return md5.hexdigest()
225
226
227
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
    with open(filepath, 'rb') as file:
241
        while True:
242
            data = file.read(blocksize)
243
            if not data:
244
                break
245
            engine.update(data)
246
247
248
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
    try:
262
        engine = hashlib.new(method)
263
        hashfunc_reader(filepath, engine, blocksize)
264
        return engine.hexdigest()
265
    except ValueError as exc:
266
        msg = "{0} HASH FAILED".format(method.upper())
267
        exceptions.handle_exception(exc, msg, exceptions.DummyException)
268
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
269
270
271
def hm4(filepath, blocksize=16 * 1024 * 1024):
272
    """
273
    Return MD4 hash of a file; depends on system SSL library.
274
275
    :param filepath: File you wish to verify.
276
    :type filepath: str
277
278
    :param blocksize: How much of file to read at once.
279
    :type blocksize: int
280
    """
281
    return ssl_hash(filepath, "md4", blocksize)
282
283
284
def hr160(filepath, blocksize=16 * 1024 * 1024):
285
    """
286
    Return RIPEMD160 hash of a file; depends on system SSL library.
287
288
    :param filepath: File you wish to verify.
289
    :type filepath: str
290
291
    :param blocksize: How much of file to read at once.
292
    :type blocksize: int
293
    """
294
    return ssl_hash(filepath, "ripemd160", blocksize)
295
296
297
def hwp(filepath, blocksize=16 * 1024 * 1024):
298
    """
299
    Return Whirlpool hash of a file; depends on system SSL library.
300
301
    :param filepath: File you wish to verify.
302
    :type filepath: str
303
304
    :param blocksize: How much of file to read at once.
305
    :type blocksize: int
306
    """
307
    return ssl_hash(filepath, "whirlpool", blocksize)
308
309
310
def hs0(filepath, blocksize=16 * 1024 * 1024):
311
    """
312
    Return SHA-0 hash of a file; depends on system SSL library.
313
314
    :param filepath: File you wish to verify.
315
    :type filepath: str
316
317
    :param blocksize: How much of file to read at once.
318
    :type blocksize: int
319
    """
320
    return ssl_hash(filepath, "sha", blocksize)
321
322
323
def gpgfile(filepath, gpginst, key=None, pword=None):
324
    """
325
    Make ASCII-armored signature files with a given private key.
326
    Takes an instance of gnupg.GPG().
327
328
    :param filepath: File you wish to verify.
329
    :type filepath: str
330
331
    :param gpginst: Instance of Python GnuPG executable.
332
    :type gpginst: gnupg.GPG()
333
334
    :param key: Key ID. 0xABCDEF01
335
    :type key: str
336
337
    :param pword: Passphrase for key.
338
    :type pword: str
339
    """
340
    with open(filepath, "rb") as file:
341
        fname = file.name + ".asc"
342
        gpginst.sign_file(file, detach=True, keyid=key, passphrase=pword, output=fname)
343
344
345
def calculate_escreens(pin, app, uptime, duration=30):
346
    """
347
    Calculate key for the Engineering Screens based on input.
348
349
    :param pin: PIN to check. 8 character hexadecimal, lowercase.
350
    :type pin: str
351
352
    :param app: App version. 10.x.y.zzzz.
353
    :type app: str
354
355
    :param uptime: Uptime in ms.
356
    :type uptime: str
357
358
    :param duration: 1, 3, 6, 15, 30 (days).
359
    :type duration: str
360
    """
361
    #: Somehow, values for lifetimes for escreens.
362
    lifetimes = {
363
        1: "",
364
        3: "Hello my baby, hello my honey, hello my rag time gal",
365
        7: "He was a boy, and she was a girl, can I make it any more obvious?",
366
        15: "So am I, still waiting, for this world to stop hating?",
367
        30: "I love myself today, not like yesterday. "
368
    }
369
    lifetimes[30] += "I'm cool, I'm calm, I'm gonna be okay"
370
    #: Escreens magic HMAC secret.
371
    secret = 'Up the time stream without a TARDIS'
372
    duration = int(duration)
373
    if duration not in [1, 3, 6, 15, 30]:
374
        duration = 1
375
    data = pin.lower() + app + str(uptime) + lifetimes[duration]
376
    newhmac = hmac.new(secret.encode(), data.encode(), digestmod=hashlib.sha1)
377
    key = newhmac.hexdigest()[:8]
378
    return key.upper()
379
380
381
def hash_get(filename, hashfunc, workingdir, blocksize=16777216):
382
    """
383
    Generate and pretty format the hash result for a file.
384
385
    :param filename: File to hash.
386
    :type filename: str
387
388
    :param hashfunc: Hash function to use.
389
    :type hashfunc: function
390
391
    :param workingdir: Working directory.
392
    :type workingdir: str
393
394
    :param blocksize: Block size. Default is 16MB.
395
    :type blocksize: int
396
    """
397
    result = hashfunc(os.path.join(workingdir, filename), blocksize)
398
    return "{0} {1}\n".format(result.upper(), os.path.basename(filename))
399
400
401
def base_hash(hashtype, source, workingdir, block, hashfunc, target, kwargs=None):
402
    """
403
    Generic hash function; get hash, write to file.
404
405
    :param hashtype: Hash type.
406
    :type hashtype: str
407
408
    :param source: File to be hashed; foobar.ext
409
    :type source: str
410
411
    :param workingdir: Path containing files you wish to verify.
412
    :type workingdir: str
413
414
    :param block: Blocksize, in bytes.
415
    :type block: int
416
417
    :param target: File to write to.
418
    :type target: file
419
420
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
421
    :type kwargs: dict
422
    """
423
    if kwargs[hashtype]:
424
        hash_generic = [hashtype.upper()]
425
        hash_generic.append(hash_get(source, hashfunc, workingdir, block))
426
        target.write("\n".join(hash_generic))
427
428
429
def hash_writer(source, dest, workingdir, kwargs=None):
430
    """
431
    Write per-file hashes.
432
433
    :param source: File to be hashed; foobar.ext
434
    :type source: str
435
436
    :param dest: Destination file; foobar.ext.cksum
437
    :type dest: str
438
439
    :param workingdir: Path containing files you wish to verify.
440
    :type workingdir: str
441
442
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
443
    :type kwargs: dict
444
    """
445
    block = int(kwargs['blocksize'])
446
    with open(dest, 'w') as target:
447
        base_hash("adler32", source, workingdir, block, ha32, target, kwargs)
448
        base_hash("crc32", source, workingdir, block, hc32, target, kwargs)
449
        base_hash("md4", source, workingdir, block, hm4, target, kwargs)
450
        base_hash("md5", source, workingdir, block, hm5, target, kwargs)
451
        base_hash("sha0", source, workingdir, block, hs0, target, kwargs)
452
        base_hash("sha1", source, workingdir, block, hs1, target, kwargs)
453
        base_hash("sha224", source, workingdir, block, hs224, target, kwargs)
454
        base_hash("sha256", source, workingdir, block, hs256, target, kwargs)
455
        base_hash("sha384", source, workingdir, block, hs384, target, kwargs)
456
        base_hash("sha512", source, workingdir, block, hs512, target, kwargs)
457
        base_hash("ripemd160", source, workingdir, block, hr160, target, kwargs)
458
        base_hash("whirlpool", source, workingdir, block, hwp, target, kwargs)
459
        base_hash("sha3224", source, workingdir, block, hs3224, target, kwargs)
460
        base_hash("sha3256", source, workingdir, block, hs3256, target, kwargs)
461
        base_hash("sha3384", source, workingdir, block, hs3384, target, kwargs)
462
        base_hash("sha3512", source, workingdir, block, hs3512, target, kwargs)
463
464
465
def filefilter(file, workingdir, extras=()):
466
    """
467
    Check if file in folder is a folder, or if it's got a forbidden extension.
468
469
    :param file: File to be hashed.
470
    :type file: str
471
472
    :param workingdir: Path containing files you wish to verify.
473
    :type workingdir: str
474
475
    :param extras: Tuple of extra extensions.
476
    :type extras: tuple
477
    """
478
    return not (os.path.isdir(os.path.join(workingdir, file)) or file.endswith(bbconstants.SUPPS + extras))
479
480
481
def verifier(ldir, kwargs=None, selective=False):
482
    """
483
    For all files in a directory, perform various hash/checksum functions.
484
    Take dict to define hashes, write output to a/individual .cksum file(s).
485
486
    :param ldir: Path containing files you wish to verify.
487
    :type ldir: str
488
489
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
490
    :type kwargs: dict
491
    """
492
    kwargs = verifier_config_loader() if kwargs is None else kwargs
493
    exts = (".txt",) if selective else ()
494
    files = [os.path.join(ldir, file) for file in os.listdir(ldir) if filefilter(file, ldir, exts)]
495
    with concurrent.futures.ThreadPoolExecutor(max_workers=utilities.workers(files)) as xec:
496
        for file in files:
497
            verifier_individual(xec, ldir, file, kwargs)
498
499
500
def verifier_individual(xec, ldir, file, kwargs):
501
    """
502
    Individually verify files through a ThreadPoolExecutor.
503
504
    :param xec: ThreadPoolExecutor instance.
505
    :type xec: concurrent.futures.ThreadPoolExecutor
506
507
    :param ldir: Path containing files you wish to verify.
508
    :type ldir: str
509
510
    :param file: Filename.
511
    :type file: str
512
513
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
514
    :type kwargs: dict
515
    """
516
    print("HASHING:", os.path.basename(file))
517
    basename = file + ".cksum"
518
    targetname = os.path.join(ldir, basename)
519
    try:
520
        xec.submit(hash_writer, file, targetname, ldir, kwargs)
521
    except Exception as exc:
1 ignored issue
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

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.

Loading history...
522
        exceptions.handle_exception(exc)
523
524
525
def gpgrunner(workingdir, keyid=None, pword=None, selective=False):
526
    """
527
    Create ASCII-armored PGP signatures for all files in a given directory, in parallel.
528
529
    :param workingdir: Path containing files you wish to verify.
530
    :type workingdir: str
531
532
    :param keyid: Key to use. 8-character hexadecimal, with or without 0x.
533
    :type keyid: str
534
535
    :param pword: Passphrase for given key.
536
    :type pword: str
537
538
    :param selective: Filtering filenames/extensions. Default is false.
539
    :type selective: bool
540
    """
541
    try:
542
        gpg = gnupg.GPG()
543
    except ValueError:
544
        print("COULD NOT FIND GnuPG!")
545
        raise SystemExit
546
    else:
547
        gpgrunner_clean(gpg, workingdir, keyid, pword, selective)
548
549
550
def gpgrunner_clean(gpg, workingdir, keyid=None, pword=None, selective=False):
551
    """
552
    Run GPG signature generation after filtering out errors.
553
554
    :param gpg: Instance of Python GnuPG executable.
555
    :type gpg: gnupg.GPG()
556
557
    :param workingdir: Path containing files you wish to verify.
558
    :type workingdir: str
559
560
    :param keyid: Key to use. 8-character hexadecimal, with or without 0x.
561
    :type keyid: str
562
563
    :param pword: Passphrase for given key.
564
    :type pword: str
565
566
    :param selective: Filtering filenames/extensions. Default is false.
567
    :type selective: bool
568
    """
569
    keyid = "0x" + keyid.upper() if not keyid.startswith("0x") else keyid.upper().replace("X", "x")
570
    dirlist = os.listdir(workingdir)
571
    files = [file for file in dirlist if filefilter(file, workingdir)]
572
    with concurrent.futures.ThreadPoolExecutor(max_workers=utilities.workers(files)) as xec:
573
        for file in files:
574
            gpgwriter(gpg, xec, file, workingdir, selective, keyid, pword)
575
576
577
def gpgwriter(gpg, xec, file, workingdir, selective=False, keyid=None, pword=None):
578
    """
579
    Write individual GPG signatures.
580
581
    :param gpg: Instance of Python GnuPG executable.
582
    :type gpg: gnupg.GPG()
583
584
    :param xec: ThreadPoolExecutor instance.
585
    :type xec: concurrent.futures.ThreadPoolExecutor
586
587
    :param file: File inside workingdir that is being verified.
588
    :type file: str
589
590
    :param workingdir: Path containing files you wish to verify.
591
    :type workingdir: str
592
593
    :param selective: Filtering filenames/extensions. Default is false.
594
    :type selective: bool
595
596
    :param keyid: Key to use. 8-character hexadecimal, with or without 0x.
597
    :type keyid: str
598
599
    :param pword: Passphrase for given key.
600
    :type pword: str
601
    """
602
    sup = bbconstants.SUPPS + (".txt",) if selective else bbconstants.SUPPS
603
    if not file.endswith(sup):
604
        aps = bbconstants.ARCSPLUS
605
        pfx = bbconstants.PREFIXES
606
        if (utilities.prepends(file, pfx, aps)) if selective else True:
607
            gpgwriter_clean(gpg, xec, file, workingdir, keyid, pword)
608
609
610
def gpgwriter_clean(gpg, xec, file, workingdir, keyid=None, pword=None):
611
    """
612
    Write individual GPG signatures after filtering file list.
613
614
    :param gpg: Instance of Python GnuPG executable.
615
    :type gpg: gnupg.GPG()
616
617
    :param xec: ThreadPoolExecutor instance.
618
    :type xec: concurrent.futures.ThreadPoolExecutor
619
620
    :param file: File inside workingdir that is being verified.
621
    :type file: str
622
623
    :param workingdir: Path containing files you wish to verify.
624
    :type workingdir: str
625
626
    :param keyid: Key to use. 8-character hexadecimal, with or without 0x.
627
    :type keyid: str
628
629
    :param pword: Passphrase for given key.
630
    :type pword: str
631
    """
632
    print("VERIFYING:", os.path.basename(file))
633
    thepath = os.path.join(workingdir, file)
634
    try:
635
        xec.submit(gpgfile, thepath, gpg, keyid, pword)
636
    except Exception as exc:
1 ignored issue
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

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.

Loading history...
637
        exceptions.handle_exception(exc)
638
639
640
def gpg_config_loader(homepath=None):
641
    """
642
    Read a ConfigParser file to get PGP key, password (optional)
643
644
    :param homepath: Folder containing ini file. Default is user directory.
645
    :type homepath: str
646
    """
647
    config = iniconfig.generic_loader('gpgrunner', homepath)
648
    gpgkey = config.get('key', fallback=None)
649
    gpgpass = config.get('pass', fallback=None)
650
    return gpgkey, gpgpass
651
652
653
def gpg_config_writer(key=None, password=None, homepath=None):
654
    """
655
    Write a ConfigParser file to store PGP key, password (optional)
656
657
    :param key: Key ID, leave as None to not write.
658
    :type key: str
659
660
    :param password: Key password, leave as None to not write.
661
    :type password: str
662
663
    :param homepath: Folder containing ini file. Default is user directory.
664
    :type homepath: str
665
    """
666
    results = {}
667
    if key is not None:
668
        results["key"] = key
669
    if password is not None:
670
        results["pass"] = password
671
    iniconfig.generic_writer("gpgrunner", results, homepath)
672
673
674
def verifier_config_loader(homepath=None):
675
    """
676
    Read a ConfigParser file to get hash preferences.
677
678
    :param homepath: Folder containing ini file. Default is user directory.
679
    :type homepath: str
680
    """
681
    ini = iniconfig.generic_loader("hashmodes", homepath)
682
    results = {}
683
    results['crc32'] = bool(ini.getboolean('crc32', fallback=False))
684
    results['adler32'] = bool(ini.getboolean('adler32', fallback=False))
685
    results['sha0'] = bool(ini.getboolean('sha0', fallback=False))
686
    results['sha1'] = bool(ini.getboolean('sha1', fallback=True))
687
    results['sha224'] = bool(ini.getboolean('sha224', fallback=False))
688
    results['sha256'] = bool(ini.getboolean('sha256', fallback=True))
689
    results['sha384'] = bool(ini.getboolean('sha384', fallback=False))
690
    results['sha512'] = bool(ini.getboolean('sha512', fallback=False))
691
    results['md5'] = bool(ini.getboolean('md5', fallback=True))
692
    results['md4'] = bool(ini.getboolean('md4', fallback=False))
693
    results['ripemd160'] = bool(ini.getboolean('ripemd160', fallback=False))
694
    results['whirlpool'] = bool(ini.getboolean('whirlpool', fallback=False))
695
    results['blocksize'] = int(ini.getint('blocksize', fallback=16777216))
696
    results['sha3224'] = bool(ini.getboolean('sha3224', fallback=False))
697
    results['sha3256'] = bool(ini.getboolean('sha3256', fallback=False))
698
    results['sha3384'] = bool(ini.getboolean('sha3384', fallback=False))
699
    results['sha3512'] = bool(ini.getboolean('sha3512', fallback=False))
700
    return results
701
702
703
def verifier_config_writer(resultdict=None, homepath=None):
704
    """
705
    Write a ConfigParser file to store hash preferences.
706
707
    :param resultdict: Dictionary of results: {method, bool}
708
    :type resultdict: dict({str, bool})
709
710
    :param homepath: Folder containing ini file. Default is user directory.
711
    :type homepath: str
712
    """
713
    if resultdict is None:
714
        resultdict = verifier_config_loader()
715
    results = {method: str(flag).lower() for method, flag in resultdict.items()}
716
    iniconfig.generic_writer("hashmodes", results, homepath)
717