Compare commits
3 Commits
570f15001e
...
a1bed9014d
Author | SHA1 | Date | |
---|---|---|---|
a1bed9014d | |||
66ab13b45d | |||
da42d7ad1d |
@ -1,4 +1,3 @@
|
|||||||
bcrypt
|
|
||||||
cryptography
|
cryptography
|
||||||
pymysql
|
pymysql
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
@ -26,17 +26,19 @@ def get_accounts(
|
|||||||
) -> None:
|
) -> None:
|
||||||
base_handler(bot, mes)
|
base_handler(bot, mes)
|
||||||
accounts = database.get.get_accounts(engine, mes.from_user.id)
|
accounts = database.get.get_accounts(engine, mes.from_user.id)
|
||||||
if accounts:
|
if not accounts:
|
||||||
accounts = [f"`{account}`" for account in accounts]
|
return send_tmp_message(bot, mes.chat.id, "У вас нет аккаунтов")
|
||||||
return send_tmp_message(
|
|
||||||
bot,
|
# Make accounts copyable and escape special chars
|
||||||
mes.chat.id,
|
accounts = [f"`{account}`" for account in accounts]
|
||||||
"Ваши аккаунты:\n"
|
send_tmp_message(
|
||||||
+ "\n".join(accounts)
|
bot,
|
||||||
+ "\nНажмите на название, чтобы скопировать",
|
mes.chat.id,
|
||||||
30,
|
"Ваши аккаунты:\n"
|
||||||
)
|
+ "\n".join(accounts)
|
||||||
send_tmp_message(bot, mes.chat.id, "У вас нет аккаунтов")
|
+ "\nНажмите на название, чтобы скопировать",
|
||||||
|
30,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def delete_all(
|
def delete_all(
|
||||||
@ -217,7 +219,7 @@ def _add_account5(
|
|||||||
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
|
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
|
||||||
|
|
||||||
salt, hash_ = database.get.get_master_pass(engine, mes.from_user.id)
|
salt, hash_ = database.get.get_master_pass(engine, mes.from_user.id)
|
||||||
if cryptography.master_pass.encrypt_master_pass(text, salt) != hash_:
|
if not cryptography.master_pass.check_master_pass(text, hash_, salt):
|
||||||
return send_tmp_message(bot, mes.chat.id, "Не подходит главный пароль")
|
return send_tmp_message(bot, mes.chat.id, "Не подходит главный пароль")
|
||||||
|
|
||||||
name, login, passwd = data["name"], data["login"], data["passwd"]
|
name, login, passwd = data["name"], data["login"], data["passwd"]
|
||||||
@ -279,7 +281,7 @@ def _get_account3(
|
|||||||
|
|
||||||
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
|
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
|
||||||
|
|
||||||
if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass:
|
if not cryptography.master_pass.check_master_pass(text, hash_pass, master_salt):
|
||||||
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
|
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
|
||||||
|
|
||||||
salt, enc_login, enc_pass = database.get.get_account_info(
|
salt, enc_login, enc_pass = database.get.get_account_info(
|
||||||
@ -376,7 +378,7 @@ def _export2(
|
|||||||
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
|
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
|
||||||
|
|
||||||
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
|
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
|
||||||
if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass:
|
if not cryptography.master_pass.check_master_pass(text, hash_pass, master_salt):
|
||||||
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
|
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
|
||||||
|
|
||||||
accounts = get_all_accounts(engine, mes.from_user.id, text)
|
accounts = get_all_accounts(engine, mes.from_user.id, text)
|
||||||
@ -443,7 +445,7 @@ def _import3(
|
|||||||
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
|
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
|
||||||
|
|
||||||
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
|
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
|
||||||
if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass:
|
if not cryptography.master_pass.check_master_pass(text, hash_pass, master_salt):
|
||||||
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
|
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
|
||||||
|
|
||||||
# List of names of accounts, which failed to be added to the database or failed tests
|
# List of names of accounts, which failed to be added to the database or failed tests
|
||||||
|
@ -1,26 +1,35 @@
|
|||||||
from typing import overload
|
import os
|
||||||
|
|
||||||
import bcrypt
|
from cryptography.exceptions import InvalidKey
|
||||||
|
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
|
||||||
|
|
||||||
|
_memory_use = 2**14
|
||||||
|
|
||||||
|
|
||||||
@overload
|
def _get_kdf(salt: bytes) -> Scrypt:
|
||||||
def encrypt_master_pass(passwd: str, salt: bytes) -> bytes:
|
kdf = Scrypt(
|
||||||
...
|
salt=salt,
|
||||||
|
length=128,
|
||||||
|
n=_memory_use,
|
||||||
|
r=8,
|
||||||
|
p=1,
|
||||||
|
)
|
||||||
|
return kdf
|
||||||
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def encrypt_master_pass(passwd: str) -> tuple[bytes, bytes]:
|
def encrypt_master_pass(passwd: str) -> tuple[bytes, bytes]:
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def encrypt_master_pass(
|
|
||||||
passwd: str, salt: bytes | None = None
|
|
||||||
) -> tuple[bytes, bytes] | bytes:
|
|
||||||
"""Hashes master password and return tuple of hashed password and salt"""
|
"""Hashes master password and return tuple of hashed password and salt"""
|
||||||
if salt is None:
|
salt = os.urandom(64)
|
||||||
salt = bcrypt.gensalt()
|
kdf = _get_kdf(salt)
|
||||||
gened_salt = True
|
return kdf.derive(passwd.encode("utf-8")), salt
|
||||||
|
|
||||||
|
|
||||||
|
def check_master_pass(passwd: str, enc_pass: bytes, salt: bytes) -> bool:
|
||||||
|
"""Checks if the master password is correct"""
|
||||||
|
kdf = _get_kdf(salt)
|
||||||
|
try:
|
||||||
|
kdf.verify(passwd.encode("utf-8"), enc_pass)
|
||||||
|
except InvalidKey:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
gened_salt = False
|
return True
|
||||||
hashed = bcrypt.hashpw(passwd.encode("utf-8"), salt)
|
|
||||||
return (hashed, salt) if gened_salt else hashed
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import base64
|
import base64
|
||||||
|
import os
|
||||||
import bcrypt
|
|
||||||
|
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
@ -23,9 +22,9 @@ def _generate_key(salt: bytes, master_pass: bytes) -> bytes:
|
|||||||
def encrypt_account_info(
|
def encrypt_account_info(
|
||||||
login: str, passwd: str, master_pass: bytes
|
login: str, passwd: str, master_pass: bytes
|
||||||
) -> tuple[bytes, bytes, bytes]:
|
) -> tuple[bytes, bytes, bytes]:
|
||||||
"""Encrypts login and password of a user using hash of their master password as a key.
|
"""Encrypts login and password of a user using their master password as a key.
|
||||||
Returns a tuple of encrypted login password and salt"""
|
Returns a tuple of encrypted login, password and salt"""
|
||||||
salt = bcrypt.gensalt()
|
salt = os.urandom(64)
|
||||||
key = _generate_key(salt, master_pass)
|
key = _generate_key(salt, master_pass)
|
||||||
f = Fernet(key)
|
f = Fernet(key)
|
||||||
enc_login = f.encrypt(login.encode("utf-8"))
|
enc_login = f.encrypt(login.encode("utf-8"))
|
||||||
@ -36,6 +35,8 @@ def encrypt_account_info(
|
|||||||
def decrypt_account_info(
|
def decrypt_account_info(
|
||||||
enc_login: bytes, enc_pass: bytes, master_pass: bytes, salt: bytes
|
enc_login: bytes, enc_pass: bytes, master_pass: bytes, salt: bytes
|
||||||
) -> tuple[str, str]:
|
) -> tuple[str, str]:
|
||||||
|
"""Decrypts login and password using their master password as a key.
|
||||||
|
Returns a tuple of decrypted login and password"""
|
||||||
key = _generate_key(salt, master_pass)
|
key = _generate_key(salt, master_pass)
|
||||||
f = Fernet(key)
|
f = Fernet(key)
|
||||||
login_bytes = f.decrypt(enc_login)
|
login_bytes = f.decrypt(enc_login)
|
||||||
|
@ -8,10 +8,10 @@ class MasterPass(sqlmodel.SQLModel, table=True):
|
|||||||
id: Optional[int] = sqlmodel.Field(primary_key=True)
|
id: Optional[int] = sqlmodel.Field(primary_key=True)
|
||||||
user_id: int = sqlmodel.Field(nullable=False, index=True, unique=True)
|
user_id: int = sqlmodel.Field(nullable=False, index=True, unique=True)
|
||||||
salt: bytes = sqlmodel.Field(
|
salt: bytes = sqlmodel.Field(
|
||||||
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
|
sa_column=sqlmodel.Column(sqlmodel.BINARY(64), nullable=False)
|
||||||
)
|
)
|
||||||
passwd: bytes = sqlmodel.Field(
|
passwd: bytes = sqlmodel.Field(
|
||||||
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
|
sa_column=sqlmodel.Column(sqlmodel.BINARY(128), nullable=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -22,11 +22,11 @@ class Account(sqlmodel.SQLModel, table=True):
|
|||||||
user_id: int = sqlmodel.Field(nullable=False, index=True)
|
user_id: int = sqlmodel.Field(nullable=False, index=True)
|
||||||
name: str = sqlmodel.Field(nullable=False, index=True, max_length=255)
|
name: str = sqlmodel.Field(nullable=False, index=True, max_length=255)
|
||||||
salt: bytes = sqlmodel.Field(
|
salt: bytes = sqlmodel.Field(
|
||||||
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
|
sa_column=sqlmodel.Column(sqlmodel.BINARY(64), nullable=False)
|
||||||
)
|
)
|
||||||
enc_login: bytes = sqlmodel.Field(
|
enc_login: bytes = sqlmodel.Field(
|
||||||
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
|
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(500), nullable=False)
|
||||||
)
|
)
|
||||||
enc_pass: bytes = sqlmodel.Field(
|
enc_pass: bytes = sqlmodel.Field(
|
||||||
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
|
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(500), nullable=False)
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user