From 7042995ecdd656be853fbf6219690395ef4c8fa0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 17:27:23 +0000 Subject: [PATCH] Force poly1305's soft backend: 424 intrinsics were reaching the binary crates/encryption -> chacha20poly1305 -> poly1305, and poly1305 0.8 auto-selects its AVX2 backend on any x86/x86_64 target unless told otherwise. Nothing told it otherwise, so it was selected: 424 `_mm*` intrinsic calls under 30 `unsafe` occurrences in one file, src/backend/avx2/helpers.rs. That is a second unaudited SIMD surface beside ndarray::simd, in the crypto path, and over seven times the size of the curve25519-dalek surface the same config file already neutralizes. Unlike dalek's -- which was gated off and therefore never in the binary -- this one was live. Reachability, not porting. The whole backend sits behind one cfg (src/backend.rs: `#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), not(poly1305_force_soft)))] pub(crate) mod avx2;`), so one rustflag compiles it out without touching a line of crypto. Same shape as the curve25519_dalek_backend="serial" line directly above it. This is deliberately NOT a Poly1305 implementation. The operator-ratified doctrine in .claude/CHACHA20_MATRYOSHKA_PLAN.md is explicit: "RustCrypto owns the algorithm; ndarray owns the SIMD... Rolling your own AEAD (HChaCha20 + Poly1305 + framing) is the footgun; it is forbidden." The sanctioned way to accelerate this later is the chacha20 route -- vendor the fork, give RustCrypto's own backend an ndarray::simd lane -- never a reimplementation. Verified, and not merely that the flag was passed: cargo build -p encryption -v emits poly1305_force_soft nm libpoly1305-*.rlib | grep avx2 0 symbols <- the code is GONE nm libpoly1305-*.rlib | grep soft 6 symbols cargo test -p encryption 46 + 13 passing, incl. the XChaCha20-Poly1305 AEAD round-trips Cost, accepted and NOT measured: the soft backend (poly1305-donna, the same algorithm) instead of AVX2, on the AEAD's MAC. That is a real throughput cost on large payloads. It is accepted because the alternative is an unaudited intrinsic surface in the crypto path, and because the sanctioned acceleration route stays open. The rustflags block is x86_64-gated, so no other target is affected. Claude-Session: https://claude.ai/code/session_01VdfbkUCBbtZhy3yjSfCDHp --- .cargo/config.toml | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 53700ac2..cd2a62da 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -48,5 +48,41 @@ # # Read by curve25519-dalek's build.rs via CARGO_CFG_CURVE25519_DALEK_BACKEND. # Verify: cargo build -p encryption -v 2>&1 | grep curve25519_dalek_backend + +# poly1305 backend: SOFT, deliberately. Same reasoning as dalek above, but +# unlike dalek this surface was actually REACHING THE BINARY. +# +# `crates/encryption` -> `chacha20poly1305` -> `poly1305`, and poly1305 0.8 +# auto-selects its AVX2 backend on any x86/x86_64 target unless told otherwise. +# That backend is 424 `_mm*` intrinsic calls under 30 `unsafe` occurrences in a +# single file (`src/backend/avx2/helpers.rs`) — over seven times dalek's 57, and +# a second unaudited SIMD surface beside `ndarray::simd`, which is what the +# matryoshka pattern exists to prevent. +# +# Reachability, not porting: the whole thing is gated behind one cfg — +# `src/backend.rs`, `#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), +# not(poly1305_force_soft)))] pub(crate) mod avx2;` — so the flag below compiles +# it out without touching a line of crypto. +# +# What it costs: the soft backend (poly1305-donna, the same algorithm) instead +# of the AVX2 one, on the AEAD's MAC. That is a real throughput cost on large +# payloads and it has NOT been measured here. It is accepted because the +# alternative is an unaudited intrinsic surface in the crypto path. +# +# What it does NOT mean: writing our own Poly1305. Per the operator-ratified +# doctrine in `.claude/CHACHA20_MATRYOSHKA_PLAN.md` — "RustCrypto owns the +# algorithm; ndarray owns the SIMD... Rolling your own AEAD (HChaCha20 + +# Poly1305 + framing) is the footgun; it is forbidden." The sanctioned way to +# accelerate this later is the chacha20 route: vendor the fork and give +# RustCrypto's own backend an `ndarray::simd` lane, never a reimplementation. +# +# Read by poly1305's src/backend.rs as a plain cfg. +# Verify: cargo build -p encryption -v 2>&1 | grep poly1305_force_soft [target.'cfg(target_arch = "x86_64")'] -rustflags = ["-Ctarget-cpu=x86-64-v3", "--cfg", "curve25519_dalek_backend=\"serial\""] +rustflags = [ + "-Ctarget-cpu=x86-64-v3", + "--cfg", + "curve25519_dalek_backend=\"serial\"", + "--cfg", + "poly1305_force_soft", +]