vaultp 0.1.0
A secure CLI password manager.
Requires Python: >=3.10

# vaultp: password manager
vaultp is command-line interface(CLI) password manager with encrypted storage and secure hashing, built to practice secure encryption, as well as for personal use.

**Table of Contents**:

- [Terminology](#terminology)
- [Architecture/Security](#architecture/security)
- [How to install](#how-to-install)
- [How to Use](#how-to-use)
- [Disclaimer](#disclaimer)
- [Creds](#credits)

## Terminology:
To prevent confusion, here are some terms used in the README that could use some definition:
- *vault/vaultp*  -  refers to the project as a whole, the official name
- *password manager*  -  refers to the project in terms of function, what it does
- *admin/admin credentials*  -  these all refer to the "admin account", the credentials from which all encryption and access to stored passwords are derived.
- *admin JSON*  -  different from above, this is the JSON file that actually stores the admin credentials from above
- *database(or DB)*  -  refers to the encrypted JSON serialized dictionary in which all of the passwords live
- *database(DB) JSON/vault JSON*  -  different from above, this is the JSON file that stores information about the database, like the ID, kdf info, and of course, the Fernet cipher token

## Architecture/Security:
**Architecture:**

When it comes to project structure, main source files are organized by function, with foundation-function files on the bottom, and more UI-fuction files on top. That is:
- Low-level files:
    - ```crypto_utils.py```
    - ```storage.py```
- Middle-level "manager" files:
    - ```auth_manager.py```
    - ```input_manager.py```
- Top-level file:
    - ```main.py```

This is more or less the order in which the files build upon themselves. In terms of function:

```crypto_utils.py```  handles all of the cryptography functions. It includes functions for hashing and verifying the admin password, deriving a Fernet key using argon2, generating salts, and encrypting/decrypting plaintext passwords and the database itself.

```storage.py```handles all interactions with the file structures. It handles the initialization of the admin and database JSON files, and all of the logic of user commands, such as add, update, delete, view, list, and more. 

```auth_manager.py``` handles verification of admin credentials, and returns a boolean value. 

```input_manager.py``` handles user input, and is used to both organize and verify user input before they are passed to storage functions. 

```main.py``` handles both argument parsing for the CLI, and collecting + organizing inputs. Custom commands are created and parsed, and user inputs are sometimes confirmed for destructive commands like del or deladmin, or checked against a whitelist for commands like update.

When it comes to the actual file structure of the password manager, there are two main files that work together, the admin JSON and the DB JSON. Both files' names contain the username of the admin. The admin JSON stores the admin username and the one-way argon2id hash of the admin password:
```python
admin_JSON={ "username": admin_username,
         "ciphertext": crypto_text # this is the argon2id hash
        }
```
The plaintext of the admin password is never stored. 

The DB JSON stores the name of the database/vault, the kdf parameters and salt, and the fernet token of the database/vault:
```python
DB_JSON= {"vault_ID": f"{admin_username}_vault_{year}",
         "kdf": {"algo": "argon2_id",
                 "salt_base64": DB_salt.decode("utf-8"),
                 "time":3, #these are argon2.PasswordHasher() default params
                 "memory":65536 ,
                 "parallelism":4 ,
                 "hash_length":32},
        "contents": entries.decode() #the fernet token, the DB
    }
```

The security lies in the multiple points of layered security. In order to access the information in the database/vault, and input admin password has to be verified against the information in the admin JSON. Once inside the DB, each of the entries are encrypted themselves, with a unique salt, and each encrypted plaintext that must be decrypted with a unique key derived from the admin password to be viewed. The encrypted DB is like a locked building, and one inside, it is full of encrypted entries, which are like locked rooms inside the building, each with their own unique key.

More on this in the next section.

**Security:**

This is arguably the most important section. So this password manager uses a combination of Fernet and Argon2id to create a secure final product. 

*Fernet* is used for symmetric encryption here, for both the database and the plaintext passwords. *Argon2* is used to hash the admin password, but it is also used to derive a Fernet keys using argon2's low_level.hash_secret() and custom parameters. (You can absolutely adjust these settings, but keep in mind a Fernet key must be a URL-safe base64 encoded *32-byte key*.)

So Argon2 is used to produce a Fernet key based on the admin password and a generated salt, and this key is used to encrypt/decrypt the database

The Fernet key itself is derived from the admin password. For each user command, the user must enter the admin password and have it verfied before operations can be performed. (I am aware that this does not make for the most smooth user experience, but for this current version, it prioritizes security.)

So how does the actual excryption/decryption flow work? First, the admin and DB JSONs are intialized. The admin password is hashed and stored, but the plaintext is never stored, and it wont appear in the shell history. A unique salt for the DB is generated and stored, and an empty DB/vault is created, excrypted, and stored as well, ready for use.

When a user wants to decrypt the DB to see the entries (but not a password plaintext yet, thanks to the double layers), they enter a password. If this password, when verified with argon2's *verify()*, returns ```True```, then that password, along with the stored DB salt, is used to generate the Fernet key to decrypt the DB Fernet Token. 

Now you're in...but not quite. After the DB decryption occurs, each password *stil* has it's own unique salt, and therefore unique key, that needs to be derived again using the master password, before you can see the plaintext of the password. This means that everytime a user requests to view a password, the key must be derived all over again.

Crypto hygiene is important, so let's cover that. Of course, none of these keys are every stored. But furthermore, after use and as soon as possible, the reference is set to None, to be on the safe side.
```python
entries_token = crypto_utils.encrypt(key, entries_plaintext)
    key = None
```

For a function like deladmin, where the entire vault, the admin and databse JSONs, is deleted, any possibly sensitive data like the salts, hashes, tokens, etc. are also set to None
```python
DB_header["contents"] = None
kdf = DB_header["kdf"]
kdf["salt_base64"] = None
```
For shell history sterility, ```getpass()``` is used to disable echo, and prevent sensitive information from being ironed into the shell. This will be expanded upon more in the "How to Use" Section.

## How to Install:
If you want to use the tool itself, open CMD, Powershell, Git Bash, WSL, whatever your Unix-like shell of choice is, and run:
```
pip install vaultp
```
If you want to work on the code locally/make some edits/dev stuff, close the repository:
```bash
git clone <https://github.com/Ktech404/password_manager>
cd password_manager
```
And install in editable mode:
```bash
python -m pip install -e .
```
To make sure things are running properly:
```bash
vaultp --help
```
And you're good to go! You only need to do the editable install once. After that, the CLI will use whatever the latest changes you make to the code are.

Super easy. 

...But in case it's not, here's how you can troubleshoot:

- *After installation, the vaultp CLI should work in any terminal that has Python scripts on the PATH, but if the command isn’t recognized, ensure your Python Scripts/ folder is added to your PATH.*

- *Make sure your Python version meets requirements: 3.10 and up. You can check this by running your terminals appropiate command.*

- *It may also be neccesary to install dependencies via pip:*
```bash
pip install -r requirements.txt
```
- *If you move around the admin or DJSON files, it may negatively affect the function of the CLI, so make sure those files are in the same folder as wherever they were created/password_manager folder exists.*

- *When running commands, it may be possible that your antivirus software may flag the script, so keep that in mind*

## How to Use:

For each of the following commands, please note that you will be prompted to enter both the admin username, and the admin password. 
- *If you forget the admin username/password, there is no way to recover your database, and you will have to manually sanitize + delete the JSON files to create a new vault under the same name.*

I'll explain all of the commands and the arguments they take in this section. 

### Creative:

```bash
newadmin --username --password #password optional
```
- ***newadmin*** creates a new admin account. This is the first command you should run, as of course, you can't do anything without it. If you try to create a new admin under an admin username that already exists it will return an error.
```bash
newcreds --site --username --password #password optional
```
- ***newcreds*** adds a new set of credentials to the password manager. Check spelling!

Notice the "password optional" comments above. For the 2 above commands, you have 2 options for entering the password argument:

**Recommended**: You can run the command without the "--password" argument, at which point you will be prompted to enter the password, and it will not be shown in scrollback.

**Works, but not recomended**: You also have the option to run the command and enter the password alongside the other argument at the same time. *NOTE*: this will mean that the plaintext of the password will be visible in scrollback. Not fun. If you are going to do this, make to sure manual clear the terminal.

### Editing:
```bash
update --entryID --field --newinfo
```
- ***update*** allows you to update the fields of an existing credential. 
    - "entryID" is how you specify which credentials you want to update, use the list command to see all credentials and entryIDs. 
    - "field" is the field you want to update, and they are the same three as newcreds: site, username, or password. The CLI will take no other inputs than these.
    - "newinfo" is whatever new information you want to replace the information in the existing field. 
    
*NOTE*: if you would like to update a password, unfortunately, the plaintext will appear in scrollback. Though it won't be saved to the shell history, I would still recommend clearing the terminal.

### Viewing:
```bash
list
```
- ***list*** takes no arguments. After entering the admin credentials, all of the stored credentials will be printed to the terminal.

```bash
sitelist --site
```
- ***sitelist*** is list's slightly cooler cousin. You can print to the terminal the credentials from whatever site you specify, if there are credentials for that website in the vault.

To clarify, the plaintext of the passwords are not printed.
```
Credentials:

Entry_ID: 0
Website: site.com
Username: website_username
Password: *******
```
The stars are just an arbitrary placeholder. Twinkle twinkle.
```bash
view --entryID
```
- ***view*** is the coolest cousin out of the bunch. It takes an entryID as an argument, since passwords are only decrypted one-by-one, and it prints the plaintext of the password to the terminal.
```bash
Credentials:

Entry_ID: 0
Website: site.com
Username: website_username
Password: #plaintext password here
```
Of course, the plaintext of the password is going to appear in scrollback. I would *highly* recommend that after admiring your very secure password and copying it to clipboard, you clear the terminal to keep it safe.

### Destructive:
```bash
del --entryID
```
- ***del*** takes the entryID of a credential as an argument, and it deletes it. No surprises.

```bash
reset
```
- ***reset*** takes no arguments. It will reset the database, that is, it will empty it. The admin JSON and database JSON **will still exist**, it will just wipe all existing entries. This cannot be undone.

```bash
deladmin
```
- ***deladmin*** takes no argument. It will **delete** the database. This means the admin JSON and the database JSON **will no longer exist**. The files are sanitized first, with references to any sensitive data (salts, hashes, tokens) being set to note, and then the files themselves being removed. This cannot be undone.

# Disclaimer:
This tool is only intended to be used for personal and educational uses. Do not use it as a substitute for professional password managers or to store extremely sensitive data. If you choose to use it to store actual data, you are responsible for your own data security and any associated risks. Have fun, use responsibly.

# Credits:
- Argon2 hashing provided by [argon2-cffi](https://github.com/hynek/argon2-cffi)
- Cryptography powered by [cryptography](https://github.com/pyca/cryptography)