# DF-3567 VERDICT

**Status: reproduced (same-source userland-object harness, in-guest) — impact: none (no in-kernel call path).**
**Fix: validated at harness level (fixed).**

## What was audited

Pass-2 full audit of `sys/crypto/blowfish/bf_skey.c` (118 LOC): the Eric Young /
KAME Blowfish key schedule. Prior batch verified `bf_enc.c` clean (213/213
cross-match); this file had no prior findings.

## Root cause

`BF_set_key(key, len, data)` clamps `len` from above (`len > 72 → 72`) at
bf_skey.c:80-81 but never from below. With `len <= 0`:

- `end = &data[len]` (bf_skey.c:84) lands at or before `data`;
- the wrap guard `if (d >= end) d = data;` after every byte load (bf_skey.c:87 et seq.)
  resets `d` unconditionally, so **every one of the 72 key-word byte loads reads
  `data[0]`** — an out-of-bounds read of a zero-length key buffer, and a silently
  wrong (constant-byte-key) schedule for any negative len.

Same defect class as the known `rc4_init` keylen==0 findings (DF-0971/DF-2366,
`sys/crypto/rc4/rc4.c:71-75`) — NOT re-reported here; this is the Blowfish instance.

## Reachability (why Info)

Full-tree grep: `BF_set_key` has **zero in-tree callers** (declaration
`sys/crypto/blowfish/blowfish.h:83` only). The old opencrypto/cryptosoft consumer was
removed from DragonFly (`sys/sys/param.h:264` notes the crypto(9) removal);
`sys/conf/files:2257` still compiles `bf_skey.c` into every `option crypto` kernel
(X86_64_GENERIC has it), so it is compiled-in dead code. No attacker-controlled path
exists today; the finding is filed so the primitive is not inherited by a future caller.

## Verification (guest: DragonFly 6.5-DEVELOPMENT #0, gcc 8.3 [DragonFly])

1. Guest `/usr/src/sys/crypto/blowfish/bf_skey.c` sha256 == audited tree
   (a77ce9b53805c4de3298c15a20be0c33205fda6a53f752570da33fc459ff1f01).
2. **Baseline primitive** (`guard_bf_len0`, guard.log): `BF_set_key(&k, 0,
   guard_page)` → SIGSEGV with `si_addr == data` (exit 42) — proves the `data[0]`
   dereference.
3. **Table integrity** (`bfkat`, run.log): 9/9 published Schneier vectors PASS
   (4EF997456198DD78 … B1B8CC0B250F09A0).
4. **Wrap loop + clamp, full range** (`bfdiff`, bfdiff.log): schedule and ciphertext
   byte-identical to LibreSSL's own `bf_skey.c`/`bf_enc.c` (guest
   /usr/src/crypto/libressl) for every len 1..120, pseudo-random keys — the >72 clamp
   (len 100 == len 72) included.
5. **Fix validation** (fixval.log): with `fix.diff` applied to a source copy,
   `guard_bf_len0` prints BUG-NOT-PRESENT (exit 0); bfkat 9/9 and bfdiff len 1..120
   still PASS — no behavioral change for len >= 1.

## Exploit chain

None — no caller, dead code, 1-byte read ceiling. Hardening only.

## kernel_refs

- sys/crypto/blowfish/bf_skey.c:80-99 (defect: no len floor; data[0] deref)
- sys/crypto/blowfish/blowfish.h:83 (only declaration; no callers)
- sys/conf/files:2257 (compiled under `option crypto`)
- sys/sys/param.h:264 (opencrypto removal — why the caller disappeared)

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
