Make key, nonce the first arguments (in this order), require kwargs past the initial few positional arguments. Add ALIGNMENT constant to each module. Add a script to generate all other modules from aegis256x4.py.

This commit is contained in:
Leo Vasanko
2025-11-04 20:26:47 -06:00
parent 6ceb2971fa
commit 02310675b7
9 changed files with 400 additions and 239 deletions

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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)

View File

@@ -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()

107
tools/gen_modules.py Normal file
View File

@@ -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 = <int> 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())