From 640e812908faecb292cbee03f4c0077776078421 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 23 Dec 2025 18:25:03 +0000 Subject: [PATCH] Project renamed to aeg, preparing for PyPI release. --- .gitignore | 6 ++--- BUILD.md | 14 ++++++------ MANIFEST.in | 2 +- README.md | 35 ++++++++++++++++++------------ pyproject.toml | 9 ++++---- setup.py | 6 ++--- {pyaegis => src/aeg}/__init__.py | 0 {pyaegis => src/aeg}/_loader.py | 2 +- {pyaegis => src/aeg}/aegis128l.py | 0 {pyaegis => src/aeg}/aegis128x2.py | 0 {pyaegis => src/aeg}/aegis128x4.py | 0 {pyaegis => src/aeg}/aegis256.py | 0 {pyaegis => src/aeg}/aegis256x2.py | 0 {pyaegis => src/aeg}/aegis256x4.py | 0 {pyaegis => src/aeg}/aegis_cdef.h | 0 {pyaegis => src/aeg}/benchmark.py | 2 +- {pyaegis => src/aeg}/util.py | 2 +- tests/test_encrypt.py | 2 +- tests/test_mac.py | 2 +- tests/test_raises.py | 2 +- tools/build_backend.py | 2 +- tools/generate.py | 2 +- tools/release.py | 10 ++++----- 23 files changed, 53 insertions(+), 45 deletions(-) rename {pyaegis => src/aeg}/__init__.py (100%) rename {pyaegis => src/aeg}/_loader.py (72%) rename {pyaegis => src/aeg}/aegis128l.py (100%) rename {pyaegis => src/aeg}/aegis128x2.py (100%) rename {pyaegis => src/aeg}/aegis128x4.py (100%) rename {pyaegis => src/aeg}/aegis256.py (100%) rename {pyaegis => src/aeg}/aegis256x2.py (100%) rename {pyaegis => src/aeg}/aegis256x4.py (100%) rename {pyaegis => src/aeg}/aegis_cdef.h (100%) rename {pyaegis => src/aeg}/benchmark.py (96%) rename {pyaegis => src/aeg}/util.py (98%) diff --git a/.gitignore b/.gitignore index 70f5900..dc02d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,9 @@ *.egg-info /dist /build -/pyaegis/build -/pyaegis/_aegis*.so -/pyaegis/_aegis*.pyd +/src/aeg/build +/src/aeg/_aegis*.so +/src/aeg/_aegis*.pyd __pycache__ !.gitignore !.gitmodules diff --git a/BUILD.md b/BUILD.md index 4477f78..286c6c7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,6 +1,6 @@ -# Building pyaegis +# Building aeg -This document contains instructions for developers who want to build pyaegis from source. +This document contains instructions for developers who want to build aeg from source. ## Prerequisites @@ -11,7 +11,7 @@ This document contains instructions for developers who want to build pyaegis fro ### Installing Zig -pyaegis uses Zig to build the underlying libaegis C library. Install Zig from [ziglang.org/download](https://ziglang.org/download/) or using your package manager: +aeg uses Zig to build the underlying libaegis C library. Install Zig from [ziglang.org/download](https://ziglang.org/download/) or using your package manager: - **macOS**: `brew install zig` - **Linux**: See [Zig installation guide](https://github.com/ziglang/zig/wiki/Install-Zig-from-a-Package-Manager) @@ -22,8 +22,8 @@ pyaegis uses Zig to build the underlying libaegis C library. Install Zig from [z Clone the repository with submodules: ```fish -git clone --recursive https://github.com/LeoVasanko/pyaegis.git -cd pyaegis +git clone --recursive https://github.com/LeoVasanko/aeg.git +cd aeg ``` If you already cloned without `--recursive`, initialize submodules: @@ -74,7 +74,7 @@ This creates files in the `dist/` directory. ## Code Generation -The Python modules and CFFI definitions are generated from C sources and templates. If you modify the core implementation in `pyaegis/aegis256x4.py` or update libaegis headers, regenerate all files: +The Python modules and CFFI definitions are generated from C sources and templates. If you modify the core implementation in `src/aeg/aegis256x4.py` or update libaegis headers, regenerate all files: ```fish python tools/generate.py @@ -100,7 +100,7 @@ If you cannot install Zig, you may manually compile in the libaegis folder (Zig, ## Project Structure -- `pyaegis/` - Python package source +- `src/aeg/` - Python package source - `libaegis/` - C library source (submodule) - `tests/` - Test suite - `tools/` - Code generation scripts and `build_backend.py` used to build libaegis diff --git a/MANIFEST.in b/MANIFEST.in index d24e1a7..40b1831 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include pyaegis/aegis_cdef.h +include src/aeg/aegis_cdef.h include setup.py include tools/build_backend.py include BUILD.md diff --git a/README.md b/README.md index c09c64c..1de4205 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -# pyaegis +# AEGIS Cipher Python Binding -[![PyPI version](https://badge.fury.io/py/pyaegis.svg)](https://badge.fury.io/py/pyaegis) +[![PyPI version](https://badge.fury.io/py/aeg.svg)](https://badge.fury.io/py/aeg) -Safe Python bindings for the AEGIS family of very fast authenticated encryption algorithms (via libaegis). +Safe Python bindings for the AEGIS family of very fast authenticated encryption algorithms via libaegis. The module runs without compilation required on Windows, Mac and Linux (has precompiled wheels). For other platforms compilation is performed at install time. + +AEGIS enables extremely fast Encryption, MAC and CSPRNG - many times faster than AES, ChaCha20 or traditional random number generators. Authenticated Encryption with Additional Data is supported with the MAC derived from the cipher state at the end, making it different from other AEADs like AES-GCM and ChaCha20-Poly1305. The whole internal state thus depends on the prior data, and it is neither Encrypt-Then-Mac nor Mac-The-Encrypt scheme when both features are used together. ## Install Using [uv](https://docs.astral.sh/uv/getting-started/installation/): ```fish -uv pip install git+https://github.com/LeoVasanko/pyaegis.git +uv pip install aeg ``` For development builds, see BUILD.md. @@ -27,7 +29,7 @@ All submodules expose the same API; pick one for your key/nonce size and platfor Normal authenticated encryption using the AEGIS-128X4 algorithm: ```python -from pyaegis import aegis128x4 as ciph +from aeg import aegis128x4 as ciph key = ciph.random_key() # Secret key (stored securely) nonce = ciph.random_nonce() # Public nonce (recreated for each message) @@ -123,7 +125,7 @@ Constants (per module): NAME, KEYBYTES, NONCEBYTES, MACBYTES, MACBYTES_LONG, RAT A cryptographically secure keyed hash is produced. The example uses all zeroes for the nonce to always produce the same hash for the same key: ```python -from pyaegis import aegis256x4 as ciph +from aeg import aegis256x4 as ciph key, nonce = ciph.random_key(), bytes(ciph.NONCEBYTES) mac = ciph.mac(key, nonce, b"message", maclen=32) @@ -146,7 +148,7 @@ b.verify(mac) # Raises ValueError Keeping the ciphertext, mac and ad separate. The ad represents a file header that needs to be tamper proofed. ```python -from pyaegis import aegis256x4 as ciph +from aeg import aegis256x4 as ciph key, nonce = ciph.random_key(), ciph.random_nonce() ct, mac = ciph.encrypt_detached(key, nonce, b"secret", ad=b"header") @@ -162,7 +164,7 @@ ciph.wipe(pt) Class-based interface for incremental updates is an alternative to the one-shot functions. Not to be confused with separately verified ciphertext frames (see the next example). ```python -from pyaegis import aegis256x4 as ciph +from aeg import aegis256x4 as ciph key, nonce = ciph.random_key(), ciph.random_nonce() enc = ciph.Encryptor(key, nonce, ad=b"header", maclen=16) @@ -182,7 +184,7 @@ It is often practical to split larger messages into frames that can be individua ```python # Encryption settings -from pyaegis import aegis128x4 as ciph +from aeg import aegis128x4 as ciph key = b"sixteenbyte key!" # 16 bytes secret key for aegis128* algorithms framebytes = 80 # In real applications 1 MiB or more is practical maclen = ciph.MACBYTES # 16 @@ -202,7 +204,7 @@ with open("encrypted.bin", "wb") as f: ```python # Decryption needs same values as encryption -from pyaegis import aegis128x4 as ciph +from aeg import aegis128x4 as ciph key = b"sixteenbyte key!" framebytes = 80 maclen = ciph.MACBYTES @@ -223,7 +225,7 @@ with open("encrypted.bin", "rb") as f: The stream generator is much faster than any traditional random number generator, cryptographically secure and seekable. Use `random_key()` for unpredictable output. ```python -from pyaegis import aegis128x4 as ciph +from aeg import aegis128x4 as ciph key = b"SeedForReplay001" # A non-random deterministic seed (16 bytes) nonce = bytearray(ciph.NONCEBYTES) # All-zeroes nonce @@ -247,7 +249,7 @@ Foreign arrays can be used. This example fills a Numpy array with random integer ```python import numpy as np -from pyaegis import aegis128x4 as ciph +from aeg import aegis128x4 as ciph key, nonce = ciph.random_key(), ciph.random_nonce() arr = np.empty(10, dtype=np.uint64) # Uninitialised integer array @@ -258,7 +260,7 @@ print(arr) In-place operations are supported when the input and the output point to the same location in memory. When using attached MAC tag, the input buffer needs to be sliced to correct length: ```python -from pyaegis import aegis256x4 as ciph +from aeg import aegis256x4 as ciph key, nonce = ciph.random_key(), ciph.random_nonce() buf = memoryview(bytearray(1000)) # memoryview[:len] is still in the same buffer (no copy) buf[:7] = b"message" @@ -279,7 +281,7 @@ Runtime CPU feature detection selects optimized code paths (AES-NI, ARM Crypto, Benchmarks using the included benchmark module, run on Intel i7-14700, linux, single core (the software is not multithreaded). Note that the results are in megabits per second, not bytes. The CPU lacks AVX-512 that makes the X4 variants faster on AMD hardware. ```fish -$ uv run -m pyaegis.benchmark +$ uv run -m aeg.benchmark AEGIS-256 103166.24 Mb/s AEGIS-256X2 184225.50 Mb/s AEGIS-256X4 194018.26 Mb/s @@ -310,3 +312,8 @@ AEGIS-256 MAC 116776.62 Mb/s AEGIS-256X2 MAC 224150.04 Mb/s AEGIS-256X4 MAC 392088.05 Mb/s ``` + + +## Alternatives + +There is also a package named `pyaegis` on PyPI that is unrelated to this module, but that also binds to libaegis C library. Note that there are also a number of modules named `aegis` from different packages not at all related to the encryption algorithm. diff --git a/pyproject.toml b/pyproject.toml index d71cb2a..f200d13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "build_backend" backend-path = ["tools"] [project] -name = "pyaegis" +name = "aeg" dynamic = ["version"] description = "Python bindings for libaegis" requires-python = ">=3.10" @@ -20,7 +20,7 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/LeoVasanko/pyaegis" +Homepage = "https://github.com/LeoVasanko/aeg" [dependency-groups] dev = [ @@ -31,9 +31,10 @@ dev = [ ] [tool.setuptools] -packages = ["pyaegis"] +package-dir = {"" = "src"} +packages = ["aeg"] [tool.setuptools.package-data] -pyaegis = ["*.h", "*.so", "*.pyd"] +aeg = ["*.h", "*.so", "*.pyd"] [tool.setuptools_scm] diff --git a/setup.py b/setup.py index 00a017d..3708ace 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -"""Setup script for pyaegis - builds CFFI extension with libaegis C library.""" +"""Setup script for aeg - builds CFFI extension with libaegis C library.""" import sys from pathlib import Path @@ -20,7 +20,7 @@ if not libaegis_include.exists(): include_dirs = [str(libaegis_include)] # Read the CDEF header -cdef_path = Path(__file__).parent / "pyaegis" / "aegis_cdef.h" +cdef_path = Path(__file__).parent / "src" / "aeg" / "aegis_cdef.h" cdef_content = cdef_path.read_text(encoding="utf-8") # Create CFFI builder @@ -29,7 +29,7 @@ ffibuilder.cdef(cdef_content) # Set the source ffibuilder.set_source( - "pyaegis._aegis", # module name + "aeg._aegis", # module name """ #include "aegis.h" #include "aegis128l.h" diff --git a/pyaegis/__init__.py b/src/aeg/__init__.py similarity index 100% rename from pyaegis/__init__.py rename to src/aeg/__init__.py diff --git a/pyaegis/_loader.py b/src/aeg/_loader.py similarity index 72% rename from pyaegis/_loader.py rename to src/aeg/_loader.py index edad13c..cd6eeb2 100644 --- a/pyaegis/_loader.py +++ b/src/aeg/_loader.py @@ -1,6 +1,6 @@ """Loader for libaegis CFFI extension module.""" -from pyaegis._aegis import ffi, lib +from aeg._aegis import ffi, lib __all__ = ["ffi", "lib"] diff --git a/pyaegis/aegis128l.py b/src/aeg/aegis128l.py similarity index 100% rename from pyaegis/aegis128l.py rename to src/aeg/aegis128l.py diff --git a/pyaegis/aegis128x2.py b/src/aeg/aegis128x2.py similarity index 100% rename from pyaegis/aegis128x2.py rename to src/aeg/aegis128x2.py diff --git a/pyaegis/aegis128x4.py b/src/aeg/aegis128x4.py similarity index 100% rename from pyaegis/aegis128x4.py rename to src/aeg/aegis128x4.py diff --git a/pyaegis/aegis256.py b/src/aeg/aegis256.py similarity index 100% rename from pyaegis/aegis256.py rename to src/aeg/aegis256.py diff --git a/pyaegis/aegis256x2.py b/src/aeg/aegis256x2.py similarity index 100% rename from pyaegis/aegis256x2.py rename to src/aeg/aegis256x2.py diff --git a/pyaegis/aegis256x4.py b/src/aeg/aegis256x4.py similarity index 100% rename from pyaegis/aegis256x4.py rename to src/aeg/aegis256x4.py diff --git a/pyaegis/aegis_cdef.h b/src/aeg/aegis_cdef.h similarity index 100% rename from pyaegis/aegis_cdef.h rename to src/aeg/aegis_cdef.h diff --git a/pyaegis/benchmark.py b/src/aeg/benchmark.py similarity index 96% rename from pyaegis/benchmark.py rename to src/aeg/benchmark.py index a718c45..abad820 100644 --- a/pyaegis/benchmark.py +++ b/src/aeg/benchmark.py @@ -12,7 +12,7 @@ Output format and throughput units mirror the Zig benchmark (Mb/s). import secrets import time -from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 MSG_LEN = 16384000 # 16 000 KiB ITERATIONS = 100 diff --git a/pyaegis/util.py b/src/aeg/util.py similarity index 98% rename from pyaegis/util.py rename to src/aeg/util.py index ea4fdd9..2469e47 100644 --- a/pyaegis/util.py +++ b/src/aeg/util.py @@ -1,4 +1,4 @@ -"""Utility helpers for pyaegis. +"""Utility helpers for aeg. Currently provides Python-side aligned allocation helpers that avoid relying on libc/posix_memalign. Memory is owned by Python; C code only borrows it. diff --git a/tests/test_encrypt.py b/tests/test_encrypt.py index 2841004..c4de0f3 100644 --- a/tests/test_encrypt.py +++ b/tests/test_encrypt.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest -from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 from .util import random_split_bytes diff --git a/tests/test_mac.py b/tests/test_mac.py index 7e71e4b..7e7decb 100644 --- a/tests/test_mac.py +++ b/tests/test_mac.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest -from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 from .util import random_split_bytes diff --git a/tests/test_raises.py b/tests/test_raises.py index b767123..73386fd 100644 --- a/tests/test_raises.py +++ b/tests/test_raises.py @@ -6,7 +6,7 @@ after calling final(), preventing accidental misuse. import pytest -from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 # All AEGIS algorithm modules ALL_ALGORITHMS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4] diff --git a/tools/build_backend.py b/tools/build_backend.py index 562c0ce..08a2928 100644 --- a/tools/build_backend.py +++ b/tools/build_backend.py @@ -15,7 +15,7 @@ def _check_zig_available(): "\n" + "=" * 70 + "\n" "ERROR: Zig compiler not found!\n" "\n" - "Building pyaegis requires the Zig compiler to build the libaegis\n" + "Building aeg requires the Zig compiler to build the libaegis\n" "static library. Please install Zig before building this package.\n" "\n" "Installation instructions:\n" diff --git a/tools/generate.py b/tools/generate.py index 4fb0e2d..0934724 100644 --- a/tools/generate.py +++ b/tools/generate.py @@ -277,7 +277,7 @@ def main() -> int: root = pathlib.Path(__file__).parent.parent libaegis_src_dir = root / "libaegis" / "src" include_dir = libaegis_src_dir / "include" - pyaegis_dir = root / "pyaegis" + pyaegis_dir = root / "src" / "aeg" if not include_dir.exists(): print(f"Include directory not found: {include_dir}", file=sys.stderr) diff --git a/tools/release.py b/tools/release.py index d19e62f..75d5888 100644 --- a/tools/release.py +++ b/tools/release.py @@ -112,8 +112,8 @@ def normalize_line_endings(repo_root: Path): """Normalize all text files to LF line endings.""" # Patterns for files to normalize patterns = [ - "pyaegis/**/*.py", - "pyaegis/**/*.h", + "src/aeg/**/*.py", + "src/aeg/**/*.h", "tests/**/*.py", "tools/**/*.py", "*.py", @@ -168,7 +168,7 @@ def main(): # Main header for the packaging process print(f"\n{'=' * 70}") print( - f"Packaging pyaegis-{version}" + f"Packaging aeg-{version}" + (" for release" if is_release else " (not release)") ) print(f"Building wheels for Python versions: {', '.join(PYTHON_VERSIONS)}") @@ -202,7 +202,7 @@ def main(): continue # Find the wheel for this version - wheel_pattern = f"pyaegis-*-cp{py_version.replace('.', '')}-*.whl" + wheel_pattern = f"aeg-*-cp{py_version.replace('.', '')}-*.whl" wheels = list(dist_dir.glob(wheel_pattern)) if not wheels: print(f"✗ Could not find wheel for Python {py_version}", file=sys.stderr) @@ -241,7 +241,7 @@ def main(): "--with", str(wheel), "-m", - "pyaegis.benchmark", + "aeg.benchmark", ] if not run_command(bench_cmd, f"Running benchmark for Python {py_version}"): print(f"✗ Benchmark failed for Python {py_version}", file=sys.stderr)