From 13445887e9ab7e5b41f16e6207af9e8cec868613 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Sat, 8 Nov 2025 15:43:01 -0600 Subject: [PATCH] Constants renamed and values extracted from C code rather than function call at runtime. Documentation update. --- README.md | 20 ++++---- pyaegis/aegis128l.py | 103 +++++++++++++++++++-------------------- pyaegis/aegis128x2.py | 103 +++++++++++++++++++-------------------- pyaegis/aegis128x4.py | 103 +++++++++++++++++++-------------------- pyaegis/aegis256.py | 103 +++++++++++++++++++-------------------- pyaegis/aegis256x2.py | 103 +++++++++++++++++++-------------------- pyaegis/aegis256x4.py | 103 +++++++++++++++++++-------------------- pyaegis/benchmark.py | 24 ++++----- tools/generate.py | 110 +++++++++++++++++++++++++++++++++++------- 9 files changed, 426 insertions(+), 346 deletions(-) diff --git a/README.md b/README.md index 11fc21f..e2bb7f4 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ assert pt == msg Common parameters and returns (applies to all items below): -- key: bytes of length a.KEYBYTES -- nonce: bytes of length a.NPUBBYTES (must be unique per (key, message)) +- key: bytes of length ciph.KEYBYTES +- nonce: bytes of length ciph.NONCEBYTES (must be unique per message) - message/ct: plain text or ciphertext - ad: optional associated data (authenticated, not encrypted) - into: optional output buffer (see below) @@ -95,10 +95,10 @@ Useful for creating pseudo random bytes as rapidly as possible. Reuse of the sam ### Miscellaneous -Constants (per module): KEYBYTES, NPUBBYTES, ABYTES_MIN, ABYTES_MAX, RATE, ALIGNMENT +Constants (per module): KEYBYTES, NONCEBYTES, MACBYTES, MACBYTES_LONG, RATE, ALIGNMENT - random_key() -> bytearray (length KEYBYTES) -- random_nonce() -> bytearray (length NPUBBYTES) +- random_nonce() -> bytearray (length NONCEBYTES) - nonce_increment(nonce) - wipe(buffer) @@ -116,7 +116,7 @@ Constants (per module): KEYBYTES, NPUBBYTES, ABYTES_MIN, ABYTES_MAX, RATE, ALIGN A cryptographically secure keyed hash is produced. The example uses all zeroes for the nonce to always produce the same hash for the same key: ```python from pyaegis import aegis256x4 as ciph -key, nonce = ciph.random_key(), bytes(ciph.NPUBBYTES) +key, nonce = ciph.random_key(), bytes(ciph.NONCEBYTES) mac = ciph.mac(key, nonce, b"message", maclen=32) print(mac) @@ -151,12 +151,12 @@ Class-based interface for incremental updates is an alternative to the one-shot from pyaegis import aegis256x4 as ciph key, nonce = ciph.random_key(), ciph.random_nonce() -enc = a.Encryptor(key, nonce, ad=b"header") +enc = ciph.Encryptor(key, nonce, ad=b"header") c1 = enc.update(b"chunk1") c2 = enc.update(b"chunk2") mac = enc.final(maclen=16) -dec = a.Decryptor(key, nonce, ad=b"header") +dec = ciph.Decryptor(key, nonce, ad=b"header") p1 = dec.update(c1) p2 = dec.update(c2) dec.final(mac) # raises ValueError on failure @@ -173,7 +173,7 @@ message = bytearray(30 * b"Attack at dawn! ") key = b"sixteenbyte key!" # 16 bytes secret key for aegis128* algorithms nonce = ciph.random_nonce() framebytes = 80 # In real applications 1 MiB or more is practical -maclen = ciph.ABYTES_MIN # 16 +maclen = ciph.MACBYTES # 16 with open("encrypted.bin", "wb") as f: f.write(nonce) # Public initial nonce sent with the ciphertext @@ -191,10 +191,10 @@ from pyaegis import aegis128x4 as ciph # Decryption needs same values as encryption key = b"sixteenbyte key!" framebytes = 80 -maclen = ciph.ABYTES_MIN +maclen = ciph.MACBYTES with open("encrypted.bin", "rb") as f: - nonce = bytearray(f.read(ciph.NPUBBYTES)) + nonce = bytearray(f.read(ciph.NONCEBYTES)) while True: frame = f.read(framebytes) if not frame: diff --git a/pyaegis/aegis128l.py b/pyaegis/aegis128l.py index dabea45..ac2e483 100644 --- a/pyaegis/aegis128l.py +++ b/pyaegis/aegis128l.py @@ -9,24 +9,26 @@ from ._loader import ffi from ._loader import lib as _lib from .util import Buffer, new_aligned_struct, nonce_increment, wipe -# Constants exposed as functions in C; mirror them as integers at module import time -KEYBYTES = _lib.aegis128l_keybytes() -NPUBBYTES = _lib.aegis128l_npubbytes() -ABYTES_MIN = _lib.aegis128l_abytes_min() -ABYTES_MAX = _lib.aegis128l_abytes_max() -TAILBYTES_MAX = _lib.aegis128l_tailbytes_max() -ALIGNMENT = 32 -RATE = 32 +KEYBYTES = 16 #: Key size in bytes (varies by algorithm) +NONCEBYTES = 16 #: Nonce size in bytes (varies by algorithm) +MACBYTES = 16 #: Normal MAC size (always 16) +MACBYTES_LONG = 32 #: Long MAC size (always 32) +ALIGNMENT = 64 #: Required alignment for internal structures +RATE = 64 #: Byte chunk size in internal processing def random_key() -> bytearray: - """Generate a random key using cryptographically secure random bytes.""" + """ + Generate a random key using cryptographically secure random bytes. + + It is recommended to wipe() the key after no longer needed to keep it secret. + """ return bytearray(secrets.token_bytes(KEYBYTES)) def random_nonce() -> bytearray: """Generate a random nonce using cryptographically secure random bytes.""" - return bytearray(secrets.token_bytes(NPUBBYTES)) + return bytearray(secrets.token_bytes(NONCEBYTES)) def _ptr(buf): @@ -39,7 +41,7 @@ def encrypt_detached( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, ct_into: Buffer | None = None, mac_into: Buffer | None = None, ) -> tuple[bytearray | memoryview, bytearray | memoryview]: @@ -47,7 +49,7 @@ def encrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -65,8 +67,8 @@ def encrypt_detached( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if ct_into is None: c = bytearray(len(message)) @@ -115,7 +117,7 @@ def decrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -130,8 +132,8 @@ def decrypt_detached( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") maclen = len(mac) if maclen not in (16, 32): raise TypeError("mac length must be 16 or 32") @@ -164,14 +166,14 @@ def encrypt( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Encrypt message with associated data, returning ciphertext with appended MAC. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -188,8 +190,8 @@ def encrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message) + maclen) else: @@ -220,14 +222,14 @@ def decrypt( ct: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Decrypt ciphertext with appended MAC and associated data. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -244,8 +246,8 @@ def decrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if len(ct) < maclen: raise TypeError("ciphertext too short for tag") expected_out = len(ct) - maclen @@ -284,7 +286,7 @@ def stream( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). + nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None). length: Number of bytes to generate (required if into is None). into: Buffer to write stream into (default: bytearray created). @@ -296,8 +298,8 @@ def stream( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if nonce is not None and len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if nonce is not None and len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: if length is None: raise TypeError("provide either into or length") @@ -326,7 +328,7 @@ def encrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). @@ -338,8 +340,8 @@ def encrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message)) else: @@ -367,7 +369,7 @@ def decrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). @@ -379,8 +381,8 @@ def decrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(ct)) else: @@ -402,14 +404,14 @@ def mac( key: Buffer, nonce: Buffer, data: Buffer, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Compute a MAC for the given data in one shot. Args: key: Key ({KEYBYTES=}) - nonce: Nonce ({NPUBBYTES=}) + nonce: Nonce ({NONCEBYTES=}) data: Data to MAC maclen: MAC length (16 or 32, default 16) into: Buffer to write MAC into (default: bytearray created) @@ -445,7 +447,7 @@ class Mac: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). Raises: TypeError: If key or nonce lengths are invalid. @@ -459,8 +461,8 @@ class Mac: # Normal init path if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") _lib.aegis128l_mac_init(self._st, _ptr(key), _ptr(nonce)) def __deepcopy__(self) -> "Mac": @@ -490,7 +492,7 @@ class Mac: def final( self, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: """Finalize and return the MAC tag. @@ -558,7 +560,7 @@ class Encryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data to bind to the encryption (optional). Raises: @@ -566,8 +568,8 @@ class Encryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis128l_state", ALIGNMENT) _lib.aegis128l_state_init( st, @@ -639,7 +641,7 @@ class Encryptor: return out if into is None else memoryview(out)[:w] # type: ignore def final( - self, into: Buffer | None = None, maclen: int = ABYTES_MIN + self, into: Buffer | None = None, maclen: int = MACBYTES ) -> bytearray | memoryview: """Finalize encryption, writing any remaining bytes and the tag. @@ -692,7 +694,7 @@ class Decryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data used during encryption (optional). Raises: @@ -700,8 +702,8 @@ class Decryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis128l_state", ALIGNMENT) _lib.aegis128l_state_init( st, @@ -796,10 +798,9 @@ def new_mac_state(): __all__ = [ # constants "KEYBYTES", - "NPUBBYTES", - "ABYTES_MIN", - "ABYTES_MAX", - "TAILBYTES_MAX", + "NONCEBYTES", + "MACBYTES", + "MACBYTES_LONG", "ALIGNMENT", "RATE", # utility functions diff --git a/pyaegis/aegis128x2.py b/pyaegis/aegis128x2.py index d0fe07e..7b1cedb 100644 --- a/pyaegis/aegis128x2.py +++ b/pyaegis/aegis128x2.py @@ -9,24 +9,26 @@ from ._loader import ffi from ._loader import lib as _lib from .util import Buffer, new_aligned_struct, nonce_increment, wipe -# Constants exposed as functions in C; mirror them as integers at module import time -KEYBYTES = _lib.aegis128x2_keybytes() -NPUBBYTES = _lib.aegis128x2_npubbytes() -ABYTES_MIN = _lib.aegis128x2_abytes_min() -ABYTES_MAX = _lib.aegis128x2_abytes_max() -TAILBYTES_MAX = _lib.aegis128x2_tailbytes_max() -ALIGNMENT = 64 -RATE = 64 +KEYBYTES = 16 #: Key size in bytes (varies by algorithm) +NONCEBYTES = 16 #: Nonce size in bytes (varies by algorithm) +MACBYTES = 16 #: Normal MAC size (always 16) +MACBYTES_LONG = 32 #: Long MAC size (always 32) +ALIGNMENT = 64 #: Required alignment for internal structures +RATE = 64 #: Byte chunk size in internal processing def random_key() -> bytearray: - """Generate a random key using cryptographically secure random bytes.""" + """ + Generate a random key using cryptographically secure random bytes. + + It is recommended to wipe() the key after no longer needed to keep it secret. + """ return bytearray(secrets.token_bytes(KEYBYTES)) def random_nonce() -> bytearray: """Generate a random nonce using cryptographically secure random bytes.""" - return bytearray(secrets.token_bytes(NPUBBYTES)) + return bytearray(secrets.token_bytes(NONCEBYTES)) def _ptr(buf): @@ -39,7 +41,7 @@ def encrypt_detached( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, ct_into: Buffer | None = None, mac_into: Buffer | None = None, ) -> tuple[bytearray | memoryview, bytearray | memoryview]: @@ -47,7 +49,7 @@ def encrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -65,8 +67,8 @@ def encrypt_detached( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if ct_into is None: c = bytearray(len(message)) @@ -115,7 +117,7 @@ def decrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -130,8 +132,8 @@ def decrypt_detached( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") maclen = len(mac) if maclen not in (16, 32): raise TypeError("mac length must be 16 or 32") @@ -164,14 +166,14 @@ def encrypt( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Encrypt message with associated data, returning ciphertext with appended MAC. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -188,8 +190,8 @@ def encrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message) + maclen) else: @@ -220,14 +222,14 @@ def decrypt( ct: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Decrypt ciphertext with appended MAC and associated data. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -244,8 +246,8 @@ def decrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if len(ct) < maclen: raise TypeError("ciphertext too short for tag") expected_out = len(ct) - maclen @@ -284,7 +286,7 @@ def stream( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). + nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None). length: Number of bytes to generate (required if into is None). into: Buffer to write stream into (default: bytearray created). @@ -296,8 +298,8 @@ def stream( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if nonce is not None and len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if nonce is not None and len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: if length is None: raise TypeError("provide either into or length") @@ -326,7 +328,7 @@ def encrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). @@ -338,8 +340,8 @@ def encrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message)) else: @@ -367,7 +369,7 @@ def decrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). @@ -379,8 +381,8 @@ def decrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(ct)) else: @@ -402,14 +404,14 @@ def mac( key: Buffer, nonce: Buffer, data: Buffer, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Compute a MAC for the given data in one shot. Args: key: Key ({KEYBYTES=}) - nonce: Nonce ({NPUBBYTES=}) + nonce: Nonce ({NONCEBYTES=}) data: Data to MAC maclen: MAC length (16 or 32, default 16) into: Buffer to write MAC into (default: bytearray created) @@ -445,7 +447,7 @@ class Mac: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). Raises: TypeError: If key or nonce lengths are invalid. @@ -459,8 +461,8 @@ class Mac: # Normal init path if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") _lib.aegis128x2_mac_init(self._st, _ptr(key), _ptr(nonce)) def __deepcopy__(self) -> "Mac": @@ -490,7 +492,7 @@ class Mac: def final( self, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: """Finalize and return the MAC tag. @@ -558,7 +560,7 @@ class Encryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data to bind to the encryption (optional). Raises: @@ -566,8 +568,8 @@ class Encryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis128x2_state", ALIGNMENT) _lib.aegis128x2_state_init( st, @@ -639,7 +641,7 @@ class Encryptor: return out if into is None else memoryview(out)[:w] # type: ignore def final( - self, into: Buffer | None = None, maclen: int = ABYTES_MIN + self, into: Buffer | None = None, maclen: int = MACBYTES ) -> bytearray | memoryview: """Finalize encryption, writing any remaining bytes and the tag. @@ -692,7 +694,7 @@ class Decryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data used during encryption (optional). Raises: @@ -700,8 +702,8 @@ class Decryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis128x2_state", ALIGNMENT) _lib.aegis128x2_state_init( st, @@ -796,10 +798,9 @@ def new_mac_state(): __all__ = [ # constants "KEYBYTES", - "NPUBBYTES", - "ABYTES_MIN", - "ABYTES_MAX", - "TAILBYTES_MAX", + "NONCEBYTES", + "MACBYTES", + "MACBYTES_LONG", "ALIGNMENT", "RATE", # utility functions diff --git a/pyaegis/aegis128x4.py b/pyaegis/aegis128x4.py index f373161..bde7cb4 100644 --- a/pyaegis/aegis128x4.py +++ b/pyaegis/aegis128x4.py @@ -9,24 +9,26 @@ from ._loader import ffi from ._loader import lib as _lib from .util import Buffer, new_aligned_struct, nonce_increment, wipe -# Constants exposed as functions in C; mirror them as integers at module import time -KEYBYTES = _lib.aegis128x4_keybytes() -NPUBBYTES = _lib.aegis128x4_npubbytes() -ABYTES_MIN = _lib.aegis128x4_abytes_min() -ABYTES_MAX = _lib.aegis128x4_abytes_max() -TAILBYTES_MAX = _lib.aegis128x4_tailbytes_max() -ALIGNMENT = 64 -RATE = 128 +KEYBYTES = 16 #: Key size in bytes (varies by algorithm) +NONCEBYTES = 16 #: Nonce size in bytes (varies by algorithm) +MACBYTES = 16 #: Normal MAC size (always 16) +MACBYTES_LONG = 32 #: Long MAC size (always 32) +ALIGNMENT = 64 #: Required alignment for internal structures +RATE = 64 #: Byte chunk size in internal processing def random_key() -> bytearray: - """Generate a random key using cryptographically secure random bytes.""" + """ + Generate a random key using cryptographically secure random bytes. + + It is recommended to wipe() the key after no longer needed to keep it secret. + """ return bytearray(secrets.token_bytes(KEYBYTES)) def random_nonce() -> bytearray: """Generate a random nonce using cryptographically secure random bytes.""" - return bytearray(secrets.token_bytes(NPUBBYTES)) + return bytearray(secrets.token_bytes(NONCEBYTES)) def _ptr(buf): @@ -39,7 +41,7 @@ def encrypt_detached( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, ct_into: Buffer | None = None, mac_into: Buffer | None = None, ) -> tuple[bytearray | memoryview, bytearray | memoryview]: @@ -47,7 +49,7 @@ def encrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -65,8 +67,8 @@ def encrypt_detached( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if ct_into is None: c = bytearray(len(message)) @@ -115,7 +117,7 @@ def decrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -130,8 +132,8 @@ def decrypt_detached( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") maclen = len(mac) if maclen not in (16, 32): raise TypeError("mac length must be 16 or 32") @@ -164,14 +166,14 @@ def encrypt( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Encrypt message with associated data, returning ciphertext with appended MAC. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -188,8 +190,8 @@ def encrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message) + maclen) else: @@ -220,14 +222,14 @@ def decrypt( ct: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Decrypt ciphertext with appended MAC and associated data. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -244,8 +246,8 @@ def decrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if len(ct) < maclen: raise TypeError("ciphertext too short for tag") expected_out = len(ct) - maclen @@ -284,7 +286,7 @@ def stream( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). + nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None). length: Number of bytes to generate (required if into is None). into: Buffer to write stream into (default: bytearray created). @@ -296,8 +298,8 @@ def stream( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if nonce is not None and len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if nonce is not None and len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: if length is None: raise TypeError("provide either into or length") @@ -326,7 +328,7 @@ def encrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). @@ -338,8 +340,8 @@ def encrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message)) else: @@ -367,7 +369,7 @@ def decrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). @@ -379,8 +381,8 @@ def decrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(ct)) else: @@ -402,14 +404,14 @@ def mac( key: Buffer, nonce: Buffer, data: Buffer, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Compute a MAC for the given data in one shot. Args: key: Key ({KEYBYTES=}) - nonce: Nonce ({NPUBBYTES=}) + nonce: Nonce ({NONCEBYTES=}) data: Data to MAC maclen: MAC length (16 or 32, default 16) into: Buffer to write MAC into (default: bytearray created) @@ -445,7 +447,7 @@ class Mac: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). Raises: TypeError: If key or nonce lengths are invalid. @@ -459,8 +461,8 @@ class Mac: # Normal init path if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") _lib.aegis128x4_mac_init(self._st, _ptr(key), _ptr(nonce)) def __deepcopy__(self) -> "Mac": @@ -490,7 +492,7 @@ class Mac: def final( self, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: """Finalize and return the MAC tag. @@ -558,7 +560,7 @@ class Encryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data to bind to the encryption (optional). Raises: @@ -566,8 +568,8 @@ class Encryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis128x4_state", ALIGNMENT) _lib.aegis128x4_state_init( st, @@ -639,7 +641,7 @@ class Encryptor: return out if into is None else memoryview(out)[:w] # type: ignore def final( - self, into: Buffer | None = None, maclen: int = ABYTES_MIN + self, into: Buffer | None = None, maclen: int = MACBYTES ) -> bytearray | memoryview: """Finalize encryption, writing any remaining bytes and the tag. @@ -692,7 +694,7 @@ class Decryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data used during encryption (optional). Raises: @@ -700,8 +702,8 @@ class Decryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis128x4_state", ALIGNMENT) _lib.aegis128x4_state_init( st, @@ -796,10 +798,9 @@ def new_mac_state(): __all__ = [ # constants "KEYBYTES", - "NPUBBYTES", - "ABYTES_MIN", - "ABYTES_MAX", - "TAILBYTES_MAX", + "NONCEBYTES", + "MACBYTES", + "MACBYTES_LONG", "ALIGNMENT", "RATE", # utility functions diff --git a/pyaegis/aegis256.py b/pyaegis/aegis256.py index fc568c5..ce2f2b1 100644 --- a/pyaegis/aegis256.py +++ b/pyaegis/aegis256.py @@ -9,24 +9,26 @@ from ._loader import ffi from ._loader import lib as _lib from .util import Buffer, new_aligned_struct, nonce_increment, wipe -# Constants exposed as functions in C; mirror them as integers at module import time -KEYBYTES = _lib.aegis256_keybytes() -NPUBBYTES = _lib.aegis256_npubbytes() -ABYTES_MIN = _lib.aegis256_abytes_min() -ABYTES_MAX = _lib.aegis256_abytes_max() -TAILBYTES_MAX = _lib.aegis256_tailbytes_max() -ALIGNMENT = 16 -RATE = 16 +KEYBYTES = 32 #: Key size in bytes (varies by algorithm) +NONCEBYTES = 32 #: Nonce size in bytes (varies by algorithm) +MACBYTES = 16 #: Normal MAC size (always 16) +MACBYTES_LONG = 32 #: Long MAC size (always 32) +ALIGNMENT = 64 #: Required alignment for internal structures +RATE = 64 #: Byte chunk size in internal processing def random_key() -> bytearray: - """Generate a random key using cryptographically secure random bytes.""" + """ + Generate a random key using cryptographically secure random bytes. + + It is recommended to wipe() the key after no longer needed to keep it secret. + """ return bytearray(secrets.token_bytes(KEYBYTES)) def random_nonce() -> bytearray: """Generate a random nonce using cryptographically secure random bytes.""" - return bytearray(secrets.token_bytes(NPUBBYTES)) + return bytearray(secrets.token_bytes(NONCEBYTES)) def _ptr(buf): @@ -39,7 +41,7 @@ def encrypt_detached( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, ct_into: Buffer | None = None, mac_into: Buffer | None = None, ) -> tuple[bytearray | memoryview, bytearray | memoryview]: @@ -47,7 +49,7 @@ def encrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -65,8 +67,8 @@ def encrypt_detached( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if ct_into is None: c = bytearray(len(message)) @@ -115,7 +117,7 @@ def decrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -130,8 +132,8 @@ def decrypt_detached( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") maclen = len(mac) if maclen not in (16, 32): raise TypeError("mac length must be 16 or 32") @@ -164,14 +166,14 @@ def encrypt( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Encrypt message with associated data, returning ciphertext with appended MAC. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -188,8 +190,8 @@ def encrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message) + maclen) else: @@ -220,14 +222,14 @@ def decrypt( ct: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Decrypt ciphertext with appended MAC and associated data. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -244,8 +246,8 @@ def decrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if len(ct) < maclen: raise TypeError("ciphertext too short for tag") expected_out = len(ct) - maclen @@ -284,7 +286,7 @@ def stream( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). + nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None). length: Number of bytes to generate (required if into is None). into: Buffer to write stream into (default: bytearray created). @@ -296,8 +298,8 @@ def stream( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if nonce is not None and len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if nonce is not None and len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: if length is None: raise TypeError("provide either into or length") @@ -326,7 +328,7 @@ def encrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). @@ -338,8 +340,8 @@ def encrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message)) else: @@ -367,7 +369,7 @@ def decrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). @@ -379,8 +381,8 @@ def decrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(ct)) else: @@ -402,14 +404,14 @@ def mac( key: Buffer, nonce: Buffer, data: Buffer, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Compute a MAC for the given data in one shot. Args: key: Key ({KEYBYTES=}) - nonce: Nonce ({NPUBBYTES=}) + nonce: Nonce ({NONCEBYTES=}) data: Data to MAC maclen: MAC length (16 or 32, default 16) into: Buffer to write MAC into (default: bytearray created) @@ -445,7 +447,7 @@ class Mac: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). Raises: TypeError: If key or nonce lengths are invalid. @@ -459,8 +461,8 @@ class Mac: # Normal init path if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") _lib.aegis256_mac_init(self._st, _ptr(key), _ptr(nonce)) def __deepcopy__(self) -> "Mac": @@ -490,7 +492,7 @@ class Mac: def final( self, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: """Finalize and return the MAC tag. @@ -558,7 +560,7 @@ class Encryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data to bind to the encryption (optional). Raises: @@ -566,8 +568,8 @@ class Encryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis256_state", ALIGNMENT) _lib.aegis256_state_init( st, @@ -639,7 +641,7 @@ class Encryptor: return out if into is None else memoryview(out)[:w] # type: ignore def final( - self, into: Buffer | None = None, maclen: int = ABYTES_MIN + self, into: Buffer | None = None, maclen: int = MACBYTES ) -> bytearray | memoryview: """Finalize encryption, writing any remaining bytes and the tag. @@ -692,7 +694,7 @@ class Decryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data used during encryption (optional). Raises: @@ -700,8 +702,8 @@ class Decryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis256_state", ALIGNMENT) _lib.aegis256_state_init( st, @@ -796,10 +798,9 @@ def new_mac_state(): __all__ = [ # constants "KEYBYTES", - "NPUBBYTES", - "ABYTES_MIN", - "ABYTES_MAX", - "TAILBYTES_MAX", + "NONCEBYTES", + "MACBYTES", + "MACBYTES_LONG", "ALIGNMENT", "RATE", # utility functions diff --git a/pyaegis/aegis256x2.py b/pyaegis/aegis256x2.py index 0057a07..3b56478 100644 --- a/pyaegis/aegis256x2.py +++ b/pyaegis/aegis256x2.py @@ -9,24 +9,26 @@ from ._loader import ffi from ._loader import lib as _lib from .util import Buffer, new_aligned_struct, nonce_increment, wipe -# Constants exposed as functions in C; mirror them as integers at module import time -KEYBYTES = _lib.aegis256x2_keybytes() -NPUBBYTES = _lib.aegis256x2_npubbytes() -ABYTES_MIN = _lib.aegis256x2_abytes_min() -ABYTES_MAX = _lib.aegis256x2_abytes_max() -TAILBYTES_MAX = _lib.aegis256x2_tailbytes_max() -ALIGNMENT = 32 -RATE = 32 +KEYBYTES = 32 #: Key size in bytes (varies by algorithm) +NONCEBYTES = 32 #: Nonce size in bytes (varies by algorithm) +MACBYTES = 16 #: Normal MAC size (always 16) +MACBYTES_LONG = 32 #: Long MAC size (always 32) +ALIGNMENT = 64 #: Required alignment for internal structures +RATE = 64 #: Byte chunk size in internal processing def random_key() -> bytearray: - """Generate a random key using cryptographically secure random bytes.""" + """ + Generate a random key using cryptographically secure random bytes. + + It is recommended to wipe() the key after no longer needed to keep it secret. + """ return bytearray(secrets.token_bytes(KEYBYTES)) def random_nonce() -> bytearray: """Generate a random nonce using cryptographically secure random bytes.""" - return bytearray(secrets.token_bytes(NPUBBYTES)) + return bytearray(secrets.token_bytes(NONCEBYTES)) def _ptr(buf): @@ -39,7 +41,7 @@ def encrypt_detached( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, ct_into: Buffer | None = None, mac_into: Buffer | None = None, ) -> tuple[bytearray | memoryview, bytearray | memoryview]: @@ -47,7 +49,7 @@ def encrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -65,8 +67,8 @@ def encrypt_detached( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if ct_into is None: c = bytearray(len(message)) @@ -115,7 +117,7 @@ def decrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -130,8 +132,8 @@ def decrypt_detached( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") maclen = len(mac) if maclen not in (16, 32): raise TypeError("mac length must be 16 or 32") @@ -164,14 +166,14 @@ def encrypt( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Encrypt message with associated data, returning ciphertext with appended MAC. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -188,8 +190,8 @@ def encrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message) + maclen) else: @@ -220,14 +222,14 @@ def decrypt( ct: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Decrypt ciphertext with appended MAC and associated data. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -244,8 +246,8 @@ def decrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if len(ct) < maclen: raise TypeError("ciphertext too short for tag") expected_out = len(ct) - maclen @@ -284,7 +286,7 @@ def stream( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). + nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None). length: Number of bytes to generate (required if into is None). into: Buffer to write stream into (default: bytearray created). @@ -296,8 +298,8 @@ def stream( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if nonce is not None and len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if nonce is not None and len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: if length is None: raise TypeError("provide either into or length") @@ -326,7 +328,7 @@ def encrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). @@ -338,8 +340,8 @@ def encrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message)) else: @@ -367,7 +369,7 @@ def decrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). @@ -379,8 +381,8 @@ def decrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(ct)) else: @@ -402,14 +404,14 @@ def mac( key: Buffer, nonce: Buffer, data: Buffer, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Compute a MAC for the given data in one shot. Args: key: Key ({KEYBYTES=}) - nonce: Nonce ({NPUBBYTES=}) + nonce: Nonce ({NONCEBYTES=}) data: Data to MAC maclen: MAC length (16 or 32, default 16) into: Buffer to write MAC into (default: bytearray created) @@ -445,7 +447,7 @@ class Mac: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). Raises: TypeError: If key or nonce lengths are invalid. @@ -459,8 +461,8 @@ class Mac: # Normal init path if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") _lib.aegis256x2_mac_init(self._st, _ptr(key), _ptr(nonce)) def __deepcopy__(self) -> "Mac": @@ -490,7 +492,7 @@ class Mac: def final( self, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: """Finalize and return the MAC tag. @@ -558,7 +560,7 @@ class Encryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data to bind to the encryption (optional). Raises: @@ -566,8 +568,8 @@ class Encryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis256x2_state", ALIGNMENT) _lib.aegis256x2_state_init( st, @@ -639,7 +641,7 @@ class Encryptor: return out if into is None else memoryview(out)[:w] # type: ignore def final( - self, into: Buffer | None = None, maclen: int = ABYTES_MIN + self, into: Buffer | None = None, maclen: int = MACBYTES ) -> bytearray | memoryview: """Finalize encryption, writing any remaining bytes and the tag. @@ -692,7 +694,7 @@ class Decryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data used during encryption (optional). Raises: @@ -700,8 +702,8 @@ class Decryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis256x2_state", ALIGNMENT) _lib.aegis256x2_state_init( st, @@ -796,10 +798,9 @@ def new_mac_state(): __all__ = [ # constants "KEYBYTES", - "NPUBBYTES", - "ABYTES_MIN", - "ABYTES_MAX", - "TAILBYTES_MAX", + "NONCEBYTES", + "MACBYTES", + "MACBYTES_LONG", "ALIGNMENT", "RATE", # utility functions diff --git a/pyaegis/aegis256x4.py b/pyaegis/aegis256x4.py index e0ab216..4ba072a 100644 --- a/pyaegis/aegis256x4.py +++ b/pyaegis/aegis256x4.py @@ -9,24 +9,26 @@ from ._loader import ffi from ._loader import lib as _lib from .util import Buffer, new_aligned_struct, nonce_increment, wipe -# Constants exposed as functions in C; mirror them as integers at module import time -KEYBYTES = _lib.aegis256x4_keybytes() -NPUBBYTES = _lib.aegis256x4_npubbytes() -ABYTES_MIN = _lib.aegis256x4_abytes_min() -ABYTES_MAX = _lib.aegis256x4_abytes_max() -TAILBYTES_MAX = _lib.aegis256x4_tailbytes_max() -ALIGNMENT = 64 -RATE = 64 +KEYBYTES = 32 #: Key size in bytes (varies by algorithm) +NONCEBYTES = 32 #: Nonce size in bytes (varies by algorithm) +MACBYTES = 16 #: Normal MAC size (always 16) +MACBYTES_LONG = 32 #: Long MAC size (always 32) +ALIGNMENT = 64 #: Required alignment for internal structures +RATE = 64 #: Byte chunk size in internal processing def random_key() -> bytearray: - """Generate a random key using cryptographically secure random bytes.""" + """ + Generate a random key using cryptographically secure random bytes. + + It is recommended to wipe() the key after no longer needed to keep it secret. + """ return bytearray(secrets.token_bytes(KEYBYTES)) def random_nonce() -> bytearray: """Generate a random nonce using cryptographically secure random bytes.""" - return bytearray(secrets.token_bytes(NPUBBYTES)) + return bytearray(secrets.token_bytes(NONCEBYTES)) def _ptr(buf): @@ -39,7 +41,7 @@ def encrypt_detached( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, ct_into: Buffer | None = None, mac_into: Buffer | None = None, ) -> tuple[bytearray | memoryview, bytearray | memoryview]: @@ -47,7 +49,7 @@ def encrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -65,8 +67,8 @@ def encrypt_detached( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if ct_into is None: c = bytearray(len(message)) @@ -115,7 +117,7 @@ def decrypt_detached( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -130,8 +132,8 @@ def decrypt_detached( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") maclen = len(mac) if maclen not in (16, 32): raise TypeError("mac length must be 16 or 32") @@ -164,14 +166,14 @@ def encrypt( message: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Encrypt message with associated data, returning ciphertext with appended MAC. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -188,8 +190,8 @@ def encrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message) + maclen) else: @@ -220,14 +222,14 @@ def decrypt( ct: Buffer, ad: Buffer | None = None, *, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Decrypt ciphertext with appended MAC and associated data. Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -244,8 +246,8 @@ def decrypt( raise TypeError("maclen must be 16 or 32") if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if len(ct) < maclen: raise TypeError("ciphertext too short for tag") expected_out = len(ct) - maclen @@ -284,7 +286,7 @@ def stream( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). + nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None). length: Number of bytes to generate (required if into is None). into: Buffer to write stream into (default: bytearray created). @@ -296,8 +298,8 @@ def stream( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if nonce is not None and len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if nonce is not None and len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: if length is None: raise TypeError("provide either into or length") @@ -326,7 +328,7 @@ def encrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). @@ -338,8 +340,8 @@ def encrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(message)) else: @@ -367,7 +369,7 @@ def decrypt_unauthenticated( Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). @@ -379,8 +381,8 @@ def decrypt_unauthenticated( """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") if into is None: out = bytearray(len(ct)) else: @@ -402,14 +404,14 @@ def mac( key: Buffer, nonce: Buffer, data: Buffer, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: f"""Compute a MAC for the given data in one shot. Args: key: Key ({KEYBYTES=}) - nonce: Nonce ({NPUBBYTES=}) + nonce: Nonce ({NONCEBYTES=}) data: Data to MAC maclen: MAC length (16 or 32, default 16) into: Buffer to write MAC into (default: bytearray created) @@ -445,7 +447,7 @@ class Mac: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). Raises: TypeError: If key or nonce lengths are invalid. @@ -459,8 +461,8 @@ class Mac: # Normal init path if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") _lib.aegis256x4_mac_init(self._st, _ptr(key), _ptr(nonce)) def __deepcopy__(self) -> "Mac": @@ -490,7 +492,7 @@ class Mac: def final( self, - maclen: int = ABYTES_MIN, + maclen: int = MACBYTES, into: Buffer | None = None, ) -> bytearray | memoryview: """Finalize and return the MAC tag. @@ -558,7 +560,7 @@ class Encryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data to bind to the encryption (optional). Raises: @@ -566,8 +568,8 @@ class Encryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis256x4_state", ALIGNMENT) _lib.aegis256x4_state_init( st, @@ -639,7 +641,7 @@ class Encryptor: return out if into is None else memoryview(out)[:w] # type: ignore def final( - self, into: Buffer | None = None, maclen: int = ABYTES_MIN + self, into: Buffer | None = None, maclen: int = MACBYTES ) -> bytearray | memoryview: """Finalize encryption, writing any remaining bytes and the tag. @@ -692,7 +694,7 @@ class Decryptor: Args: key: Key ({KEYBYTES=}). - nonce: Nonce ({NPUBBYTES=}). + nonce: Nonce ({NONCEBYTES=}). ad: Associated data used during encryption (optional). Raises: @@ -700,8 +702,8 @@ class Decryptor: """ if len(key) != KEYBYTES: raise TypeError(f"key length must be {KEYBYTES}") - if len(nonce) != NPUBBYTES: - raise TypeError(f"nonce length must be {NPUBBYTES}") + if len(nonce) != NONCEBYTES: + raise TypeError(f"nonce length must be {NONCEBYTES}") st, owner = new_aligned_struct("aegis256x4_state", ALIGNMENT) _lib.aegis256x4_state_init( st, @@ -796,10 +798,9 @@ def new_mac_state(): __all__ = [ # constants "KEYBYTES", - "NPUBBYTES", - "ABYTES_MIN", - "ABYTES_MAX", - "TAILBYTES_MAX", + "NONCEBYTES", + "MACBYTES", + "MACBYTES_LONG", "ALIGNMENT", "RATE", # utility functions diff --git a/pyaegis/benchmark.py b/pyaegis/benchmark.py index a4797ef..24d38cf 100644 --- a/pyaegis/benchmark.py +++ b/pyaegis/benchmark.py @@ -3,7 +3,7 @@ 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 = ABYTES_MIN) +- AEGIS encrypt (attached tag, maclen = MACBYTES) - AEGIS MAC (clone state pattern) Output format and throughput units mirror the Zig benchmark (Mb/s). @@ -31,12 +31,12 @@ def _random_bytes(n: int) -> bytes: return os.urandom(n) -def bench_encrypt(alg_name: str, a) -> None: - key = _random_bytes(a.KEYBYTES) - nonce = _random_bytes(a.NPUBBYTES) +def bench_encrypt(alg_name: str, 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 = a.ABYTES_MIN + maclen = ciph.MACBYTES buf = bytearray(MSG_LEN + maclen) # Initialize buffer with random data buf[:] = _random_bytes(len(buf)) @@ -45,7 +45,7 @@ def bench_encrypt(alg_name: str, a) -> None: t0 = time.perf_counter() for _ in range(ITERATIONS): - a.encrypt(key, nonce, mview, None, maclen=maclen, into=buf) + ciph.encrypt(key, nonce, mview, None, maclen=maclen, into=buf) t1 = time.perf_counter() # Prevent any unrealistic optimization assumptions @@ -59,21 +59,21 @@ def bench_encrypt(alg_name: str, a) -> None: print(f"{alg_name}\t{throughput_mbps:10.2f} Mb/s") -def bench_mac(alg_name: str, a) -> None: - key = _random_bytes(a.KEYBYTES) - nonce = _random_bytes(a.NPUBBYTES) +def bench_mac(alg_name: str, ciph) -> None: + key = _random_bytes(ciph.KEYBYTES) + nonce = _random_bytes(ciph.NONCEBYTES) buf = bytearray(MSG_LEN) buf[:] = _random_bytes(len(buf)) - mac0 = a.Mac(key, nonce) - mac_out = bytearray(a.ABYTES_MAX) + mac0 = ciph.Mac(key, nonce) + mac_out = bytearray(ciph.MACBYTES_LONG) t0 = time.perf_counter() for _ in range(ITERATIONS): mac = mac0.clone() mac.update(buf) - mac.final(maclen=a.ABYTES_MAX, into=mac_out) + mac.final(maclen=ciph.MACBYTES_LONG, into=mac_out) t1 = time.perf_counter() _ = mac_out[0] diff --git a/tools/generate.py b/tools/generate.py index 24f3e24..ce52fa4 100644 --- a/tools/generate.py +++ b/tools/generate.py @@ -103,21 +103,44 @@ def generate_cdef(include_dir: pathlib.Path) -> str: return "\n".join(lines) -def extract_constants(common_h_path: pathlib.Path) -> Tuple[int, int]: - content = common_h_path.read_text(encoding="utf-8") +def extract_constants( + common_h_path: pathlib.Path, header_path: pathlib.Path +) -> Dict[str, int]: + """Extract constants from common.h (ALIGNMENT, RATE) and main header (KEYBYTES, NPUBBYTES, ABYTES_*).""" + constants = {} - align_match = re.search(r"^\s*#define\s+ALIGNMENT\s+(\d+)", content, re.MULTILINE) - rate_match = re.search(r"^\s*#define\s+RATE\s+(\d+)", content, re.MULTILINE) + # Extract from common.h + common_content = common_h_path.read_text(encoding="utf-8") + align_match = re.search( + r"^\s*#define\s+ALIGNMENT\s+(\d+)", common_content, re.MULTILINE + ) + rate_match = re.search(r"^\s*#define\s+RATE\s+(\d+)", common_content, re.MULTILINE) if not align_match or not rate_match: raise ValueError( f"Could not extract ALIGNMENT and/or RATE from {common_h_path}" ) - return int(align_match.group(1)), int(rate_match.group(1)) + constants["ALIGNMENT"] = int(align_match.group(1)) + constants["RATE"] = int(rate_match.group(1)) + + # Extract from main header + header_content = header_path.read_text(encoding="utf-8") + variant = header_path.stem # e.g., "aegis256x4" + + for const_name in ["KEYBYTES", "NPUBBYTES", "ABYTES_MIN", "ABYTES_MAX"]: + pattern = rf"^\s*#define\s+{variant}_{const_name}\s+(\d+)" + match = re.search(pattern, header_content, re.MULTILINE) + if not match: + raise ValueError(f"Could not extract {const_name} from {header_path}") + constants[const_name] = int(match.group(1)) + + return constants -def extract_all_constants(libaegis_src_dir: pathlib.Path) -> Dict[str, Tuple[int, int]]: +def extract_all_constants( + libaegis_src_dir: pathlib.Path, include_dir: pathlib.Path +) -> Dict[str, Dict[str, int]]: variants = [ "aegis128l", "aegis128x2", @@ -130,13 +153,18 @@ def extract_all_constants(libaegis_src_dir: pathlib.Path) -> Dict[str, Tuple[int for variant in variants: common_h = libaegis_src_dir / variant / f"{variant}_common.h" + header_h = include_dir / f"{variant}.h" + if not common_h.exists(): print(f"Warning: {common_h} not found, skipping {variant}", file=sys.stderr) continue + if not header_h.exists(): + print(f"Warning: {header_h} not found, skipping {variant}", file=sys.stderr) + continue + try: - alignment, rate = extract_constants(common_h) - constants[variant] = (alignment, rate) + constants[variant] = extract_constants(common_h, header_h) except Exception as e: print(f"Error extracting constants from {variant}: {e}", file=sys.stderr) @@ -155,7 +183,8 @@ def algo_label(name: str) -> str: return "AEGIS-" + name[5:].upper() -def generate_variant(template_src: str, variant: str, alignment: int, rate: int) -> str: +def generate_variant(template_src: str, variant: str, constants: Dict[str, int]) -> str: + """Generate a variant module from the template with substituted constants.""" s = template_src.replace("aegis256x4", variant).replace( "AEGIS-256X4", algo_label(variant) ) @@ -165,15 +194,34 @@ def generate_variant(template_src: str, variant: str, alignment: int, rate: int) "# All modules are generated from aegis256x4.py by tools/generate.py!", s, ) - s = replace_constant(ALIGNMENT_RE, s, alignment) - s = replace_constant(RATE_RE, s, rate) + s = replace_constant(ALIGNMENT_RE, s, constants["ALIGNMENT"]) + s = replace_constant(RATE_RE, s, constants["RATE"]) + + # Replace the constant assignments + s = re.sub(r"KEYBYTES = \d+", f"KEYBYTES = {constants['KEYBYTES']}", s) + s = re.sub( + r"NONCEBYTES = \d+", + f"NONCEBYTES = {constants['NPUBBYTES']}", + s, + ) + s = re.sub( + r"MACBYTES = \d+", + f"MACBYTES = {constants['ABYTES_MIN']}", + s, + ) + s = re.sub( + r"MACBYTES_LONG = \d+", + f"MACBYTES_LONG = {constants['ABYTES_MAX']}", + s, + ) + return s def generate_python_modules( template_path: pathlib.Path, output_dir: pathlib.Path, - constants: Dict[str, Tuple[int, int]], + constants: Dict[str, Dict[str, int]], ) -> Tuple[list[pathlib.Path], list[pathlib.Path]]: if not template_path.exists(): raise FileNotFoundError(f"Template not found: {template_path}") @@ -184,13 +232,37 @@ def generate_python_modules( updated = [] unchanged = [] - for variant, (alignment, rate) in constants.items(): + for variant, const_dict in constants.items(): dst = output_dir / f"{variant}.py" if variant == "aegis256x4": - new_content = replace_constant(ALIGNMENT_RE, template_src, alignment) - new_content = replace_constant(RATE_RE, new_content, rate) + # Update template in place with its own constants + new_content = replace_constant( + ALIGNMENT_RE, template_src, const_dict["ALIGNMENT"] + ) + new_content = replace_constant(RATE_RE, new_content, const_dict["RATE"]) + # Replace the constant assignments for the template itself + new_content = re.sub( + r"KEYBYTES = \d+", + f"KEYBYTES = {const_dict['KEYBYTES']}", + new_content, + ) + new_content = re.sub( + r"NONCEBYTES = \d+", + f"NONCEBYTES = {const_dict['NPUBBYTES']}", + new_content, + ) + new_content = re.sub( + r"MACBYTES = \d+", + f"MACBYTES = {const_dict['ABYTES_MIN']}", + new_content, + ) + new_content = re.sub( + r"MACBYTES_LONG = \d+", + f"MACBYTES_LONG = {const_dict['ABYTES_MAX']}", + new_content, + ) else: - new_content = generate_variant(template_src, variant, alignment, rate) + new_content = generate_variant(template_src, variant, const_dict) if dst.exists() and dst.read_text(encoding="utf-8") == new_content: unchanged.append(dst) @@ -216,7 +288,7 @@ def main() -> int: return 1 print("Step 1: Extracting constants from C sources...", file=sys.stderr) - constants = extract_all_constants(libaegis_src_dir) + constants = extract_all_constants(libaegis_src_dir, include_dir) if not constants: print("Error: No constants extracted", file=sys.stderr) return 1 @@ -243,7 +315,9 @@ def main() -> int: if unchanged: print( " - No changes to", - f"{len(unchanged)} modules" if len(unchanged) > 1 else unchanged[0], + f"{len(unchanged)} modules" + if len(unchanged) > 1 + else unchanged[0].name, file=sys.stderr, ) except Exception as e: