# DF-3569 — camellia_set_key() accepts any key length (API-boundary validation gap)

## What

`sys/crypto/camellia/camellia-api.c:37-42` — `camellia_set_key()` is a `void`
wrapper that forwards `bits` to `Camellia_Ekeygen()` **without validating
anything**, then unconditionally stores `ctx->bits = bits`. The only key-length
guard in the whole path is the `KASSERT` at
`sys/crypto/camellia/camellia.c:1253-1254`, which is compiled out on
production kernels (no `INVARIANTS`). Under those semantics:

- `Camellia_Ekeygen()`'s switch takes `default: break;` — the 68-word key
  schedule is **never generated** (whatever the caller left in
  `ctx->subkey` stays there),
- `ctx->bits` records the invalid length anyway,
- the subsequent `Camellia_EncryptBlock`/`Camellia_DecryptBlock`
  (camellia.c:1271-1330) switches take `default: break;` too, so the 16-byte
  block is copied src→dst **with no round function**: encryption silently
  degenerates to the **identity function**.

Twin of DF-3568 (`rijndael_set_key`, same class) and the API-side companion of
DF-3560 (camellia.c unzeroed schedules / KASSERT-only guard). Unlike rijndael
there is **no memory-safety violation** — camellia's `default:` paths touch
neither the key buffer nor the schedule — the failure is purely cryptographic:
a caller that forwards an unchecked length (e.g. user-controlled) gets a
silent no-op cipher instead of an error.

## Reachability (why Info, not higher)

- **Zero in-tree consumers.** Tree-wide search finds no caller of
  `camellia_set_key`/`camellia_encrypt`/`camellia_decrypt` and no includer of
  `crypto/camellia/camellia.h` outside the directory itself. The only
  references are the build registrations `sys/conf/files:2258-2259`
  (`optional crypto`) and `sys/crypto/Makefile:19-20` (the `crypto.ko`
  primitive bundle).
- Compiled into every `option crypto` kernel / `crypto.ko` as **dead code**;
  out-of-tree kmods could consume it, which is not a base-system attack path.
- No unprivileged entry point exists today → latent hardening of the API
  contract for future callers, filed Info per the batch precedent (DF-3568).

## Build (on the audit guest, sources at /usr/src byte-identical to the tree)

```sh
cd /tmp/df3569   # contains guard_cam_badbits.c, fix.diff
cc -O2 -DNDEBUG -I /usr/src/sys -o guard_prod guard_cam_badbits.c \
   /usr/src/sys/crypto/camellia/camellia-api.c \
   /usr/src/sys/crypto/camellia/camellia.c
cc -O2 -I /usr/src/sys -o guard_dbg guard_cam_badbits.c \
   /usr/src/sys/crypto/camellia/camellia-api.c \
   /usr/src/sys/crypto/camellia/camellia.c
mkdir -p fixroot/sys/crypto && cp -R /usr/src/sys/crypto/camellia fixroot/sys/crypto/
(cd fixroot && patch -p1 < ../fix.diff)
cc -O2 -DNDEBUG -I /tmp/df3569/fixroot/sys -I /usr/src/sys -o guard_fix \
   guard_cam_badbits.c fixroot/sys/crypto/camellia/camellia-api.c \
   /usr/src/sys/crypto/camellia/camellia.c
```

`camellia.c:40-43` maps `KASSERT`→`assert()` in userland, so `-DNDEBUG`
reproduces exact production-kernel semantics and the no-flag build reproduces
INVARIANTS semantics.

## Run / expected

```sh
./guard_prod kat && ./guard_prod guard; echo exit=$?   # expect BUG-CONFIRMED, exit 42
./guard_dbg  guard; echo exit=$?                       # expect assert abort (SIGABRT)
./guard_fix  kat && ./guard_fix guard; echo exit=$?    # expect RESULT-PASS + BUG-NOT-PRESENT, exit 0
```

Observed (run.log / fixval.log):
- prod `kat`: RFC 3713 Appendix A vectors for 128/192/256 all PASS, decrypt
  roundtrip OK, schedule-bounds canary OK (writes stay inside `subkey[68]`).
- prod `guard`: bits ∈ {64, 0, −1, 129} — no trap, `ctx->bits` set to the
  invalid value, schedule sentinel `0xA5A5A5A5` intact (never generated),
  ciphertext == plaintext byte-for-byte → `BUG-CONFIRMED`, **exit 42**.
- dbg `guard`: `Assertion failed ... camellia.c, line 1254` → SIGABRT
  (the guard is debug-only).
- fix `kat`: still RESULT-PASS (0 failures); fix `guard`: all invalid lengths
  REJECTED before touching key material, `encrypt` refuses and leaves dst
  untouched → `BUG-NOT-PRESENT`, **exit 0**.

Harness-level fix validation only (no kernel build) — justified because no
in-tree caller can deliver an invalid length today (same rationale as
DF-3568).
