Constants renamed and values extracted from C code rather than function call at runtime. Documentation update.

This commit is contained in:
Leo Vasanko
2025-11-08 15:43:01 -06:00
parent 751a929836
commit 13445887e9
9 changed files with 426 additions and 346 deletions

View File

@@ -42,8 +42,8 @@ assert pt == msg
Common parameters and returns (applies to all items below): Common parameters and returns (applies to all items below):
- key: bytes of length a.KEYBYTES - key: bytes of length ciph.KEYBYTES
- nonce: bytes of length a.NPUBBYTES (must be unique per (key, message)) - nonce: bytes of length ciph.NONCEBYTES (must be unique per message)
- message/ct: plain text or ciphertext - message/ct: plain text or ciphertext
- ad: optional associated data (authenticated, not encrypted) - ad: optional associated data (authenticated, not encrypted)
- into: optional output buffer (see below) - into: optional output buffer (see below)
@@ -95,10 +95,10 @@ Useful for creating pseudo random bytes as rapidly as possible. Reuse of the sam
### Miscellaneous ### Miscellaneous
Constants (per module): KEYBYTES, NPUBBYTES, ABYTES_MIN, ABYTES_MAX, RATE, ALIGNMENT Constants (per module): KEYBYTES, NONCEBYTES, MACBYTES, MACBYTES_LONG, RATE, ALIGNMENT
- random_key() -> bytearray (length KEYBYTES) - random_key() -> bytearray (length KEYBYTES)
- random_nonce() -> bytearray (length NPUBBYTES) - random_nonce() -> bytearray (length NONCEBYTES)
- nonce_increment(nonce) - nonce_increment(nonce)
- wipe(buffer) - wipe(buffer)
@@ -116,7 +116,7 @@ Constants (per module): KEYBYTES, NPUBBYTES, ABYTES_MIN, ABYTES_MAX, RATE, ALIGN
A cryptographically secure keyed hash is produced. The example uses all zeroes for the nonce to always produce the same hash for the same key: A cryptographically secure keyed hash is produced. The example uses all zeroes for the nonce to always produce the same hash for the same key:
```python ```python
from pyaegis import aegis256x4 as ciph from pyaegis import aegis256x4 as ciph
key, nonce = ciph.random_key(), bytes(ciph.NPUBBYTES) key, nonce = ciph.random_key(), bytes(ciph.NONCEBYTES)
mac = ciph.mac(key, nonce, b"message", maclen=32) mac = ciph.mac(key, nonce, b"message", maclen=32)
print(mac) print(mac)
@@ -151,12 +151,12 @@ Class-based interface for incremental updates is an alternative to the one-shot
from pyaegis import aegis256x4 as ciph from pyaegis import aegis256x4 as ciph
key, nonce = ciph.random_key(), ciph.random_nonce() key, nonce = ciph.random_key(), ciph.random_nonce()
enc = a.Encryptor(key, nonce, ad=b"header") enc = ciph.Encryptor(key, nonce, ad=b"header")
c1 = enc.update(b"chunk1") c1 = enc.update(b"chunk1")
c2 = enc.update(b"chunk2") c2 = enc.update(b"chunk2")
mac = enc.final(maclen=16) mac = enc.final(maclen=16)
dec = a.Decryptor(key, nonce, ad=b"header") dec = ciph.Decryptor(key, nonce, ad=b"header")
p1 = dec.update(c1) p1 = dec.update(c1)
p2 = dec.update(c2) p2 = dec.update(c2)
dec.final(mac) # raises ValueError on failure dec.final(mac) # raises ValueError on failure
@@ -173,7 +173,7 @@ message = bytearray(30 * b"Attack at dawn! ")
key = b"sixteenbyte key!" # 16 bytes secret key for aegis128* algorithms key = b"sixteenbyte key!" # 16 bytes secret key for aegis128* algorithms
nonce = ciph.random_nonce() nonce = ciph.random_nonce()
framebytes = 80 # In real applications 1 MiB or more is practical framebytes = 80 # In real applications 1 MiB or more is practical
maclen = ciph.ABYTES_MIN # 16 maclen = ciph.MACBYTES # 16
with open("encrypted.bin", "wb") as f: with open("encrypted.bin", "wb") as f:
f.write(nonce) # Public initial nonce sent with the ciphertext f.write(nonce) # Public initial nonce sent with the ciphertext
@@ -191,10 +191,10 @@ from pyaegis import aegis128x4 as ciph
# Decryption needs same values as encryption # Decryption needs same values as encryption
key = b"sixteenbyte key!" key = b"sixteenbyte key!"
framebytes = 80 framebytes = 80
maclen = ciph.ABYTES_MIN maclen = ciph.MACBYTES
with open("encrypted.bin", "rb") as f: with open("encrypted.bin", "rb") as f:
nonce = bytearray(f.read(ciph.NPUBBYTES)) nonce = bytearray(f.read(ciph.NONCEBYTES))
while True: while True:
frame = f.read(framebytes) frame = f.read(framebytes)
if not frame: if not frame:

View File

@@ -9,24 +9,26 @@ from ._loader import ffi
from ._loader import lib as _lib from ._loader import lib as _lib
from .util import Buffer, new_aligned_struct, nonce_increment, wipe from .util import Buffer, new_aligned_struct, nonce_increment, wipe
# Constants exposed as functions in C; mirror them as integers at module import time KEYBYTES = 16 #: Key size in bytes (varies by algorithm)
KEYBYTES = _lib.aegis128l_keybytes() NONCEBYTES = 16 #: Nonce size in bytes (varies by algorithm)
NPUBBYTES = _lib.aegis128l_npubbytes() MACBYTES = 16 #: Normal MAC size (always 16)
ABYTES_MIN = _lib.aegis128l_abytes_min() MACBYTES_LONG = 32 #: Long MAC size (always 32)
ABYTES_MAX = _lib.aegis128l_abytes_max() ALIGNMENT = 64 #: Required alignment for internal structures
TAILBYTES_MAX = _lib.aegis128l_tailbytes_max() RATE = 64 #: Byte chunk size in internal processing
ALIGNMENT = 32
RATE = 32
def random_key() -> bytearray: def random_key() -> bytearray:
"""Generate a random key using cryptographically secure random bytes.""" """
Generate a random key using cryptographically secure random bytes.
It is recommended to wipe() the key after no longer needed to keep it secret.
"""
return bytearray(secrets.token_bytes(KEYBYTES)) return bytearray(secrets.token_bytes(KEYBYTES))
def random_nonce() -> bytearray: def random_nonce() -> bytearray:
"""Generate a random nonce using cryptographically secure random bytes.""" """Generate a random nonce using cryptographically secure random bytes."""
return bytearray(secrets.token_bytes(NPUBBYTES)) return bytearray(secrets.token_bytes(NONCEBYTES))
def _ptr(buf): def _ptr(buf):
@@ -39,7 +41,7 @@ def encrypt_detached(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
ct_into: Buffer | None = None, ct_into: Buffer | None = None,
mac_into: Buffer | None = None, mac_into: Buffer | None = None,
) -> tuple[bytearray | memoryview, bytearray | memoryview]: ) -> tuple[bytearray | memoryview, bytearray | memoryview]:
@@ -47,7 +49,7 @@ def encrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -65,8 +67,8 @@ def encrypt_detached(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if ct_into is None: if ct_into is None:
c = bytearray(len(message)) c = bytearray(len(message))
@@ -115,7 +117,7 @@ def decrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
mac: The MAC to verify. mac: The MAC to verify.
ad: Associated data (optional). ad: Associated data (optional).
@@ -130,8 +132,8 @@ def decrypt_detached(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
maclen = len(mac) maclen = len(mac)
if maclen not in (16, 32): if maclen not in (16, 32):
raise TypeError("mac length must be 16 or 32") raise TypeError("mac length must be 16 or 32")
@@ -164,14 +166,14 @@ def encrypt(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Encrypt message with associated data, returning ciphertext with appended MAC. f"""Encrypt message with associated data, returning ciphertext with appended MAC.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -188,8 +190,8 @@ def encrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message) + maclen) out = bytearray(len(message) + maclen)
else: else:
@@ -220,14 +222,14 @@ def decrypt(
ct: Buffer, ct: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Decrypt ciphertext with appended MAC and associated data. f"""Decrypt ciphertext with appended MAC and associated data.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext with MAC to decrypt. ct: The ciphertext with MAC to decrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -244,8 +246,8 @@ def decrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if len(ct) < maclen: if len(ct) < maclen:
raise TypeError("ciphertext too short for tag") raise TypeError("ciphertext too short for tag")
expected_out = len(ct) - maclen expected_out = len(ct) - maclen
@@ -284,7 +286,7 @@ def stream(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None).
length: Number of bytes to generate (required if into is None). length: Number of bytes to generate (required if into is None).
into: Buffer to write stream into (default: bytearray created). into: Buffer to write stream into (default: bytearray created).
@@ -296,8 +298,8 @@ def stream(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if nonce is not None and len(nonce) != NPUBBYTES: if nonce is not None and len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
if length is None: if length is None:
raise TypeError("provide either into or length") raise TypeError("provide either into or length")
@@ -326,7 +328,7 @@ def encrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
into: Buffer to write ciphertext into (default: bytearray created). into: Buffer to write ciphertext into (default: bytearray created).
@@ -338,8 +340,8 @@ def encrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message)) out = bytearray(len(message))
else: else:
@@ -367,7 +369,7 @@ def decrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
into: Buffer to write plaintext into (default: bytearray created). into: Buffer to write plaintext into (default: bytearray created).
@@ -379,8 +381,8 @@ def decrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(ct)) out = bytearray(len(ct))
else: else:
@@ -402,14 +404,14 @@ def mac(
key: Buffer, key: Buffer,
nonce: Buffer, nonce: Buffer,
data: Buffer, data: Buffer,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Compute a MAC for the given data in one shot. f"""Compute a MAC for the given data in one shot.
Args: Args:
key: Key ({KEYBYTES=}) key: Key ({KEYBYTES=})
nonce: Nonce ({NPUBBYTES=}) nonce: Nonce ({NONCEBYTES=})
data: Data to MAC data: Data to MAC
maclen: MAC length (16 or 32, default 16) maclen: MAC length (16 or 32, default 16)
into: Buffer to write MAC into (default: bytearray created) into: Buffer to write MAC into (default: bytearray created)
@@ -445,7 +447,7 @@ class Mac:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
Raises: Raises:
TypeError: If key or nonce lengths are invalid. TypeError: If key or nonce lengths are invalid.
@@ -459,8 +461,8 @@ class Mac:
# Normal init path # Normal init path
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
_lib.aegis128l_mac_init(self._st, _ptr(key), _ptr(nonce)) _lib.aegis128l_mac_init(self._st, _ptr(key), _ptr(nonce))
def __deepcopy__(self) -> "Mac": def __deepcopy__(self) -> "Mac":
@@ -490,7 +492,7 @@ class Mac:
def final( def final(
self, self,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize and return the MAC tag. """Finalize and return the MAC tag.
@@ -558,7 +560,7 @@ class Encryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data to bind to the encryption (optional). ad: Associated data to bind to the encryption (optional).
Raises: Raises:
@@ -566,8 +568,8 @@ class Encryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis128l_state", ALIGNMENT) st, owner = new_aligned_struct("aegis128l_state", ALIGNMENT)
_lib.aegis128l_state_init( _lib.aegis128l_state_init(
st, st,
@@ -639,7 +641,7 @@ class Encryptor:
return out if into is None else memoryview(out)[:w] # type: ignore return out if into is None else memoryview(out)[:w] # type: ignore
def final( def final(
self, into: Buffer | None = None, maclen: int = ABYTES_MIN self, into: Buffer | None = None, maclen: int = MACBYTES
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize encryption, writing any remaining bytes and the tag. """Finalize encryption, writing any remaining bytes and the tag.
@@ -692,7 +694,7 @@ class Decryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data used during encryption (optional). ad: Associated data used during encryption (optional).
Raises: Raises:
@@ -700,8 +702,8 @@ class Decryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis128l_state", ALIGNMENT) st, owner = new_aligned_struct("aegis128l_state", ALIGNMENT)
_lib.aegis128l_state_init( _lib.aegis128l_state_init(
st, st,
@@ -796,10 +798,9 @@ def new_mac_state():
__all__ = [ __all__ = [
# constants # constants
"KEYBYTES", "KEYBYTES",
"NPUBBYTES", "NONCEBYTES",
"ABYTES_MIN", "MACBYTES",
"ABYTES_MAX", "MACBYTES_LONG",
"TAILBYTES_MAX",
"ALIGNMENT", "ALIGNMENT",
"RATE", "RATE",
# utility functions # utility functions

View File

@@ -9,24 +9,26 @@ from ._loader import ffi
from ._loader import lib as _lib from ._loader import lib as _lib
from .util import Buffer, new_aligned_struct, nonce_increment, wipe from .util import Buffer, new_aligned_struct, nonce_increment, wipe
# Constants exposed as functions in C; mirror them as integers at module import time KEYBYTES = 16 #: Key size in bytes (varies by algorithm)
KEYBYTES = _lib.aegis128x2_keybytes() NONCEBYTES = 16 #: Nonce size in bytes (varies by algorithm)
NPUBBYTES = _lib.aegis128x2_npubbytes() MACBYTES = 16 #: Normal MAC size (always 16)
ABYTES_MIN = _lib.aegis128x2_abytes_min() MACBYTES_LONG = 32 #: Long MAC size (always 32)
ABYTES_MAX = _lib.aegis128x2_abytes_max() ALIGNMENT = 64 #: Required alignment for internal structures
TAILBYTES_MAX = _lib.aegis128x2_tailbytes_max() RATE = 64 #: Byte chunk size in internal processing
ALIGNMENT = 64
RATE = 64
def random_key() -> bytearray: def random_key() -> bytearray:
"""Generate a random key using cryptographically secure random bytes.""" """
Generate a random key using cryptographically secure random bytes.
It is recommended to wipe() the key after no longer needed to keep it secret.
"""
return bytearray(secrets.token_bytes(KEYBYTES)) return bytearray(secrets.token_bytes(KEYBYTES))
def random_nonce() -> bytearray: def random_nonce() -> bytearray:
"""Generate a random nonce using cryptographically secure random bytes.""" """Generate a random nonce using cryptographically secure random bytes."""
return bytearray(secrets.token_bytes(NPUBBYTES)) return bytearray(secrets.token_bytes(NONCEBYTES))
def _ptr(buf): def _ptr(buf):
@@ -39,7 +41,7 @@ def encrypt_detached(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
ct_into: Buffer | None = None, ct_into: Buffer | None = None,
mac_into: Buffer | None = None, mac_into: Buffer | None = None,
) -> tuple[bytearray | memoryview, bytearray | memoryview]: ) -> tuple[bytearray | memoryview, bytearray | memoryview]:
@@ -47,7 +49,7 @@ def encrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -65,8 +67,8 @@ def encrypt_detached(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if ct_into is None: if ct_into is None:
c = bytearray(len(message)) c = bytearray(len(message))
@@ -115,7 +117,7 @@ def decrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
mac: The MAC to verify. mac: The MAC to verify.
ad: Associated data (optional). ad: Associated data (optional).
@@ -130,8 +132,8 @@ def decrypt_detached(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
maclen = len(mac) maclen = len(mac)
if maclen not in (16, 32): if maclen not in (16, 32):
raise TypeError("mac length must be 16 or 32") raise TypeError("mac length must be 16 or 32")
@@ -164,14 +166,14 @@ def encrypt(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Encrypt message with associated data, returning ciphertext with appended MAC. f"""Encrypt message with associated data, returning ciphertext with appended MAC.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -188,8 +190,8 @@ def encrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message) + maclen) out = bytearray(len(message) + maclen)
else: else:
@@ -220,14 +222,14 @@ def decrypt(
ct: Buffer, ct: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Decrypt ciphertext with appended MAC and associated data. f"""Decrypt ciphertext with appended MAC and associated data.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext with MAC to decrypt. ct: The ciphertext with MAC to decrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -244,8 +246,8 @@ def decrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if len(ct) < maclen: if len(ct) < maclen:
raise TypeError("ciphertext too short for tag") raise TypeError("ciphertext too short for tag")
expected_out = len(ct) - maclen expected_out = len(ct) - maclen
@@ -284,7 +286,7 @@ def stream(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None).
length: Number of bytes to generate (required if into is None). length: Number of bytes to generate (required if into is None).
into: Buffer to write stream into (default: bytearray created). into: Buffer to write stream into (default: bytearray created).
@@ -296,8 +298,8 @@ def stream(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if nonce is not None and len(nonce) != NPUBBYTES: if nonce is not None and len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
if length is None: if length is None:
raise TypeError("provide either into or length") raise TypeError("provide either into or length")
@@ -326,7 +328,7 @@ def encrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
into: Buffer to write ciphertext into (default: bytearray created). into: Buffer to write ciphertext into (default: bytearray created).
@@ -338,8 +340,8 @@ def encrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message)) out = bytearray(len(message))
else: else:
@@ -367,7 +369,7 @@ def decrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
into: Buffer to write plaintext into (default: bytearray created). into: Buffer to write plaintext into (default: bytearray created).
@@ -379,8 +381,8 @@ def decrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(ct)) out = bytearray(len(ct))
else: else:
@@ -402,14 +404,14 @@ def mac(
key: Buffer, key: Buffer,
nonce: Buffer, nonce: Buffer,
data: Buffer, data: Buffer,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Compute a MAC for the given data in one shot. f"""Compute a MAC for the given data in one shot.
Args: Args:
key: Key ({KEYBYTES=}) key: Key ({KEYBYTES=})
nonce: Nonce ({NPUBBYTES=}) nonce: Nonce ({NONCEBYTES=})
data: Data to MAC data: Data to MAC
maclen: MAC length (16 or 32, default 16) maclen: MAC length (16 or 32, default 16)
into: Buffer to write MAC into (default: bytearray created) into: Buffer to write MAC into (default: bytearray created)
@@ -445,7 +447,7 @@ class Mac:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
Raises: Raises:
TypeError: If key or nonce lengths are invalid. TypeError: If key or nonce lengths are invalid.
@@ -459,8 +461,8 @@ class Mac:
# Normal init path # Normal init path
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
_lib.aegis128x2_mac_init(self._st, _ptr(key), _ptr(nonce)) _lib.aegis128x2_mac_init(self._st, _ptr(key), _ptr(nonce))
def __deepcopy__(self) -> "Mac": def __deepcopy__(self) -> "Mac":
@@ -490,7 +492,7 @@ class Mac:
def final( def final(
self, self,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize and return the MAC tag. """Finalize and return the MAC tag.
@@ -558,7 +560,7 @@ class Encryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data to bind to the encryption (optional). ad: Associated data to bind to the encryption (optional).
Raises: Raises:
@@ -566,8 +568,8 @@ class Encryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis128x2_state", ALIGNMENT) st, owner = new_aligned_struct("aegis128x2_state", ALIGNMENT)
_lib.aegis128x2_state_init( _lib.aegis128x2_state_init(
st, st,
@@ -639,7 +641,7 @@ class Encryptor:
return out if into is None else memoryview(out)[:w] # type: ignore return out if into is None else memoryview(out)[:w] # type: ignore
def final( def final(
self, into: Buffer | None = None, maclen: int = ABYTES_MIN self, into: Buffer | None = None, maclen: int = MACBYTES
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize encryption, writing any remaining bytes and the tag. """Finalize encryption, writing any remaining bytes and the tag.
@@ -692,7 +694,7 @@ class Decryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data used during encryption (optional). ad: Associated data used during encryption (optional).
Raises: Raises:
@@ -700,8 +702,8 @@ class Decryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis128x2_state", ALIGNMENT) st, owner = new_aligned_struct("aegis128x2_state", ALIGNMENT)
_lib.aegis128x2_state_init( _lib.aegis128x2_state_init(
st, st,
@@ -796,10 +798,9 @@ def new_mac_state():
__all__ = [ __all__ = [
# constants # constants
"KEYBYTES", "KEYBYTES",
"NPUBBYTES", "NONCEBYTES",
"ABYTES_MIN", "MACBYTES",
"ABYTES_MAX", "MACBYTES_LONG",
"TAILBYTES_MAX",
"ALIGNMENT", "ALIGNMENT",
"RATE", "RATE",
# utility functions # utility functions

View File

@@ -9,24 +9,26 @@ from ._loader import ffi
from ._loader import lib as _lib from ._loader import lib as _lib
from .util import Buffer, new_aligned_struct, nonce_increment, wipe from .util import Buffer, new_aligned_struct, nonce_increment, wipe
# Constants exposed as functions in C; mirror them as integers at module import time KEYBYTES = 16 #: Key size in bytes (varies by algorithm)
KEYBYTES = _lib.aegis128x4_keybytes() NONCEBYTES = 16 #: Nonce size in bytes (varies by algorithm)
NPUBBYTES = _lib.aegis128x4_npubbytes() MACBYTES = 16 #: Normal MAC size (always 16)
ABYTES_MIN = _lib.aegis128x4_abytes_min() MACBYTES_LONG = 32 #: Long MAC size (always 32)
ABYTES_MAX = _lib.aegis128x4_abytes_max() ALIGNMENT = 64 #: Required alignment for internal structures
TAILBYTES_MAX = _lib.aegis128x4_tailbytes_max() RATE = 64 #: Byte chunk size in internal processing
ALIGNMENT = 64
RATE = 128
def random_key() -> bytearray: def random_key() -> bytearray:
"""Generate a random key using cryptographically secure random bytes.""" """
Generate a random key using cryptographically secure random bytes.
It is recommended to wipe() the key after no longer needed to keep it secret.
"""
return bytearray(secrets.token_bytes(KEYBYTES)) return bytearray(secrets.token_bytes(KEYBYTES))
def random_nonce() -> bytearray: def random_nonce() -> bytearray:
"""Generate a random nonce using cryptographically secure random bytes.""" """Generate a random nonce using cryptographically secure random bytes."""
return bytearray(secrets.token_bytes(NPUBBYTES)) return bytearray(secrets.token_bytes(NONCEBYTES))
def _ptr(buf): def _ptr(buf):
@@ -39,7 +41,7 @@ def encrypt_detached(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
ct_into: Buffer | None = None, ct_into: Buffer | None = None,
mac_into: Buffer | None = None, mac_into: Buffer | None = None,
) -> tuple[bytearray | memoryview, bytearray | memoryview]: ) -> tuple[bytearray | memoryview, bytearray | memoryview]:
@@ -47,7 +49,7 @@ def encrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -65,8 +67,8 @@ def encrypt_detached(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if ct_into is None: if ct_into is None:
c = bytearray(len(message)) c = bytearray(len(message))
@@ -115,7 +117,7 @@ def decrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
mac: The MAC to verify. mac: The MAC to verify.
ad: Associated data (optional). ad: Associated data (optional).
@@ -130,8 +132,8 @@ def decrypt_detached(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
maclen = len(mac) maclen = len(mac)
if maclen not in (16, 32): if maclen not in (16, 32):
raise TypeError("mac length must be 16 or 32") raise TypeError("mac length must be 16 or 32")
@@ -164,14 +166,14 @@ def encrypt(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Encrypt message with associated data, returning ciphertext with appended MAC. f"""Encrypt message with associated data, returning ciphertext with appended MAC.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -188,8 +190,8 @@ def encrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message) + maclen) out = bytearray(len(message) + maclen)
else: else:
@@ -220,14 +222,14 @@ def decrypt(
ct: Buffer, ct: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Decrypt ciphertext with appended MAC and associated data. f"""Decrypt ciphertext with appended MAC and associated data.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext with MAC to decrypt. ct: The ciphertext with MAC to decrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -244,8 +246,8 @@ def decrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if len(ct) < maclen: if len(ct) < maclen:
raise TypeError("ciphertext too short for tag") raise TypeError("ciphertext too short for tag")
expected_out = len(ct) - maclen expected_out = len(ct) - maclen
@@ -284,7 +286,7 @@ def stream(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None).
length: Number of bytes to generate (required if into is None). length: Number of bytes to generate (required if into is None).
into: Buffer to write stream into (default: bytearray created). into: Buffer to write stream into (default: bytearray created).
@@ -296,8 +298,8 @@ def stream(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if nonce is not None and len(nonce) != NPUBBYTES: if nonce is not None and len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
if length is None: if length is None:
raise TypeError("provide either into or length") raise TypeError("provide either into or length")
@@ -326,7 +328,7 @@ def encrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
into: Buffer to write ciphertext into (default: bytearray created). into: Buffer to write ciphertext into (default: bytearray created).
@@ -338,8 +340,8 @@ def encrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message)) out = bytearray(len(message))
else: else:
@@ -367,7 +369,7 @@ def decrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
into: Buffer to write plaintext into (default: bytearray created). into: Buffer to write plaintext into (default: bytearray created).
@@ -379,8 +381,8 @@ def decrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(ct)) out = bytearray(len(ct))
else: else:
@@ -402,14 +404,14 @@ def mac(
key: Buffer, key: Buffer,
nonce: Buffer, nonce: Buffer,
data: Buffer, data: Buffer,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Compute a MAC for the given data in one shot. f"""Compute a MAC for the given data in one shot.
Args: Args:
key: Key ({KEYBYTES=}) key: Key ({KEYBYTES=})
nonce: Nonce ({NPUBBYTES=}) nonce: Nonce ({NONCEBYTES=})
data: Data to MAC data: Data to MAC
maclen: MAC length (16 or 32, default 16) maclen: MAC length (16 or 32, default 16)
into: Buffer to write MAC into (default: bytearray created) into: Buffer to write MAC into (default: bytearray created)
@@ -445,7 +447,7 @@ class Mac:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
Raises: Raises:
TypeError: If key or nonce lengths are invalid. TypeError: If key or nonce lengths are invalid.
@@ -459,8 +461,8 @@ class Mac:
# Normal init path # Normal init path
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
_lib.aegis128x4_mac_init(self._st, _ptr(key), _ptr(nonce)) _lib.aegis128x4_mac_init(self._st, _ptr(key), _ptr(nonce))
def __deepcopy__(self) -> "Mac": def __deepcopy__(self) -> "Mac":
@@ -490,7 +492,7 @@ class Mac:
def final( def final(
self, self,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize and return the MAC tag. """Finalize and return the MAC tag.
@@ -558,7 +560,7 @@ class Encryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data to bind to the encryption (optional). ad: Associated data to bind to the encryption (optional).
Raises: Raises:
@@ -566,8 +568,8 @@ class Encryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis128x4_state", ALIGNMENT) st, owner = new_aligned_struct("aegis128x4_state", ALIGNMENT)
_lib.aegis128x4_state_init( _lib.aegis128x4_state_init(
st, st,
@@ -639,7 +641,7 @@ class Encryptor:
return out if into is None else memoryview(out)[:w] # type: ignore return out if into is None else memoryview(out)[:w] # type: ignore
def final( def final(
self, into: Buffer | None = None, maclen: int = ABYTES_MIN self, into: Buffer | None = None, maclen: int = MACBYTES
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize encryption, writing any remaining bytes and the tag. """Finalize encryption, writing any remaining bytes and the tag.
@@ -692,7 +694,7 @@ class Decryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data used during encryption (optional). ad: Associated data used during encryption (optional).
Raises: Raises:
@@ -700,8 +702,8 @@ class Decryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis128x4_state", ALIGNMENT) st, owner = new_aligned_struct("aegis128x4_state", ALIGNMENT)
_lib.aegis128x4_state_init( _lib.aegis128x4_state_init(
st, st,
@@ -796,10 +798,9 @@ def new_mac_state():
__all__ = [ __all__ = [
# constants # constants
"KEYBYTES", "KEYBYTES",
"NPUBBYTES", "NONCEBYTES",
"ABYTES_MIN", "MACBYTES",
"ABYTES_MAX", "MACBYTES_LONG",
"TAILBYTES_MAX",
"ALIGNMENT", "ALIGNMENT",
"RATE", "RATE",
# utility functions # utility functions

View File

@@ -9,24 +9,26 @@ from ._loader import ffi
from ._loader import lib as _lib from ._loader import lib as _lib
from .util import Buffer, new_aligned_struct, nonce_increment, wipe from .util import Buffer, new_aligned_struct, nonce_increment, wipe
# Constants exposed as functions in C; mirror them as integers at module import time KEYBYTES = 32 #: Key size in bytes (varies by algorithm)
KEYBYTES = _lib.aegis256_keybytes() NONCEBYTES = 32 #: Nonce size in bytes (varies by algorithm)
NPUBBYTES = _lib.aegis256_npubbytes() MACBYTES = 16 #: Normal MAC size (always 16)
ABYTES_MIN = _lib.aegis256_abytes_min() MACBYTES_LONG = 32 #: Long MAC size (always 32)
ABYTES_MAX = _lib.aegis256_abytes_max() ALIGNMENT = 64 #: Required alignment for internal structures
TAILBYTES_MAX = _lib.aegis256_tailbytes_max() RATE = 64 #: Byte chunk size in internal processing
ALIGNMENT = 16
RATE = 16
def random_key() -> bytearray: def random_key() -> bytearray:
"""Generate a random key using cryptographically secure random bytes.""" """
Generate a random key using cryptographically secure random bytes.
It is recommended to wipe() the key after no longer needed to keep it secret.
"""
return bytearray(secrets.token_bytes(KEYBYTES)) return bytearray(secrets.token_bytes(KEYBYTES))
def random_nonce() -> bytearray: def random_nonce() -> bytearray:
"""Generate a random nonce using cryptographically secure random bytes.""" """Generate a random nonce using cryptographically secure random bytes."""
return bytearray(secrets.token_bytes(NPUBBYTES)) return bytearray(secrets.token_bytes(NONCEBYTES))
def _ptr(buf): def _ptr(buf):
@@ -39,7 +41,7 @@ def encrypt_detached(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
ct_into: Buffer | None = None, ct_into: Buffer | None = None,
mac_into: Buffer | None = None, mac_into: Buffer | None = None,
) -> tuple[bytearray | memoryview, bytearray | memoryview]: ) -> tuple[bytearray | memoryview, bytearray | memoryview]:
@@ -47,7 +49,7 @@ def encrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -65,8 +67,8 @@ def encrypt_detached(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if ct_into is None: if ct_into is None:
c = bytearray(len(message)) c = bytearray(len(message))
@@ -115,7 +117,7 @@ def decrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
mac: The MAC to verify. mac: The MAC to verify.
ad: Associated data (optional). ad: Associated data (optional).
@@ -130,8 +132,8 @@ def decrypt_detached(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
maclen = len(mac) maclen = len(mac)
if maclen not in (16, 32): if maclen not in (16, 32):
raise TypeError("mac length must be 16 or 32") raise TypeError("mac length must be 16 or 32")
@@ -164,14 +166,14 @@ def encrypt(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Encrypt message with associated data, returning ciphertext with appended MAC. f"""Encrypt message with associated data, returning ciphertext with appended MAC.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -188,8 +190,8 @@ def encrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message) + maclen) out = bytearray(len(message) + maclen)
else: else:
@@ -220,14 +222,14 @@ def decrypt(
ct: Buffer, ct: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Decrypt ciphertext with appended MAC and associated data. f"""Decrypt ciphertext with appended MAC and associated data.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext with MAC to decrypt. ct: The ciphertext with MAC to decrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -244,8 +246,8 @@ def decrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if len(ct) < maclen: if len(ct) < maclen:
raise TypeError("ciphertext too short for tag") raise TypeError("ciphertext too short for tag")
expected_out = len(ct) - maclen expected_out = len(ct) - maclen
@@ -284,7 +286,7 @@ def stream(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None).
length: Number of bytes to generate (required if into is None). length: Number of bytes to generate (required if into is None).
into: Buffer to write stream into (default: bytearray created). into: Buffer to write stream into (default: bytearray created).
@@ -296,8 +298,8 @@ def stream(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if nonce is not None and len(nonce) != NPUBBYTES: if nonce is not None and len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
if length is None: if length is None:
raise TypeError("provide either into or length") raise TypeError("provide either into or length")
@@ -326,7 +328,7 @@ def encrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
into: Buffer to write ciphertext into (default: bytearray created). into: Buffer to write ciphertext into (default: bytearray created).
@@ -338,8 +340,8 @@ def encrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message)) out = bytearray(len(message))
else: else:
@@ -367,7 +369,7 @@ def decrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
into: Buffer to write plaintext into (default: bytearray created). into: Buffer to write plaintext into (default: bytearray created).
@@ -379,8 +381,8 @@ def decrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(ct)) out = bytearray(len(ct))
else: else:
@@ -402,14 +404,14 @@ def mac(
key: Buffer, key: Buffer,
nonce: Buffer, nonce: Buffer,
data: Buffer, data: Buffer,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Compute a MAC for the given data in one shot. f"""Compute a MAC for the given data in one shot.
Args: Args:
key: Key ({KEYBYTES=}) key: Key ({KEYBYTES=})
nonce: Nonce ({NPUBBYTES=}) nonce: Nonce ({NONCEBYTES=})
data: Data to MAC data: Data to MAC
maclen: MAC length (16 or 32, default 16) maclen: MAC length (16 or 32, default 16)
into: Buffer to write MAC into (default: bytearray created) into: Buffer to write MAC into (default: bytearray created)
@@ -445,7 +447,7 @@ class Mac:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
Raises: Raises:
TypeError: If key or nonce lengths are invalid. TypeError: If key or nonce lengths are invalid.
@@ -459,8 +461,8 @@ class Mac:
# Normal init path # Normal init path
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
_lib.aegis256_mac_init(self._st, _ptr(key), _ptr(nonce)) _lib.aegis256_mac_init(self._st, _ptr(key), _ptr(nonce))
def __deepcopy__(self) -> "Mac": def __deepcopy__(self) -> "Mac":
@@ -490,7 +492,7 @@ class Mac:
def final( def final(
self, self,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize and return the MAC tag. """Finalize and return the MAC tag.
@@ -558,7 +560,7 @@ class Encryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data to bind to the encryption (optional). ad: Associated data to bind to the encryption (optional).
Raises: Raises:
@@ -566,8 +568,8 @@ class Encryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis256_state", ALIGNMENT) st, owner = new_aligned_struct("aegis256_state", ALIGNMENT)
_lib.aegis256_state_init( _lib.aegis256_state_init(
st, st,
@@ -639,7 +641,7 @@ class Encryptor:
return out if into is None else memoryview(out)[:w] # type: ignore return out if into is None else memoryview(out)[:w] # type: ignore
def final( def final(
self, into: Buffer | None = None, maclen: int = ABYTES_MIN self, into: Buffer | None = None, maclen: int = MACBYTES
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize encryption, writing any remaining bytes and the tag. """Finalize encryption, writing any remaining bytes and the tag.
@@ -692,7 +694,7 @@ class Decryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data used during encryption (optional). ad: Associated data used during encryption (optional).
Raises: Raises:
@@ -700,8 +702,8 @@ class Decryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis256_state", ALIGNMENT) st, owner = new_aligned_struct("aegis256_state", ALIGNMENT)
_lib.aegis256_state_init( _lib.aegis256_state_init(
st, st,
@@ -796,10 +798,9 @@ def new_mac_state():
__all__ = [ __all__ = [
# constants # constants
"KEYBYTES", "KEYBYTES",
"NPUBBYTES", "NONCEBYTES",
"ABYTES_MIN", "MACBYTES",
"ABYTES_MAX", "MACBYTES_LONG",
"TAILBYTES_MAX",
"ALIGNMENT", "ALIGNMENT",
"RATE", "RATE",
# utility functions # utility functions

View File

@@ -9,24 +9,26 @@ from ._loader import ffi
from ._loader import lib as _lib from ._loader import lib as _lib
from .util import Buffer, new_aligned_struct, nonce_increment, wipe from .util import Buffer, new_aligned_struct, nonce_increment, wipe
# Constants exposed as functions in C; mirror them as integers at module import time KEYBYTES = 32 #: Key size in bytes (varies by algorithm)
KEYBYTES = _lib.aegis256x2_keybytes() NONCEBYTES = 32 #: Nonce size in bytes (varies by algorithm)
NPUBBYTES = _lib.aegis256x2_npubbytes() MACBYTES = 16 #: Normal MAC size (always 16)
ABYTES_MIN = _lib.aegis256x2_abytes_min() MACBYTES_LONG = 32 #: Long MAC size (always 32)
ABYTES_MAX = _lib.aegis256x2_abytes_max() ALIGNMENT = 64 #: Required alignment for internal structures
TAILBYTES_MAX = _lib.aegis256x2_tailbytes_max() RATE = 64 #: Byte chunk size in internal processing
ALIGNMENT = 32
RATE = 32
def random_key() -> bytearray: def random_key() -> bytearray:
"""Generate a random key using cryptographically secure random bytes.""" """
Generate a random key using cryptographically secure random bytes.
It is recommended to wipe() the key after no longer needed to keep it secret.
"""
return bytearray(secrets.token_bytes(KEYBYTES)) return bytearray(secrets.token_bytes(KEYBYTES))
def random_nonce() -> bytearray: def random_nonce() -> bytearray:
"""Generate a random nonce using cryptographically secure random bytes.""" """Generate a random nonce using cryptographically secure random bytes."""
return bytearray(secrets.token_bytes(NPUBBYTES)) return bytearray(secrets.token_bytes(NONCEBYTES))
def _ptr(buf): def _ptr(buf):
@@ -39,7 +41,7 @@ def encrypt_detached(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
ct_into: Buffer | None = None, ct_into: Buffer | None = None,
mac_into: Buffer | None = None, mac_into: Buffer | None = None,
) -> tuple[bytearray | memoryview, bytearray | memoryview]: ) -> tuple[bytearray | memoryview, bytearray | memoryview]:
@@ -47,7 +49,7 @@ def encrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -65,8 +67,8 @@ def encrypt_detached(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if ct_into is None: if ct_into is None:
c = bytearray(len(message)) c = bytearray(len(message))
@@ -115,7 +117,7 @@ def decrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
mac: The MAC to verify. mac: The MAC to verify.
ad: Associated data (optional). ad: Associated data (optional).
@@ -130,8 +132,8 @@ def decrypt_detached(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
maclen = len(mac) maclen = len(mac)
if maclen not in (16, 32): if maclen not in (16, 32):
raise TypeError("mac length must be 16 or 32") raise TypeError("mac length must be 16 or 32")
@@ -164,14 +166,14 @@ def encrypt(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Encrypt message with associated data, returning ciphertext with appended MAC. f"""Encrypt message with associated data, returning ciphertext with appended MAC.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -188,8 +190,8 @@ def encrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message) + maclen) out = bytearray(len(message) + maclen)
else: else:
@@ -220,14 +222,14 @@ def decrypt(
ct: Buffer, ct: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Decrypt ciphertext with appended MAC and associated data. f"""Decrypt ciphertext with appended MAC and associated data.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext with MAC to decrypt. ct: The ciphertext with MAC to decrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -244,8 +246,8 @@ def decrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if len(ct) < maclen: if len(ct) < maclen:
raise TypeError("ciphertext too short for tag") raise TypeError("ciphertext too short for tag")
expected_out = len(ct) - maclen expected_out = len(ct) - maclen
@@ -284,7 +286,7 @@ def stream(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None).
length: Number of bytes to generate (required if into is None). length: Number of bytes to generate (required if into is None).
into: Buffer to write stream into (default: bytearray created). into: Buffer to write stream into (default: bytearray created).
@@ -296,8 +298,8 @@ def stream(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if nonce is not None and len(nonce) != NPUBBYTES: if nonce is not None and len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
if length is None: if length is None:
raise TypeError("provide either into or length") raise TypeError("provide either into or length")
@@ -326,7 +328,7 @@ def encrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
into: Buffer to write ciphertext into (default: bytearray created). into: Buffer to write ciphertext into (default: bytearray created).
@@ -338,8 +340,8 @@ def encrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message)) out = bytearray(len(message))
else: else:
@@ -367,7 +369,7 @@ def decrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
into: Buffer to write plaintext into (default: bytearray created). into: Buffer to write plaintext into (default: bytearray created).
@@ -379,8 +381,8 @@ def decrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(ct)) out = bytearray(len(ct))
else: else:
@@ -402,14 +404,14 @@ def mac(
key: Buffer, key: Buffer,
nonce: Buffer, nonce: Buffer,
data: Buffer, data: Buffer,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Compute a MAC for the given data in one shot. f"""Compute a MAC for the given data in one shot.
Args: Args:
key: Key ({KEYBYTES=}) key: Key ({KEYBYTES=})
nonce: Nonce ({NPUBBYTES=}) nonce: Nonce ({NONCEBYTES=})
data: Data to MAC data: Data to MAC
maclen: MAC length (16 or 32, default 16) maclen: MAC length (16 or 32, default 16)
into: Buffer to write MAC into (default: bytearray created) into: Buffer to write MAC into (default: bytearray created)
@@ -445,7 +447,7 @@ class Mac:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
Raises: Raises:
TypeError: If key or nonce lengths are invalid. TypeError: If key or nonce lengths are invalid.
@@ -459,8 +461,8 @@ class Mac:
# Normal init path # Normal init path
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
_lib.aegis256x2_mac_init(self._st, _ptr(key), _ptr(nonce)) _lib.aegis256x2_mac_init(self._st, _ptr(key), _ptr(nonce))
def __deepcopy__(self) -> "Mac": def __deepcopy__(self) -> "Mac":
@@ -490,7 +492,7 @@ class Mac:
def final( def final(
self, self,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize and return the MAC tag. """Finalize and return the MAC tag.
@@ -558,7 +560,7 @@ class Encryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data to bind to the encryption (optional). ad: Associated data to bind to the encryption (optional).
Raises: Raises:
@@ -566,8 +568,8 @@ class Encryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis256x2_state", ALIGNMENT) st, owner = new_aligned_struct("aegis256x2_state", ALIGNMENT)
_lib.aegis256x2_state_init( _lib.aegis256x2_state_init(
st, st,
@@ -639,7 +641,7 @@ class Encryptor:
return out if into is None else memoryview(out)[:w] # type: ignore return out if into is None else memoryview(out)[:w] # type: ignore
def final( def final(
self, into: Buffer | None = None, maclen: int = ABYTES_MIN self, into: Buffer | None = None, maclen: int = MACBYTES
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize encryption, writing any remaining bytes and the tag. """Finalize encryption, writing any remaining bytes and the tag.
@@ -692,7 +694,7 @@ class Decryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data used during encryption (optional). ad: Associated data used during encryption (optional).
Raises: Raises:
@@ -700,8 +702,8 @@ class Decryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis256x2_state", ALIGNMENT) st, owner = new_aligned_struct("aegis256x2_state", ALIGNMENT)
_lib.aegis256x2_state_init( _lib.aegis256x2_state_init(
st, st,
@@ -796,10 +798,9 @@ def new_mac_state():
__all__ = [ __all__ = [
# constants # constants
"KEYBYTES", "KEYBYTES",
"NPUBBYTES", "NONCEBYTES",
"ABYTES_MIN", "MACBYTES",
"ABYTES_MAX", "MACBYTES_LONG",
"TAILBYTES_MAX",
"ALIGNMENT", "ALIGNMENT",
"RATE", "RATE",
# utility functions # utility functions

View File

@@ -9,24 +9,26 @@ from ._loader import ffi
from ._loader import lib as _lib from ._loader import lib as _lib
from .util import Buffer, new_aligned_struct, nonce_increment, wipe from .util import Buffer, new_aligned_struct, nonce_increment, wipe
# Constants exposed as functions in C; mirror them as integers at module import time KEYBYTES = 32 #: Key size in bytes (varies by algorithm)
KEYBYTES = _lib.aegis256x4_keybytes() NONCEBYTES = 32 #: Nonce size in bytes (varies by algorithm)
NPUBBYTES = _lib.aegis256x4_npubbytes() MACBYTES = 16 #: Normal MAC size (always 16)
ABYTES_MIN = _lib.aegis256x4_abytes_min() MACBYTES_LONG = 32 #: Long MAC size (always 32)
ABYTES_MAX = _lib.aegis256x4_abytes_max() ALIGNMENT = 64 #: Required alignment for internal structures
TAILBYTES_MAX = _lib.aegis256x4_tailbytes_max() RATE = 64 #: Byte chunk size in internal processing
ALIGNMENT = 64
RATE = 64
def random_key() -> bytearray: def random_key() -> bytearray:
"""Generate a random key using cryptographically secure random bytes.""" """
Generate a random key using cryptographically secure random bytes.
It is recommended to wipe() the key after no longer needed to keep it secret.
"""
return bytearray(secrets.token_bytes(KEYBYTES)) return bytearray(secrets.token_bytes(KEYBYTES))
def random_nonce() -> bytearray: def random_nonce() -> bytearray:
"""Generate a random nonce using cryptographically secure random bytes.""" """Generate a random nonce using cryptographically secure random bytes."""
return bytearray(secrets.token_bytes(NPUBBYTES)) return bytearray(secrets.token_bytes(NONCEBYTES))
def _ptr(buf): def _ptr(buf):
@@ -39,7 +41,7 @@ def encrypt_detached(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
ct_into: Buffer | None = None, ct_into: Buffer | None = None,
mac_into: Buffer | None = None, mac_into: Buffer | None = None,
) -> tuple[bytearray | memoryview, bytearray | memoryview]: ) -> tuple[bytearray | memoryview, bytearray | memoryview]:
@@ -47,7 +49,7 @@ def encrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -65,8 +67,8 @@ def encrypt_detached(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if ct_into is None: if ct_into is None:
c = bytearray(len(message)) c = bytearray(len(message))
@@ -115,7 +117,7 @@ def decrypt_detached(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
mac: The MAC to verify. mac: The MAC to verify.
ad: Associated data (optional). ad: Associated data (optional).
@@ -130,8 +132,8 @@ def decrypt_detached(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
maclen = len(mac) maclen = len(mac)
if maclen not in (16, 32): if maclen not in (16, 32):
raise TypeError("mac length must be 16 or 32") raise TypeError("mac length must be 16 or 32")
@@ -164,14 +166,14 @@ def encrypt(
message: Buffer, message: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Encrypt message with associated data, returning ciphertext with appended MAC. f"""Encrypt message with associated data, returning ciphertext with appended MAC.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -188,8 +190,8 @@ def encrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message) + maclen) out = bytearray(len(message) + maclen)
else: else:
@@ -220,14 +222,14 @@ def decrypt(
ct: Buffer, ct: Buffer,
ad: Buffer | None = None, ad: Buffer | None = None,
*, *,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Decrypt ciphertext with appended MAC and associated data. f"""Decrypt ciphertext with appended MAC and associated data.
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext with MAC to decrypt. ct: The ciphertext with MAC to decrypt.
ad: Associated data (optional). ad: Associated data (optional).
maclen: MAC length (16 or 32, default 16). maclen: MAC length (16 or 32, default 16).
@@ -244,8 +246,8 @@ def decrypt(
raise TypeError("maclen must be 16 or 32") raise TypeError("maclen must be 16 or 32")
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if len(ct) < maclen: if len(ct) < maclen:
raise TypeError("ciphertext too short for tag") raise TypeError("ciphertext too short for tag")
expected_out = len(ct) - maclen expected_out = len(ct) - maclen
@@ -284,7 +286,7 @@ def stream(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}, uses zeroes for nonce if None). nonce: Nonce ({NONCEBYTES=}, uses zeroes for nonce if None).
length: Number of bytes to generate (required if into is None). length: Number of bytes to generate (required if into is None).
into: Buffer to write stream into (default: bytearray created). into: Buffer to write stream into (default: bytearray created).
@@ -296,8 +298,8 @@ def stream(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if nonce is not None and len(nonce) != NPUBBYTES: if nonce is not None and len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
if length is None: if length is None:
raise TypeError("provide either into or length") raise TypeError("provide either into or length")
@@ -326,7 +328,7 @@ def encrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
message: The plaintext message to encrypt. message: The plaintext message to encrypt.
into: Buffer to write ciphertext into (default: bytearray created). into: Buffer to write ciphertext into (default: bytearray created).
@@ -338,8 +340,8 @@ def encrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(message)) out = bytearray(len(message))
else: else:
@@ -367,7 +369,7 @@ def decrypt_unauthenticated(
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ct: The ciphertext to decrypt. ct: The ciphertext to decrypt.
into: Buffer to write plaintext into (default: bytearray created). into: Buffer to write plaintext into (default: bytearray created).
@@ -379,8 +381,8 @@ def decrypt_unauthenticated(
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
if into is None: if into is None:
out = bytearray(len(ct)) out = bytearray(len(ct))
else: else:
@@ -402,14 +404,14 @@ def mac(
key: Buffer, key: Buffer,
nonce: Buffer, nonce: Buffer,
data: Buffer, data: Buffer,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
f"""Compute a MAC for the given data in one shot. f"""Compute a MAC for the given data in one shot.
Args: Args:
key: Key ({KEYBYTES=}) key: Key ({KEYBYTES=})
nonce: Nonce ({NPUBBYTES=}) nonce: Nonce ({NONCEBYTES=})
data: Data to MAC data: Data to MAC
maclen: MAC length (16 or 32, default 16) maclen: MAC length (16 or 32, default 16)
into: Buffer to write MAC into (default: bytearray created) into: Buffer to write MAC into (default: bytearray created)
@@ -445,7 +447,7 @@ class Mac:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
Raises: Raises:
TypeError: If key or nonce lengths are invalid. TypeError: If key or nonce lengths are invalid.
@@ -459,8 +461,8 @@ class Mac:
# Normal init path # Normal init path
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
_lib.aegis256x4_mac_init(self._st, _ptr(key), _ptr(nonce)) _lib.aegis256x4_mac_init(self._st, _ptr(key), _ptr(nonce))
def __deepcopy__(self) -> "Mac": def __deepcopy__(self) -> "Mac":
@@ -490,7 +492,7 @@ class Mac:
def final( def final(
self, self,
maclen: int = ABYTES_MIN, maclen: int = MACBYTES,
into: Buffer | None = None, into: Buffer | None = None,
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize and return the MAC tag. """Finalize and return the MAC tag.
@@ -558,7 +560,7 @@ class Encryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data to bind to the encryption (optional). ad: Associated data to bind to the encryption (optional).
Raises: Raises:
@@ -566,8 +568,8 @@ class Encryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis256x4_state", ALIGNMENT) st, owner = new_aligned_struct("aegis256x4_state", ALIGNMENT)
_lib.aegis256x4_state_init( _lib.aegis256x4_state_init(
st, st,
@@ -639,7 +641,7 @@ class Encryptor:
return out if into is None else memoryview(out)[:w] # type: ignore return out if into is None else memoryview(out)[:w] # type: ignore
def final( def final(
self, into: Buffer | None = None, maclen: int = ABYTES_MIN self, into: Buffer | None = None, maclen: int = MACBYTES
) -> bytearray | memoryview: ) -> bytearray | memoryview:
"""Finalize encryption, writing any remaining bytes and the tag. """Finalize encryption, writing any remaining bytes and the tag.
@@ -692,7 +694,7 @@ class Decryptor:
Args: Args:
key: Key ({KEYBYTES=}). key: Key ({KEYBYTES=}).
nonce: Nonce ({NPUBBYTES=}). nonce: Nonce ({NONCEBYTES=}).
ad: Associated data used during encryption (optional). ad: Associated data used during encryption (optional).
Raises: Raises:
@@ -700,8 +702,8 @@ class Decryptor:
""" """
if len(key) != KEYBYTES: if len(key) != KEYBYTES:
raise TypeError(f"key length must be {KEYBYTES}") raise TypeError(f"key length must be {KEYBYTES}")
if len(nonce) != NPUBBYTES: if len(nonce) != NONCEBYTES:
raise TypeError(f"nonce length must be {NPUBBYTES}") raise TypeError(f"nonce length must be {NONCEBYTES}")
st, owner = new_aligned_struct("aegis256x4_state", ALIGNMENT) st, owner = new_aligned_struct("aegis256x4_state", ALIGNMENT)
_lib.aegis256x4_state_init( _lib.aegis256x4_state_init(
st, st,
@@ -796,10 +798,9 @@ def new_mac_state():
__all__ = [ __all__ = [
# constants # constants
"KEYBYTES", "KEYBYTES",
"NPUBBYTES", "NONCEBYTES",
"ABYTES_MIN", "MACBYTES",
"ABYTES_MAX", "MACBYTES_LONG",
"TAILBYTES_MAX",
"ALIGNMENT", "ALIGNMENT",
"RATE", "RATE",
# utility functions # utility functions

View File

@@ -3,7 +3,7 @@
Python benchmark matching src/test/benchmark.zig for all supported Aegis algorithms. Python benchmark matching src/test/benchmark.zig for all supported Aegis algorithms.
It performs two benchmarks with the same parameters as the Zig version: It performs two benchmarks with the same parameters as the Zig version:
- AEGIS encrypt (attached tag, maclen = ABYTES_MIN) - AEGIS encrypt (attached tag, maclen = MACBYTES)
- AEGIS MAC (clone state pattern) - AEGIS MAC (clone state pattern)
Output format and throughput units mirror the Zig benchmark (Mb/s). Output format and throughput units mirror the Zig benchmark (Mb/s).
@@ -31,12 +31,12 @@ def _random_bytes(n: int) -> bytes:
return os.urandom(n) return os.urandom(n)
def bench_encrypt(alg_name: str, a) -> None: def bench_encrypt(alg_name: str, ciph) -> None:
key = _random_bytes(a.KEYBYTES) key = _random_bytes(ciph.KEYBYTES)
nonce = _random_bytes(a.NPUBBYTES) nonce = _random_bytes(ciph.NONCEBYTES)
# Single buffer, as in Zig: c_out == m buffer, with tag appended # Single buffer, as in Zig: c_out == m buffer, with tag appended
maclen = a.ABYTES_MIN maclen = ciph.MACBYTES
buf = bytearray(MSG_LEN + maclen) buf = bytearray(MSG_LEN + maclen)
# Initialize buffer with random data # Initialize buffer with random data
buf[:] = _random_bytes(len(buf)) buf[:] = _random_bytes(len(buf))
@@ -45,7 +45,7 @@ def bench_encrypt(alg_name: str, a) -> None:
t0 = time.perf_counter() t0 = time.perf_counter()
for _ in range(ITERATIONS): for _ in range(ITERATIONS):
a.encrypt(key, nonce, mview, None, maclen=maclen, into=buf) ciph.encrypt(key, nonce, mview, None, maclen=maclen, into=buf)
t1 = time.perf_counter() t1 = time.perf_counter()
# Prevent any unrealistic optimization assumptions # Prevent any unrealistic optimization assumptions
@@ -59,21 +59,21 @@ def bench_encrypt(alg_name: str, a) -> None:
print(f"{alg_name}\t{throughput_mbps:10.2f} Mb/s") print(f"{alg_name}\t{throughput_mbps:10.2f} Mb/s")
def bench_mac(alg_name: str, a) -> None: def bench_mac(alg_name: str, ciph) -> None:
key = _random_bytes(a.KEYBYTES) key = _random_bytes(ciph.KEYBYTES)
nonce = _random_bytes(a.NPUBBYTES) nonce = _random_bytes(ciph.NONCEBYTES)
buf = bytearray(MSG_LEN) buf = bytearray(MSG_LEN)
buf[:] = _random_bytes(len(buf)) buf[:] = _random_bytes(len(buf))
mac0 = a.Mac(key, nonce) mac0 = ciph.Mac(key, nonce)
mac_out = bytearray(a.ABYTES_MAX) mac_out = bytearray(ciph.MACBYTES_LONG)
t0 = time.perf_counter() t0 = time.perf_counter()
for _ in range(ITERATIONS): for _ in range(ITERATIONS):
mac = mac0.clone() mac = mac0.clone()
mac.update(buf) mac.update(buf)
mac.final(maclen=a.ABYTES_MAX, into=mac_out) mac.final(maclen=ciph.MACBYTES_LONG, into=mac_out)
t1 = time.perf_counter() t1 = time.perf_counter()
_ = mac_out[0] _ = mac_out[0]

View File

@@ -103,21 +103,44 @@ def generate_cdef(include_dir: pathlib.Path) -> str:
return "\n".join(lines) return "\n".join(lines)
def extract_constants(common_h_path: pathlib.Path) -> Tuple[int, int]: def extract_constants(
content = common_h_path.read_text(encoding="utf-8") common_h_path: pathlib.Path, header_path: pathlib.Path
) -> Dict[str, int]:
"""Extract constants from common.h (ALIGNMENT, RATE) and main header (KEYBYTES, NPUBBYTES, ABYTES_*)."""
constants = {}
align_match = re.search(r"^\s*#define\s+ALIGNMENT\s+(\d+)", content, re.MULTILINE) # Extract from common.h
rate_match = re.search(r"^\s*#define\s+RATE\s+(\d+)", content, re.MULTILINE) common_content = common_h_path.read_text(encoding="utf-8")
align_match = re.search(
r"^\s*#define\s+ALIGNMENT\s+(\d+)", common_content, re.MULTILINE
)
rate_match = re.search(r"^\s*#define\s+RATE\s+(\d+)", common_content, re.MULTILINE)
if not align_match or not rate_match: if not align_match or not rate_match:
raise ValueError( raise ValueError(
f"Could not extract ALIGNMENT and/or RATE from {common_h_path}" f"Could not extract ALIGNMENT and/or RATE from {common_h_path}"
) )
return int(align_match.group(1)), int(rate_match.group(1)) constants["ALIGNMENT"] = int(align_match.group(1))
constants["RATE"] = int(rate_match.group(1))
# Extract from main header
header_content = header_path.read_text(encoding="utf-8")
variant = header_path.stem # e.g., "aegis256x4"
for const_name in ["KEYBYTES", "NPUBBYTES", "ABYTES_MIN", "ABYTES_MAX"]:
pattern = rf"^\s*#define\s+{variant}_{const_name}\s+(\d+)"
match = re.search(pattern, header_content, re.MULTILINE)
if not match:
raise ValueError(f"Could not extract {const_name} from {header_path}")
constants[const_name] = int(match.group(1))
return constants
def extract_all_constants(libaegis_src_dir: pathlib.Path) -> Dict[str, Tuple[int, int]]: def extract_all_constants(
libaegis_src_dir: pathlib.Path, include_dir: pathlib.Path
) -> Dict[str, Dict[str, int]]:
variants = [ variants = [
"aegis128l", "aegis128l",
"aegis128x2", "aegis128x2",
@@ -130,13 +153,18 @@ def extract_all_constants(libaegis_src_dir: pathlib.Path) -> Dict[str, Tuple[int
for variant in variants: for variant in variants:
common_h = libaegis_src_dir / variant / f"{variant}_common.h" common_h = libaegis_src_dir / variant / f"{variant}_common.h"
header_h = include_dir / f"{variant}.h"
if not common_h.exists(): if not common_h.exists():
print(f"Warning: {common_h} not found, skipping {variant}", file=sys.stderr) print(f"Warning: {common_h} not found, skipping {variant}", file=sys.stderr)
continue continue
if not header_h.exists():
print(f"Warning: {header_h} not found, skipping {variant}", file=sys.stderr)
continue
try: try:
alignment, rate = extract_constants(common_h) constants[variant] = extract_constants(common_h, header_h)
constants[variant] = (alignment, rate)
except Exception as e: except Exception as e:
print(f"Error extracting constants from {variant}: {e}", file=sys.stderr) print(f"Error extracting constants from {variant}: {e}", file=sys.stderr)
@@ -155,7 +183,8 @@ def algo_label(name: str) -> str:
return "AEGIS-" + name[5:].upper() return "AEGIS-" + name[5:].upper()
def generate_variant(template_src: str, variant: str, alignment: int, rate: int) -> str: def generate_variant(template_src: str, variant: str, constants: Dict[str, int]) -> str:
"""Generate a variant module from the template with substituted constants."""
s = template_src.replace("aegis256x4", variant).replace( s = template_src.replace("aegis256x4", variant).replace(
"AEGIS-256X4", algo_label(variant) "AEGIS-256X4", algo_label(variant)
) )
@@ -165,15 +194,34 @@ def generate_variant(template_src: str, variant: str, alignment: int, rate: int)
"# All modules are generated from aegis256x4.py by tools/generate.py!", "# All modules are generated from aegis256x4.py by tools/generate.py!",
s, s,
) )
s = replace_constant(ALIGNMENT_RE, s, alignment) s = replace_constant(ALIGNMENT_RE, s, constants["ALIGNMENT"])
s = replace_constant(RATE_RE, s, rate) s = replace_constant(RATE_RE, s, constants["RATE"])
# Replace the constant assignments
s = re.sub(r"KEYBYTES = \d+", f"KEYBYTES = {constants['KEYBYTES']}", s)
s = re.sub(
r"NONCEBYTES = \d+",
f"NONCEBYTES = {constants['NPUBBYTES']}",
s,
)
s = re.sub(
r"MACBYTES = \d+",
f"MACBYTES = {constants['ABYTES_MIN']}",
s,
)
s = re.sub(
r"MACBYTES_LONG = \d+",
f"MACBYTES_LONG = {constants['ABYTES_MAX']}",
s,
)
return s return s
def generate_python_modules( def generate_python_modules(
template_path: pathlib.Path, template_path: pathlib.Path,
output_dir: pathlib.Path, output_dir: pathlib.Path,
constants: Dict[str, Tuple[int, int]], constants: Dict[str, Dict[str, int]],
) -> Tuple[list[pathlib.Path], list[pathlib.Path]]: ) -> Tuple[list[pathlib.Path], list[pathlib.Path]]:
if not template_path.exists(): if not template_path.exists():
raise FileNotFoundError(f"Template not found: {template_path}") raise FileNotFoundError(f"Template not found: {template_path}")
@@ -184,13 +232,37 @@ def generate_python_modules(
updated = [] updated = []
unchanged = [] unchanged = []
for variant, (alignment, rate) in constants.items(): for variant, const_dict in constants.items():
dst = output_dir / f"{variant}.py" dst = output_dir / f"{variant}.py"
if variant == "aegis256x4": if variant == "aegis256x4":
new_content = replace_constant(ALIGNMENT_RE, template_src, alignment) # Update template in place with its own constants
new_content = replace_constant(RATE_RE, new_content, rate) new_content = replace_constant(
ALIGNMENT_RE, template_src, const_dict["ALIGNMENT"]
)
new_content = replace_constant(RATE_RE, new_content, const_dict["RATE"])
# Replace the constant assignments for the template itself
new_content = re.sub(
r"KEYBYTES = \d+",
f"KEYBYTES = {const_dict['KEYBYTES']}",
new_content,
)
new_content = re.sub(
r"NONCEBYTES = \d+",
f"NONCEBYTES = {const_dict['NPUBBYTES']}",
new_content,
)
new_content = re.sub(
r"MACBYTES = \d+",
f"MACBYTES = {const_dict['ABYTES_MIN']}",
new_content,
)
new_content = re.sub(
r"MACBYTES_LONG = \d+",
f"MACBYTES_LONG = {const_dict['ABYTES_MAX']}",
new_content,
)
else: else:
new_content = generate_variant(template_src, variant, alignment, rate) new_content = generate_variant(template_src, variant, const_dict)
if dst.exists() and dst.read_text(encoding="utf-8") == new_content: if dst.exists() and dst.read_text(encoding="utf-8") == new_content:
unchanged.append(dst) unchanged.append(dst)
@@ -216,7 +288,7 @@ def main() -> int:
return 1 return 1
print("Step 1: Extracting constants from C sources...", file=sys.stderr) print("Step 1: Extracting constants from C sources...", file=sys.stderr)
constants = extract_all_constants(libaegis_src_dir) constants = extract_all_constants(libaegis_src_dir, include_dir)
if not constants: if not constants:
print("Error: No constants extracted", file=sys.stderr) print("Error: No constants extracted", file=sys.stderr)
return 1 return 1
@@ -243,7 +315,9 @@ def main() -> int:
if unchanged: if unchanged:
print( print(
" - No changes to", " - No changes to",
f"{len(unchanged)} modules" if len(unchanged) > 1 else unchanged[0], f"{len(unchanged)} modules"
if len(unchanged) > 1
else unchanged[0].name,
file=sys.stderr, file=sys.stderr,
) )
except Exception as e: except Exception as e: