Video Demo: <URL HERE>
This project is a password manager built in Python that securely stores and encrypts login credentials and secure notes using SQLite and the cryptography library. The main reason for choosing a functional programming approach over object-oriented programming (OOP) is simplicity and modularity. Since each function is responsible for a specific task, such as encryption, database management, or user interaction, the code remains more readable and easier to debug. A class-based structure could have been used to encapsulate the database and encryption logic, but for a relatively small project like this, functional programming keeps things straightforward.
A key part of this project is encryption. To protect stored passwords and notes, I used Fernet encryption, which is part of the cryptography library. Fernet provides symmetric encryption using AES-128 in CBC mode with HMAC authentication, ensuring that data is both encrypted and tamper-proof. The reason I chose Fernet over other encryption methods is that it’s simple to implement and automatically manages encryption and decryption securely. Since the master password is used to generate an encryption key, only the user who knows the password can decrypt the data.
For additional security, the project makes use of password hashing with PBKDF2 (Password-Based Key Derivation Function 2). Instead of storing the user’s password directly, it is used to generate a key using PBKDF2 with the SHA3-256 hashing algorithm. SHA3-256 was chosen over SHA-256 because it's more resistant to certain cryptographic attacks. The derived key is then encoded using Base64, making it easier to store and use within the Fernet encryption system. To prevent attacks like rainbow table attacks, a salt is added before hashing the password. The salt is randomly generated using os.urandom(16), and it is stored in a file so that the same salt can be retrieved when needed.
One design decision was using different salts for encrypting the database versus encrypting individual credentials. The database encryption salt is stored in one file, while a separate salt is used for encrypting passwords and notes. This approach adds an extra layer of security because even if an attacker manages to get access to one salt, they still wouldn’t be able to decrypt everything. the salt file also have restricted access and are only accessable for the current user, which provides another security layer. - Although i could only try it out for the windows.
The program starts by asking for the database name and a master password. If no database name is provided, it defaults to "Database". This design choice was made to make the program more user-friendly, allowing users to quickly access or create a database without unnecessary complexity. The database connection function (database_connection) either decrypts an existing database or creates a new one if none exists. The schema for the database is stored in a separate SQL file, making it easy to modify or extend the structure in the future.
One feature I added was storing usernames separately from logins. Instead of saving everything in one table, the database is structured so that passwords, usernames, and emails are stored in separate tables and referenced via foreign keys. This allows the same username to be reused across multiple logins without redundancy, which can be useful. Additionally, since sensitive information is encrypted, even if someone gains access to the database file, they won’t be able to see the stored credentials without the master password.
When inserting new credentials or notes, the program encrypts the data before storing it. The encryption functions (encrypting_inputs and decrypting_inputs) use Fernet encryption with the user’s password-derived key. The reason for encrypting individual pieces of data rather than just the entire database is that it allows the program to retrieve specific entries without having to decrypt everything at once. This makes it more efficient while still keeping everything secure.
The retrieval functions (retrieve_login and retrieve_note) allow users to search for stored credentials or notes using a title or website. One interesting part of this implementation is that while the passwords and usernames are encrypted, the titles are stored in plaintext. The reason for this is usability, if the titles were encrypted, the program wouldn’t be able to list them in a meaningful way for users to search. However, everything else remains encrypted until it is needed.
To ensure database security, when the program exits, the database is encrypted again and the decrypted version is deleted. This prevents any plaintext database from being left behind. The function encrypt_Database reads the database file, encrypts its contents, saves the encrypted version, and then deletes the original. This guarantees that even if someone accesses the machine, they won’t find a readable database file.
A small but important detail is the use of sqlite3.Row in some database retrieval functions. By setting connection.row_factory = sqlite3.Row, query results can be accessed using column names instead of just indexes. This makes the code more readable and avoids potential errors if the column order changes in the future.
The choice of SQLite as the database was mainly due to its simplicity. SQLite doesn’t require a separate server, making it perfect for a local password manager. It’s lightweight, easy to integrate with Python, and supports all the features needed for this project, including foreign keys and transactions.
The user interface is kept simple with a text-based menu, allowing users to navigate through the options efficiently. While a GUI could improve usability, the command-line approach ensures that the program remains lightweight and doesn’t introduce unnecessary dependencies.
Overall, the main focus of this project was security, efficiency, and usability. The combination of PBKDF2 password hashing, Fernet encryption, and separate salts ensures a high level of security. The functional programming style keeps the code modular and easy to understand, while the database structure balances security with usability. If I were to extend this project, I might consider adding a GUI, implementing two-factor authentication, or integrating cloud storage options for encrypted backups. But for now, this serves as a solid, secure, and easy-to-use password manager.
As for the use of AI, i used LLMs for information gathering eg to choose the encryption Library and to check small things like 'What was the Keyword for this again'. I also used it to help me create a structure of how the Password manager could look at the very beginning. Furthermore i used it to show me examplecode for the encryption usage to use as a blueprint. Lastly it wrote the description above for me, with me just giving information about decisions i have made i thought to be important.