fil: pad chunk bodies to a length ladder, document the residual #196
Loading…
Reference in a new issue
No description provided.
Delete branch "onionwire/t_7a5e3d1d-pad-fil-chunks-to-length-buckets-and-doc"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Pad
filchunk bodies to a length ladder and document the residual.What changed
src/padding.rs:FIL_BUCKETS = [512, 1024, 2048, 4096, 8192, 16384, 32768, 49152],FIL_MAX_BODY = 49150,fil_bucket_for(),encode_fil(). The encoding isbyte-identical to chat's (
uint16 pad_len || data || random fill);encode/decodewere factored through one shared helper, so the chat ladder and thechat bucket-boundary tests are unchanged.
src/file.rs:encode()pads the chunk body before the one-shot Noisesession sees it;
decode_checked()unpads and returnsErrfor a malformedor overrunning
pad_len(decode()keeps its oldOptionshape on top).data_cap()is clamped toFIL_MAX_BODYand accounts for the 2-bytepad_len.overhead()is the new public "bytes before the padded body".src/node.rs: only the twofilarms — the send arm collectsencode()'sResult, the ingest arm callsdecode_checked()and logs a dropped malformedchunk instead of silently returning.
docs/PROTOCOL.md: “Length buckets” gains afilladder subsection,“Framing” stops claiming
filis unpadded, and thefilsection states thepadded layout, the 49150-byte raw cap, and the residual.
Why this ladder
Chat stops at 32 KiB because a chat body is a line of human text. A
filchunk is bounded by the frame budget instead, so reusing chat's ceiling would
halve the chunk size and nearly double the one-shot sessions a file costs
(33 chunks for 1 MiB, 3s pacing each). The rungs below 32 KiB are chat's,
starting at 512 rather than 256 because every
filframe already carries~115 bytes of ASCII header. The top rung is a half-step at 49152: the next
doubling (65536) cannot fit one Noise body at all (65519 plaintext max, tag
included), and 48 KiB leaves the worst-case header (128-byte name, 5-digit
idx/total, 243 bytes) more than 16 KiB of slack. 1 MiB is now 22 one-shotsinstead of 17.
Deviation from the card's wording (deliberate)
The card says PROTOCOL must state that
pad_lenis “authenticated by theratchet”.
filchunks are not ratcheted — one-shot sessions are a lock, andthis PR does not add a persistent session, a channel binding, or chunk
ratcheting. The spec therefore says the true thing:
pad_lenrides inside theone-shot Noise session and is authenticated by that session's
ChaCha20-Poly1305 tag, exactly as chat's padding is authenticated by the
ratchet's AEAD. Tampering with the length fails the session, not a checksum.
Interop
This is a wire-format change inside
filwith no version marker of its own(the frame version is unchanged). A peer on the old tree sees the two
pad_lenbytes as chunk data and fails the file's SHA-256, so a mixed-versionpair fails closed rather than shipping a corrupted file — but both ends must be
on this change for file transfer to work. Call it out in the release notes for
the cut that ships it.
Residual (the THREAT_MODEL row this PR cannot write)
docs/THREAT_MODEL.md:221still reads “rcp,tip,fil… are not paddedand still reveal their own sizes.” That line is stale with this PR, but that
file is scoped to another card, so the row to add there is:
Verification
cargo test --locked --target x86_64-unknown-linux-gnu: 38 suites,573 passed, 0 failed.
cargo clippy --locked --all-targets --target x86_64-unknown-linux-gnu -- -D warnings: clean.cargo test --lockedand clippy-D warningsclean.627 bytes; 510-byte (bucket-sized) file → 627 bytes. Header is 115,
padded body 512. Identical, so the tail chunk no longer reveals the size.
every_fil_frame_lands_on_the_fil_ladder(8 file sizes, everychunk on a rung),
a_one_byte_file_and_a_bucket_sized_file_send_identical_frame_lengths,an_oversized_pad_len_is_a_clean_protocol_error_not_a_truncation,a_malformed_fil_frame_is_an_error_while_chat_is_not_a_fil_frame,a_padded_multi_chunk_transfer_round_trips_with_an_equal_hash, plus paddingladder/encoding tests.
Base is the parent card's branch (its PR #193 is still open and also touches
docs/PROTOCOL.md); merge order is #193 first.