- Mac class follows hashlib API: digest functions added and finalization no longer modifies state. - Encryptor and Decryptor now raise RuntimeError if still used after final. Documentation updated with the changes and further examples. Tests updated with the changes, new test module for error cases (test_raises). Docstrings improved.
97 lines
2.6 KiB
Python
97 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Python benchmark matching src/test/benchmark.zig for all supported Aegis algorithms.
|
|
|
|
It performs two benchmarks with the same parameters as the Zig version:
|
|
- AEGIS encrypt (attached tag, maclen = MACBYTES)
|
|
- AEGIS MAC (clone state pattern)
|
|
|
|
Output format and throughput units mirror the Zig benchmark (Mb/s).
|
|
"""
|
|
|
|
import os
|
|
import time
|
|
|
|
from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4
|
|
|
|
MSG_LEN = 16384000 # 16 000 KiB
|
|
ITERATIONS = 100
|
|
|
|
|
|
def _random_bytes(n: int) -> bytes:
|
|
return os.urandom(n)
|
|
|
|
|
|
def bench_encrypt(ciph) -> None:
|
|
key = _random_bytes(ciph.KEYBYTES)
|
|
nonce = _random_bytes(ciph.NONCEBYTES)
|
|
|
|
# Single buffer, as in Zig: c_out == m buffer, with tag appended
|
|
maclen = ciph.MACBYTES
|
|
buf = bytearray(MSG_LEN + maclen)
|
|
# Initialize buffer with random data
|
|
buf[:] = _random_bytes(len(buf))
|
|
|
|
mview = memoryview(buf)[:MSG_LEN]
|
|
|
|
t0 = time.perf_counter()
|
|
for _ in range(ITERATIONS):
|
|
ciph.encrypt(key, nonce, mview, None, maclen=maclen, into=buf)
|
|
t1 = time.perf_counter()
|
|
|
|
# Prevent any unrealistic optimization assumptions
|
|
_ = buf[0]
|
|
|
|
bits = MSG_LEN * ITERATIONS * 8
|
|
elapsed_s = t1 - t0
|
|
throughput_mbps = (
|
|
(bits / (elapsed_s * 1_000_000)) if elapsed_s > 0 else float("inf")
|
|
)
|
|
print(f"{ciph.NAME}\t{throughput_mbps:10.2f} Mb/s")
|
|
|
|
|
|
def bench_mac(ciph) -> None:
|
|
key = _random_bytes(ciph.KEYBYTES)
|
|
nonce = _random_bytes(ciph.NONCEBYTES)
|
|
|
|
buf = bytearray(MSG_LEN)
|
|
buf[:] = _random_bytes(len(buf))
|
|
|
|
mac0 = ciph.Mac(key, nonce, maclen=ciph.MACBYTES_LONG)
|
|
mac_out = bytearray(ciph.MACBYTES_LONG)
|
|
|
|
t0 = time.perf_counter()
|
|
for _ in range(ITERATIONS):
|
|
mac = mac0.clone()
|
|
mac.update(buf)
|
|
mac.final(into=mac_out)
|
|
t1 = time.perf_counter()
|
|
|
|
_ = mac_out[0]
|
|
|
|
bits = MSG_LEN * ITERATIONS * 8
|
|
elapsed_s = t1 - t0
|
|
throughput_mbps = (
|
|
(bits / (elapsed_s * 1_000_000)) if elapsed_s > 0 else float("inf")
|
|
)
|
|
print(f"{ciph.NAME} MAC\t{throughput_mbps:10.2f} Mb/s")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# aegis_init() is called in the loader at import time already
|
|
# Run encrypt benchmarks in order: 256, 256x2, 256x4, 128l, 128x2, 128x4
|
|
bench_encrypt(aegis256)
|
|
bench_encrypt(aegis256x2)
|
|
bench_encrypt(aegis256x4)
|
|
bench_encrypt(aegis128l)
|
|
bench_encrypt(aegis128x2)
|
|
bench_encrypt(aegis128x4)
|
|
|
|
# Run MAC benchmarks in order: 128l, 128x2, 128x4, 256, 256x2, 256x4
|
|
bench_mac(aegis128l)
|
|
bench_mac(aegis128x2)
|
|
bench_mac(aegis128x4)
|
|
bench_mac(aegis256)
|
|
bench_mac(aegis256x2)
|
|
bench_mac(aegis256x4)
|