# DF-3565 — VERDICT

**Status: reproduced** (the unsafe-default behavior — silent acceptance of
the weak key 0x0101010101010101 by the default `des_set_key()` path —
demonstrated live; impact ceiling: fail-open key validation on a privileged,
legacy configuration surface).

## Narrative

Pass-2 audit of `sys/crypto/des/des_setkey.c` (233 LOC, Eric Young lineage).
The file contains a *correct and complete* weak/semi-weak detection table
(16 entries, des_setkey.c:97-115) and parity machinery — but the kernel
global `des_check_key` (des_setkey.c:65) defaults to 0, and `des_set_key()`
(des_setkey.c:142-153) consults it to decide between the checked and
unchecked paths. No code in the tree ever sets it, so the shipped kernel
always takes `des_set_key_unchecked()` and a weak key such as
0101010101010101 is silently accepted (rc=0) — demonstrated in the baseline
run.

The detection machinery itself was proven live (all 16 weak keys → -2 via
`des_set_key_checked`; 4096 random + 16 near-miss negatives → accepted), and
the schedule computation (shifts2, PC1/PC2, des_skb) was proven correct
against LibreSSL 3.6.1 over 2666 keys both directions, so the defect is
purely the default, not the tables.

Reachability: consumers are the kernel cryptoapi/`des_ecb.c` plumbing
(root-configured SAs at most) and legacy `smb_crypt.c` (NTLM-derived keys).
An attacker cannot choose the key; the risk is an operator quietly
deploying a cryptographically broken SA. Hence Info severity — a
fail-open default worth fixing, not an exploitable primitive.

## Fix validation

`fix.diff` flips the default to `des_check_key = 1`. On a patched copy of
the file (never applied to `sys/`):

- baseline: `des_set_key(0x0101010101010101) rc=0 (0=ACCEPTED) [des_check_key=0]`
- patched:  `des_set_key(0x0101010101010101) rc=-2` — weak key rejected by
  the default path; all other 8915 checks still pass (the single reported
  FAIL in the patched run is the harness's acceptance assertion inverting,
  i.e. the fix working).

See `fix_run.log`.

## Negative results recorded during this audit (weight for the clean parts)

- Memory safety: fixed-size tables only; `des_set_key_unchecked` writes
  exactly 32 DES_LONGs across the 16-entry schedule (in bounds);
  `des_is_weak_key` memcmp is `sizeof(des_cblock)`=8 on both sides.
- Parity table `odd_parity[256]` (podd.h): verified against independently
  computed parity, all 256 entries.
- shifts2 rotation logic (28-bit rotate-left by 1 or 2 in a 32-bit word)
  verified by construction and by the 2666-key ECB cross-match.
- The `weak_key` field of `struct des_ks_struct` is never written by this
  file (dead field) — noted, harmless.

## Kernel references

- sys/crypto/des/des_setkey.c:65 (insecure default)
- sys/crypto/des/des_setkey.c:142-153 (dispatch around the default)
- sys/crypto/des/des_setkey.c:159-167 (checked path, verified correct)
- sys/crypto/des/des_setkey.c:97-115 (weak key table, verified complete)
- sys/netproto/smb/smb_crypt.c (legacy consumer)

## 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.
