β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2369

Multi-block CFB1 never advances input/outBuffer leaking plaintext and stale output as ciphertext for blocks > 1

Summary

In both rijndael_blockEncrypt MODE_CFB1 and rijndael_blockDecrypt MODE_CFB1 per-block outer loop for(i=numBlocks i>0 i--) never advances input or outBuffer pointers unlike every ECB/CBC branch (which all do input+=16 outBuffer+=16). For numBlocks>1 function re-operates on same first 16 bytes every iteration never touches outBuffer[16..16*numBlocks-1] yet returns 128*numBlocks claiming it produced that many bits. Encrypt leaves plaintext for blocks 2..n untouched in ciphertext output (confidentiality break). Decrypt re-reads input[0..15] each block leaves output[16..] uninitialised/stale. No in-tree kernel caller uses MODE_CFB1 (only test00.c) so latent. [VERIFIED pass2: reproduced - MODE_CFB1 blockEncrypt returns 256 bits while out 16-31 stays at sentinel, loop never advances, plaintext blocks 2-n leak as ciphertext; pointer-advance fix-validated both directions, latent no in-tree caller]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2369 Β· 7 files
FileTypeDescriptionSize
env.txt β€” 412 B view raw
fix.diff β€” 579 B view raw
rig β€” 4.0 KB ↓ download
run_fixed_kernel.log β€” 2.8 KB view raw
run_kernel_baseline.log β€” 172 B view raw
verdict.json β€” 2.1 KB view raw
fix.diff β€” 579 B view raw

Fix verification

not_testable
↓ fix.diffper-fix-DF-2369

Confirmed kernel references

Detail

Evidence (decisive lines)

session baseline (ct[16..31] = e7*16 sentinel INTACT, rc=256; decrypt stale) vs run_fixed_kernel.log (ct[16..31] = 40 24 d0 ..., decrypt changed), fix.diff

PoC changes

initial ECB KAT used raw key bytes where the legacy makeKey API wants ASCII hex keyMaterial - the FIPS vector now matches

Verified recommended fix

advance input/outBuffer per block in both CFB1 loops

Verdict

Real in-kernel rijndael KPI, MODE_CFB1, two blocks (inputLen=256 bits): rijndael_blockEncrypt returns 256 claiming two produced blocks while out[16..31] stays at its pre-planted sentinel - the per-block loop never advances input/outBuffer (every other mode in the same function does), so encrypting leaves plaintext blocks 2..n in the ciphertext output and decrypt leaves out[16..] stale; the inner loop also re-reads input[0..15] each outer pass. With the pointer advance both blocks are processed (ct[16..31] now ciphertext, decrypt writes through block 2). The ECB KAT control of the same KPI matches FIPS-197; the legacy CFB1 path has no in-tree caller (cryptosoft never selects MODE_CFB1) - latent correctness/assurance defect, confirmed as filed.