diff --git a/aegis/aegis128l.py b/aegis/aegis128l.py index a98bad1..8879cad 100644 --- a/aegis/aegis128l.py +++ b/aegis/aegis128l.py @@ -18,6 +18,7 @@ NPUBBYTES = _lib.aegis128l_npubbytes() ABYTES_MIN = _lib.aegis128l_abytes_min() ABYTES_MAX = _lib.aegis128l_abytes_max() TAILBYTES_MAX = _lib.aegis128l_tailbytes_max() +ALIGNMENT = 32 def _ptr(buf): @@ -33,10 +34,11 @@ def _ptr(buf): def encrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, ct_into: Buffer | None = None, mac_into: Buffer | None = None, @@ -44,8 +46,8 @@ def encrypt_detached( """Encrypt message with associated data, returning ciphertext and MAC separately. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -99,18 +101,19 @@ def encrypt_detached( def decrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, mac: Buffer, ad: Buffer | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with detached MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -158,18 +161,19 @@ def decrypt_detached( def encrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Encrypt message with associated data, returning ciphertext with appended MAC. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -216,18 +220,19 @@ def encrypt( def decrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with appended MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -274,16 +279,17 @@ def decrypt( def stream( - nonce: Buffer | None, key: Buffer, + nonce: Buffer | None, length: int | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Generate a stream of pseudorandom bytes. Args: - nonce: Nonce (32 bytes, uses zeroes for nonce if None). key: Key (32 bytes). + nonce: Nonce (32 bytes, 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). @@ -314,17 +320,18 @@ def stream( def encrypt_unauthenticated( - message: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + message: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Encrypt message without authentication (for testing/debugging). Args: - message: The plaintext message to encrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). Returns: @@ -356,17 +363,18 @@ def encrypt_unauthenticated( def decrypt_unauthenticated( - ct: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + ct: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext without authentication (for testing/debugging). Args: - ct: The ciphertext to decrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). Returns: @@ -399,56 +407,56 @@ def decrypt_unauthenticated( # This is missing from C API but convenient to have here def mac( - data: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + data: Buffer, maclen: int = ABYTES_MIN, ) -> memoryview: """Compute a MAC for the given data in one shot. Args: - data: Data to MAC - nonce: Nonce (32 bytes) key: Key (32 bytes) + nonce: Nonce (32 bytes) + data: Data to MAC maclen: MAC length (16 or 32, default 16) Returns: MAC bytes """ - mac_state = Mac(nonce, key) + mac_state = Mac(key, nonce) mac_state.update(data) return mac_state.final(maclen) class Mac: - """AEGIS-256X4 MAC state wrapper. + """AEGIS-128L MAC state wrapper. Usage: - mac = Mac(nonce, key) + mac = Mac(key, nonce) mac.update(data) tag = mac.final() # defaults to 16-byte MAC # or verify: - mac2 = Mac(nonce, key); mac2.update(data); mac2.verify(tag) + mac2 = Mac(key, nonce); mac2.update(data); mac2.verify(tag) """ __slots__ = ("_st", "_nonce", "_key") def __init__( self, - nonce: Buffer, key: Buffer, + nonce: Buffer, _other=None, ) -> None: """Initialize a MAC state with a nonce and key. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). Raises: TypeError: If key or nonce lengths are invalid. """ - raw = alloc_aligned(ffi.sizeof("aegis128l_mac_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis128l_mac_state"), ALIGNMENT) st = ffi.cast("aegis128l_mac_state *", raw) self._st = ffi.gc(st, libc.free) if _other is not None: @@ -553,12 +561,12 @@ class Encryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental encryptor. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data to bind to the encryption (optional). Raises: @@ -570,7 +578,7 @@ class Encryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis128l_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis128l_state"), ALIGNMENT) st = ffi.cast("aegis128l_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis128l_state_init( @@ -704,12 +712,12 @@ class Decryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental decryptor for detached tags. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data used during encryption (optional). Raises: @@ -721,7 +729,7 @@ class Decryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis128l_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis128l_state"), ALIGNMENT) st = ffi.cast("aegis128l_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis128l_state_init( @@ -812,15 +820,15 @@ def new_state(): The returned object is an ffi cdata pointer with automatic finalizer. """ - # Allocate with 64-byte alignment using libc.posix_memalign - raw = alloc_aligned(ffi.sizeof("aegis128l_state"), 32) + # Allocate with required alignment using libc.posix_memalign + raw = alloc_aligned(ffi.sizeof("aegis128l_state"), ALIGNMENT) ptr = ffi.cast("aegis128l_state *", raw) return ffi.gc(ptr, libc.free) def new_mac_state(): """Allocate and return a new aegis128l_mac_state* with proper alignment.""" - raw = alloc_aligned(ffi.sizeof("aegis128l_mac_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis128l_mac_state"), ALIGNMENT) ptr = ffi.cast("aegis128l_mac_state *", raw) return ffi.gc(ptr, libc.free) @@ -832,6 +840,7 @@ __all__ = [ "ABYTES_MIN", "ABYTES_MAX", "TAILBYTES_MAX", + "ALIGNMENT", # one-shot functions "encrypt_detached", "decrypt_detached", diff --git a/aegis/aegis128x2.py b/aegis/aegis128x2.py index 9bf91af..17b4ade 100644 --- a/aegis/aegis128x2.py +++ b/aegis/aegis128x2.py @@ -18,6 +18,7 @@ NPUBBYTES = _lib.aegis128x2_npubbytes() ABYTES_MIN = _lib.aegis128x2_abytes_min() ABYTES_MAX = _lib.aegis128x2_abytes_max() TAILBYTES_MAX = _lib.aegis128x2_tailbytes_max() +ALIGNMENT = 64 def _ptr(buf): @@ -33,10 +34,11 @@ def _ptr(buf): def encrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, ct_into: Buffer | None = None, mac_into: Buffer | None = None, @@ -44,8 +46,8 @@ def encrypt_detached( """Encrypt message with associated data, returning ciphertext and MAC separately. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -99,18 +101,19 @@ def encrypt_detached( def decrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, mac: Buffer, ad: Buffer | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with detached MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -158,18 +161,19 @@ def decrypt_detached( def encrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Encrypt message with associated data, returning ciphertext with appended MAC. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -216,18 +220,19 @@ def encrypt( def decrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with appended MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -274,16 +279,17 @@ def decrypt( def stream( - nonce: Buffer | None, key: Buffer, + nonce: Buffer | None, length: int | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Generate a stream of pseudorandom bytes. Args: - nonce: Nonce (32 bytes, uses zeroes for nonce if None). key: Key (32 bytes). + nonce: Nonce (32 bytes, 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). @@ -314,17 +320,18 @@ def stream( def encrypt_unauthenticated( - message: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + message: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Encrypt message without authentication (for testing/debugging). Args: - message: The plaintext message to encrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). Returns: @@ -356,17 +363,18 @@ def encrypt_unauthenticated( def decrypt_unauthenticated( - ct: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + ct: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext without authentication (for testing/debugging). Args: - ct: The ciphertext to decrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). Returns: @@ -399,56 +407,56 @@ def decrypt_unauthenticated( # This is missing from C API but convenient to have here def mac( - data: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + data: Buffer, maclen: int = ABYTES_MIN, ) -> memoryview: """Compute a MAC for the given data in one shot. Args: - data: Data to MAC - nonce: Nonce (32 bytes) key: Key (32 bytes) + nonce: Nonce (32 bytes) + data: Data to MAC maclen: MAC length (16 or 32, default 16) Returns: MAC bytes """ - mac_state = Mac(nonce, key) + mac_state = Mac(key, nonce) mac_state.update(data) return mac_state.final(maclen) class Mac: - """AEGIS-256X4 MAC state wrapper. + """AEGIS-128X2 MAC state wrapper. Usage: - mac = Mac(nonce, key) + mac = Mac(key, nonce) mac.update(data) tag = mac.final() # defaults to 16-byte MAC # or verify: - mac2 = Mac(nonce, key); mac2.update(data); mac2.verify(tag) + mac2 = Mac(key, nonce); mac2.update(data); mac2.verify(tag) """ __slots__ = ("_st", "_nonce", "_key") def __init__( self, - nonce: Buffer, key: Buffer, + nonce: Buffer, _other=None, ) -> None: """Initialize a MAC state with a nonce and key. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). Raises: TypeError: If key or nonce lengths are invalid. """ - raw = alloc_aligned(ffi.sizeof("aegis128x2_mac_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x2_mac_state"), ALIGNMENT) st = ffi.cast("aegis128x2_mac_state *", raw) self._st = ffi.gc(st, libc.free) if _other is not None: @@ -553,12 +561,12 @@ class Encryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental encryptor. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data to bind to the encryption (optional). Raises: @@ -570,7 +578,7 @@ class Encryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis128x2_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x2_state"), ALIGNMENT) st = ffi.cast("aegis128x2_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis128x2_state_init( @@ -704,12 +712,12 @@ class Decryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental decryptor for detached tags. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data used during encryption (optional). Raises: @@ -721,7 +729,7 @@ class Decryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis128x2_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x2_state"), ALIGNMENT) st = ffi.cast("aegis128x2_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis128x2_state_init( @@ -812,15 +820,15 @@ def new_state(): The returned object is an ffi cdata pointer with automatic finalizer. """ - # Allocate with 64-byte alignment using libc.posix_memalign - raw = alloc_aligned(ffi.sizeof("aegis128x2_state"), 64) + # Allocate with required alignment using libc.posix_memalign + raw = alloc_aligned(ffi.sizeof("aegis128x2_state"), ALIGNMENT) ptr = ffi.cast("aegis128x2_state *", raw) return ffi.gc(ptr, libc.free) def new_mac_state(): """Allocate and return a new aegis128x2_mac_state* with proper alignment.""" - raw = alloc_aligned(ffi.sizeof("aegis128x2_mac_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x2_mac_state"), ALIGNMENT) ptr = ffi.cast("aegis128x2_mac_state *", raw) return ffi.gc(ptr, libc.free) @@ -832,6 +840,7 @@ __all__ = [ "ABYTES_MIN", "ABYTES_MAX", "TAILBYTES_MAX", + "ALIGNMENT", # one-shot functions "encrypt_detached", "decrypt_detached", diff --git a/aegis/aegis128x4.py b/aegis/aegis128x4.py index b305389..b5d320d 100644 --- a/aegis/aegis128x4.py +++ b/aegis/aegis128x4.py @@ -18,6 +18,7 @@ NPUBBYTES = _lib.aegis128x4_npubbytes() ABYTES_MIN = _lib.aegis128x4_abytes_min() ABYTES_MAX = _lib.aegis128x4_abytes_max() TAILBYTES_MAX = _lib.aegis128x4_tailbytes_max() +ALIGNMENT = 64 def _ptr(buf): @@ -33,10 +34,11 @@ def _ptr(buf): def encrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, ct_into: Buffer | None = None, mac_into: Buffer | None = None, @@ -44,8 +46,8 @@ def encrypt_detached( """Encrypt message with associated data, returning ciphertext and MAC separately. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -99,18 +101,19 @@ def encrypt_detached( def decrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, mac: Buffer, ad: Buffer | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with detached MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -158,18 +161,19 @@ def decrypt_detached( def encrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Encrypt message with associated data, returning ciphertext with appended MAC. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -216,18 +220,19 @@ def encrypt( def decrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with appended MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -274,16 +279,17 @@ def decrypt( def stream( - nonce: Buffer | None, key: Buffer, + nonce: Buffer | None, length: int | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Generate a stream of pseudorandom bytes. Args: - nonce: Nonce (32 bytes, uses zeroes for nonce if None). key: Key (32 bytes). + nonce: Nonce (32 bytes, 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). @@ -314,17 +320,18 @@ def stream( def encrypt_unauthenticated( - message: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + message: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Encrypt message without authentication (for testing/debugging). Args: - message: The plaintext message to encrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). Returns: @@ -356,17 +363,18 @@ def encrypt_unauthenticated( def decrypt_unauthenticated( - ct: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + ct: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext without authentication (for testing/debugging). Args: - ct: The ciphertext to decrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). Returns: @@ -399,56 +407,56 @@ def decrypt_unauthenticated( # This is missing from C API but convenient to have here def mac( - data: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + data: Buffer, maclen: int = ABYTES_MIN, ) -> memoryview: """Compute a MAC for the given data in one shot. Args: - data: Data to MAC - nonce: Nonce (32 bytes) key: Key (32 bytes) + nonce: Nonce (32 bytes) + data: Data to MAC maclen: MAC length (16 or 32, default 16) Returns: MAC bytes """ - mac_state = Mac(nonce, key) + mac_state = Mac(key, nonce) mac_state.update(data) return mac_state.final(maclen) class Mac: - """AEGIS-256X4 MAC state wrapper. + """AEGIS-128X4 MAC state wrapper. Usage: - mac = Mac(nonce, key) + mac = Mac(key, nonce) mac.update(data) tag = mac.final() # defaults to 16-byte MAC # or verify: - mac2 = Mac(nonce, key); mac2.update(data); mac2.verify(tag) + mac2 = Mac(key, nonce); mac2.update(data); mac2.verify(tag) """ __slots__ = ("_st", "_nonce", "_key") def __init__( self, - nonce: Buffer, key: Buffer, + nonce: Buffer, _other=None, ) -> None: """Initialize a MAC state with a nonce and key. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). Raises: TypeError: If key or nonce lengths are invalid. """ - raw = alloc_aligned(ffi.sizeof("aegis128x4_mac_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x4_mac_state"), ALIGNMENT) st = ffi.cast("aegis128x4_mac_state *", raw) self._st = ffi.gc(st, libc.free) if _other is not None: @@ -553,12 +561,12 @@ class Encryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental encryptor. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data to bind to the encryption (optional). Raises: @@ -570,7 +578,7 @@ class Encryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis128x4_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x4_state"), ALIGNMENT) st = ffi.cast("aegis128x4_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis128x4_state_init( @@ -704,12 +712,12 @@ class Decryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental decryptor for detached tags. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data used during encryption (optional). Raises: @@ -721,7 +729,7 @@ class Decryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis128x4_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x4_state"), ALIGNMENT) st = ffi.cast("aegis128x4_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis128x4_state_init( @@ -812,15 +820,15 @@ def new_state(): The returned object is an ffi cdata pointer with automatic finalizer. """ - # Allocate with 64-byte alignment using libc.posix_memalign - raw = alloc_aligned(ffi.sizeof("aegis128x4_state"), 64) + # Allocate with required alignment using libc.posix_memalign + raw = alloc_aligned(ffi.sizeof("aegis128x4_state"), ALIGNMENT) ptr = ffi.cast("aegis128x4_state *", raw) return ffi.gc(ptr, libc.free) def new_mac_state(): """Allocate and return a new aegis128x4_mac_state* with proper alignment.""" - raw = alloc_aligned(ffi.sizeof("aegis128x4_mac_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis128x4_mac_state"), ALIGNMENT) ptr = ffi.cast("aegis128x4_mac_state *", raw) return ffi.gc(ptr, libc.free) @@ -832,6 +840,7 @@ __all__ = [ "ABYTES_MIN", "ABYTES_MAX", "TAILBYTES_MAX", + "ALIGNMENT", # one-shot functions "encrypt_detached", "decrypt_detached", diff --git a/aegis/aegis256.py b/aegis/aegis256.py index c67d7d0..7ce31a8 100644 --- a/aegis/aegis256.py +++ b/aegis/aegis256.py @@ -18,6 +18,7 @@ NPUBBYTES = _lib.aegis256_npubbytes() ABYTES_MIN = _lib.aegis256_abytes_min() ABYTES_MAX = _lib.aegis256_abytes_max() TAILBYTES_MAX = _lib.aegis256_tailbytes_max() +ALIGNMENT = 16 def _ptr(buf): @@ -33,10 +34,11 @@ def _ptr(buf): def encrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, ct_into: Buffer | None = None, mac_into: Buffer | None = None, @@ -44,8 +46,8 @@ def encrypt_detached( """Encrypt message with associated data, returning ciphertext and MAC separately. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -99,18 +101,19 @@ def encrypt_detached( def decrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, mac: Buffer, ad: Buffer | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with detached MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -158,18 +161,19 @@ def decrypt_detached( def encrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Encrypt message with associated data, returning ciphertext with appended MAC. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -216,18 +220,19 @@ def encrypt( def decrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with appended MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -274,16 +279,17 @@ def decrypt( def stream( - nonce: Buffer | None, key: Buffer, + nonce: Buffer | None, length: int | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Generate a stream of pseudorandom bytes. Args: - nonce: Nonce (32 bytes, uses zeroes for nonce if None). key: Key (32 bytes). + nonce: Nonce (32 bytes, 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). @@ -314,17 +320,18 @@ def stream( def encrypt_unauthenticated( - message: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + message: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Encrypt message without authentication (for testing/debugging). Args: - message: The plaintext message to encrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). Returns: @@ -356,17 +363,18 @@ def encrypt_unauthenticated( def decrypt_unauthenticated( - ct: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + ct: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext without authentication (for testing/debugging). Args: - ct: The ciphertext to decrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). Returns: @@ -399,56 +407,56 @@ def decrypt_unauthenticated( # This is missing from C API but convenient to have here def mac( - data: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + data: Buffer, maclen: int = ABYTES_MIN, ) -> memoryview: """Compute a MAC for the given data in one shot. Args: - data: Data to MAC - nonce: Nonce (32 bytes) key: Key (32 bytes) + nonce: Nonce (32 bytes) + data: Data to MAC maclen: MAC length (16 or 32, default 16) Returns: MAC bytes """ - mac_state = Mac(nonce, key) + mac_state = Mac(key, nonce) mac_state.update(data) return mac_state.final(maclen) class Mac: - """AEGIS-256X4 MAC state wrapper. + """AEGIS-256 MAC state wrapper. Usage: - mac = Mac(nonce, key) + mac = Mac(key, nonce) mac.update(data) tag = mac.final() # defaults to 16-byte MAC # or verify: - mac2 = Mac(nonce, key); mac2.update(data); mac2.verify(tag) + mac2 = Mac(key, nonce); mac2.update(data); mac2.verify(tag) """ __slots__ = ("_st", "_nonce", "_key") def __init__( self, - nonce: Buffer, key: Buffer, + nonce: Buffer, _other=None, ) -> None: """Initialize a MAC state with a nonce and key. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). Raises: TypeError: If key or nonce lengths are invalid. """ - raw = alloc_aligned(ffi.sizeof("aegis256_mac_state"), 16) + raw = alloc_aligned(ffi.sizeof("aegis256_mac_state"), ALIGNMENT) st = ffi.cast("aegis256_mac_state *", raw) self._st = ffi.gc(st, libc.free) if _other is not None: @@ -553,12 +561,12 @@ class Encryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental encryptor. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data to bind to the encryption (optional). Raises: @@ -570,7 +578,7 @@ class Encryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis256_state"), 16) + raw = alloc_aligned(ffi.sizeof("aegis256_state"), ALIGNMENT) st = ffi.cast("aegis256_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis256_state_init( @@ -704,12 +712,12 @@ class Decryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental decryptor for detached tags. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data used during encryption (optional). Raises: @@ -721,7 +729,7 @@ class Decryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis256_state"), 16) + raw = alloc_aligned(ffi.sizeof("aegis256_state"), ALIGNMENT) st = ffi.cast("aegis256_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis256_state_init( @@ -812,15 +820,15 @@ def new_state(): The returned object is an ffi cdata pointer with automatic finalizer. """ - # Allocate with 64-byte alignment using libc.posix_memalign - raw = alloc_aligned(ffi.sizeof("aegis256_state"), 16) + # Allocate with required alignment using libc.posix_memalign + raw = alloc_aligned(ffi.sizeof("aegis256_state"), ALIGNMENT) ptr = ffi.cast("aegis256_state *", raw) return ffi.gc(ptr, libc.free) def new_mac_state(): """Allocate and return a new aegis256_mac_state* with proper alignment.""" - raw = alloc_aligned(ffi.sizeof("aegis256_mac_state"), 16) + raw = alloc_aligned(ffi.sizeof("aegis256_mac_state"), ALIGNMENT) ptr = ffi.cast("aegis256_mac_state *", raw) return ffi.gc(ptr, libc.free) @@ -832,6 +840,7 @@ __all__ = [ "ABYTES_MIN", "ABYTES_MAX", "TAILBYTES_MAX", + "ALIGNMENT", # one-shot functions "encrypt_detached", "decrypt_detached", diff --git a/aegis/aegis256x2.py b/aegis/aegis256x2.py index 646e0ef..94f78ac 100644 --- a/aegis/aegis256x2.py +++ b/aegis/aegis256x2.py @@ -18,6 +18,7 @@ NPUBBYTES = _lib.aegis256x2_npubbytes() ABYTES_MIN = _lib.aegis256x2_abytes_min() ABYTES_MAX = _lib.aegis256x2_abytes_max() TAILBYTES_MAX = _lib.aegis256x2_tailbytes_max() +ALIGNMENT = 32 def _ptr(buf): @@ -33,10 +34,11 @@ def _ptr(buf): def encrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, ct_into: Buffer | None = None, mac_into: Buffer | None = None, @@ -44,8 +46,8 @@ def encrypt_detached( """Encrypt message with associated data, returning ciphertext and MAC separately. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -99,18 +101,19 @@ def encrypt_detached( def decrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, mac: Buffer, ad: Buffer | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with detached MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -158,18 +161,19 @@ def decrypt_detached( def encrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Encrypt message with associated data, returning ciphertext with appended MAC. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -216,18 +220,19 @@ def encrypt( def decrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with appended MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -274,16 +279,17 @@ def decrypt( def stream( - nonce: Buffer | None, key: Buffer, + nonce: Buffer | None, length: int | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Generate a stream of pseudorandom bytes. Args: - nonce: Nonce (32 bytes, uses zeroes for nonce if None). key: Key (32 bytes). + nonce: Nonce (32 bytes, 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). @@ -314,17 +320,18 @@ def stream( def encrypt_unauthenticated( - message: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + message: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Encrypt message without authentication (for testing/debugging). Args: - message: The plaintext message to encrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). Returns: @@ -356,17 +363,18 @@ def encrypt_unauthenticated( def decrypt_unauthenticated( - ct: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + ct: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext without authentication (for testing/debugging). Args: - ct: The ciphertext to decrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). Returns: @@ -399,56 +407,56 @@ def decrypt_unauthenticated( # This is missing from C API but convenient to have here def mac( - data: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + data: Buffer, maclen: int = ABYTES_MIN, ) -> memoryview: """Compute a MAC for the given data in one shot. Args: - data: Data to MAC - nonce: Nonce (32 bytes) key: Key (32 bytes) + nonce: Nonce (32 bytes) + data: Data to MAC maclen: MAC length (16 or 32, default 16) Returns: MAC bytes """ - mac_state = Mac(nonce, key) + mac_state = Mac(key, nonce) mac_state.update(data) return mac_state.final(maclen) class Mac: - """AEGIS-256X4 MAC state wrapper. + """AEGIS-256X2 MAC state wrapper. Usage: - mac = Mac(nonce, key) + mac = Mac(key, nonce) mac.update(data) tag = mac.final() # defaults to 16-byte MAC # or verify: - mac2 = Mac(nonce, key); mac2.update(data); mac2.verify(tag) + mac2 = Mac(key, nonce); mac2.update(data); mac2.verify(tag) """ __slots__ = ("_st", "_nonce", "_key") def __init__( self, - nonce: Buffer, key: Buffer, + nonce: Buffer, _other=None, ) -> None: """Initialize a MAC state with a nonce and key. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). Raises: TypeError: If key or nonce lengths are invalid. """ - raw = alloc_aligned(ffi.sizeof("aegis256x2_mac_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis256x2_mac_state"), ALIGNMENT) st = ffi.cast("aegis256x2_mac_state *", raw) self._st = ffi.gc(st, libc.free) if _other is not None: @@ -553,12 +561,12 @@ class Encryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental encryptor. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data to bind to the encryption (optional). Raises: @@ -570,7 +578,7 @@ class Encryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis256x2_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis256x2_state"), ALIGNMENT) st = ffi.cast("aegis256x2_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis256x2_state_init( @@ -704,12 +712,12 @@ class Decryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental decryptor for detached tags. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data used during encryption (optional). Raises: @@ -721,7 +729,7 @@ class Decryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis256x2_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis256x2_state"), ALIGNMENT) st = ffi.cast("aegis256x2_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis256x2_state_init( @@ -812,15 +820,15 @@ def new_state(): The returned object is an ffi cdata pointer with automatic finalizer. """ - # Allocate with 64-byte alignment using libc.posix_memalign - raw = alloc_aligned(ffi.sizeof("aegis256x2_state"), 32) + # Allocate with required alignment using libc.posix_memalign + raw = alloc_aligned(ffi.sizeof("aegis256x2_state"), ALIGNMENT) ptr = ffi.cast("aegis256x2_state *", raw) return ffi.gc(ptr, libc.free) def new_mac_state(): """Allocate and return a new aegis256x2_mac_state* with proper alignment.""" - raw = alloc_aligned(ffi.sizeof("aegis256x2_mac_state"), 32) + raw = alloc_aligned(ffi.sizeof("aegis256x2_mac_state"), ALIGNMENT) ptr = ffi.cast("aegis256x2_mac_state *", raw) return ffi.gc(ptr, libc.free) @@ -832,6 +840,7 @@ __all__ = [ "ABYTES_MIN", "ABYTES_MAX", "TAILBYTES_MAX", + "ALIGNMENT", # one-shot functions "encrypt_detached", "decrypt_detached", diff --git a/aegis/aegis256x4.py b/aegis/aegis256x4.py index 7213ca3..e681868 100644 --- a/aegis/aegis256x4.py +++ b/aegis/aegis256x4.py @@ -18,6 +18,7 @@ NPUBBYTES = _lib.aegis256x4_npubbytes() ABYTES_MIN = _lib.aegis256x4_abytes_min() ABYTES_MAX = _lib.aegis256x4_abytes_max() TAILBYTES_MAX = _lib.aegis256x4_tailbytes_max() +ALIGNMENT = 64 def _ptr(buf): @@ -33,10 +34,11 @@ def _ptr(buf): def encrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, ct_into: Buffer | None = None, mac_into: Buffer | None = None, @@ -44,8 +46,8 @@ def encrypt_detached( """Encrypt message with associated data, returning ciphertext and MAC separately. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -99,18 +101,19 @@ def encrypt_detached( def decrypt_detached( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, mac: Buffer, ad: Buffer | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with detached MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext to decrypt. mac: The MAC to verify. ad: Associated data (optional). @@ -158,18 +161,19 @@ def decrypt_detached( def encrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, message: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Encrypt message with associated data, returning ciphertext with appended MAC. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). message: The plaintext message to encrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -216,18 +220,19 @@ def encrypt( def decrypt( - nonce: Buffer, key: Buffer, + nonce: Buffer, ct: Buffer, ad: Buffer | None = None, + *, maclen: int = ABYTES_MIN, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext with appended MAC and associated data. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ct: The ciphertext with MAC to decrypt. ad: Associated data (optional). maclen: MAC length (16 or 32, default 16). @@ -274,16 +279,17 @@ def decrypt( def stream( - nonce: Buffer | None, key: Buffer, + nonce: Buffer | None, length: int | None = None, + *, into: Buffer | None = None, ) -> memoryview: """Generate a stream of pseudorandom bytes. Args: - nonce: Nonce (32 bytes, uses zeroes for nonce if None). key: Key (32 bytes). + nonce: Nonce (32 bytes, 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). @@ -314,17 +320,18 @@ def stream( def encrypt_unauthenticated( - message: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + message: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Encrypt message without authentication (for testing/debugging). Args: - message: The plaintext message to encrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + message: The plaintext message to encrypt. into: Buffer to write ciphertext into (default: bytearray created). Returns: @@ -356,17 +363,18 @@ def encrypt_unauthenticated( def decrypt_unauthenticated( - ct: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + ct: Buffer, + *, into: Buffer | None = None, ) -> memoryview: """Decrypt ciphertext without authentication (for testing/debugging). Args: - ct: The ciphertext to decrypt. - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). + ct: The ciphertext to decrypt. into: Buffer to write plaintext into (default: bytearray created). Returns: @@ -399,23 +407,23 @@ def decrypt_unauthenticated( # This is missing from C API but convenient to have here def mac( - data: Buffer, - nonce: Buffer, key: Buffer, + nonce: Buffer, + data: Buffer, maclen: int = ABYTES_MIN, ) -> memoryview: """Compute a MAC for the given data in one shot. Args: - data: Data to MAC - nonce: Nonce (32 bytes) key: Key (32 bytes) + nonce: Nonce (32 bytes) + data: Data to MAC maclen: MAC length (16 or 32, default 16) Returns: MAC bytes """ - mac_state = Mac(nonce, key) + mac_state = Mac(key, nonce) mac_state.update(data) return mac_state.final(maclen) @@ -424,31 +432,31 @@ class Mac: """AEGIS-256X4 MAC state wrapper. Usage: - mac = Mac(nonce, key) + mac = Mac(key, nonce) mac.update(data) tag = mac.final() # defaults to 16-byte MAC # or verify: - mac2 = Mac(nonce, key); mac2.update(data); mac2.verify(tag) + mac2 = Mac(key, nonce); mac2.update(data); mac2.verify(tag) """ __slots__ = ("_st", "_nonce", "_key") def __init__( self, - nonce: Buffer, key: Buffer, + nonce: Buffer, _other=None, ) -> None: """Initialize a MAC state with a nonce and key. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). Raises: TypeError: If key or nonce lengths are invalid. """ - raw = alloc_aligned(ffi.sizeof("aegis256x4_mac_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis256x4_mac_state"), ALIGNMENT) st = ffi.cast("aegis256x4_mac_state *", raw) self._st = ffi.gc(st, libc.free) if _other is not None: @@ -553,12 +561,12 @@ class Encryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental encryptor. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data to bind to the encryption (optional). Raises: @@ -570,7 +578,7 @@ class Encryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis256x4_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis256x4_state"), ALIGNMENT) st = ffi.cast("aegis256x4_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis256x4_state_init( @@ -704,12 +712,12 @@ class Decryptor: __slots__ = ("_st",) - def __init__(self, nonce: Buffer, key: Buffer, ad: Buffer | None = None): + def __init__(self, key: Buffer, nonce: Buffer, ad: Buffer | None = None): """Create an incremental decryptor for detached tags. Args: - nonce: Nonce (32 bytes). key: Key (32 bytes). + nonce: Nonce (32 bytes). ad: Associated data used during encryption (optional). Raises: @@ -721,7 +729,7 @@ class Decryptor: raise TypeError(f"key length must be {KEYBYTES}") if nonce.nbytes != NPUBBYTES: raise TypeError(f"nonce length must be {NPUBBYTES}") - raw = alloc_aligned(ffi.sizeof("aegis256x4_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis256x4_state"), ALIGNMENT) st = ffi.cast("aegis256x4_state *", raw) st = ffi.gc(st, libc.free) _lib.aegis256x4_state_init( @@ -812,15 +820,15 @@ def new_state(): The returned object is an ffi cdata pointer with automatic finalizer. """ - # Allocate with 64-byte alignment using libc.posix_memalign - raw = alloc_aligned(ffi.sizeof("aegis256x4_state"), 64) + # Allocate with required alignment using libc.posix_memalign + raw = alloc_aligned(ffi.sizeof("aegis256x4_state"), ALIGNMENT) ptr = ffi.cast("aegis256x4_state *", raw) return ffi.gc(ptr, libc.free) def new_mac_state(): """Allocate and return a new aegis256x4_mac_state* with proper alignment.""" - raw = alloc_aligned(ffi.sizeof("aegis256x4_mac_state"), 64) + raw = alloc_aligned(ffi.sizeof("aegis256x4_mac_state"), ALIGNMENT) ptr = ffi.cast("aegis256x4_mac_state *", raw) return ffi.gc(ptr, libc.free) @@ -832,6 +840,7 @@ __all__ = [ "ABYTES_MIN", "ABYTES_MAX", "TAILBYTES_MAX", + "ALIGNMENT", # one-shot functions "encrypt_detached", "decrypt_detached", diff --git a/examples/aegis256x4_demo.py b/examples/aegis256x4_demo.py index 7c25449..92ba895 100644 --- a/examples/aegis256x4_demo.py +++ b/examples/aegis256x4_demo.py @@ -30,9 +30,9 @@ def demo(): # Detached encrypt/decrypt ciphertext, mac = a.encrypt_detached( - nonce, key, message, associated_data, maclen=16 + key, nonce, message, associated_data, maclen=16 ) - plaintext = a.decrypt_detached(nonce, key, ciphertext, mac, associated_data) + plaintext = a.decrypt_detached(key, nonce, ciphertext, mac, associated_data) print( "detached enc: c=", hx(ciphertext), @@ -43,30 +43,30 @@ def demo(): ) # Attached encrypt/decrypt - ciphertext_with_tag = a.encrypt(nonce, key, message, associated_data, maclen=32) - plaintext2 = a.decrypt(nonce, key, ciphertext_with_tag, associated_data, maclen=32) + ciphertext_with_tag = a.encrypt(key, nonce, message, associated_data, maclen=32) + plaintext2 = a.decrypt(key, nonce, ciphertext_with_tag, associated_data, maclen=32) print( "attached enc: ct=", hx(ciphertext_with_tag), " dec_ok=", plaintext2 == message ) # Stream generation (None nonce allowed) -> deterministic for a given key stream = bytearray(64) - a.stream(None, key, into=stream) + a.stream(key, None, into=stream) print("stream (first 16 bytes):", hx(stream, 16)) # Unauthenticated mode round-trip (INSECURE; compatibility only) c2 = bytearray(len(message)) - a.encrypt_unauthenticated(message, nonce, key, into=c2) + a.encrypt_unauthenticated(key, nonce, message, into=c2) m2 = bytearray(len(message)) - a.decrypt_unauthenticated(c2, nonce, key, into=m2) + a.decrypt_unauthenticated(key, nonce, c2, into=m2) print("unauth round-trip ok:", bytes(m2) == message) # MAC: compute then verify - mac_state = a.Mac(nonce, key) + mac_state = a.Mac(key, nonce) mac_state.update(message) mac32 = mac_state.final(32) - mac_verify_state = a.Mac(nonce, key) + mac_verify_state = a.Mac(key, nonce) mac_verify_state.update(message) try: mac_verify_state.verify(mac32) @@ -81,7 +81,7 @@ def demo(): src = bytearray(total) dst = bytearray(total) t0 = time.perf_counter() - a.encrypt_unauthenticated(src, nonce, key, into=dst) + a.encrypt_unauthenticated(key, nonce, src, into=dst) t1 = time.perf_counter() secs = t1 - t0 gib = total / float(1 << 30) diff --git a/examples/benchmark.py b/examples/benchmark.py index 754b030..912b639 100644 --- a/examples/benchmark.py +++ b/examples/benchmark.py @@ -45,7 +45,7 @@ def bench_encrypt(alg_name: str, a) -> None: t0 = time.perf_counter() for _ in range(ITERATIONS): - a.encrypt(nonce, key, mview, None, maclen=maclen, into=buf) + a.encrypt(key, nonce, mview, None, maclen=maclen, into=buf) t1 = time.perf_counter() # Prevent any unrealistic optimization assumptions @@ -66,7 +66,7 @@ def bench_mac(alg_name: str, a) -> None: buf = bytearray(MSG_LEN) buf[:] = _random_bytes(len(buf)) - mac0 = a.Mac(nonce, key) + mac0 = a.Mac(key, nonce) mac_out = bytearray(a.ABYTES_MAX) t0 = time.perf_counter() diff --git a/tools/gen_modules.py b/tools/gen_modules.py new file mode 100644 index 0000000..d727a57 --- /dev/null +++ b/tools/gen_modules.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +""" +Regenerate aegis*.py modules from the canonical template aegis256x4.py. + +Changes per variant: +- Replace module name (aegis256x4 -> target) +- Replace label (AEGIS-256X4 -> target label like AEGIS-128L) +- Replace only the ALIGNMENT = value + +We do not touch alloc_aligned(...) calls or any code formatting. Blank lines +after ALIGNMENT are preserved. +""" + +import pathlib +import re +import sys + +# Template and target locations +ROOT = ( + pathlib.Path(__file__).resolve().parents[2] + if (pathlib.Path(__file__).resolve().parents[0].name == "tools") + else pathlib.Path.cwd() +) +PY_DIR = ROOT / "python" +AEGIS_DIR = PY_DIR / "aegis" +TEMPLATE = AEGIS_DIR / "aegis256x4.py" + +# Variants to generate (template excluded) and their ALIGNMENT values +VARIANT_ALIGN = { + "aegis256": 16, + "aegis256x2": 32, + "aegis256x4": 64, + "aegis128l": 32, + "aegis128x2": 64, + "aegis128x4": 64, +} + +TEMPLATE_NAME = "aegis256x4" +TEMPLATE_LABEL = "AEGIS-256X4" + +ALIGNMENT_LINE_RE = re.compile(r"^(ALIGNMENT\s*=\s*)(\d+)(\s*)$", re.MULTILINE) + + +def set_alignment_only(text: str, value: int) -> str: + """Replace only the numeric ALIGNMENT value, preserving surrounding whitespace and lines. + + This preserves any empty lines following the ALIGNMENT assignment because + the line ending is not part of the match; we keep any trailing spaces too. + """ + + def _sub(m: re.Match[str]) -> str: + prefix, _num, suffix = m.group(1), m.group(2), m.group(3) + return f"{prefix}{value}{suffix}" + + return ALIGNMENT_LINE_RE.sub(_sub, text) + + +def algo_label(name: str) -> str: + """Return the canonical label like AEGIS-256X4 for a module name like aegis256x4.""" + if not name.startswith("aegis"): + raise ValueError(f"Unexpected algorithm name: {name}") + return "AEGIS-" + name[5:].upper() + + +def generate_variant(template_src: str, variant: str) -> str: + # 1) replace lowercase template name + s = template_src.replace(TEMPLATE_NAME, variant) + # 2) replace uppercase label + s = s.replace(TEMPLATE_LABEL, algo_label(variant)) + # 3) set ALIGNMENT constant value using fallback map + align_value = VARIANT_ALIGN.get(variant, 64) + s = set_alignment_only(s, align_value) + return s + + +def main() -> int: + if not TEMPLATE.exists(): + print(f"Template not found: {TEMPLATE}", file=sys.stderr) + return 2 + template_src = TEMPLATE.read_text(encoding="utf-8") + + # Safety: ensure we are working from an up-to-date template that contains expected tokens + if TEMPLATE_NAME not in template_src or TEMPLATE_LABEL not in template_src: + print( + "Template file does not contain expected identifiers; aborting.", + file=sys.stderr, + ) + return 3 + + wrote = [] + for variant in VARIANT_ALIGN.keys(): + # Skip the template itself; recreate all other modules + if variant == TEMPLATE_NAME: + continue + dst = AEGIS_DIR / f"{variant}.py" + content = generate_variant(template_src, variant) + dst.write_text(content, encoding="utf-8") + wrote.append(dst.relative_to(ROOT)) + + print("Generated modules:") + for p in wrote: + print(" -", p) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())