Rename module to pyaegis, implement build with zig.

This commit is contained in:
Leo Vasanko
2025-11-06 10:47:54 -06:00
parent 8898cec50f
commit 42ddaac6bc
16 changed files with 354 additions and 494 deletions
-467
View File
@@ -1,467 +0,0 @@
"""Dynamic loader for libaegis using CFFI (ABI mode).
This module avoids compiling any C shim. It loads the shared library built by
the project (e.g., build-shared/libaegis.so) or a system-installed libaegis.
Environment variables:
- AEGIS_LIB_PATH: full path to the libaegis shared library to load
- AEGIS_LIB_DIR: directory containing the shared library (libaegis.so)
Exports:
- ffi: a cffi.FFI instance with the libaegis API declared
- lib: the loaded libaegis shared library (ffi.dlopen)
- libc: the C runtime (for posix_memalign/free on POSIX)
- alloc_aligned(size, alignment): return (void*) pointer with requested alignment
"""
import os
import sys
from typing import Any
from cffi import FFI
try:
# ctypes is only used to resolve libc reliably across platforms
import ctypes.util as _ctypes_util # type: ignore
except Exception: # pragma: no cover - very unlikely to happen
_ctypes_util = None # type: ignore[assignment]
ffi = FFI()
# Public API from headers (aegis.h and all aegis variant headers). Keep it macro-free.
ffi.cdef(
r"""
typedef unsigned char uint8_t;
typedef unsigned long size_t;
/* aegis.h */
int aegis_init(void);
int aegis_verify_16(const uint8_t *x, const uint8_t *y);
int aegis_verify_32(const uint8_t *x, const uint8_t *y);
/* aegis128l.h */
typedef struct {
/* CRYPTO_ALIGN(32) */ uint8_t opaque[256];
} aegis128l_state;
typedef struct {
/* CRYPTO_ALIGN(32) */ uint8_t opaque[384];
} aegis128l_mac_state;
size_t aegis128l_keybytes(void);
size_t aegis128l_npubbytes(void);
size_t aegis128l_abytes_min(void);
size_t aegis128l_abytes_max(void);
size_t aegis128l_tailbytes_max(void);
int aegis128l_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m,
size_t mlen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis128l_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis128l_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
int aegis128l_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
void aegis128l_state_init(aegis128l_state *st_, const uint8_t *ad, size_t adlen,
const uint8_t *npub, const uint8_t *k);
int aegis128l_state_encrypt_update(aegis128l_state *st_, uint8_t *c, size_t clen_max,
size_t *written, const uint8_t *m, size_t mlen);
int aegis128l_state_encrypt_detached_final(aegis128l_state *st_, uint8_t *c, size_t clen_max,
size_t *written, uint8_t *mac, size_t maclen);
int aegis128l_state_encrypt_final(aegis128l_state *st_, uint8_t *c, size_t clen_max,
size_t *written, size_t maclen);
int aegis128l_state_decrypt_detached_update(aegis128l_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *c, size_t clen);
int aegis128l_state_decrypt_detached_final(aegis128l_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *mac, size_t maclen);
void aegis128l_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k);
void aegis128l_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen,
const uint8_t *npub, const uint8_t *k);
void aegis128l_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen,
const uint8_t *npub, const uint8_t *k);
void aegis128l_mac_init(aegis128l_mac_state *st_, const uint8_t *k, const uint8_t *npub);
int aegis128l_mac_update(aegis128l_mac_state *st_, const uint8_t *m, size_t mlen);
int aegis128l_mac_final(aegis128l_mac_state *st_, uint8_t *mac, size_t maclen);
int aegis128l_mac_verify(aegis128l_mac_state *st_, const uint8_t *mac, size_t maclen);
void aegis128l_mac_reset(aegis128l_mac_state *st_);
void aegis128l_mac_state_clone(aegis128l_mac_state *dst, const aegis128l_mac_state *src);
/* aegis128x2.h */
typedef struct {
/* CRYPTO_ALIGN(64) */ uint8_t opaque[448];
} aegis128x2_state;
typedef struct {
/* CRYPTO_ALIGN(64) */ uint8_t opaque[704];
} aegis128x2_mac_state;
size_t aegis128x2_keybytes(void);
size_t aegis128x2_npubbytes(void);
size_t aegis128x2_abytes_min(void);
size_t aegis128x2_abytes_max(void);
size_t aegis128x2_tailbytes_max(void);
int aegis128x2_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m,
size_t mlen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis128x2_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis128x2_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
int aegis128x2_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
void aegis128x2_state_init(aegis128x2_state *st_, const uint8_t *ad, size_t adlen,
const uint8_t *npub, const uint8_t *k);
int aegis128x2_state_encrypt_update(aegis128x2_state *st_, uint8_t *c, size_t clen_max,
size_t *written, const uint8_t *m, size_t mlen);
int aegis128x2_state_encrypt_detached_final(aegis128x2_state *st_, uint8_t *c, size_t clen_max,
size_t *written, uint8_t *mac, size_t maclen);
int aegis128x2_state_encrypt_final(aegis128x2_state *st_, uint8_t *c, size_t clen_max,
size_t *written, size_t maclen);
int aegis128x2_state_decrypt_detached_update(aegis128x2_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *c, size_t clen);
int aegis128x2_state_decrypt_detached_final(aegis128x2_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *mac, size_t maclen);
void aegis128x2_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k);
void aegis128x2_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen,
const uint8_t *npub, const uint8_t *k);
void aegis128x2_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen,
const uint8_t *npub, const uint8_t *k);
void aegis128x2_mac_init(aegis128x2_mac_state *st_, const uint8_t *k, const uint8_t *npub);
int aegis128x2_mac_update(aegis128x2_mac_state *st_, const uint8_t *m, size_t mlen);
int aegis128x2_mac_final(aegis128x2_mac_state *st_, uint8_t *mac, size_t maclen);
int aegis128x2_mac_verify(aegis128x2_mac_state *st_, const uint8_t *mac, size_t maclen);
void aegis128x2_mac_reset(aegis128x2_mac_state *st_);
void aegis128x2_mac_state_clone(aegis128x2_mac_state *dst, const aegis128x2_mac_state *src);
/* aegis128x4.h */
typedef struct {
/* CRYPTO_ALIGN(64) */ uint8_t opaque[832];
} aegis128x4_state;
typedef struct {
/* CRYPTO_ALIGN(64) */ uint8_t opaque[1344];
} aegis128x4_mac_state;
size_t aegis128x4_keybytes(void);
size_t aegis128x4_npubbytes(void);
size_t aegis128x4_abytes_min(void);
size_t aegis128x4_abytes_max(void);
size_t aegis128x4_tailbytes_max(void);
int aegis128x4_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m,
size_t mlen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis128x4_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis128x4_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
int aegis128x4_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
void aegis128x4_state_init(aegis128x4_state *st_, const uint8_t *ad, size_t adlen,
const uint8_t *npub, const uint8_t *k);
int aegis128x4_state_encrypt_update(aegis128x4_state *st_, uint8_t *c, size_t clen_max,
size_t *written, const uint8_t *m, size_t mlen);
int aegis128x4_state_encrypt_detached_final(aegis128x4_state *st_, uint8_t *c, size_t clen_max,
size_t *written, uint8_t *mac, size_t maclen);
int aegis128x4_state_encrypt_final(aegis128x4_state *st_, uint8_t *c, size_t clen_max,
size_t *written, size_t maclen);
int aegis128x4_state_decrypt_detached_update(aegis128x4_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *c, size_t clen);
int aegis128x4_state_decrypt_detached_final(aegis128x4_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *mac, size_t maclen);
void aegis128x4_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k);
void aegis128x4_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen,
const uint8_t *npub, const uint8_t *k);
void aegis128x4_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen,
const uint8_t *npub, const uint8_t *k);
void aegis128x4_mac_init(aegis128x4_mac_state *st_, const uint8_t *k, const uint8_t *npub);
int aegis128x4_mac_update(aegis128x4_mac_state *st_, const uint8_t *m, size_t mlen);
int aegis128x4_mac_final(aegis128x4_mac_state *st_, uint8_t *mac, size_t maclen);
int aegis128x4_mac_verify(aegis128x4_mac_state *st_, const uint8_t *mac, size_t maclen);
void aegis128x4_mac_reset(aegis128x4_mac_state *st_);
void aegis128x4_mac_state_clone(aegis128x4_mac_state *dst, const aegis128x4_mac_state *src);
/* aegis256.h */
typedef struct {
/* CRYPTO_ALIGN(16) */ uint8_t opaque[192];
} aegis256_state;
typedef struct {
/* CRYPTO_ALIGN(16) */ uint8_t opaque[288];
} aegis256_mac_state;
size_t aegis256_keybytes(void);
size_t aegis256_npubbytes(void);
size_t aegis256_abytes_min(void);
size_t aegis256_abytes_max(void);
size_t aegis256_tailbytes_max(void);
int aegis256_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m,
size_t mlen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis256_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis256_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
int aegis256_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
void aegis256_state_init(aegis256_state *st_, const uint8_t *ad, size_t adlen,
const uint8_t *npub, const uint8_t *k);
int aegis256_state_encrypt_update(aegis256_state *st_, uint8_t *c, size_t clen_max,
size_t *written, const uint8_t *m, size_t mlen);
int aegis256_state_encrypt_detached_final(aegis256_state *st_, uint8_t *c, size_t clen_max,
size_t *written, uint8_t *mac, size_t maclen);
int aegis256_state_encrypt_final(aegis256_state *st_, uint8_t *c, size_t clen_max,
size_t *written, size_t maclen);
int aegis256_state_decrypt_detached_update(aegis256_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *c, size_t clen);
int aegis256_state_decrypt_detached_final(aegis256_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *mac, size_t maclen);
void aegis256_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k);
void aegis256_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen,
const uint8_t *npub, const uint8_t *k);
void aegis256_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen,
const uint8_t *npub, const uint8_t *k);
void aegis256_mac_init(aegis256_mac_state *st_, const uint8_t *k, const uint8_t *npub);
int aegis256_mac_update(aegis256_mac_state *st_, const uint8_t *m, size_t mlen);
int aegis256_mac_final(aegis256_mac_state *st_, uint8_t *mac, size_t maclen);
int aegis256_mac_verify(aegis256_mac_state *st_, const uint8_t *mac, size_t maclen);
void aegis256_mac_reset(aegis256_mac_state *st_);
void aegis256_mac_state_clone(aegis256_mac_state *dst, const aegis256_mac_state *src);
/* aegis256x2.h */
typedef struct {
/* CRYPTO_ALIGN(32) */ uint8_t opaque[320];
} aegis256x2_state;
typedef struct {
/* CRYPTO_ALIGN(32) */ uint8_t opaque[512];
} aegis256x2_mac_state;
size_t aegis256x2_keybytes(void);
size_t aegis256x2_npubbytes(void);
size_t aegis256x2_abytes_min(void);
size_t aegis256x2_abytes_max(void);
size_t aegis256x2_tailbytes_max(void);
int aegis256x2_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m,
size_t mlen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis256x2_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis256x2_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
int aegis256x2_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
void aegis256x2_state_init(aegis256x2_state *st_, const uint8_t *ad, size_t adlen,
const uint8_t *npub, const uint8_t *k);
int aegis256x2_state_encrypt_update(aegis256x2_state *st_, uint8_t *c, size_t clen_max,
size_t *written, const uint8_t *m, size_t mlen);
int aegis256x2_state_encrypt_detached_final(aegis256x2_state *st_, uint8_t *c, size_t clen_max,
size_t *written, uint8_t *mac, size_t maclen);
int aegis256x2_state_encrypt_final(aegis256x2_state *st_, uint8_t *c, size_t clen_max,
size_t *written, size_t maclen);
int aegis256x2_state_decrypt_detached_update(aegis256x2_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *c, size_t clen);
int aegis256x2_state_decrypt_detached_final(aegis256x2_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *mac, size_t maclen);
void aegis256x2_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k);
void aegis256x2_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen,
const uint8_t *npub, const uint8_t *k);
void aegis256x2_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen,
const uint8_t *npub, const uint8_t *k);
void aegis256x2_mac_init(aegis256x2_mac_state *st_, const uint8_t *k, const uint8_t *npub);
int aegis256x2_mac_update(aegis256x2_mac_state *st_, const uint8_t *m, size_t mlen);
int aegis256x2_mac_final(aegis256x2_mac_state *st_, uint8_t *mac, size_t maclen);
int aegis256x2_mac_verify(aegis256x2_mac_state *st_, const uint8_t *mac, size_t maclen);
void aegis256x2_mac_reset(aegis256x2_mac_state *st_);
void aegis256x2_mac_state_clone(aegis256x2_mac_state *dst, const aegis256x2_mac_state *src);
/* aegis256x4.h */
typedef struct {
/* CRYPTO_ALIGN(64) */ uint8_t opaque[576];
} aegis256x4_state;
typedef struct {
/* CRYPTO_ALIGN(64) */ uint8_t opaque[960];
} aegis256x4_mac_state;
size_t aegis256x4_keybytes(void);
size_t aegis256x4_npubbytes(void);
size_t aegis256x4_abytes_min(void);
size_t aegis256x4_abytes_max(void);
size_t aegis256x4_tailbytes_max(void);
int aegis256x4_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m,
size_t mlen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis256x4_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
const uint8_t *k);
int aegis256x4_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
int aegis256x4_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
size_t adlen, const uint8_t *npub, const uint8_t *k);
void aegis256x4_state_init(aegis256x4_state *st_, const uint8_t *ad, size_t adlen,
const uint8_t *npub, const uint8_t *k);
int aegis256x4_state_encrypt_update(aegis256x4_state *st_, uint8_t *c, size_t clen_max,
size_t *written, const uint8_t *m, size_t mlen);
int aegis256x4_state_encrypt_detached_final(aegis256x4_state *st_, uint8_t *c, size_t clen_max,
size_t *written, uint8_t *mac, size_t maclen);
int aegis256x4_state_encrypt_final(aegis256x4_state *st_, uint8_t *c, size_t clen_max,
size_t *written, size_t maclen);
int aegis256x4_state_decrypt_detached_update(aegis256x4_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *c, size_t clen);
int aegis256x4_state_decrypt_detached_final(aegis256x4_state *st_, uint8_t *m, size_t mlen_max,
size_t *written, const uint8_t *mac, size_t maclen);
void aegis256x4_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k);
void aegis256x4_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen,
const uint8_t *npub, const uint8_t *k);
void aegis256x4_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen,
const uint8_t *npub, const uint8_t *k);
void aegis256x4_mac_init(aegis256x4_mac_state *st_, const uint8_t *k, const uint8_t *npub);
int aegis256x4_mac_update(aegis256x4_mac_state *st_, const uint8_t *m, size_t mlen);
int aegis256x4_mac_final(aegis256x4_mac_state *st_, uint8_t *mac, size_t maclen);
int aegis256x4_mac_verify(aegis256x4_mac_state *st_, const uint8_t *mac, size_t maclen);
void aegis256x4_mac_reset(aegis256x4_mac_state *st_);
void aegis256x4_mac_state_clone(aegis256x4_mac_state *dst, const aegis256x4_mac_state *src);
/* libc bits for aligned allocation on POSIX */
int posix_memalign(void **memptr, size_t alignment, size_t size);
void free(void *ptr);
"""
)
def _platform_lib_name() -> str:
if sys.platform.startswith("linux"):
return "libaegis.so"
if sys.platform == "darwin":
return "libaegis.dylib"
if os.name == "nt":
return "aegis.dll"
return "libaegis.so"
def _load_libaegis():
# Let the dynamic loader search system paths
try:
lib = ffi.dlopen(_platform_lib_name())
return lib
except Exception as e:
hint = "Install libaegis system-wide."
raise OSError(f"Could not load libaegis: {e}\n{hint}")
def _load_libc():
# Use ctypes.util to find a usable libc name; fallback to None-dlopen on POSIX
if _ctypes_util is not None:
libc_name = _ctypes_util.find_library("c") # type: ignore[attr-defined]
if libc_name:
try:
return ffi.dlopen(libc_name)
except Exception:
pass
# Fallback: try process globals (works on many Unix platforms)
try:
return ffi.dlopen(None)
except Exception as e:
raise OSError(f"Unable to load libc for aligned allocation: {e}")
lib: Any = _load_libaegis()
libc: Any = _load_libc()
# Initialize CPU feature selection (recommended by the library)
try:
lib.aegis_init()
except Exception:
# Non-fatal; functions will still work, maybe slower
pass
def alloc_aligned(size: int, alignment: int = 64):
"""Allocate aligned memory via posix_memalign(); returns a void* cdata.
The returned pointer must be freed with libc.free(). Attach a GC finalizer
at call sites using ffi.gc(ptr, libc.free) after casting to the target type.
"""
memptr = ffi.new("void **")
rc = libc.posix_memalign(memptr, alignment, size)
if rc != 0 or memptr[0] == ffi.NULL:
raise MemoryError(f"posix_memalign({alignment}, {size}) failed with rc={rc}")
return memptr[0]
+1 -1
View File
@@ -10,7 +10,7 @@ Covers:
import time
from aegis import aegis256x4 as a
from pyaegis import aegis256x4 as a
def hx(b, limit: int | None = None) -> str:
+1 -1
View File
@@ -12,7 +12,7 @@ Output format and throughput units mirror the Zig benchmark (Mb/s).
import os
import time
from aegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4
from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4
MSG_LEN = 16384000 # 16 MiB
ITERATIONS = 100
+90
View File
@@ -0,0 +1,90 @@
"""Dynamic loader for libaegis using CFFI (ABI mode).
This module loads the shared library bundled with the package.
Exports:
- ffi: a cffi.FFI instance with the libaegis API declared
- lib: the loaded libaegis shared library (ffi.dlopen)
- libc: the C runtime (for posix_memalign/free on POSIX)
- alloc_aligned(size, alignment): return (void*) pointer with requested alignment
"""
import os
import sys
from typing import Any
from cffi import FFI
ffi = FFI()
# Load CFFI cdef declarations from text file
cdef_path = os.path.join(os.path.dirname(__file__), "build", "aegis_cdef.h")
with open(cdef_path, "r", encoding="utf-8") as f:
cdef_content = f.read()
ffi.cdef(cdef_content)
try:
# ctypes is only used to resolve libc reliably across platforms
import ctypes.util as _ctypes_util # type: ignore
except Exception: # pragma: no cover - very unlikely to happen
_ctypes_util = None # type: ignore[assignment]
def _platform_lib_name() -> str:
if sys.platform == "darwin":
return "libaegis.dylib"
if os.name == "nt":
return "aegis.dll"
return "libaegis.so"
def _load_libaegis():
pkg_dir = os.path.dirname(__file__)
candidate = os.path.join(pkg_dir, "build", _platform_lib_name())
if os.path.exists(candidate):
try:
return ffi.dlopen(candidate)
except Exception as e:
raise OSError(f"Failed to load libaegis from {candidate}: {e}")
else:
raise OSError(f"Could not find libaegis at {candidate}")
def _load_libc():
# Use ctypes.util to find a usable libc name; fallback to None-dlopen on POSIX
if _ctypes_util is not None:
libc_name = _ctypes_util.find_library("c") # type: ignore[attr-defined]
if libc_name:
try:
return ffi.dlopen(libc_name)
except Exception:
pass
# Fallback: try process globals (works on many Unix platforms)
try:
return ffi.dlopen(None)
except Exception as e:
raise OSError(f"Unable to load libc for aligned allocation: {e}")
lib: Any = _load_libaegis()
libc: Any = _load_libc()
# Initialize CPU feature selection (recommended by the library)
try:
lib.aegis_init()
except Exception:
# Non-fatal; functions will still work, maybe slower
pass
def alloc_aligned(size: int, alignment: int = 64):
"""Allocate aligned memory via posix_memalign(); returns a void* cdata.
The returned pointer must be freed with libc.free(). Attach a GC finalizer
at call sites using ffi.gc(ptr, libc.free) after casting to the target type.
"""
memptr = ffi.new("void **")
rc = libc.posix_memalign(memptr, alignment, size)
if rc != 0 or memptr[0] == ffi.NULL:
raise MemoryError(f"posix_memalign({alignment}, {size}) failed with rc={rc}")
return memptr[0]
+11 -1
View File
@@ -1,5 +1,5 @@
[build-system]
requires = ["hatchling"]
requires = ["hatchling", "cffi>=2.0.0"]
build-backend = "hatchling.build"
[project]
@@ -26,3 +26,13 @@ Homepage = "https://github.com/aegis-aead/libaegis"
dev = [
"pytest>=8.4.2",
]
[tool.hatch.build.hooks.custom]
# Placeholder build hook for compiling libaegis with Zig during wheel builds.
# The actual Zig build is intentionally not executed yet.
path = "tools/build_hook.py"
[tool.hatch.build.targets.wheel]
# Ensure only the Python package is included by default; native artifacts
# will be added by the build hook once implemented.
packages = ["pyaegis"]
+7 -12
View File
@@ -2,13 +2,8 @@ import json
from pathlib import Path
from sys import stderr
import aegis.aegis128l
import aegis.aegis128x2
import aegis.aegis128x4
import aegis.aegis256
import aegis.aegis256x2
import aegis.aegis256x4
import pytest
from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4
from .util import random_split_bytes
@@ -20,12 +15,12 @@ def load_encryption_test_vectors():
# Map filename to algorithm module
algorithm_files = {
"aegis-128l-test-vectors.json": aegis.aegis128l,
"aegis-128x2-test-vectors.json": aegis.aegis128x2,
"aegis-128x4-test-vectors.json": aegis.aegis128x4,
"aegis-256-test-vectors.json": aegis.aegis256,
"aegis-256x2-test-vectors.json": aegis.aegis256x2,
"aegis-256x4-test-vectors.json": aegis.aegis256x4,
"aegis-128l-test-vectors.json": aegis128l,
"aegis-128x2-test-vectors.json": aegis128x2,
"aegis-128x4-test-vectors.json": aegis128x4,
"aegis-256-test-vectors.json": aegis256,
"aegis-256x2-test-vectors.json": aegis256x2,
"aegis-256x4-test-vectors.json": aegis256x4,
}
for filename, alg_module in algorithm_files.items():
+7 -12
View File
@@ -1,13 +1,8 @@
import json
from pathlib import Path
import aegis.aegis128l
import aegis.aegis128x2
import aegis.aegis128x4
import aegis.aegis256
import aegis.aegis256x2
import aegis.aegis256x4
import pytest
from pyaegis import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4
from .util import random_split_bytes
@@ -24,17 +19,17 @@ def load_mac_test_vectors():
def get_algorithm_module(name):
"""Map test vector name to algorithm module."""
if "128L" in name:
return aegis.aegis128l
return aegis128l
elif "128X2" in name:
return aegis.aegis128x2
return aegis128x2
elif "128X4" in name:
return aegis.aegis128x4
return aegis128x4
elif "256" in name and "256X2" not in name and "256X4" not in name:
return aegis.aegis256
return aegis256
elif "256X2" in name:
return aegis.aegis256x2
return aegis256x2
elif "256X4" in name:
return aegis.aegis256x4
return aegis256x4
else:
raise ValueError(f"Unknown algorithm in test vector name: {name}")
+70
View File
@@ -0,0 +1,70 @@
"""Hatch build hook for building dynamic libaegis library using Zig."""
import shutil
import subprocess
from pathlib import Path
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
class BuildHook(BuildHookInterface):
"""Build dynamic library with Zig and include in wheel."""
def initialize(self, version: str, build_data: dict) -> None:
"""Build library with Zig and add it to the wheel."""
if self.target_name != "wheel":
return
if not shutil.which("zig"):
raise RuntimeError("Zig compiler not found in PATH")
libaegis_dir = Path(self.root) / "libaegis"
original_build_zig = libaegis_dir / "build.zig"
if not original_build_zig.exists():
raise RuntimeError(f"libaegis source not found at {libaegis_dir}")
# Prepare a temporary build directory (avoid touching original files)
build_dir = Path.cwd() / "libaegis-build"
build_dir.mkdir(exist_ok=True)
build_zig = build_dir / "build.zig"
build_zig.write_text(
original_build_zig.read_text(encoding="utf-8").replace(
".linkage = .static,", ".linkage = .dynamic,"
),
encoding="utf-8",
)
for res in "build.zig.zon", "src":
(build_dir / res).symlink_to(libaegis_dir / res)
self.app.display_info("[aegis] Building libaegis dynamic library with Zig...")
try:
subprocess.run(
["zig", "build", "-Drelease"],
check=True,
cwd=str(build_dir),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
except subprocess.CalledProcessError as e:
output = e.stdout.decode(errors="replace") if e.stdout else ""
raise RuntimeError(f"Zig build failed:\n{output}") from e
lib_dir = build_dir / "zig-out" / "lib"
dynamic_lib = None
for lib_file in lib_dir.iterdir():
if lib_file.name.startswith("libaegis") and lib_file.suffix in (
".so",
".dylib",
".dll",
):
dynamic_lib = lib_file
break
if not dynamic_lib or not dynamic_lib.exists():
raise RuntimeError(f"Built dynamic library not found in {lib_dir}")
if "force_include" not in build_data:
build_data["force_include"] = {}
dest_rel = str(Path("build") / dynamic_lib.name)
build_data["force_include"][str(dynamic_lib)] = dest_rel
self.app.display_info(f"[aegis] Added dynamic library to wheel: {dest_rel}")
+167
View File
@@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""Generate CFFI cdef string from libaegis headers.
This script parses the C header files and extracts function declarations,
typedefs, and struct definitions to generate the cdef() string needed by CFFI.
"""
import pathlib
import re
import sys
def preprocess_content(content: str) -> str:
"""Remove comments, preprocessor directives, and extern "C" blocks."""
# Remove multi-line comments
content = re.sub(r"/\*.*?\*/", " ", content, flags=re.DOTALL)
# Remove line comments
content = re.sub(r"//.*$", "", content, flags=re.MULTILINE)
# Remove preprocessor directives
content = re.sub(r"^\s*#.*$", "", content, flags=re.MULTILINE)
# Remove extern "C" blocks
content = re.sub(r'extern\s+"C"\s*\{', "", content)
content = re.sub(r"(?:^|\n)\s*\}\s*(?:\n|$)", "\n", content, flags=re.MULTILINE)
return content
def clean_declaration(text: str) -> str:
"""Clean up a C declaration for CFFI consumption."""
# Remove __attribute__(...) with proper nesting
while "__attribute__" in text:
old = text
text = re.sub(r"__attribute__\s*\(\([^()]*\)\)", "", text)
if text == old:
break
# Remove CRYPTO_ALIGN(...)
text = re.sub(r"CRYPTO_ALIGN\s*\(\s*\d+\s*\)", "", text)
# Normalize whitespace but preserve structure
lines = []
for line in text.split("\n"):
line = re.sub(r"\s+", " ", line).strip()
if line:
lines.append(line)
return " ".join(lines)
def extract_declarations(header_path: pathlib.Path) -> list[str]:
"""Extract function declarations and typedefs from a header file."""
content = header_path.read_text(encoding="utf-8")
content = preprocess_content(content)
declarations = []
# Extract typedefs (including structs)
typedef_pattern = r"typedef\s+struct\s+\w+\s*\{[^}]+\}\s*\w+\s*;"
for match in re.finditer(typedef_pattern, content, re.DOTALL):
decl = clean_declaration(match.group(0))
if decl:
declarations.append(decl)
# Extract function declarations - more permissive pattern
func_pattern = r"((?:const\s+)?(?:int|void|size_t)\s+\w+\s*\([^;]+?\)\s*;)"
for match in re.finditer(func_pattern, content, re.DOTALL):
decl = clean_declaration(match.group(0))
if decl and "aegis" in decl.lower():
declarations.append(decl)
return declarations
def format_declaration(decl: str, max_width: int = 100) -> str:
"""Format a declaration for readability, with intelligent line breaking."""
# If it's short enough, return as-is
if len(decl) <= max_width:
return decl
# For function declarations, try to break at parameter boundaries
if "(" in decl and ")" in decl:
# Find the function name and opening paren
match = re.match(r"(.*?\s+\w+\s*)\((.*)\)(.*)", decl)
if match:
prefix, params, suffix = match.groups()
# Break parameters if they're too long
if len(prefix) + len(params) + 2 > max_width:
# Split parameters
param_list = [p.strip() for p in params.split(",")]
if len(param_list) > 1:
formatted_params = (",\n" + " " * (len(prefix) + 1)).join(
param_list
)
return f"{prefix}({formatted_params}){suffix}"
return decl
def generate_cdef(include_dir: pathlib.Path) -> str:
"""Generate the complete CFFI cdef string from all aegis headers."""
lines = [
"typedef unsigned char uint8_t;",
"typedef unsigned long size_t;",
"",
]
# Header files in order, skipping aegis.h as it might be included elsewhere
headers = [
"aegis.h",
"aegis128l.h",
"aegis128x2.h",
"aegis128x4.h",
"aegis256.h",
"aegis256x2.h",
"aegis256x4.h",
]
for header_name in headers:
header_path = include_dir / header_name
if not header_path.exists():
print(f"Warning: {header_name} not found", file=sys.stderr)
continue
lines.append(f"/* {header_name} */")
declarations = extract_declarations(header_path)
for decl in declarations:
formatted = format_declaration(decl)
lines.append(formatted)
lines.append("")
# Add libc bits for aligned allocation
lines.extend(
[
"/* libc bits for aligned allocation on POSIX */",
"int posix_memalign(void **memptr, size_t alignment, size_t size);",
"void free(void *ptr);",
]
)
return "\n".join(lines)
def main() -> int:
# Find the include directory
root = pathlib.Path(__file__).resolve().parents[2]
include_dir = root / "src" / "include"
if not include_dir.exists():
print(f"Include directory not found: {include_dir}", file=sys.stderr)
return 1
cdef_string = generate_cdef(include_dir)
# Write to a file in the pyaegis/build subdirectory
output_dir = root / "python" / "pyaegis" / "build"
output_dir.mkdir(exist_ok=True)
output_path = output_dir / "aegis_cdef.h"
output_path.write_text(cdef_string, encoding="utf-8")
print(f"Generated: {output_path}", file=sys.stderr)
return 0
if __name__ == "__main__":
raise SystemExit(main())