Checklist
- I am able to reproduce the bug with the latest version given here: CLICK THIS LINK.
- I made sure that there are no existing issues - open or closed - which I could contribute my information to.
- I have taken the time to fill in all the required details. I understand that the bug report will be dismissed otherwise.
- This issue contains only one bug.
- I have read and understood the contribution guidelines.
Affected version
2.0.1 (and current main, d55754d)
Steps to reproduce the bug
Reproduced directly against 2.0.1's MessagePadding, driving the same call sequence FragmentManager.createFragments() uses:
- Take a frame of 513-768, 1009-1792 or >=2033 bytes ending in 0x01.
- pad(frame, optimalBlockSize(frame.size)) -> returns the frame unchanged
- unpad(that) -> returns it one byte shorter
Over 200k random frames of 513-4096 bytes, 693 came back truncated (1 in 288 across that mix; 1 in 256 inside the affected bands). Frames of 769-1008 and 1793-2032 are unaffected, which is the tell: those are the sizes that actually do get padded.
On air: send >512-byte signed messages repeatedly between two devices and roughly one in 256 never arrives, on any receiver, with nothing in the sender's log.
Expected behavior
Every fragmented message reassembles and decodes.
Actual behavior
createFragments() encodes with packet.toBinaryData() (padded) and then calls MessagePadding.unpad() to strip the padding before splitting. But across the fragmentation range no padding was ever applied: pad() returns the data unchanged when paddingNeeded > 255, and optimalBlockSize() returns the input size unchanged above 2048.
An unpadded frame ends with the last byte of the Ed25519 signature - encode writes the signature last - and that byte is uniformly random. unpad() reads it as a PKCS#7 pad length, so when it happens to be 0x01 the validation passes trivially and a real signature byte is stripped. Every receiver reassembles the fragments correctly and then fails decodeCore's size check, so the message dies silently on all of them at once.
Screenshots/Screen recordings
No response
Affected Android/Custom ROM version
No response
Affected device model
No response
Additional information
One-line fix - ask the encoder for the unpadded frame instead of un-padding afterwards:
val fullData = packet.toBinaryData(padding = false)Byte-identical for frames that actually were padded, so nothing changes on the wire. iOS doesn't have this: BLEOutboundFragmentPlanner passes the padding flag into toBinaryData(padding:) and never un-pads.
Hit this while stress-testing long messages in a fork - three receiving devices logged "Failed to decode reassembled packet" for the same message at the same moment, which is what pointed at the sender.
Activity
0No activity yet.