Skip to content
Merged
Show file tree
Hide file tree
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
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,14 @@ If you want to use fast vectorised key stream functions, install with both `nump

## 🚦 Benchmarks: VernamVeil vs AES-256-CBC

VernamVeil prioritises educational value and cryptographic experimentation over raw speed. As expected, it is about 4-13% slower than highly optimised, hardware-accelerated cyphers like AES-256-CBC. This is due to its Python implementation and focus on flexibility rather than production-grade speed or safety. The following benchmarks compare VernamVeil (using its fastest configuration: NumPy vectorisation, C extension enabled, with `generate_keyed_hash_fx` and `blake3` hashing) to OpenSSL's AES-256-CBC on the same Ubuntu Linux machine.
VernamVeil is competitive with OpenSSL's AES-256-CBC in terms of speed. As both cyphers can leverage hardware acceleration (AES via the AES-NI instruction set, and VernamVeil via SIMD instructions for its hashing component), the exact benchmarks vary significantly from machine to machine. On an Apple MacBook Pro with an M1 Max chip, VernamVeil is about 36% faster for encoding than AES-256-CBC but 40% slower for decoding. The slower decryption speed is because AES-256-CBC can be fully parallelised during decryption. In contrast, VernamVeil's stateful seed evolution creates a dependency chain between data blocks, making its decryption process inherently sequential. Furthermore, these benchmarks test VernamVeil with all its security features enabled (Authenticated Encryption, Synthetic IV, and Obfuscation), whereas the AES-256-CBC comparison performs raw, unauthenticated encryption. The focus is therefore on benchmarking VernamVeil's default secure configuration, not on a direct feature-for-feature comparison. The following benchmarks compare VernamVeil (using its recommended configuration: NumPy vectorisation, C extension enabled, with `generate_keyed_hash_fx` and `blake3` hashing) to OpenSSL's AES-256-CBC on the same machine.

### 📊 Summary

| Algorithm | Encode Time | Decode Time |
|--------------|-------------|-------------|
| VernamVeil | 1.119 s | 1.328 s |
| AES-256-CBC | 1.760 s | 0.950 s |

### ‍💻 Benchmark Setup

Expand All @@ -498,34 +505,27 @@ openssl rand -hex 16 > iv.hex
```bash
vernamveil encode --infile /tmp/original.bin --outfile /tmp/output.enc --fx-file fx.py --seed-file seed.hex --buffer-size 134217728 --chunk-size 1048576 --delimiter-size 32 --padding-range 100 200 --decoy-ratio 0.01 --hash-name blake3 --verbosity info
```
_Time: 3.125s_
_Time: 1.119s_

**Decoding:**
```bash
vernamveil decode --infile /tmp/output.enc --outfile /tmp/output.dec --fx-file fx.py --seed-file seed.hex --buffer-size 136349200 --chunk-size 1048576 --delimiter-size 32 --padding-range 100 200 --decoy-ratio 0.01 --hash-name blake3 --verbosity info
```
_Time: 2.975s_
_Time: 1.328s_

### 🐇 AES-256-CBC (OpenSSL)

**Encoding:**
```bash
time openssl enc -aes-256-cbc -in /tmp/original.bin -out /tmp/output.enc -K $(cat key.hex) -iv $(cat iv.hex) -pbkdf2
```
_Time: 3.007s_
_Time: 1.760s_

**Decoding:**
```bash
time openssl enc -d -aes-256-cbc -in /tmp/output.enc -out /tmp/output.dec -K $(cat key.hex) -iv $(cat iv.hex) -pbkdf2
```
_Time: 2.636s_

### 📊 Summary

| Algorithm | Encode Time | Decode Time |
|--------------|-------------|-------------|
| VernamVeil | 3.125 s | 2.975 s |
| AES-256-CBC | 3.007 s | 2.636 s |
_Time: 0.950s_

---

Expand Down
11 changes: 10 additions & 1 deletion vernamveil/_cypher.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@
class _Cypher(ABC):
"""Abstract base class for cyphers; provides utils that are common to all subclasses."""

def __init__(self, fx: FX) -> None:
def __init__(self, fx: FX, siv_seed_initialisation: bool) -> None:
"""Initialise a cypher instance.

Args:
fx (FX): A callable object that generates keystream bytes. This function is critical for the
encryption process and should be carefully designed to ensure cryptographic security.
siv_seed_initialisation (bool): Enables synthetic IV seed initialisation based on the message to
resist seed reuse
"""
self._fx = fx
self._siv_seed_initialisation = siv_seed_initialisation

@abstractmethod
def _generate_delimiter(self, seed: bytes | bytearray) -> tuple[memoryview, bytes | bytearray]:
Expand Down Expand Up @@ -231,6 +234,7 @@ def writer_thread_func() -> None:
reader_thread.start()
writer_thread.start()

original_siv_seed_initialisation = self._siv_seed_initialisation
try:
# Generate the initial block delimiter
current_seed = self._hash(seed, [b"block_delimiter"])
Expand Down Expand Up @@ -260,6 +264,7 @@ def writer_thread_func() -> None:
# Wrap in a vectorised memoryview
block = np.frombuffer(block, dtype=np.uint8).data
processed_block, current_seed = self.encode(block, current_seed)
self._siv_seed_initialisation = False # Disable SIV after the first block

# Write the processed block to the output file
if not queue_put(write_q, processed_block):
Expand Down Expand Up @@ -303,6 +308,7 @@ def writer_thread_func() -> None:

# Decode the complete block
processed_block, current_seed = self.decode(complete_block, current_seed)
self._siv_seed_initialisation = False # Disable SIV after the first block
if not queue_put(write_q, processed_block):
break

Expand Down Expand Up @@ -333,6 +339,9 @@ def writer_thread_func() -> None:
exception_queue.put(main_exc)
raise
finally:
# Restore the original SIV seed initialisation state
self._siv_seed_initialisation = original_siv_seed_initialisation

# Wait for threads to finish
if reader_thread.is_alive():
reader_thread.join()
Expand Down
3 changes: 1 addition & 2 deletions vernamveil/_vernamveil.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def __init__(
ValueError: If `decoy_ratio` is negative.
ValueError: If `hash_name` is not "blake2b", "blake3" or "sha256".
"""
super().__init__(fx)
super().__init__(fx, siv_seed_initialisation)
# Validate input
if chunk_size < 8:
raise ValueError("chunk_size must be at least 8 bytes.")
Expand All @@ -93,7 +93,6 @@ def __init__(
self._delimiter_size = delimiter_size
self._padding_range = padding_range
self._decoy_ratio = decoy_ratio
self._siv_seed_initialisation = siv_seed_initialisation
self._auth_encrypt = auth_encrypt
self._hash_name = hash_name

Expand Down
Loading