Skip to content

ORE v2 (3/n): efficient unary encoding + hardware AES on aarch64 - #80

Merged
coderdan merged 3 commits into
feat/ore-v2-core-refactorfrom
feat/ore-v2-efficient-encoding
Oct 12, 2026
Merged

coderdan merged 3 commits into
feat/ore-v2-core-refactorfrom
feat/ore-v2-efficient-encoding

Conversation

@coderdan

Copy link
Copy Markdown
Contributor

Stacked on #79. Plan §2 (docs/plans/2026-06-12-ore-v2-architecture.md).

Headline numbers (Apple M1 Max, u64 encrypt)

Configuration encrypt-8 vs old default
main, software AES (old default on ARM) 381.1 µs 1.0×
main, hardware AES 39.4 µs 9.7×
this PR, hardware AES (new default) 25.1 µs 15.2×

Full matrix in docs/benchmarks/2026-06-13-pr3-results.md.

What

All byte-identical — the 12 compatibility vectors pass unchanged.

  1. Hardware AES on aarch64: aes v0.8 requires --cfg aes_armv8 and was silently running software AES (~60×/block) on every default ARM build. Workspace .cargo/config.toml sets 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.
  2. Bulk right-block encoding (−36% encrypt under hw AES): hash LSBs packed straight into the block bitvector (hash_all_into, no Vec), indicator mask XORed over the top in one linear pass of the PRP's inverse table (indicator_mask_xor) — replacing 256 per-bit invert lookups + branchy bit-sets per block. Equivalence vs the per-bit reference pinned by quickcheck.
  3. Hasher hoist: the nonce key schedule was rebuilt once per block; now once per encryption.
  4. PRNG pointer flattening (−18% encrypt-left): one branch per byte instead of three, byte stream exactly preserved (including the historical skip-byte-0-on-regeneration quirk, now documented as load-bearing).
  5. Knuth shuffle skips its degenerate i=0 iteration: it always lands on swap(0,0) after an expected 256 rejection draws, and the RNG is dropped immediately after — unobservable, saves a full PRNG regeneration per PRP.

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.

@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch from eb98f36 to 980ae10 Compare June 12, 2026 15:23
coderdan added a commit that referenced this pull request Jun 16, 2026
…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.
@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch from 6c361b4 to 183ba9c Compare June 16, 2026 10:02
@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch from 183ba9c to fb3c8a9 Compare October 8, 2026 08:36
@coderdan

coderdan commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto the updated #79 (which picked up main's docs refresh, #91). One conflict, in packages/ore-rs/README.md:

  • main's refresh says ARMv8 hardware AES is detected at runtime with no configuration. That is wrong for aes 0.8.4, which compiles its ARMv8 backend only under cfg(aes_armv8) (aes-0.8.4/src/lib.rs:134). Kept this PR's ARMv8 section, under main's heading style.
  • Kept main's new Security section, which replaced the old Security Warning and roadmap.

prng.rs merged without conflict and still carries the ZA-0001 drop wipe and its compile-time ZeroizeOnDrop check. #81, #82 and #83 rebased on top with no further conflicts; fmt, clippy (1.78) and tests on stable and 1.78 pass at each tip, except a pre-existing aarch64-only 1.78 clippy unreachable_code in simd.rs from #82 that x86_64 CI does not compile.

@coderdan
coderdan marked this pull request as ready for review October 8, 2026 08:50
@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch 2 times, most recently from 18771d7 to ad1eed2 Compare October 8, 2026 09:52
@coderdan
coderdan requested a balanced review from Copilot October 8, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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.

@coderdan
coderdan requested a review from freshtonic October 8, 2026 23:41
coderdan added a commit that referenced this pull request Oct 9, 2026
…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.
@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch from ad1eed2 to 824b5e1 Compare October 9, 2026 09:31
@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch from 824b5e1 to 151b6c0 Compare October 12, 2026 00:54
… 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.
@coderdan
coderdan force-pushed the feat/ore-v2-efficient-encoding branch from 151b6c0 to 3709809 Compare October 12, 2026 01:05

@freshtonic freshtonic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@coderdan
coderdan merged commit 463e5fa into main Oct 12, 2026
4 checks passed
@coderdan
coderdan deleted the feat/ore-v2-efficient-encoding branch October 12, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants