|
| 1 | +# `vendor/chacha20` — what it actually reaches |
| 2 | + |
| 3 | +> **Status: MEASURED, 2026-07-29.** Every row below is read from |
| 4 | +> `Cargo.lock`, a `rustc --print cfg`, or a manifest on disk. No estimates. |
| 5 | +
|
| 6 | +## READ BY: |
| 7 | +- Anyone about to re-sync, extend, or delete `vendor/chacha20` |
| 8 | +- Anyone citing "the AdaWorldAPI chacha20 fork" as an accelerated dependency |
| 9 | +- Anyone applying the P0 fork rule to a crate that also has a vendored copy |
| 10 | + |
| 11 | +## P0 TRIGGER |
| 12 | +About to state that a consumer's ChaCha20 keystream is accelerated because |
| 13 | +ndarray patches `chacha20`? **Read the two reach tables first. The patch and |
| 14 | +the acceleration have different, and both narrower-than-assumed, reach.** |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## Two different questions, two different answers |
| 19 | + |
| 20 | +"Does the fork reach consumer X?" is really two questions, and conflating |
| 21 | +them is what produced the overstated claims in the manifest comments. |
| 22 | + |
| 23 | +1. **Does the `[patch.crates-io]` apply** — i.e. does the build link the |
| 24 | + vendored source at all? |
| 25 | +2. **Does `ndarray_simd` compile** — i.e. does the added backend get |
| 26 | + selected, or does the vendored crate fall through to RustCrypto's own? |
| 27 | + |
| 28 | +A build can answer yes to (1) and no to (2). Most do. |
| 29 | + |
| 30 | +## Reach of the patch |
| 31 | + |
| 32 | +`[patch.crates-io]` takes effect **only from the top-level workspace |
| 33 | +manifest**. It is ignored in a dependency's manifest, so ndarray's patch |
| 34 | +does not travel with ndarray — each consumer must declare its own. |
| 35 | + |
| 36 | +| repo | declares a chacha20 patch | links the vendored source | |
| 37 | +|---|---|---| |
| 38 | +| ndarray | yes — `Cargo.toml:488` | **yes** | |
| 39 | +| MedCare-rs | yes — `vendor/ndarray/vendor/chacha20` | **yes** | |
| 40 | +| lance-graph | no | no — registry crate | |
| 41 | +| OGAR | no | no — registry crate | |
| 42 | +| tesseract-rs | no | no — registry crate | |
| 43 | +| stockfish-rs | no | no — registry crate | |
| 44 | +| a2ui-rs | no | no — registry crate | |
| 45 | + |
| 46 | +Evidence for the two "yes" rows: ndarray's `Cargo.lock` carries |
| 47 | +`chacha20 0.9.1` with **no `source` line**, which is how a path override |
| 48 | +appears; MedCare-rs's root manifest declares |
| 49 | +`chacha20 = { path = "vendor/ndarray/vendor/chacha20" }`, and its |
| 50 | +`vendor/ndarray` symlink canonicalizes to the same checkout, so one |
| 51 | +`ndarray` node resolves. |
| 52 | + |
| 53 | +Evidence for the five "no" rows: `grep 'chacha20'` and `grep '\[patch'` |
| 54 | +across each root manifest. lance-graph's manifest contains only a comment |
| 55 | +*about* patching, explicitly recording that no `[patch]` block exists there. |
| 56 | + |
| 57 | +## Reach of the `ndarray_simd` backend |
| 58 | + |
| 59 | +`vendor/chacha20/src/backends.rs` selects the added backend under |
| 60 | + |
| 61 | +```rust |
| 62 | +all(target_arch = "x86_64", target_feature = "avx512f", …) // or |
| 63 | +all(target_arch = "wasm32", target_feature = "simd128") |
| 64 | +``` |
| 65 | + |
| 66 | +`avx512f` is a **compile-time** feature cfg, so it is present only if the |
| 67 | +build pins a target-cpu that includes it. Measured: |
| 68 | + |
| 69 | +``` |
| 70 | +$ rustc --print cfg -Ctarget-cpu=x86-64-v3 | grep avx |
| 71 | +target_feature="avx2" # <- no avx512f |
| 72 | +$ rustc --print cfg -Ctarget-cpu=x86-64-v4 | grep avx512f |
| 73 | +target_feature="avx512f" |
| 74 | +``` |
| 75 | + |
| 76 | +| build | target-cpu | `avx512f` | backend compiled | |
| 77 | +|---|---|---|---| |
| 78 | +| ndarray, default | `x86-64-v3` (`.cargo/config.toml`) | absent | RustCrypto soft/avx2/sse2 | |
| 79 | +| ndarray, `--config .cargo/config-avx512.toml` | `x86-64-v4` | present | **`ndarray_simd`** | |
| 80 | +| MedCare-rs, default | none — no `.cargo/config.toml` at all | absent | RustCrypto soft/avx2/sse2 | |
| 81 | +| wasm32 + `simd128` | — | n/a | **`ndarray_simd`** | |
| 82 | +| the five unpatched repos | — | — | not present in the source they link | |
| 83 | + |
| 84 | +**No default build of any repo in the workspace runs `ndarray_simd`.** Two |
| 85 | +opt-in configurations do: an explicitly-selected AVX-512 x86_64 build, and a |
| 86 | +wasm32 build with `simd128`. |
| 87 | + |
| 88 | +This is not a defect — the fallthrough is correct and the comments in |
| 89 | +MedCare-rs's manifest describe it accurately ("non-avx512 builds fall through |
| 90 | +to RustCrypto's own backends"). It is a scope fact that keeps being stated |
| 91 | +one size too large. |
| 92 | + |
| 93 | +## One manifest comment overstates it |
| 94 | + |
| 95 | +`Cargo.toml:481-482` reads: |
| 96 | + |
| 97 | +> This transitively accelerates the `encryption` crate's XChaCha20-Poly1305 |
| 98 | +> keystream … **on any x86_64+avx512f build (the workspace's |
| 99 | +> `target-cpu=x86-64-v4`)**. |
| 100 | +
|
| 101 | +The workspace's pinned target-cpu is **v3**, in `.cargo/config.toml`. `v4` |
| 102 | +is the opt-in `.cargo/config-avx512.toml`. The conditional half of the |
| 103 | +sentence is right; the parenthetical asserts the default build satisfies it, |
| 104 | +and it does not. |
| 105 | + |
| 106 | +**Not edited here.** Manifest string changes in this repo need an operator |
| 107 | +ruling; the finding is recorded instead of applied. |
| 108 | + |
| 109 | +## The fork the P0 rule names is not this tree |
| 110 | + |
| 111 | +`AdaWorldAPI/stream-ciphers` exists and is now cloned in-session. |
| 112 | + |
| 113 | +| | `AdaWorldAPI/stream-ciphers` | `ndarray/vendor/chacha20` | |
| 114 | +|---|---|---| |
| 115 | +| what it is | mirror of RustCrypto upstream | hand-copied source tree | |
| 116 | +| head | `5f3430b` "Release chacha20 v0.10.1 (#574)" | — | |
| 117 | +| branches | `master` only | — | |
| 118 | +| chacha20 version | **0.10.1** | **0.9.1** | |
| 119 | +| `cipher` | 0.5 | 0.4.4 | |
| 120 | +| edition / rust-version | 2024 / 1.85 | 2021 / 1.95 | |
| 121 | +| `backends/ndarray_simd.rs` | **absent** | present | |
| 122 | +| `backends/avx512.rs` | **present (upstream's own)** | absent | |
| 123 | +| `repository` field | RustCrypto | rewritten to `AdaWorldAPI/ndarray` | |
| 124 | + |
| 125 | +Three consequences worth stating plainly: |
| 126 | + |
| 127 | +1. **The fork carries no AdaWorldAPI delta.** One commit, no branches beyond |
| 128 | + `master`. It is a mirror, not a modified fork — so "depend on the fork" |
| 129 | + and "depend on upstream" are currently the same bytes. |
| 130 | +2. **The vendored tree is not a checkout of the fork**, and is one minor |
| 131 | + release behind it. It cannot be re-synced by `git pull`; the re-sync |
| 132 | + procedure its own header describes (bump the source, re-apply the backend |
| 133 | + and the four cfg branches, re-run the vectors) is a manual port, and |
| 134 | + 0.9.1 → 0.10.1 crosses a `cipher` major (0.4 → 0.5). |
| 135 | +3. **Upstream now ships its own AVX-512 backend.** `backends/avx512.rs` |
| 136 | + at 0.10.1 occupies the exact niche `ndarray_simd.rs` was written to fill. |
| 137 | + Whether the ndarray-lane version still earns its place is a measurement |
| 138 | + question — and the instrument for it already exists |
| 139 | + (`.claude/knowledge/simd-codegen-oracle/`), with the relevant lane already |
| 140 | + measured: the u32 ARX triple on `U32x16` hits the AVX2 instruction floor |
| 141 | + (`td-t22-asm-investigation.md`). |
| 142 | + |
| 143 | +## What is NOT claimed here |
| 144 | + |
| 145 | +- Not that the vendored backend is wrong. It is untested by default builds, |
| 146 | + which is a different statement. |
| 147 | +- Not that the patch should be removed. That is a decision, and it depends on |
| 148 | + (3) above plus the AVX-512 deployment question, neither of which this audit |
| 149 | + settles. |
| 150 | +- Not that MedCare-rs's wiring is incorrect. It is the one consumer that |
| 151 | + declared its patch deliberately and documented the fallthrough honestly. |
| 152 | + |
| 153 | +## Open, for an operator ruling |
| 154 | + |
| 155 | +1. **Point `vendor/chacha20` at the fork, or keep the copy?** The P0 rule |
| 156 | + says depend on the AdaWorldAPI fork. Today the tree is neither — a copy of |
| 157 | + a version the fork does not carry, with `repository` rewritten to point at |
| 158 | + ndarray itself. |
| 159 | +2. **Does `ndarray_simd` still beat upstream's own `avx512.rs`?** Answerable |
| 160 | + with the oracle. Unmeasured today, on both sides. |
| 161 | +3. **The v3/v4 manifest comment** — correct in place, or leave the record and |
| 162 | + annotate? |
0 commit comments