# Keyper

A basic password manager with a TUI interface.

## Storage

The [sled] database is used for on-disk storage. However, [sled] does not natively support encryption.

Instead, we use an encryption scheme composed from well-tested primitives implemented in the [RustCrypto] cryptographic libraries.

### Salt Encryption

A salt is a cryptographic value used to add randomness to inputs, and is commonly used in key-derivation algorithms.

We use the following scheme for password-based key derivation (in pseudo-code):

```text
SALT = {random 32-bytes}
PASSWORD = {user-input password}
ENCRYPTION_KEY = sha3::TurboShake256::hash(SALT | PASSWORD);
```

However, we also want to keep the salt value secret when storing to disk (maybe a bit of paranoia is a good thing? :3)

The following scheme is used to encrypt the salt for storage (in pseudo-code):

```text
SALT = {zeroes 32-bytes}
PASSWORD = {user-input password}

SALT_KEY = sha3::TurboShake256::hash(SALT | PASSWORD);
SALT_NONCE = {random 12-bytes}

ENCRYPTED_SALT = chacha20poly1305::encrypt(SALT_KEY, SALT_NONCE, SALT)
```

The entry is then stored in the database as:

```text
SALT_DB_KEY = sha3::TurboShake256::hash("salt");
sled::Db::insert(SALT_DB_KEY, SALT_NONCE | ENCRYPTED_SALT);
```

### Entry Encryption

Entries are encrypted in much the same way, with the slight change that the fields need to be length-prefix encoded.

We currently use 32-bit, little-endian length fields.

```text
TITLE = Entry.title;
CONTENT = Entry.content;
TITLE_LEN = (TITLE.len() as u32).to_le_bytes();
CONTENT_LEN = (CONTENT.len() as u32).to_le_bytes();
ENTRY_NONCE = {random 12-bytes}
ENTRY_INDEX = EntryList.len()
ENTRY_PLAINTEXT = TITLE_LEN | TITLE | CONTENT_LEN | CONTENT

ENTRY_DB_KEY = sha3::TurboShake256::hash(ENTRY_INDEX)
ENCRYPTED_ENTRY = chacha20poly1305::encrypt(ENCRYPTION_KEY, ENTRY_NONCE, ENTRY_PLAINTEXT)

sled::Db::insert(ENTRY_DB_KEY, ENTRY_NONCE | ENCRYPTED_ENTRY) 
```

Using the length-prefixed value encoding is very common, and allows for extending the `Entry` fields almost indefinitely.

By using the AEAD ChaCha20Poly1305 algorithm, we also get the benefit of database corruption protection.

If someone messes with your database entries, they won't decrypt properly.

## Credits

- TUI powered by [ratatui]
- Crypto provided by [RustCrypto]
- Storage provided by [sled]

[ratatui]: https://docs.rs/ratatui
[RustCrypto]: https://github.com/RustCrypto
[sled]: https://docs.rs/sled
