@ -0,0 +1,484 @@
#!/usr/bin/env python3
# Key Master v1.00
# 27/08/2021
# https://t.me/ssleg © 2021
import sqlite3
from hashlib import md5 , sha256
from os import path
from random import randint
import PySimpleGUI as Sg
import cryptocode
class LoginCard :
""" класс хранения карточки логина/пароля """
__slots__ = [ ' __row_id ' , ' __encrypt_key ' , ' __hash ' , ' __name ' , ' __login ' , ' __password ' ]
def __init__ ( self , encrypt_key , row_id = None , name = None , login = None , password = None ) :
self . __row_id = row_id
self . __encrypt_key = encrypt_key
if row_id is None :
self . __name = name
self . __login = login
self . __password = password
control_string = self . __name + self . __login + self . __password
self . __hash = md5 ( control_string . encode ( ) ) . hexdigest ( )
self . __write_card_to_db ( mode = 0 )
else :
cursor . execute ( ' select * from logins where row_id=? ' , ( row_id , ) )
row = cursor . fetchone ( )
db_name = row [ 1 ]
db_login = row [ 2 ]
db_password = row [ 3 ]
self . __hash = row [ 4 ]
self . __name = cryptocode . decrypt ( db_name , encrypt_key )
self . __login = cryptocode . decrypt ( db_login , encrypt_key )
self . __password = cryptocode . decrypt ( db_password , encrypt_key )
def is_valid ( self ) :
control_string = self . __name + self . __login + self . __password
if type ( control_string ) == str :
control_hash = md5 ( control_string . encode ( ) ) . hexdigest ( )
if control_hash == self . __hash :
return True
return False
def __write_card_to_db ( self , mode ) :
db_name = cryptocode . encrypt ( self . __name , self . __encrypt_key )
db_login = cryptocode . encrypt ( self . __login , self . __encrypt_key )
db_password = cryptocode . encrypt ( self . __password , self . __encrypt_key )
if mode == 0 :
entry = ( db_name , db_login , db_password , self . __hash )
cursor . execute ( ' insert into logins (row_name, login, password, hash) values (?,?,?,?) ' , entry )
con . commit ( )
cursor . execute ( ' select row_id from logins where row_name=? ' , ( db_name , ) )
row = cursor . fetchone ( )
self . __row_id = row [ 0 ]
else :
entry = ( db_name , db_login , db_password , self . __hash , self . __row_id )
cursor . execute ( ' update logins set row_name=?, login=?, password=?, hash=? where row_id=? ' , entry )
con . commit ( )
def get_name ( self ) :
return self . __name
def get_login ( self ) :
return self . __login
def get_password ( self ) :
return self . __password
def get_id ( self ) :
return self . __row_id
def get_full_text ( self ) :
full_text = ' Логин: \n ' + self . __login + ' \n \n Пароль: \n ' + self . __password
return full_text
def update_card ( self , name , login , password ) :
self . __name = name
self . __login = login
self . __password = password
control_string = self . __name + self . __login + self . __password
self . __hash = md5 ( control_string . encode ( ) ) . hexdigest ( )
self . __write_card_to_db ( mode = 1 )
def delete_card ( self ) :
cursor . execute ( ' delete from logins where row_id=? ' , ( self . __row_id , ) )
con . commit ( )
class AllCards :
""" класс хранения всех карточек """
__slots__ = [ ' __encrypt_key ' , ' __cards ' ]
def __init__ ( self , encrypt_key ) :
self . __encrypt_key = encrypt_key
row_ids = [ ]
cursor . execute ( ' select row_id from logins where row_id>1 ' )
for row in cursor . fetchall ( ) :
row_ids . append ( row [ 0 ] )
self . __cards = [ ]
for row in row_ids :
self . __cards . append ( LoginCard ( encrypt_key , row_id = row ) )
self . sort_cards ( )
def validate ( self ) :
error_numbers = [ ]
for card in self . __cards :
valid = card . is_valid ( )
if not valid :
error_numbers . append ( card . get_id ( ) )
if len ( error_numbers ) > 0 :
return False , error_numbers
else :
return True , error_numbers
def get_names_list ( self ) :
names_list = [ ]
for card in self . __cards :
names_list . append ( card . get_name ( ) )
return names_list
def add_card ( self , name , login , password ) :
self . __cards . append ( LoginCard ( self . __encrypt_key , name = name , login = login , password = password ) )
def get_card_by_name ( self , name ) :
for card in self . __cards :
if card . get_name ( ) == name :
return card . get_full_text ( )
def get_card_login ( self , name ) :
for card in self . __cards :
if card . get_name ( ) == name :
return card . get_login ( )
def get_card_password ( self , name ) :
for card in self . __cards :
if card . get_name ( ) == name :
return card . get_password ( )
def get_card_id ( self , name ) :
for card in self . __cards :
if card . get_name ( ) == name :
return card . get_id ( )
@staticmethod
def __sort_key ( element : LoginCard ) :
return element . get_name ( )
def sort_cards ( self ) :
self . __cards . sort ( key = self . __sort_key )
def get_popular_logins ( self ) :
logins = { }
logins_count = [ ]
for card in self . __cards :
login = card . get_login ( )
index = logins . get ( login )
if index is None :
logins [ login ] = len ( logins_count )
logins_count . append ( [ 1 , login ] )
else :
logins_count [ index ] [ 0 ] + = 1
popular = [ ]
for item in logins_count :
if item [ 0 ] > 1 :
popular . append ( item [ 1 ] )
return popular
def update_card ( self , row_id , name , login , password ) :
for card in self . __cards :
if card . get_id ( ) == row_id :
card . update_card ( name , login , password )
break
def delete_card ( self , name ) :
index = 0
while index < len ( self . __cards ) :
card_name = self . __cards [ index ] . get_name ( )
if card_name == name :
break
index + = 1
self . __cards [ index ] . delete_card ( )
self . __cards . pop ( index )
# окно первого входа и задания пароля
def system_init ( ) :
status = False
password = ' '
layout = [ [ Sg . Text (
' Задайте мастер-пароль шифрования. \n Внимание, пароль нигде не сохраняется и не может быть восстановлен! ' ) ] ,
[ Sg . Text ( ' Пароль: ' ) ] ,
[ Sg . InputText ( size = ( 20 , 1 ) , border_width = 3 ) ] ,
[ Sg . Text ( ' Повторите пароль: ' ) ] ,
[ Sg . InputText ( size = ( 20 , 1 ) , border_width = 3 ) ] ,
[ Sg . Button ( ' Ok ' ) , Sg . Text ( ' ' , key = ' Err_text ' , text_color = ' firebrick1 ' , font = ' style:bold ' ) ] ]
window = Sg . Window ( " KeyMaster - приветствие " , layout )
while True :
event , values = window . read ( )
if event == Sg . WIN_CLOSED :
break
if event == ' Ok ' :
input_word = values [ 0 ]
input_word2 = values [ 1 ]
if input_word == input_word2 :
if len ( input_word ) > = 4 :
status = True
password = sha256 ( input_word . encode ( ) ) . hexdigest ( )
break
else :
err = ' Пароль слишком короткий, минимум 4 символа! '
window . Element ( ' Err_text ' ) . Update ( err )
else :
err = ' Пароли не совпадают! '
window . Element ( ' Err_text ' ) . Update ( err )
window . close ( )
return status , password
# окно ввода и проверки пароля
def system_login ( ) :
attempts = 3
status = False
password = ' '
layout = [ [ Sg . Text ( ' Пароль: ' ) , Sg . InputText ( size = ( 20 , 1 ) , border_width = 3 , password_char = ' * ' ) ] ,
[ Sg . Button ( ' Ok ' ) , Sg . Text ( ' ' , key = ' Err_text ' , text_color = ' firebrick1 ' , font = ' style:bold ' ) ] ]
window = Sg . Window ( " KeyMaster - вход " , layout )
while True :
event , values = window . read ( )
if event == Sg . WIN_CLOSED :
break
if event == ' Ok ' :
input_word = values [ 0 ]
if len ( input_word ) > = 4 :
password = sha256 ( input_word . encode ( ) ) . hexdigest ( )
tmp = LoginCard ( encrypt_key = password , row_id = 1 )
if tmp . is_valid ( ) :
status = True
break
else :
attempts - = 1
if attempts > 0 :
err = ' Неверный пароль, \n осталось попыток: ' + str ( attempts )
window . Element ( ' Err_text ' ) . Update ( err )
else :
break
else :
attempts - = 1
if attempts > 0 :
err = ' Пароль минимум 4 символа. \n осталось попыток: ' + str ( attempts )
window . Element ( ' Err_text ' ) . Update ( err )
else :
break
window . close ( )
return status , password
# окно сообщения о б ошибках
def err_msg ( message ) :
# noinspection SpellCheckingInspection
layout = [ [ Sg . Text ( ' Oшиб ка : ' ) ] ,
[ Sg . Text ( message ) ] ,
[ Sg . Button ( ' Ok ' ) ] ]
window = Sg . Window ( " KeyMaster - ошибка " , layout , modal = True )
while True :
event , values = window . read ( )
if event == ' Ok ' or event == Sg . WIN_CLOSED :
break
window . close ( )
# окно подтверждения удаления
def confirm_delete ( card_name ) :
confirm_flag = False
layout = [ [ Sg . Text ( ' Вы уверены, что хотите удалить карточку: ' ) ] ,
[ Sg . Text ( card_name + ' ? ' ) ] ,
[ Sg . Text ( ' Это необратимое действие!!! ' , text_color = ' firebrick1 ' , font = ' style:bold ' ) ] ,
[ Sg . Button ( ' Ok ' ) , Sg . Button ( ' Отменить ' , focus = True ) ] ]
window = Sg . Window ( " KeyMaster - подтверждение " , layout , modal = True )
while True :
event , values = window . read ( )
if event == ' Отменить ' or event == Sg . WIN_CLOSED :
break
if event == ' Ok ' :
confirm_flag = True
break
window . close ( )
return confirm_flag
# окно создания или редактирования карточки логина
def add_or_edit_card ( used_list , popular_list , mode , name = ' ' , login = ' ' , password = ' ' ) :
card_added = False
card_name = name
card_login = login
card_password = password
layout = [ [ Sg . Text ( ' Имя: ' ) ] ,
[ Sg . InputText ( card_name , size = ( 42 , 1 ) , border_width = 3 ) ] ,
[ Sg . Text ( ' Логин: ' ) ] ,
[ Sg . InputCombo ( popular_list , default_value = card_login , size = ( 41 , 1 ) ) ] ,
[ Sg . Text ( ' Пароль: ' ) ] ,
[ Sg . InputText ( card_password , size = ( 42 , 1 ) , border_width = 3 , key = ' pass ' ) ] ,
[ Sg . Button ( ' Ok ' ) , Sg . Button ( ' Отменить ' ) , Sg . Button ( ' Сгенерировать пароль ' ) ] ]
if mode == 0 :
window = Sg . Window ( " KeyMaster - новая карточка " , layout , modal = True )
else :
window = Sg . Window ( " KeyMaster - редактор карточки " , layout , modal = True )
while True :
event , values = window . read ( )
if event == ' Отменить ' or event == Sg . WIN_CLOSED :
break
if event == ' Ok ' :
error_flag = False
card_name = values [ 0 ]
card_login = values [ 1 ]
card_password = values [ ' pass ' ]
if card_name in used_list and mode == 0 :
err_msg ( ' такое имя уже используется. ' )
error_flag = True
else :
if card_name == ' ' :
err_msg ( ' введите имя. ' )
error_flag = True
if card_login == ' ' and not error_flag :
err_msg ( ' введите логин. ' )
error_flag = True
if card_password == ' ' and not error_flag :
err_msg ( ' введите пароль. ' )
error_flag = True
if not error_flag :
card_added = True
break
if event == ' Сгенерировать пароль ' :
new_pass = ' '
for i in range ( 0 , 16 ) :
m = randint ( 0 , 35 )
if m > 9 :
m + = 55
m2 = randint ( 0 , 1 )
if m2 == 0 :
new_pass + = chr ( m )
else :
new_pass + = chr ( m + 32 )
else :
new_pass + = str ( m )
window . Element ( ' pass ' ) . Update ( new_pass )
window . close ( )
return card_added , card_name , card_login , card_password
# главное окно
def main ( ) :
keys_disabled = True
my_cards = AllCards ( master_key )
valid , error_numbers = my_cards . validate ( )
if not valid :
message = ' Внимание, карточки ## '
for number in error_numbers :
message + = str ( number ) + ' , '
message = message [ 0 : len ( message ) - 2 ]
message + = ' повреждены! Используйте бэкап. '
err_msg ( message )
return
layout = [ [ Sg . Text ( ' Главное окно ' ) ] ,
[ Sg . Listbox ( my_cards . get_names_list ( ) , select_mode = ' LISTBOX_SELECT_MODE_SINGLE ' , enable_events = True ,
size = ( 20 , 10 ) , key = ' list ' ) , Sg . Text ( key = ' card ' ) ] ,
[ Sg . Button ( ' Добавить ' ) , Sg . Button ( ' Редактировать ' , disabled = True , key = ' edit ' ) ,
Sg . Button ( ' Скопировать пароль ' , disabled = True , key = ' copy ' ) ] ,
[ Sg . Button ( ' Удалить ' , disabled = True , key = ' delete ' ) ] ]
window = Sg . Window ( " KeyMaster - главная " , layout )
while True :
event , values = window . read ( )
if event == Sg . WIN_CLOSED :
break
name = window . Element ( ' list ' ) . get ( )
if len ( name ) > 0 :
if not keys_disabled :
window . Element ( ' edit ' ) . update ( disabled = True )
window . Element ( ' copy ' ) . update ( disabled = True )
window . Element ( ' delete ' ) . update ( disabled = True )
keys_disabled = True
if event == ' Добавить ' :
added , name , login , password = add_or_edit_card ( my_cards . get_names_list ( ) , my_cards . get_popular_logins ( ) ,
mode = 0 )
if added :
my_cards . add_card ( name = name , login = login , password = password )
my_cards . sort_cards ( )
window . Element ( ' list ' ) . Update ( my_cards . get_names_list ( ) )
if event == ' list ' :
name = window . Element ( ' list ' ) . get ( )
if len ( name ) > 0 :
if keys_disabled :
window . Element ( ' edit ' ) . update ( disabled = False )
window . Element ( ' copy ' ) . update ( disabled = False )
window . Element ( ' delete ' ) . update ( disabled = False )
keys_disabled = False
card = my_cards . get_card_by_name ( name [ 0 ] )
window . Element ( ' card ' ) . Update ( card )
if event == ' copy ' :
name = window . Element ( ' list ' ) . get ( )
if len ( name ) > 0 :
password = my_cards . get_card_password ( name [ 0 ] )
Sg . clipboard_set ( password )
if event == ' edit ' :
name = window . Element ( ' list ' ) . get ( )
if len ( name ) > 0 :
card_name = name [ 0 ]
card_id = my_cards . get_card_id ( card_name )
login = my_cards . get_card_login ( card_name )
password = my_cards . get_card_password ( card_name )
added , name_new , login_new , password_new = add_or_edit_card ( my_cards . get_names_list ( ) ,
my_cards . get_popular_logins ( ) , mode = 1 ,
name = card_name , login = login ,
password = password )
if added :
my_cards . update_card ( row_id = card_id , name = name_new , login = login_new , password = password_new )
my_cards . sort_cards ( )
window . Element ( ' list ' ) . Update ( my_cards . get_names_list ( ) )
if event == ' delete ' :
name = window . Element ( ' list ' ) . get ( )
if len ( name ) > 0 :
card_name = name [ 0 ]
confirmed = confirm_delete ( card_name )
if confirmed :
my_cards . delete_card ( card_name )
my_cards . sort_cards ( )
window . Element ( ' list ' ) . Update ( my_cards . get_names_list ( ) )
window . close ( )
# начало исполнения кода.
if not path . exists ( ' base.sqlite ' ) :
login_status , master_key = system_init ( )
if login_status :
con = sqlite3 . connect ( ' base.sqlite ' )
cursor = con . cursor ( )
cursor . executescript ( '''
create table logins
(
row_id integer
constraint logins_pk
primary key autoincrement ,
row_name text not null ,
login text not null ,
password text not null ,
hash text not null
) ;
''' )
con . commit ( )
pass_save = LoginCard ( master_key , name = ' master_login ' , login = ' 0 ' , password = ' 0 ' )
else :
con = sqlite3 . connect ( ' base.sqlite ' )
cursor = con . cursor ( )
login_status , master_key = system_login ( )
if login_status :
main ( )