Compare commits

..

No commits in common. "a1bed9014d6fc680b1604fdf9804c748b01c8774" and "570f15001e874a4d3454279531843d5a6384130d" have entirely different histories.

5 changed files with 44 additions and 55 deletions

View File

@ -1,3 +1,4 @@
bcrypt
cryptography
pymysql
python-dotenv

View File

@ -26,12 +26,9 @@ def get_accounts(
) -> None:
base_handler(bot, mes)
accounts = database.get.get_accounts(engine, mes.from_user.id)
if not accounts:
return send_tmp_message(bot, mes.chat.id, "У вас нет аккаунтов")
# Make accounts copyable and escape special chars
if accounts:
accounts = [f"`{account}`" for account in accounts]
send_tmp_message(
return send_tmp_message(
bot,
mes.chat.id,
"Ваши аккаунты:\n"
@ -39,6 +36,7 @@ def get_accounts(
+ "\nНажмите на название, чтобы скопировать",
30,
)
send_tmp_message(bot, mes.chat.id, "У вас нет аккаунтов")
def delete_all(
@ -219,7 +217,7 @@ def _add_account5(
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
salt, hash_ = database.get.get_master_pass(engine, mes.from_user.id)
if not cryptography.master_pass.check_master_pass(text, hash_, salt):
if cryptography.master_pass.encrypt_master_pass(text, salt) != hash_:
return send_tmp_message(bot, mes.chat.id, "Не подходит главный пароль")
name, login, passwd = data["name"], data["login"], data["passwd"]
@ -281,7 +279,7 @@ def _get_account3(
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
if not cryptography.master_pass.check_master_pass(text, hash_pass, master_salt):
if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass:
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
salt, enc_login, enc_pass = database.get.get_account_info(
@ -378,7 +376,7 @@ def _export2(
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
if not cryptography.master_pass.check_master_pass(text, hash_pass, master_salt):
if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass:
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
accounts = get_all_accounts(engine, mes.from_user.id, text)
@ -445,7 +443,7 @@ def _import3(
return send_tmp_message(bot, mes.chat.id, "Успешная отмена")
master_salt, hash_pass = database.get.get_master_pass(engine, mes.from_user.id)
if not cryptography.master_pass.check_master_pass(text, hash_pass, master_salt):
if cryptography.master_pass.encrypt_master_pass(text, master_salt) != hash_pass:
return send_tmp_message(bot, mes.chat.id, "Не подходит мастер пароль")
# List of names of accounts, which failed to be added to the database or failed tests

View File

@ -1,35 +1,26 @@
import os
from typing import overload
from cryptography.exceptions import InvalidKey
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
_memory_use = 2**14
import bcrypt
def _get_kdf(salt: bytes) -> Scrypt:
kdf = Scrypt(
salt=salt,
length=128,
n=_memory_use,
r=8,
p=1,
)
return kdf
@overload
def encrypt_master_pass(passwd: str, salt: bytes) -> bytes:
...
@overload
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"""
salt = os.urandom(64)
kdf = _get_kdf(salt)
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
if salt is None:
salt = bcrypt.gensalt()
gened_salt = True
else:
return True
gened_salt = False
hashed = bcrypt.hashpw(passwd.encode("utf-8"), salt)
return (hashed, salt) if gened_salt else hashed

View File

@ -1,5 +1,6 @@
import base64
import os
import bcrypt
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
@ -22,9 +23,9 @@ def _generate_key(salt: bytes, master_pass: bytes) -> bytes:
def encrypt_account_info(
login: str, passwd: str, master_pass: bytes
) -> tuple[bytes, bytes, bytes]:
"""Encrypts login and password of a user using their master password as a key.
Returns a tuple of encrypted login, password and salt"""
salt = os.urandom(64)
"""Encrypts login and password of a user using hash of their master password as a key.
Returns a tuple of encrypted login password and salt"""
salt = bcrypt.gensalt()
key = _generate_key(salt, master_pass)
f = Fernet(key)
enc_login = f.encrypt(login.encode("utf-8"))
@ -35,8 +36,6 @@ def encrypt_account_info(
def decrypt_account_info(
enc_login: bytes, enc_pass: bytes, master_pass: bytes, salt: bytes
) -> 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)
f = Fernet(key)
login_bytes = f.decrypt(enc_login)

View File

@ -8,10 +8,10 @@ class MasterPass(sqlmodel.SQLModel, table=True):
id: Optional[int] = sqlmodel.Field(primary_key=True)
user_id: int = sqlmodel.Field(nullable=False, index=True, unique=True)
salt: bytes = sqlmodel.Field(
sa_column=sqlmodel.Column(sqlmodel.BINARY(64), nullable=False)
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
)
passwd: bytes = sqlmodel.Field(
sa_column=sqlmodel.Column(sqlmodel.BINARY(128), nullable=False)
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
)
@ -22,11 +22,11 @@ class Account(sqlmodel.SQLModel, table=True):
user_id: int = sqlmodel.Field(nullable=False, index=True)
name: str = sqlmodel.Field(nullable=False, index=True, max_length=255)
salt: bytes = sqlmodel.Field(
sa_column=sqlmodel.Column(sqlmodel.BINARY(64), nullable=False)
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
)
enc_login: bytes = sqlmodel.Field(
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(500), nullable=False)
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
)
enc_pass: bytes = sqlmodel.Field(
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(500), nullable=False)
sa_column=sqlmodel.Column(sqlmodel.VARBINARY(255), nullable=False)
)