Changed the way the master password hashing works
Switched from Bcrypt to Scrypt for master password hashing Changed models to use new sizes for hashes and salts, doubled the size of enc_login and enc_passwd for accounts Created new function to check master password validity Increased salt sizes for accounts and master passwords Removed bcrypt from requirements
This commit is contained in:
parent
66ab13b45d
commit
a1bed9014d
@ -1,4 +1,3 @@
|
|||||||
bcrypt
|
|
||||||
cryptography
|
cryptography
|
||||||
pymysql
|
pymysql
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
@ -219,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"]
|
||||||
@ -281,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(
|
||||||
@ -378,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)
|
||||||
@ -445,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
|
||||||
@ -25,7 +24,7 @@ def encrypt_account_info(
|
|||||||
) -> tuple[bytes, bytes, bytes]:
|
) -> tuple[bytes, bytes, bytes]:
|
||||||
"""Encrypts login and password of a user using 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"))
|
||||||
|
@ -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