You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+34-2Lines changed: 34 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,6 +22,7 @@ A multi-threaded AEAD encryption engine built in Rust. Encrypts and decrypts fil
22
22
-**In-place encryption**: `seal_in_place_separate_tag` / `open_in_place` via `ring` minimizes allocation in the hot loop
23
23
-**Password zeroization**: Keys and passwords are securely wiped from memory after use
24
24
-**O_DIRECT + sector-aligned format**: 4 KiB-aligned header and chunk slots enable `O_DIRECT` I/O, bypassing the kernel page cache for DMA-speed reads/writes on NVMe. Buffer pools use `std::alloc` with 4096-byte alignment
25
+
-**Directory encryption**: Encrypt entire directories as a single encrypted archive. Tar-based packing preserves file names, permissions, timestamps, and directory structure inside the ciphertext. Extraction validates against path traversal and symlink escape attacks
25
26
-**Self-describing file format**: Header stores cipher, chunk size, original file size, salt, base nonce, and Argon2id KDF parameters
> **Security note:**`--password` / `-p` passes the password as a CLI argument, which is visible in `ps` output and shell history. For interactive use, omit it to get the secure hidden prompt. For scripting, prefer clearing history afterward or using a wrapper that reads from a file descriptor.
109
110
111
+
### Encrypt a directory
112
+
113
+
```bash
114
+
# Encrypt a directory (auto-detects, produces mydir.tar.enc)
Directory encryption creates a temporary tar archive (`.concryptor-*.tar`, 0600 permissions, CSPRNG-named), encrypts it, then auto-deletes the temp file. File names, directory structure, permissions, and timestamps are all inside the encrypted payload.
Without `--extract`, decrypting a directory archive produces the intermediate `.tar` file, which you can inspect or extract manually.
147
+
123
148
### Help
124
149
125
150
```bash
@@ -157,7 +182,7 @@ The salt and base nonce are generated fresh from `rand::thread_rng()` (backed by
157
182
158
183
## Security Design
159
184
160
-
-**Nonce derivation**: `chunk_nonce = base_nonce XOR chunk_index` (TLS 1.3 style). Swapping chunks causes decryption failure because the nonce at position N won't match the nonce used to encrypt the chunk originally at position M.
185
+
-**Nonce derivation**: `chunk_nonce = base_nonce XOR chunk_index` (TLS 1.3 style). Swapping chunks causes decryption failure because the nonce at position N won't match the nonce used to encrypt the chunk originally at position M. Note: XOR-based nonce derivation has a theoretical weakness when the *same key* is used across multiple streams (distinct base nonces can produce overlapping nonce spaces). This does not apply to Concryptor because each encryption generates a fresh 128-bit random salt, producing a unique Argon2id key per file. Nonce uniqueness only matters under the same key, and key reuse probability is ~2^-128 per file pair.
161
186
-**Header-authenticated AAD**: Every chunk's AEAD call uses `AAD = full_aligned_header (4096) || chunk_index (8 LE) || is_final (1)` (4105 bytes total). The entire 4 KiB header sector (core fields, KDF parameters, and reserved padding) is bound into every chunk's authentication tag. Modifying *any* header byte (cipher type, chunk size, original size, salt, nonce, KDF parameters, or reserved padding) invalidates all chunks. This prevents truncation attacks where an adversary edits `original_size` and removes trailing chunks, and also prevents smuggling data into the reserved padding region. Legacy v3 files are decrypted with 52-byte AAD for backward compatibility; no downgrade from v4 to v3 is possible because the version byte itself is within the authenticated AAD.
162
187
-**STREAM-style final chunk indicator**: The last byte of the AAD is `0x01` for the final chunk and `0x00` for all others. This prevents two attacks:
163
188
-**Truncation**: Removing the final chunk and promoting a non-final chunk to the end fails because the non-final chunk was encrypted with `is_final = 0x00` but decryption expects `0x01`.
@@ -169,7 +194,7 @@ The salt and base nonce are generated fresh from `rand::thread_rng()` (backed by
169
194
## Testing
170
195
171
196
```bash
172
-
# Run the full test suite (40 tests)
197
+
# Run the full test suite (67 tests)
173
198
cargo test
174
199
175
200
# Run benchmarks (HTML reports in target/criterion/)
0 commit comments