# DF-3565 — Kernel DES weak/semi-weak key checking disabled by default

**Target:** `sys/crypto/des/des_setkey.c` (pass 2 GLM 5.3 re-audit)
**Class:** insecure default / CWE-1188 (Initialization with insecure default)
**Severity:** Info (privileged configuration surface; DES itself legacy)

## What

`int des_check_key = 0;` (sys/crypto/des/des_setkey.c:65) ships the kernel
DES key-schedule entry point in **unchecked** mode. `des_set_key()`
(des_setkey.c:142-153) only routes through `des_set_key_checked()` (parity
+ weak/semi-weak rejection) when this global is set — and nothing in the
tree ever sets it. Consequently every kernel DES consumer silently accepts:

- keys with wrong parity, and
- all 16 weak/semi-weak keys (0101010101010101, FEFEFEFEFEFEFEFE, …),
  which produce the pathological schedules the table at des_setkey.c:97-115
  exists to reject (E(C(k)) patterns with 2^2-key meet-in-the-middle).

Verified live (baseline):

```
== default policy: des_set_key with des_check_key==0 ==
   des_set_key(0x0101010101010101) rc=0 (0=ACCEPTED) [des_check_key=0]
```

## Reachability analysis (why Info, not higher)

- In-tree consumers: `sys/crypto/des/des_ecb.c` (kernel crypto plumbing) and
  `sys/netproto/smb/smb_crypt.c` (legacy smbfs, NTLM-derived keys).
  DragonFly has no opencrypto/cryptosoft; IPsec ESP-DES via cryptoapi would
  require root-configured SAs. Keys are therefore admin-chosen or
  hash-derived — an attacker cannot steer a derived key onto the weak list
  without breaking NTLM first.
- Impact ceiling: an admin who configures a weak DES key gets no warning,
  yielding cryptographically broken confidentiality on that SA. It is a
  fail-open default on a legacy algorithm, not an escalation.
- Related hygiene noted during audit (same class as DF-3564, filed there):
  `des_set_key_unchecked` keeps key-derived register material (c, d, s, t,
  t2 at des_setkey.c:172-222) on the stack after returning; no verbatim key
  copy is made (the input key is only read via c2l), so the marginal
  exposure is small.

## Also verified live in this pack (the reason the file is otherwise sound)

`des_kat.c` — 8916 checks, 0 failures:

- `odd_parity[]` table (podd.h) matches independently computed odd parity
  for all 256 bytes; `des_check_key_parity` accepts its own output and
  rejects single-bit parity breaks.
- All 16 weak/semi-weak keys are detected by `des_is_weak_key` and rejected
  (-2) by `des_set_key_checked`; 4096 random keys + 16 one-bit near-misses
  of the weak list produce no false positives.
- The key schedule itself (shifts2 table, PC1/PC2 via des_skb) is correct:
  2000 random keys + 666 weak-key schedules round-trip ECB encrypt/decrypt
  byte-identical to LibreSSL 3.6.1 `DES_set_key_unchecked`/`DES_ecb_encrypt`,
  plus the classic anchor key=133457799BBCDFF1, pt=0123456789ABCDEF →
  ct=85E813540F0AB405.

## Fix

`fix.diff`: default `des_check_key = 1`. Validated live on a patched copy:
`des_set_key(0x0101010101010101)` then returns **-2** (rejected) via the
default path and every KAT still passes. Deploy note: any consumer relying
on accepting non-odd-parity keys (smb_crypt derives keys without forcing
parity in places) would start failing — that is the desired fail-closed
behavior, but should be announced.

## Reproduce

```
cc -O0 -g -I shim -c /usr/src/sys/crypto/des/des_setkey.c -o des_setkey.o
cc -O0 -g -I shim -c /usr/src/sys/crypto/des/des_enc.c    -o des_enc.o
cc -O0 -g -I shim -c /usr/src/sys/crypto/des/des_ecb.c    -o des_ecb.o
cc -O0 -g -I shim des_kat.c des_setkey.o des_enc.o des_ecb.o \
   -I/usr/local/include -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lcrypto \
   -o des_kat
./des_kat        # watch the "default policy" section
```
