-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVaultLib.py
More file actions
193 lines (166 loc) · 7.28 KB
/
VaultLib.py
File metadata and controls
193 lines (166 loc) · 7.28 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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import os
import base64
import json
import secrets
import string
import subprocess
import shutil
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.fernet import Fernet, InvalidToken
class Vault:
def __init__(self, vault_name:str, master_password, out_image, mask_source = None, signature: bytes = b"NEO_SPACE_MARKER", user: str = 'Default', os: str = "Arch Linux", show_logo: bool = False):
self.db_path = out_image
self.mask_source = mask_source
self.vault_name = vault_name
self.user = user
self.os = os
self.salt_size = 16
self.signature = signature
# Печатаем приветствие в стиле Arch
if show_logo:
self._print_logo()
self.salt = self._get_or_create_salt()
self.key = self._derive_key(master_password)
self.cipher = Fernet(self.key)
def _print_logo(self):
"""Просто потому что I use Arch btw"""
print(f"""
-`
.o+` [ {self.vault_name} ]
`ooo/ [ User: {self.user} ]
`+oooo: [ OS: {self.os} ]
`+oooooo: [ Status: Encrypted ]
-+oooooo+:
`/:-:++oooo+: [ Mask: {self.mask_source} ]
`/++++/+++++++: [ Source: {self.db_path} ]
`/++++++++++++++:
`/+++ooooooooooooo/`
./ooossssqssoooossssw`
.oossssso-````/ossssss+
-osssssso. :ssssssso.
:osssssss/ ossssoooo.
/ossssssss/ ossssoooo.
/osssssoooo/ ossssoooo.
""")
def _show_decoy(self):
if os.path.exists(self.db_path):
try:
subprocess.Popen(['xdg-open', self.db_path],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except Exception: pass
def _get_or_create_salt(self):
if os.path.exists(self.db_path):
with open(self.db_path, "rb") as f:
content = f.read()
idx = content.find(self.signature)
if idx != -1:
start = idx + len(self.signature)
return content[start : start + self.salt_size]
return os.urandom(self.salt_size)
def _derive_key(self, password):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=self.salt,
iterations=200000,
)
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
def _save_db(self, data):
if os.path.exists(self.db_path):
with open(self.db_path, "rb") as f:
content = f.read()
idx = content.find(self.signature)
image_base = content[:idx] if idx != -1 else content
elif self.mask_source and os.path.exists(self.mask_source):
with open(self.mask_source, "rb") as f:
image_base = f.read()
else:
image_base = base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==")
with open(self.db_path, "wb") as f:
f.write(image_base)
f.write(self.signature)
f.write(self.salt)
encrypted_data = self.cipher.encrypt(json.dumps(data).encode())
f.write(encrypted_data)
def _load_db(self):
if not os.path.exists(self.db_path): return {}
try:
with open(self.db_path, "rb") as f:
content = f.read()
idx = content.find(self.signature)
if idx == -1: return {}
data_start = idx + len(self.signature) + self.salt_size
decrypted_json = self.cipher.decrypt(content[data_start:]).decode()
return json.loads(decrypted_json)
except Exception: return {}
def get_services(self) -> list[str] | None:
"""Возвращает список всех сохраненных сервисов"""
to_ret: list[str] = []
data = self._load_db()
if not data:
print("[!] Секреты не найдены или неверный пароль.")
return None
for i, service in enumerate(data.keys(), 1):
to_ret.append(service)
return to_ret
def list_entries(self):
"""Выводит список всех сохраненных сервисов"""
data = self._load_db()
if not data:
print("[!] Секреты не найдены или неверный пароль.")
return
print("\n" + "─" * 30)
print(f"{'#':<3} | {'СЕРВИС':<20}")
print("─" * 30)
for i, service in enumerate(data.keys(), 1):
print(f"{i} | {service}")
# print(f"{i:<3} | {service:<20}")
print("─" * 30 + "\n")
def add_entry(self, service, login, length=20):
"""
Добавить Сервис, Логин, Пароль в хранилище
"""
alphabet = string.ascii_letters + string.digits + "!@#$%^&*()_+-="
password = ''.join(secrets.choice(alphabet) for _ in range(length))
data = self._load_db()
data[service] = {"login": login, "password": password}
self._save_db(data)
print(f"[+] Успешно скрыто в {self.db_path}")
return password
def remove_entry(self, service: str) -> bool:
"""
Удаляет сервис из хранилища.
Возвращает True при успешном удалении, иначе False.
"""
data = self._load_db()
if not data:
print(f"[!] Ошибка доступа или база пуста при попытке удаления {service}")
return False
if service in data:
del data[service]
self._save_db(data)
print(f"[-] Сервис '{service}' успешно удален из {self.db_path}")
return True
else:
print(f"[?] Сервис '{service}' не найден в базе.")
return False
def get_entry(self, service):
data = self._load_db()
if not data: return [1, "Доступ запрещен или база пуста"]
if service not in data: return [0, "Не найдено"]
return [2, data[service]]
if __name__ == '__main__':
vault = Vault(vault_name="NeoVault", signature=b"NEO_SPACE_MARKER", master_password="Aleksey_Secure_2026", user='Alex', out_image="cat.png", mask_source="mask_cat.jpg")
# Показать список:
vault.list_entries()
services = vault.get_services()
if services:
for service in services:
# Прочитать:
res = vault.get_entry(service)
if res[0] == 2:
print(f"[S] Сервис: {service} | Логин: {res[1]['login']} | Пасс: {res[1]['password']}")
elif res[0] == 1:
print('[!] Неверный мастер-пароль')