Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Streaming:

- **Magic**: Literal `b"VAULTX03"` — identifies the streaming format version
- **Salt**: 128-bit fresh `OsRng`-generated per file
- **Nonce**: 56-bit fresh `OsRng`-generated per file; `EncryptorBE32` appends an internal 5-byte counter/flag (12 bytes total internally)
- **Nonce**: 56-bit (7 bytes) fresh `OsRng`-generated per file; `EncryptorBE32` expands this to a 12-byte nonce internally by appending a 5-byte counter + 1-byte flag

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correct the BE32 nonce expansion byte count

For anyone implementing or verifying VAULTX03 from the README, this describes a 7-byte prefix plus a 5-byte counter plus a 1-byte flag, which totals 13 bytes even though the expanded AEAD nonce is 12 bytes. The implementation documents the internal suffix as 5 bytes total (4-byte counter + 1-byte flag) in neuron-encrypt/src/crypto.rs:53-54, so this should say 4-byte counter + 1-byte flag or 5-byte counter/flag to avoid misdocumenting the file format.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 The Roast: Quick math check: 7 bytes + 5 bytes + 1 byte = 13 bytes, not 12. Your calculator needs recalibration. The BE32 construction splits the 12-byte nonce as 7-byte stream_nonce + 4-byte counter + 1-byte flag, not 5-byte counter.

🩹 The Fix: Change '5-byte counter + 1-byte flag' to '4-byte counter + 1-byte flag' to accurately reflect the BE32 nonce expansion.

📏 Severity: suggestion


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

- **Body**: Sequence of AES-256-GCM-SIV encrypted chunks. Intermediate chunks via `encrypt_next`, final chunk via `encrypt_last` to prevent truncation attacks.

### Legacy VAULTX02 Format
Expand Down Expand Up @@ -562,23 +562,34 @@ For full setup, JNI architecture details, and building instructions, see the [An

| Layer | Algorithm | Parameters |
|-------|-----------|------------|
| Encryption (V3) | XChaCha20-Poly1305 (streaming AEAD) | 192-bit nonce, 128-bit tag, 1 MB chunks |
| Encryption (V2 legacy) | AES-256-GCM-SIV | 96-bit nonce, 128-bit tag |
| Key Derivation | Argon2id | m=65536 (64 MB), t=3 iterations, p=1 lane |
| Encryption (V3) | AES-256-GCM-SIV (BE32 streaming) | 56-bit stream nonce per file, 128-bit auth tag per 1 MB chunk |
| Encryption (V2 legacy) | AES-256-GCM-SIV | 96-bit nonce, 128-bit auth tag |
| Key Derivation | Argon2id | m=65536 (64 MB), t=3 iterations, p=4 lanes |
| Key Expansion | HKDF-SHA512 | Two separate 256-bit subkeys: enc + nonce |
| Integrity | Poly1305 / GHASH | Per-chunk authentication; any bit-flip fails |
| Integrity | GHASH (GCM-SIV) | Per-chunk authentication; any bit-flip fails |
| RNG | OS CSPRNG (`OsRng`) | `getrandom` crate; no `rand` global state |
| Memory Hygiene | `Zeroizing<T>` | Key material zeroed from RAM on drop |

### Key Derivation Details

```
passphrase + 32-byte OsRng salt
Argon2id (64 MB, 3 passes)
64-byte master key
├─ HKDF-Extract+Expand → 32-byte encryption key
└─ HKDF-Extract+Expand → 32-byte nonce seed
V3 (VAULTX03 streaming):
passphrase + 16-byte OsRng salt
Argon2id (64 MB, 3 passes, 4 lanes)
64-byte master key
├─ HKDF-Extract+Expand → 32-byte encryption key
└─ HKDF-Extract+Expand → 32-byte nonce seed

V2 (VAULTX02 legacy):
passphrase + 32-byte OsRng salt
Argon2id (64 MB, 3 passes, 4 lanes)
64-byte master key
├─ HKDF-Extract+Expand → 32-byte encryption key
└─ HKDF-Extract+Expand → 32-byte nonce seed
```

The salt is stored in plaintext in the file header. The master key and all intermediate key material are stored in `Zeroizing<Vec<u8>>` which zeroes memory on drop.
Expand All @@ -587,8 +598,8 @@ The salt is stored in plaintext in the file header. The master key and all inter

**Protected against:**
- Offline brute-force: Argon2id with 64 MB memory cost makes GPU attacks expensive.
- Ciphertext tampering: Every 1 MB chunk has an independent Poly1305 authentication tag. Corruption of any byte fails decryption of that chunk.
- Nonce reuse: Each encryption generates a fresh 24-byte random nonce via `OsRng`. With 192-bit nonces, collision probability is negligible even at billions of files.
- Ciphertext tampering: Every 1 MB chunk has an independent GCM-SIV authentication tag (GHASH). Corruption of any byte fails decryption of that chunk.
- Nonce reuse: Each encryption generates a fresh 7-byte (56-bit) random nonce via `OsRng`. `EncryptorBE32` expands it to 12 bytes internally with a per-chunk counter. Fresh nonce per file makes collision probability negligible.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not call 56-bit random nonce collisions negligible

For high-volume or automated use, this new 56-bit nonce statement overstates the collision margin: with only 7 random bytes and no uniqueness tracking in encrypt_file, birthday probability is already about 6.7% at 100 million encryptions and effectively certain around 1 billion. The old 192-bit claim made that wording true, but with VAULTX03 this should either quantify the risk or rely on AES-GCM-SIV's nonce-misuse resistance instead of saying random per-file collisions are negligible.

Useful? React with 👍 / 👎.

- Sensitive data in memory: Plaintext buffers and keys are wrapped in `Zeroizing<>`, zeroed on drop. Passwords are cleared from UI state immediately after the crypto thread starts.

**Not protected against:**
Expand Down
Loading