found hashcash implementation
This commit is contained in:
parent
31e9744aa0
commit
49f38d0429
105
hashcash.py
Normal file
105
hashcash.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
Module to generate and validate HashCash stamps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'prussell'
|
||||||
|
|
||||||
|
from hashlib import sha1
|
||||||
|
from datetime import datetime
|
||||||
|
from random import randint
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
rand_chars = ([chr(x) for x in range(ord('a'), ord('z'))] +
|
||||||
|
[chr(x) for x in range(ord('A'), ord('Z'))] +
|
||||||
|
[chr(x) for x in range(ord('0'), ord('9'))] +
|
||||||
|
['+', '-', '/'])
|
||||||
|
|
||||||
|
|
||||||
|
char_map = {'0' : '0000',
|
||||||
|
'1' : '0001',
|
||||||
|
'2' : '0010',
|
||||||
|
'3' : '0011',
|
||||||
|
'4' : '0100',
|
||||||
|
'5' : '0101',
|
||||||
|
'6' : '0110',
|
||||||
|
'7' : '0111',
|
||||||
|
'8' : '1000',
|
||||||
|
'9' : '1001',
|
||||||
|
'a' : '1010',
|
||||||
|
'b' : '1011',
|
||||||
|
'c' : '1100',
|
||||||
|
'd' : '1101',
|
||||||
|
'e' : '1110',
|
||||||
|
'f' : '1111'}
|
||||||
|
|
||||||
|
|
||||||
|
rc_len = len(rand_chars)
|
||||||
|
|
||||||
|
min_bits = 0
|
||||||
|
# Max number of bits for SHA-1 stamps
|
||||||
|
max_bits = 160
|
||||||
|
default_bits = 15
|
||||||
|
|
||||||
|
def is_valid(stamp : str) -> bool:
|
||||||
|
return validate(int(stamp.split(':')[1]), stamp)
|
||||||
|
|
||||||
|
def validate(nbits : int, stamp : str, encoding : str ='utf-8') -> bool:
|
||||||
|
if nbits < min_bits or nbits > max_bits:
|
||||||
|
raise ValueError("Param 'nbits' must be in range [0, 160), but is {}".format(nbits))
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
total = 0
|
||||||
|
N = int(nbits/8)
|
||||||
|
hashed = sha1(stamp.encode(encoding)).digest()
|
||||||
|
|
||||||
|
while i < N:
|
||||||
|
total |= hashed[i]
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
remainder = nbits % 8
|
||||||
|
if remainder != 0:
|
||||||
|
total |= hashed[i] >> (8 - remainder)
|
||||||
|
|
||||||
|
return total == 0
|
||||||
|
|
||||||
|
def generate(nbits : int, resource : str, encoding : str ='utf-8') -> str:
|
||||||
|
# ver:bits:date:resource:[ext]:rand:counter
|
||||||
|
ver = 1
|
||||||
|
bits = nbits
|
||||||
|
date_str = datetime.utcnow().strftime("%Y%m%d%H%M%S")
|
||||||
|
ext = ''
|
||||||
|
rand = ''.join(rand_chars[randint(0, rc_len-1)] for x in range(0, 10))
|
||||||
|
counter = 0
|
||||||
|
|
||||||
|
result = None
|
||||||
|
while result is None:
|
||||||
|
#stamp = ":".join(str(elem) for elem in [ver, bits, date_str, resource, ext, rand, counter])
|
||||||
|
stamp = "{}{}".format(resource,counter)
|
||||||
|
|
||||||
|
if validate(nbits, stamp, encoding=encoding):
|
||||||
|
result = stamp
|
||||||
|
break
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
parser = ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument("NBITS", type=int, default=default_bits, help="Number of leading zeroes in a stamp", choices=range(max_bits+1))
|
||||||
|
parser.add_argument("RESOURCE", help="The resource string to use in the stamp. Ex: email address, ip address, etc")
|
||||||
|
parser.add_argument('-v', '--validate', action='store_true', help="Validate RESOURCE as a HashCash stamp")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
func = generate
|
||||||
|
|
||||||
|
if args.validate:
|
||||||
|
func = validate
|
||||||
|
|
||||||
|
print(func(args.NBITS, args.RESOURCE))
|
Loading…
Reference in New Issue
Block a user