-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMaster vault.py
More file actions
159 lines (141 loc) · 4.96 KB
/
Master vault.py
File metadata and controls
159 lines (141 loc) · 4.96 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import os
import json
import getpass
import bcrypt
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
VAULT_FILE = "vault.bin"
NONCE_SIZE = 12
MASTER_PASS_COUNT = 5
def hash_password(pw):
return bcrypt.hashpw(pw.encode(), bcrypt.gensalt())
def check_password(pw, hashed):
return bcrypt.checkpw(pw.encode(), hashed)
def generate_key(master_passwords):
# Derive a 32-byte AES key from concatenated master passwords (simple SHA256 here)
import hashlib
concat = "".join(master_passwords).encode()
return hashlib.sha256(concat).digest()
def encrypt(data, key):
aesgcm = AESGCM(key)
nonce = os.urandom(NONCE_SIZE)
ct = aesgcm.encrypt(nonce, data, None)
return nonce + ct
def decrypt(enc_data, key):
aesgcm = AESGCM(key)
nonce = enc_data[:NONCE_SIZE]
ct = enc_data[NONCE_SIZE:]
return aesgcm.decrypt(nonce, ct, None)
def create_vault(master_passwords):
key = generate_key(master_passwords)
empty_data = json.dumps({"master_hashes": [hash_password(pw).decode() for pw in master_passwords], "vault": {}}).encode()
encrypted = encrypt(empty_data, key)
with open(VAULT_FILE, "wb") as f:
f.write(encrypted)
print("Vault created and encrypted.")
def load_vault(master_passwords):
if not os.path.exists(VAULT_FILE):
print("Vault file not found. Creating new vault.")
create_vault(master_passwords)
key = generate_key(master_passwords)
with open(VAULT_FILE, "rb") as f:
enc = f.read()
try:
dec = decrypt(enc, key)
data = json.loads(dec)
# verify master passwords hashes:
for i, h in enumerate(data["master_hashes"]):
if not check_password(master_passwords[i], h.encode()):
raise ValueError("Master password incorrect.")
return data, key
except Exception as e:
print("Failed to unlock vault:", e)
return None, None
def save_vault(data, key):
raw = json.dumps(data).encode()
encrypted = encrypt(raw, key)
with open(VAULT_FILE, "wb") as f:
f.write(encrypted)
def input_master_passwords():
print(f"Enter your {MASTER_PASS_COUNT} master passwords:")
pwds = []
for i in range(MASTER_PASS_COUNT):
pw = getpass.getpass(f"Master password {i+1}: ")
pwds.append(pw)
return pwds
def add_entry(data):
user = input("New username: ").strip()
if user in data["vault"]:
print("Username exists.")
return
pw = getpass.getpass("Password for this username: ")
data["vault"][user] = pw
print("Entry added.")
def edit_entry(data):
user = input("Username to edit: ").strip()
if user not in data["vault"]:
print("Not found.")
return
pw = getpass.getpass("New password: ")
data["vault"][user] = pw
print("Entry updated.")
def change_master_passwords(data):
print("Changing master passwords. Enter old passwords first.")
old_pwds = input_master_passwords()
# verify old passwords:
for i, h in enumerate(data["master_hashes"]):
if not check_password(old_pwds[i], h.encode()):
print("Old master password incorrect. Abort.")
return False
print("Enter new master passwords:")
new_pwds = input_master_passwords()
data["master_hashes"] = [hash_password(pw).decode() for pw in new_pwds]
print("Master passwords updated.")
return new_pwds
def main():
if not os.path.exists(VAULT_FILE):
print("No vault found. Setup your 5 master passwords.")
master_passwords = input_master_passwords()
create_vault(master_passwords)
else:
master_passwords = input_master_passwords()
data, key = load_vault(master_passwords)
if data is None:
print("Cannot unlock vault. Exiting.")
return
while True:
print("\nOptions:")
print("1 - Add new username/password")
print("2 - Edit existing password")
print("3 - Change master passwords")
print("4 - List usernames")
print("5 - View password for username")
print("6 - Exit")
choice = input("Choice: ").strip()
if choice == "1":
add_entry(data)
elif choice == "2":
edit_entry(data)
elif choice == "3":
new_pwds = change_master_passwords(data)
if new_pwds:
master_passwords = new_pwds
key = generate_key(master_passwords)
elif choice == "4":
print("Usernames:")
for u in data["vault"]:
print(" -", u)
elif choice == "5":
u = input("Username to view password: ").strip()
if u in data["vault"]:
print(f"Password for {u}: {data['vault'][u]}")
else:
print("Not found.")
elif choice == "6":
save_vault(data, key)
print("Vault saved. Exiting.")
break
else:
print("Invalid choice.")
save_vault(data, key)
if __name__ == "__main__":
main()