Repository navigation
ORE v2 (3/n): efficient unary encoding + hardware AES on aarch64 - #80
Conversation
eb98f36 to
980ae10
Compare
…dead code) Code-review follow-ups on the efficient-encoding PR (no wire change; compat vectors remain byte-identical): - Extract pack_bits_lsb_first (primitives.rs) as the single home of the right-ciphertext LSB-first bit-packing convention, shared by Hash::hash_all_into and Prp::indicator_mask_xor (previously duplicated in both). It carries a real assert!(out.len()*8 == src.len()), replacing both debug_asserts and the hardcoded 256 in indicator_mask_xor, so the chunks_exact(8) remainder can no longer be dropped silently in release. - Remove the dead RightBitVec::set_bit (trait method + impl); the bulk encoder writes via as_mut_bytes and the only set_bit callers are tests hitting the inherent RightBlock32::set_bit. Fix the dangling [Self::set_bit] doc link. - Correct the stale Aes128Prng comment (it buffers and regenerates 256 bytes; it does not panic). The aes_armv8 RUSTFLAGS footgun is tracked separately for the aes 0.8->0.9 upgrade (#86), which removes the cfg entirely.
6c361b4 to
183ba9c
Compare
183ba9c to
fb3c8a9
Compare
|
Rebased onto the updated #79 (which picked up
|
18771d7 to
ad1eed2
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
Cryptographic compatibility and aarch64 backend behavior need human validation beyond the available static review evidence.
0 open findings
What changed in this PR
Continues the ORE v2 refactor with faster right-block encoding and aarch64 hardware AES configuration, while preserving ciphertext compatibility.
Changes:
- Packs hash bits directly into ciphertext blocks and applies bulk indicator masks.
- Simplifies PRNG indexing and removes the shuffle’s redundant final iteration.
- Enables ARM hardware AES for workspace builds and documents configuration and benchmarks.
| File | Description |
|---|---|
packages/ore-rs/src/scheme/width.rs |
Exposes bulk bitvector access. |
packages/ore-rs/src/scheme/bit2/block_types.rs |
Adds mutable block-byte access. |
packages/ore-rs/src/scheme/bit2.rs |
Uses bulk right-block encoding. |
packages/ore-rs/src/primitives/prp/prng.rs |
Flattens indexing while preserving byte-stream behavior. |
packages/ore-rs/src/primitives/prp.rs |
Optimizes shuffle and adds mask equivalence tests. |
packages/ore-rs/src/primitives/hash.rs |
Packs hash outputs without allocation. |
packages/ore-rs/src/primitives.rs |
Adds shared packing and bulk primitive interfaces. |
packages/ore-rs/README.md |
Documents downstream ARM AES configuration. |
docs/plans/2026-06-12-ore-v2-architecture.md |
Updates AES guidance and benchmark-based design decisions. |
docs/benchmarks/2026-06-13-pr3-results.md |
Records performance results and remaining bottlenecks. |
.cargo/config.toml |
Enables the aarch64 AES backend configuration. |
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
…dead code) Code-review follow-ups on the efficient-encoding PR (no wire change; compat vectors remain byte-identical): - Extract pack_bits_lsb_first (primitives.rs) as the single home of the right-ciphertext LSB-first bit-packing convention, shared by Hash::hash_all_into and Prp::indicator_mask_xor (previously duplicated in both). It carries a real assert!(out.len()*8 == src.len()), replacing both debug_asserts and the hardcoded 256 in indicator_mask_xor, so the chunks_exact(8) remainder can no longer be dropped silently in release. - Remove the dead RightBitVec::set_bit (trait method + impl); the bulk encoder writes via as_mut_bytes and the only set_bit callers are tests hitting the inherent RightBlock32::set_bit. Fix the dangling [Self::set_bit] doc link. - Correct the stale Aes128Prng comment (it buffers and regenerates 256 bytes; it does not panic). The aes_armv8 RUSTFLAGS footgun is tracked separately for the aes 0.8->0.9 upgrade (#86), which removes the cfg entirely.
ad1eed2 to
824b5e1
Compare
824b5e1 to
151b6c0
Compare
… on aarch64 All changes are byte-identical to the previous implementation, verified by the PR 1 compatibility vectors: - Right-block encoding is now two bulk passes: hash bits packed straight into the block bitvector (hash_all_into, no Vec), then the PRP XORs its indicator mask over the top in one linear walk of its inverse table (indicator_mask_xor) — replacing DOMAIN per-bit invert lookups and branchy bit-sets per block. A quickcheck test pins mask equivalence against the per-bit reference. - The nonce-keyed hasher is constructed once per encryption instead of once per block (the key schedule was being rebuilt N times). - PRNG pointer flattened (one branch per byte instead of three), exactly preserving the historical byte stream including the skip-byte-0-after- regeneration quirk, which is load-bearing for ciphertext bytes. - The Knuth shuffle skips its degenerate i=0 iteration (always swap(0,0), reached only after an expected 256 rejection-sampled draws; the RNG is dropped immediately after, so the skip is unobservable). - .cargo/config.toml enables ARMv8 hardware AES for workspace builds: aes v0.8 requires --cfg aes_armv8 on aarch64 and falls back to software AES (~60x slower per block on M1 Max) without it. README updated for downstream builds (stable Rust suffices; the old nightly advice was outdated). Plan updates riding along: §5(b) benchmark gate result (key expansion ~84% of per-block work at Bit6 width on M1 Max -> decision rule selects Candidate B, CMAC with cached prefix state) and the corrected §3 aarch64 AES assumption. Part of the ORE v2 program (docs/plans/2026-06-12-ore-v2-architecture.md, PR 3).
…dead code) Code-review follow-ups on the efficient-encoding PR (no wire change; compat vectors remain byte-identical): - Extract pack_bits_lsb_first (primitives.rs) as the single home of the right-ciphertext LSB-first bit-packing convention, shared by Hash::hash_all_into and Prp::indicator_mask_xor (previously duplicated in both). It carries a real assert!(out.len()*8 == src.len()), replacing both debug_asserts and the hardcoded 256 in indicator_mask_xor, so the chunks_exact(8) remainder can no longer be dropped silently in release. - Remove the dead RightBitVec::set_bit (trait method + impl); the bulk encoder writes via as_mut_bytes and the only set_bit callers are tests hitting the inherent RightBlock32::set_bit. Fix the dangling [Self::set_bit] doc link. - Correct the stale Aes128Prng comment (it buffers and regenerates 256 bytes; it does not panic). The aes_armv8 RUSTFLAGS footgun is tracked separately for the aes 0.8->0.9 upgrade (#86), which removes the cfg entirely.
151b6c0 to
3709809
Compare
freshtonic
left a comment
There was a problem hiding this comment.
Approved after the round-2 security re-review. The round-1 findings for this PR are fixed or have no remaining blocking items.
🤖 Generated with Claude Code
Stacked on #79. Plan §2 (
docs/plans/2026-06-12-ore-v2-architecture.md).Headline numbers (Apple M1 Max, u64 encrypt)
Full matrix in
docs/benchmarks/2026-06-13-pr3-results.md.What
All byte-identical — the 12 compatibility vectors pass unchanged.
aesv0.8 requires--cfg aes_armv8and was silently running software AES (~60×/block) on every default ARM build. Workspace.cargo/config.tomlsets it; README replaces the outdated nightly advice (stable ≥1.61 suffices). Downstream crates need the cfg in their own builds — worth an announcement when this releases.hash_all_into, noVec), indicator mask XORed over the top in one linear pass of the PRP's inverse table (indicator_mask_xor) — replacing 256 per-bitinvertlookups + branchy bit-sets per block. Equivalence vs the per-bit reference pinned by quickcheck.What's now dominant
PRP construction: ~2.2 µs/block, ~70% of encrypt under hardware AES, and wire-frozen for this scheme. That elevates the plan's open question 1 (constant-time small-domain PRP for new schemes) — see the PR 5 notes.