os.open() on Windows defaults to O_TEXT, so the CRT translated \r\n in
binary ciphertext: writes grew, offsets shifted, reads came up short and
the RAF callbacks failed with EIO (probe/read/write depending on where
a 0x0A byte landed). Pass O_BINARY where it exists.
Also reconfigure stdout/stderr to UTF-8 in tools/release.py so the
✓/✗/⚠ summary lines don't crash on cp1252 Windows consoles.
The generated cdef typedef'd size_t as unsigned long: 8 bytes on LP64
but only 4 on 64-bit Windows, where C's size_t is 8. cffi's import-time
struct layout verification then failed (aegis_raf_scratch.len: cdef
says 4, C compiler says 8). cffi pre-defines size_t/uint8_t with the
correct platform sizes, so don't typedef them.
Also hard-fail 32-bit build attempts in setup.py; 32-bit platforms are
no longer supported.
- requires-python >= 3.12; abi3 limited API tag cp310 -> cp312
- util.Buffer now imported directly from collections.abc (no fallback)
- release.py: ABI3 wheel covers 3.12-3.15; PyPy wheels dropped (PyPy
has no 3.12 support yet)
Running generate.py standalone previously rewrote _ciphers.py with
formatting that differed from the committed (ruff-formatted) style,
creating spurious diffs. release.py already ran ruff after generation;
now generate.py does too, so both entry points leave the tree in the
same state.
Implement Python bindings for the libaegis 0.10.6 RAF API:
- Thin per-variant wrappers (raf_create/open/read/write/truncate/size/
sync/close, merkle_rebuild/verify/commitment) generated from a template
in tools/generate.py, with errno-mapped exceptions.
- aeg.raf: high-level file-like API (Raf, create/open/probe,
derive_master_key) with pluggable Storage backends (FileStorage,
BytesIOStorage) and Merkle tree integrity via a MerkleHasher protocol
(SHA256MerkleHasher default), adapted from pyaegis (MIT).
- aeg.random: CSPRNG facility. Random produces AEGIS keystream seeded
once from the OS, exposed per cipher module as cipher.random() and
usable as the RAF RNG (as_raf_rng) or standalone RandomSource.
- Tests: roundtrip/random-access/seek/truncate/Merkle coverage for all
six variants, pyaegis-style API checks, and a Merkle fuzz test.
Matrix job builds and tests wheels on linux/macos/windows (no Gitea
release, unlike mediahive); publish job uploads the merged dist/ to PyPI
only after all platforms succeed. release.py gains --no-upload for the
matrix jobs.