-
Notifications
You must be signed in to change notification settings - Fork 0
/
hashlock.py
125 lines (97 loc) · 3.8 KB
/
hashlock.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python3
# HashLock v2.0
import sys
import argparse
import getpass
from string import ascii_lowercase, ascii_uppercase, digits
from hashlib import pbkdf2_hmac
class HashLockError(Exception):
pass
def __inputcheck(masterpass, service, length, chars, counter):
# Check masterpass
if masterpass == "":
raise HashLockError("Master Password cannot be empty")
# Check service
if service =="":
raise HashLockError("Service cannot be empty")
# Encode
try:
masterpass_bytes = masterpass.encode("utf-8")
service_bytes = service.encode("utf-8")
except UnicodeEncodeError:
raise HashLockError('Master password and Service must be UTF-8 encoded.')
# Check length
if length < 8:
raise HashLockError("Length must be 8 or more")
# Check counter
if counter not in range(0, 256):
raise HashLockError('Count must be between 0 and 255 inclusive.')
return masterpass_bytes, service_bytes
def __get_character_set(char_set):
# initialize character sets
character_subset = {
"l": ascii_lowercase,
"u": ascii_uppercase,
"d": digits,
"s": "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?"
}
count = 0
character_set = ""
for para in 'luds':
if char_set.find(para) != -1:
count += 1
character_set += character_subset[para]
if count < len(char_set):
raise HashLockError('Character set must only contain l, u, d, and/or s')
return character_set
def __hash(key, length):
# initialize salt
salt = bytes.fromhex("c3eb7751a4a23058ccbf785e10b40ee5")
# derive
return pbkdf2_hmac("sha256", key, salt, 100000, length)
def __renderpassword(entropy, character_set):
password = ""
for char in entropy:
password += character_set[int(char)%(len(character_set))]
return password
def hashlock(masterpass, service, length=20, chars='luds', counter=0):
'''
Return a password generated from a master password and a service. User can specify length of output password, character set to use, and password update counter.
Character sets:
l: abcdefghijklmnopqrstuvwxyz
u: ABCDEFGHIJKLMNOPQRSTUVWXYZ
d: 0123456789
s: `~!@#$%^&*()-_=+[{]}\|;:'",<.>/?
'''
masterpass_bytes, service_bytes = __inputcheck(masterpass, service, length, chars, counter)
counter_bytes=bytes([counter])
character_set = __get_character_set(chars)
# calculate entropy of password wth hash
entropy = __hash(service_bytes + counter_bytes + masterpass_bytes, length)
# render password with character set
return __renderpassword(entropy, character_set)
def main():
parser = argparse.ArgumentParser(description='HashLock: A deterministic password manager/generator')
# Required arguments
parser.add_argument('service', metavar='SERVICE', help='service to generate password for.')
# Optional arguments
parser.add_argument('-l', '--length', type=int, default=20, help='length of output password. Must be 8 or more. [Default: 20]')
parser.add_argument('-s', '--set', metavar='CHARACTER SET', type=str, default='luds', help="character set of output password. [Default: luds]")
parser.add_argument('-c', '--counter', type=int, default=0, help='count for updated output password. [Default: 0]')
parser.add_argument('-v', '--version', action='version', version="HashLock v2.0")
args = parser.parse_args()
masterpass = getpass.getpass("Master Password: ")
try:
password = hashlock(masterpass, args.service, args.length, args.set, args.counter)
except HashLockError as e:
print(e)
sys.exit()
print(password)
masterpass = ""
password = ""
args.service = ""
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('')