pitt 0.4.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pitt-0.4.7/PKG-INFO ADDED
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.3
2
+ Name: pitt
3
+ Version: 0.4.7
4
+ Summary: an extremely simple terminal-based password manager
5
+ Author: quandelaa
6
+ Author-email: quandelaa <sergiotobamasilalahi@gmail.com>
7
+ Requires-Dist: argon2-cffi>=25.1.0
8
+ Requires-Dist: cryptography>=49.0.0
9
+ Requires-Dist: platformdirs>=4.10.1
10
+ Requires-Dist: pyperclip>=1.11.0
11
+ Requires-Dist: rich>=15.0.0
12
+ Requires-Python: >=3.12
13
+ Description-Content-Type: text/markdown
14
+
15
+ # pitt
16
+
17
+ an extremely simple terminal-based password manager.
18
+
19
+ ## installation
20
+
21
+ ```bash
22
+ pip install git+https://github.com/quandelaa/pitt.git
23
+ ```
24
+
25
+ ## usage
26
+
27
+ ### commands
28
+
29
+ 1. `pitt init`:
30
+ * asks for a master password
31
+ * when given, encrypts it using Argon2id and stores it in the database
32
+
33
+ example:
34
+ ```bash
35
+ pitt init
36
+ ```
37
+
38
+ 2. `pitt add`:
39
+ * create random password
40
+ * encrypts the random password using Fernet
41
+ * stores the encrypted password with a specified username or service or note in the database
42
+
43
+ example:
44
+ ```bash
45
+ pitt add --service github --username quandelaa --note "this is my song"
46
+ ```
47
+
48
+ 3. `pitt get`:
49
+ * gets the password that is saved with the given service or username
50
+ * decrypts the password
51
+ * copies it to user's clipboard
52
+
53
+ example:
54
+ ```bash
55
+ pitt get --service github --username quandelaa
56
+ ```
57
+
58
+ 4. `pitt list`:
59
+ * lists only the details (service, username and notes) of all the saved passwords
60
+
61
+ example:
62
+ ```bash
63
+ pitt list"
64
+ ```
65
+
66
+ 4. `pitt del`:
67
+ * deletes a password in the password vault that is saved with the given service or username
68
+
69
+ example:
70
+ ```bash
71
+ pitt del --service github --username quandelaa
72
+ ```
73
+
74
+ ## license
75
+
76
+ this project is licensed under the MIT License - see the LICENSE file for details
77
+
78
+ ## credits
79
+
80
+ inspired by: themohitnair's sfnx | (https://github.com/themohitnair/sfnx)
81
+
82
+ ---
83
+
84
+ curated by me with passion and a teensy bit of stress
pitt-0.4.7/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # pitt
2
+
3
+ an extremely simple terminal-based password manager.
4
+
5
+ ## installation
6
+
7
+ ```bash
8
+ pip install git+https://github.com/quandelaa/pitt.git
9
+ ```
10
+
11
+ ## usage
12
+
13
+ ### commands
14
+
15
+ 1. `pitt init`:
16
+ * asks for a master password
17
+ * when given, encrypts it using Argon2id and stores it in the database
18
+
19
+ example:
20
+ ```bash
21
+ pitt init
22
+ ```
23
+
24
+ 2. `pitt add`:
25
+ * create random password
26
+ * encrypts the random password using Fernet
27
+ * stores the encrypted password with a specified username or service or note in the database
28
+
29
+ example:
30
+ ```bash
31
+ pitt add --service github --username quandelaa --note "this is my song"
32
+ ```
33
+
34
+ 3. `pitt get`:
35
+ * gets the password that is saved with the given service or username
36
+ * decrypts the password
37
+ * copies it to user's clipboard
38
+
39
+ example:
40
+ ```bash
41
+ pitt get --service github --username quandelaa
42
+ ```
43
+
44
+ 4. `pitt list`:
45
+ * lists only the details (service, username and notes) of all the saved passwords
46
+
47
+ example:
48
+ ```bash
49
+ pitt list"
50
+ ```
51
+
52
+ 4. `pitt del`:
53
+ * deletes a password in the password vault that is saved with the given service or username
54
+
55
+ example:
56
+ ```bash
57
+ pitt del --service github --username quandelaa
58
+ ```
59
+
60
+ ## license
61
+
62
+ this project is licensed under the MIT License - see the LICENSE file for details
63
+
64
+ ## credits
65
+
66
+ inspired by: themohitnair's sfnx | (https://github.com/themohitnair/sfnx)
67
+
68
+ ---
69
+
70
+ curated by me with passion and a teensy bit of stress
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "pitt"
3
+ version = "0.4.7"
4
+ description = "an extremely simple terminal-based password manager"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "quandelaa", email = "sergiotobamasilalahi@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "argon2-cffi>=25.1.0",
12
+ "cryptography>=49.0.0",
13
+ "platformdirs>=4.10.1",
14
+ "pyperclip>=1.11.0",
15
+ "rich>=15.0.0",
16
+ ]
17
+
18
+ [project.scripts]
19
+ pitt = "pitt.main:main"
20
+
21
+ [build-system]
22
+ requires = ["uv_build>=0.11.26,<0.12.0"]
23
+ build-backend = "uv_build"
24
+
25
+ [dependency-groups]
26
+ dev = []
File without changes
@@ -0,0 +1,118 @@
1
+ import sqlite3 as sql
2
+ from .utils import get_db_path
3
+
4
+ def init_db() -> None:
5
+ """
6
+ Initialize the database that will store the encrypted passwords.
7
+ """
8
+
9
+ db_path = str(get_db_path())
10
+
11
+ conn = sql.connect(db_path)
12
+ cur = conn.cursor()
13
+
14
+ cur.executescript(
15
+ """CREATE TABLE IF NOT EXISTS passwords (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ service TEXT,
18
+ username TEXT,
19
+ note TEXT,
20
+ password BLOB NOT NULL);"""
21
+ )
22
+
23
+ cur.executescript(
24
+ """CREATE TABLE IF NOT EXISTS master (
25
+ master_hash TEXT NOT NULL,
26
+ salt BLOB NOT NULL);"""
27
+ )
28
+
29
+ conn.commit()
30
+ conn.close()
31
+
32
+ def configure_vault(salt: bytes, new_hash: str) -> None:
33
+ """
34
+ Configures the current master password hash stored in the passwords database
35
+ """
36
+
37
+ db_path = str(get_db_path())
38
+
39
+ conn = sql.connect(db_path)
40
+ cur = conn.cursor()
41
+
42
+ cur.execute("SELECT COUNT(*) FROM master")
43
+ row_count = cur.fetchone()[0]
44
+
45
+ if row_count == 0:
46
+ cur.execute("INSERT INTO master (salt, master_hash) VALUES ('', '')")
47
+
48
+ cur.execute("UPDATE master SET (master_hash, salt) = (?, ?)", (new_hash, salt))
49
+
50
+ conn.commit()
51
+ conn.close()
52
+
53
+ def store_password(service: str | None, username: str | None, note: str | None, password: bytes) -> None:
54
+ """
55
+ Stores the password inside of the created database
56
+ """
57
+
58
+ db_path = str(get_db_path())
59
+
60
+ conn = sql.connect(db_path)
61
+ cur = conn.cursor()
62
+
63
+ cur.execute("INSERT INTO passwords (service, username, note, password) VALUES (?, ?, ?, ?)", (service, username, note, password))
64
+
65
+ conn.commit()
66
+ conn.close()
67
+
68
+ def get_by_properties(service: str | None, username: str | None) -> list:
69
+ """
70
+ Gets the encrypted password in the passwords database based on given service and username
71
+ """
72
+
73
+ db_path = str(get_db_path())
74
+
75
+ conn = sql.connect(db_path)
76
+ cur = conn.cursor()
77
+
78
+ if username is None and service is not None:
79
+ cur.execute("SELECT * FROM passwords WHERE service = ?", (service,))
80
+ elif username is not None and service is None:
81
+ cur.execute("SELECT * FROM passwords WHERE username = ?", (username,))
82
+ elif username is not None and service is not None:
83
+ cur.execute("SELECT * FROM passwords WHERE (service, username) = (?, ?)", (service, username))
84
+
85
+ results = cur.fetchall()
86
+
87
+ return results
88
+
89
+ def get_all() -> list:
90
+ """
91
+ Gets the encrypted password in the passwords database based on given service and username
92
+ """
93
+
94
+ db_path = str(get_db_path())
95
+
96
+ conn = sql.connect(db_path)
97
+ cur = conn.cursor()
98
+
99
+ cur.execute("SELECT * FROM passwords")
100
+
101
+ results = cur.fetchall()
102
+
103
+ return results
104
+
105
+ def delete_by_password(encrypted: bytes) -> None:
106
+ """
107
+ Deletes a password entry
108
+ """
109
+
110
+ db_path = str(get_db_path())
111
+
112
+ conn = sql.connect(db_path)
113
+ cur = conn.cursor()
114
+
115
+ cur.execute("DELETE FROM passwords WHERE password = ?", (encrypted,))
116
+
117
+ conn.commit()
118
+ conn.close()
@@ -0,0 +1,275 @@
1
+ from rich.console import Console
2
+ from getpass import getpass
3
+ from pyperclip import copy
4
+ from os import urandom
5
+ from .db_handler import init_db, configure_vault, store_password, get_by_properties, get_all, delete_by_password
6
+ from .security import master_encrypt, verify_master_password, encrypt, decrypt, derive_key
7
+ from .utils import get_db_path, check_db_exists, get_vault_property, create_password
8
+
9
+ def init() -> None:
10
+ """
11
+ Initialize the password manager's master password and the database
12
+ """
13
+
14
+ try:
15
+ console = Console()
16
+
17
+ if check_db_exists() is True:
18
+ db_path = get_db_path()
19
+ console.print(f"[bold yellow]:| your database was already initialized at [italic]{db_path}")
20
+
21
+ return
22
+
23
+ console.print("[bold yellow]! initializing..\n")
24
+
25
+ master_password = getpass("> master password: ")
26
+ confirm = getpass("> enter the password again (confirm): ")
27
+
28
+ if master_password != confirm:
29
+ console.print("\n[bold red]:( passwords do not match, sorry -- aborting")
30
+ return
31
+
32
+ console.print("\n[bold green]:) matched!")
33
+
34
+ db_path = str(get_db_path())
35
+ init_db()
36
+
37
+ console.print(f"[bold green]:) database set up at [italic]{db_path}")
38
+
39
+ salt = urandom(16)
40
+
41
+ new_hash = master_encrypt(master_password)
42
+ configure_vault(salt, new_hash)
43
+
44
+ console.print("[bold green]:) master password successfully set up!\n")
45
+ console.print("[bold yellow]! do pitt -h for help")
46
+ except KeyboardInterrupt:
47
+ console.print("\n[bold red]bye!")
48
+ return
49
+ except Exception as e:
50
+ console.print(f"\n[bold red]:( error: {e}")
51
+ console.print("[bold yellow]! do pitt -h for help")
52
+
53
+ return
54
+
55
+ def add(service: str | None, username: str | None, note: str | None) -> None:
56
+ """
57
+ Adds a password to the password vault with the given service, username and a note
58
+ """
59
+
60
+ try:
61
+ console = Console()
62
+
63
+ if service is None and username is None and note is None:
64
+ console.print("[bold red]:( you have to provide atleast a note, a username or a service")
65
+ return
66
+
67
+ master_password = getpass("> master password: ")
68
+
69
+ m_hash, salt = get_vault_property()
70
+
71
+ verify = verify_master_password(master_password, m_hash)
72
+
73
+ if verify is False:
74
+ console.print("[bold red]:( wrong password!")
75
+ return
76
+
77
+ password = create_password()
78
+
79
+ console.print("\n[bold green]:) password created!")
80
+
81
+ key = derive_key(master_password, salt)
82
+ encrypted = encrypt(key, password)
83
+
84
+ console.print("[bold green]:) password encrypted successfully!")
85
+
86
+ store_password(service, username, note, encrypted)
87
+
88
+ console.print("[bold green]:) password stored in database successfully!")
89
+ console.print("\n[bold yellow]! do pitt -h for help")
90
+ except KeyboardInterrupt:
91
+ console.print("\n[bold red]bye!")
92
+ return
93
+ except Exception as e:
94
+ console.print(f"\n[bold red]:( error: {e}")
95
+ console.print("[bold yellow]! do pitt -h for help")
96
+
97
+ return
98
+
99
+ def get(service: str | None, username: str | None) -> None:
100
+ """
101
+ Copies a password to the clipboard
102
+ """
103
+
104
+ try:
105
+ console = Console()
106
+
107
+ if service is None and username is None:
108
+ console.print("[bold red]:( you have to provide atleast a username or service")
109
+ return
110
+
111
+ master_password = getpass("> master password: ")
112
+
113
+ m_hash, salt = get_vault_property()
114
+
115
+ verify = verify_master_password(master_password, m_hash)
116
+
117
+ if verify is False:
118
+ console.print("[bold red]:( wrong password!")
119
+ return
120
+
121
+ console.print(f"[bold green]:) verification successful!")
122
+
123
+ results = get_by_properties(service, username)
124
+
125
+ if len(results) > 1:
126
+ console.print(f"\n[bold yellow]found passwords:\n")
127
+
128
+ for i, row in enumerate(results):
129
+ console.print(f"{i+1}. [bold]service: [/][honeydew2]{row[1]}[/], [bold]username: [/][light_cyan1]{row[2]}[/], [bold]note: [/][cornsilk1]{row[3]}")
130
+
131
+ console.print(f"\n[bold yellow]you have registered {len(results)} password that is saved with the given service or username")
132
+
133
+ p_index = int(console.input(f"[bold yellow]which one is the password to copy (index): "))
134
+ encrypted_password = results[p_index-1][4]
135
+
136
+ key = derive_key(master_password, salt)
137
+ byte_password = decrypt(key, encrypted_password)
138
+
139
+ password = byte_password.decode('utf-8')
140
+
141
+ copy(password)
142
+
143
+ console.print(f"\n[bold green]:) copied to the clipboard successfully!")
144
+ elif len(results) == 1:
145
+ row = results[0]
146
+
147
+ encrypted_password = row[4]
148
+
149
+ key = derive_key(master_password, salt)
150
+ byte_password = decrypt(key, encrypted_password)
151
+
152
+ password = byte_password.decode('utf-8')
153
+
154
+ copy(password)
155
+
156
+ console.print(f"[bold green]:) copied to the clipboard successfully!")
157
+ elif len(results) == 0:
158
+ console.print(f"\n[bold yellow]you have no registered password that is saved with the given service or username")
159
+ except KeyboardInterrupt:
160
+ console.print("\n[bold red]bye!")
161
+ return
162
+ except Exception as e:
163
+ console.print(f"\n[bold red]:( error: {e}")
164
+ console.print("[bold yellow]! do pitt -h for help")
165
+
166
+ return
167
+
168
+ def list_cmd() -> None:
169
+ """
170
+ Lists all the saved passwords's details
171
+ """
172
+
173
+ try:
174
+ console = Console()
175
+
176
+ master_password = getpass("> master password: ")
177
+
178
+ m_hash, _ = get_vault_property()
179
+
180
+ verify = verify_master_password(master_password, m_hash)
181
+
182
+ if verify is False:
183
+ console.print("[bold red]:( wrong password!")
184
+ return
185
+
186
+ console.print(f"[bold green]:) verification successful!")
187
+
188
+ passwords_results = get_all()
189
+
190
+ console.print(f"\n[bold yellow]saved passwords:")
191
+
192
+ for password in passwords_results:
193
+ console.print(f"{password[0]}. [bold]service: [/][honeydew2]{password[1]}[/], [bold]username: [/][light_cyan1]{password[2]}[/], [bold]note: [/][cornsilk1]{password[3]}")
194
+ except KeyboardInterrupt:
195
+ console.print("\n[bold red]bye!")
196
+ return
197
+ except Exception as e:
198
+ console.print(f"\n[bold red]:( error: {e}")
199
+ console.print("[bold yellow]! do pitt -h for help")
200
+
201
+ return
202
+
203
+ def del_cmd(service: str | None, username: str | None) -> None:
204
+ """
205
+ Deletes a password from the password vault
206
+ """
207
+
208
+ try:
209
+ console = Console()
210
+
211
+ if service is None and username is None:
212
+ console.print("[bold red]:( you have to provide atleast a username or service")
213
+ return
214
+
215
+ master_password = getpass("> master password: ")
216
+
217
+ m_hash, _ = get_vault_property()
218
+
219
+ verify = verify_master_password(master_password, m_hash)
220
+
221
+ if verify is False:
222
+ console.print("[bold red]:( wrong password!")
223
+ return
224
+
225
+ console.print(f"[bold green]:) verification successful!")
226
+
227
+ results = get_by_properties(service, username)
228
+
229
+ if len(results) > 1:
230
+ console.print(f"\n[bold yellow]found passwords:\n")
231
+
232
+ for i, row in enumerate(results):
233
+ console.print(f"{i+1}. [bold]service: [/][honeydew2]{row[1]}[/], [bold]username: [/][light_cyan1]{row[2]}[/], [bold]note: [/][cornsilk1]{row[3]}")
234
+
235
+ console.print(f"\n[bold yellow]you have registered {len(results)} password that is saved with the given service or username")
236
+
237
+ p_index = int(console.input(f"[bold yellow]which one is the password to delete (index): "))
238
+ encrypted_password = results[p_index-1][4]
239
+
240
+ delete_by_password(encrypted_password)
241
+ elif len(results) == 1:
242
+ encrypted_password = results[0][4]
243
+
244
+ res = None
245
+ console.print()
246
+
247
+ while res not in ('y', 'n', ''):
248
+ res = console.input("[bold yellow]! are you sure about this (y/N): ").lower()
249
+
250
+ if res == 'y':
251
+ res2 = None
252
+ console.print()
253
+
254
+ while res2 not in ('y', 'n', ''):
255
+ res2 = console.input("[bold yellow]! are you really sure about this (y/N): ").lower()
256
+
257
+ if res2 == '' or res == 'n':
258
+ return
259
+ elif res == '' or res == 'n':
260
+ return
261
+
262
+ delete_by_password(encrypted_password)
263
+ elif len(results) == 0:
264
+ console.print(f"\n[bold yellow]you have no registered password that is saved with the given service or username")
265
+ return
266
+
267
+ console.print(f"\n[bold green]:) deletion successful!")
268
+ except KeyboardInterrupt:
269
+ console.print("\n[bold red]bye!")
270
+ return
271
+ except Exception as e:
272
+ console.print(f"\n[bold red]:( error: {e}")
273
+ console.print("[bold yellow]! do pitt -h for help")
274
+
275
+ return
@@ -0,0 +1,16 @@
1
+ from .functions import init, add, get, list_cmd, del_cmd
2
+ from .parse import parse
3
+
4
+ def main() -> None:
5
+ args = parse()
6
+
7
+ if args.command == "init":
8
+ init()
9
+ elif args.command == "add":
10
+ add(args.service, args.username, args.note)
11
+ elif args.command == "get":
12
+ get(args.service, args.username)
13
+ elif args.command == "list":
14
+ list_cmd()
15
+ elif args.command == "del":
16
+ del_cmd(args.service, args.username)
@@ -0,0 +1,30 @@
1
+ import argparse
2
+
3
+ def parse() -> argparse.Namespace:
4
+ """
5
+ Returns the parsed arguments
6
+ """
7
+
8
+ parser = argparse.ArgumentParser(allow_abbrev=False, prog="pitt")
9
+ subparser = parser.add_subparsers(dest="command", required=True, help="commands")
10
+
11
+ subparser.add_parser("init", help="set up a master password to initialize the password pit of doom and despair")
12
+ subparser.add_parser("list", help="lists all the saved password")
13
+
14
+ del_password_parser = subparser.add_parser("del", help="delete a saved password")
15
+ get_password_parser = subparser.add_parser("get", help="copy a stored password to the clipboard")
16
+ add_password_parser = subparser.add_parser("add", help="generate and store a new randomized password")
17
+
18
+ del_password_parser.add_argument("-s", "--service", help="specify the service assigned to the password that is to be deleted")
19
+ del_password_parser.add_argument("-u", "--username", help="specify the username assigned to the password that is to be copied to clipboard")
20
+
21
+ get_password_parser.add_argument("-s", "--service", help="specify the service assigned to the password that is to be copied to clipboard")
22
+ get_password_parser.add_argument("-u", "--username", help="specify the username assigned to the password that is to be copied to clipboard")
23
+
24
+ add_password_parser.add_argument("-s", "--service", help="specify the service that the new generated password belongs to")
25
+ add_password_parser.add_argument("-u", "--username", help="specify the username that the new generated password belongs to")
26
+ add_password_parser.add_argument("-n", "--note", help="specify notes about this particular password")
27
+
28
+ args = parser.parse_args()
29
+
30
+ return args
@@ -0,0 +1,59 @@
1
+ from argon2 import PasswordHasher, exceptions, low_level, Type
2
+ from cryptography.fernet import Fernet
3
+ import base64
4
+ import os
5
+
6
+ def master_encrypt(password: str) -> str:
7
+ """
8
+ Returns the encrypted hash of a given master password
9
+ """
10
+
11
+ kdf = PasswordHasher()
12
+ hash = kdf.hash(password)
13
+
14
+ return hash
15
+
16
+ def verify_master_password(password: str, hash: str) -> bool:
17
+ """
18
+ Verifies whether the inputted master password matches with the stored master password
19
+ """
20
+
21
+ kdf = PasswordHasher()
22
+
23
+ try:
24
+ kdf.verify(hash, password)
25
+ except exceptions.VerifyMismatchError:
26
+ return False
27
+
28
+ return True
29
+
30
+ def derive_key(password: str, salt: bytes) -> bytes:
31
+ """
32
+ Derives the 64-byte Argon2id key from the password
33
+ """
34
+
35
+ key = low_level.hash_secret_raw(password.encode("utf-8"), salt, 3, 65536, 1, 32, Type.ID)
36
+
37
+ b64_key = base64.urlsafe_b64encode(key)
38
+
39
+ return b64_key
40
+
41
+ def encrypt(key: bytes, password: str) -> bytes:
42
+ """
43
+ Encrypts the given password using Fernet
44
+ """
45
+
46
+ f = Fernet(key)
47
+ token = f.encrypt(password.encode("utf-8"))
48
+
49
+ return token
50
+
51
+ def decrypt(key: bytes, encrypted: bytes) -> bytes:
52
+ """
53
+ Decrypts the given encrypted password using Fernet
54
+ """
55
+
56
+ f = Fernet(key)
57
+ password = f.decrypt(encrypted)
58
+
59
+ return password
@@ -0,0 +1,79 @@
1
+ import sqlite3 as sql
2
+ from random import choices, randint
3
+ from platformdirs import PlatformDirs
4
+ from pathlib import Path
5
+
6
+ def get_db_path() -> Path:
7
+ """
8
+ Returns the default database path for all operating systems
9
+ """
10
+
11
+ dirs = PlatformDirs("pitt")
12
+ return dirs.user_data_path / "passwords.db"
13
+
14
+ def check_db_exists() -> bool:
15
+ """
16
+ Returns whether the password vault database exists
17
+ """
18
+
19
+ db_path = get_db_path()
20
+ return db_path.is_file()
21
+
22
+ def check_master_password_exists() -> bool:
23
+ """
24
+ Returns whether the master password has been set up in the password vault database
25
+ """
26
+
27
+ if check_db_exists() is False:
28
+ return False
29
+
30
+ db_path = get_db_path()
31
+
32
+ conn = sql.connect(db_path)
33
+ cur = conn.cursor()
34
+
35
+ cur.execute("SELECT master_hash FROM master")
36
+
37
+ vals = cur.fetchone()
38
+ conn.close()
39
+
40
+ if len(vals) == 0:
41
+ return False
42
+
43
+ return True
44
+
45
+ def get_vault_property() -> tuple:
46
+ """
47
+ Returns master vault's properties
48
+ """
49
+
50
+ if check_db_exists() is False:
51
+ raise RuntimeError("database doesn't exist.")
52
+ elif check_master_password_exists() is False:
53
+ raise RuntimeError("master password isn't set yet the database file is created.")
54
+
55
+ db_path = get_db_path()
56
+
57
+ conn = sql.connect(db_path)
58
+ cur = conn.cursor()
59
+
60
+ cur.execute("SELECT master_hash, salt FROM master")
61
+
62
+ rows = cur.fetchone()
63
+ conn.close()
64
+
65
+ return rows
66
+
67
+ def create_password() -> str:
68
+ """
69
+ Creates a random password
70
+ """
71
+
72
+ items = ['"', '`', '1', '2', '3','4','5','6','7','8','9','0','-','=','q','w','e','r','t','y','u','i','o','p','[',']','\\','a','s','d','f','g','h','j','k','l',';','z','x','c','v','b','n','m',',','.','/','~','!','@','#',"$","%","^","&","*",'(',')','_','+','Q','W','E','R','T','Y','U','I','O','P','{','}','|','A','S','D','F','G','H','J','K','L',':','Z','X','C','V','B','N','M','<','>','?']
73
+
74
+ p_len = randint(35,50)
75
+ list_char = choices(items, k=p_len)
76
+
77
+ password = ''.join(list_char)
78
+
79
+ return password