# DF-3567 — BF_set_key dereferences data[0] when len <= 0 (dead code, hardening)

**File:** `sys/crypto/blowfish/bf_skey.c:83-99`
**Class:** CWE-125 out-of-bounds read (1 byte, 72 dereferences) — same defect class as the
known rc4_init keylen==0 findings DF-0971/DF-2366, but in a different file.
**Reachability today:** NONE — `BF_set_key` has zero in-tree callers (verified by full-tree
grep; `sys/opencrypto` no longer exists, `option crypto` still compiles the object into
every kernel: `sys/conf/files:2257`). Info/hardening severity precisely because it is
compiled-in dead code.

## Reproduce (in-guest, same-source userland-object pattern)

Guest sources verified sha256-identical to the audited tree first
(`bf_skey.c = a77ce9b5...`). Harnesses compiled on the guest against
`/usr/src/sys` with a one-line `sys/systm.h` shim (`shim/sys/systm.h`).

```
build:  cc -O2 -DNDEBUG -I shim -I /usr/src/sys -o guard_bf_len0 \
            bf/guard_bf_len0.c /usr/src/sys/crypto/blowfish/bf_skey.c \
            /usr/src/sys/crypto/blowfish/bf_enc.c
run:    ./guard_bf_len0        # zero-length key buffer modeled at a PROT_NONE page
```

## Expected output (baseline)

```
calling BF_set_key(&key, len=0, data=guard-page 0x800475000)
FAULT-CONFIRMED: sig=11 si_addr=0x800475000 (expected data[0] at guard page)
exit=42
```

si_addr == data ⇒ the first of the 72 key-schedule byte loads (`ri = *(d++)`,
bf_skey.c:86) dereferences `data[0]` even though a zero-length key has no readable
bytes. With a kmalloc'd zero-length block in-kernel the read stays inside the slab
(silent), but it is still an out-of-bounds read of the caller's object.

## Expected output (with fix.diff applied to a copy of the source)

```
calling BF_set_key(&key, len=0, data=guard-page 0x800475000)
BUG-NOT-PRESENT: survived len=0
exit=0
```

## Correctness cross-checks (prove the fix and the tables)

- `bfkat`: 9/9 published Schneier vectors PASS (bf_pi.h digit-table integrity).
- `bfdiff`: kernel schedule+ciphertext byte-identical to LibreSSL's own `bf_skey.c`
  for every key length 1..120 with pseudo-random keys (wrap loop + >72 clamp).
- Both still PASS after the fix (no behavior change for len >= 1).

See `run.log` (KAT + openssl cross-match), `bfdiff.log`, `guard.log` (baseline fault),
`fixval.log` (patched: no fault, KATs still green).
