* Fast: written in [rust](https://www.rust-lang.org), encrypts with [AES-256-CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) or [XChaCha20](https://en.wikipedia.org/wiki/Salsa20#XChaCha)
* Password brute-force resistance with [Argon2](https://en.wikipedia.org/wiki/Argon2)
* Encryption from STDIN/STDOUT or from files
* Adjustable performance & secuity parameters
## Disclamer
doby is provided "as is", without any warranty of any kind. I'm not a professional cryptographer. This program didn't receive any security audit and therefore __shouldn't be considered fully secure__.
## Usage
Encryption
```bash
doby my-super-secret-source-code.rs encrypted.doby
```
Decryption
```bash
doby encrypted.doby decrypted.rs
```
If you ommit file path or use `-`, doby operates from `stdin/stdout`
```bash
# Read from stdin and write to stdout
cat my-super-secret-music.flac | doby > encrypted.doby
# Read from a file and output to stdout
doby encrypted.doby > decrypted.flac
# Read from stdin and save to a file
cat my-super-secret-logs-file.log | doby - logs.doby
```
Speicfy password from the command line
```bash
doby -p "A super very ultra strong passphrase" my-super-secret-document.pdf document.doby
```
Double encryption
```bash
doby -p "first password" my-super-secret-database.db | doby -f - double-encrypted.doby
cargo build --release #outputs to ./target/release/doby
```
## Cryptographic details
### Encryption
doby first derives your password with Argon2 (version 19) in Argon2id mode with a 64 bytes long random salt. A `master_key` of 32 bytes is thus generated.
```rust
let master_key: [u8; 32] = argon2id(
password,
random_password_salt,
argon2_time_cost,
argon2_memory_cost,
argon2_parallelism,
);
```
Then, doby uses [HKDF](https://en.wikipedia.org/wiki/HKDF) with a new random salt to compute the `encryption_key`and the `authentication_key`.
```rust
let hkdf = Hkdf::new(
random_hkdf_salt,
master_key, //ikm
blake3, //hash function
);
let encryption_key: [u8; 32] = hkdf.expand(b"doby_encryption_key");
let authentication_key: [u8; 32] = hkdf.expand(b"doby_authentication_key");
```
Next, doby initializes a [BLAKE3](https://en.wikipedia.org/wiki/BLAKE_%28hash_function%29#BLAKE3) HMAC with `authentication_key` and add all public encryption parameters to it.
Now, doby initializes a symmetric cipher with `encryption_key` and `random_nonce` (either AES-CTR or XChaCha20, based on the `--cipher` option) and starts the actual encryption. It reads chunks from the plaintext (according to the `--block-size` parameter), encrypts them with the cipher and updates the HMAC with the ciphertext.
Once the whole plaintext is encrypted, doby computes and appends the HMAC to the ciphertext.
```rust
output.write(hmac.digest());
```
### Decryption
doby reads the public encryption values from the input header to get all parameters needed to re-derive the `master_key`from the password with Argon2.
```rust
let master_key: [u8; 32] = argon2id(
password,
password_salt_read_from_input,
argon2_time_cost_read_from_input,
argon2_memory_cost_read_from_input,
argon2_parallelism_read_from_input,
);
```
`encryption_key`and `authentication_key` are computed from `master_key` and the HKDF salt in the same way as during encryption. The HMAC is also initialized and updated with the values read from the header.