pamoja_lorawan/packages/fragment.rs
1//! Fragmented data block transport, TS004-2.0.0.
2//!
3//! A firmware image does not fit in a LoRaWAN frame, so it goes across in pieces. The server
4//! cuts the block into `nb_frag` fragments of `frag_size` bytes and sends them one per
5//! downlink, then keeps sending: every fragment after the first `nb_frag` is a coded one,
6//! the exclusive-or of a pseudo-random half of the originals. A device that missed some of
7//! the originals can solve for them from any `nb_frag` independent fragments it did hear, so
8//! a session survives losses without anyone asking for a retransmission.
9//!
10//! - [`parity_line`] and [`prbs23`] build the matrix rows those coded fragments come from.
11//! - [`Fragmenter`] cuts a block up and produces any fragment of the session, coded or not.
12//! - [`Defragmenter`] puts one back together from whatever arrives, in storage the caller
13//! provides: it never allocates, and it tells you how much it needs.
14//! - [`FragCommand`] carries the session setup, status, delete and acknowledgment commands
15//! on port [`PORT`], and [`data_block_int_key`] with [`BlockMic`] check the reassembled
16//! block against the code the setup carried.
17//!
18//! The coded fragments a device must agree with its server about are seeded from the index
19//! among the coded fragments, not the index within the whole session. That is what Semtech's
20//! LoRa Basics Modem and ChirpStack's fragmentation server both do, and the two have to
21//! agree for any of this to work. The MATLAB in the specification's appendix reads as though
22//! the whole-session index were the seed.
23//!
24//! # Examples
25//!
26//! A block goes across in five fragments, two are lost, and two coded fragments rebuild it:
27//!
28//! ```
29//! use pamoja_lorawan::packages::fragment::{Defragmenter, Fragmenter};
30//!
31//! let block = b"the quick brown fox jumps!";
32//! let sender = Fragmenter::new(block, 6)?;
33//! assert_eq!((sender.nb_frag(), sender.padding()), (5, 4));
34//!
35//! let mut store = [0u8; 30];
36//! let mut matrix = [0u8; 64];
37//! let mut receiver = Defragmenter::new(sender.nb_frag(), 6, &mut store, &mut matrix)?;
38//!
39//! // Fragments 3 and 4 never arrive; the coded ones that follow stand in for them.
40//! let mut piece = [0u8; 6];
41//! for n in [1, 2, 5, 6, 8] {
42//! sender.fragment(n, &mut piece)?;
43//! receiver.fragment(n, &piece)?;
44//! }
45//! assert!(receiver.done());
46//! assert_eq!(&receiver.block()[..block.len()], block);
47//! # Ok::<(), Box<dyn std::error::Error>>(())
48//! ```
49
50use super::{payload, write, PackageVersion};
51use crate::crypto::Cipher;
52use crate::{Direction, LorawanError};
53
54/// The port the fragmented data block transport package is spoken on, section 3.
55pub const PORT: u8 = 201;
56
57/// The identifier of this package, section 3.
58pub const PACKAGE: u8 = 3;
59
60/// The version of this package implemented here, section 3.
61pub const VERSION: u8 = 2;
62
63/// How many fragmentation sessions a device may hold at once, section 3.3.
64pub const SESSIONS: usize = 4;
65
66/// The most fragments one session carries, which the fourteen-bit index bounds.
67pub const MAX_FRAGMENTS: u16 = (1 << 14) - 1;
68
69/// The longest command of this package other than a data fragment, in bytes.
70pub const MAX_COMMAND: usize = 17;
71
72const CID_PACKAGE_VERSION: u8 = 0x00;
73const CID_SESSION_STATUS: u8 = 0x01;
74const CID_SESSION_SETUP: u8 = 0x02;
75const CID_SESSION_DELETE: u8 = 0x03;
76const CID_BLOCK_RECEIVED: u8 = 0x04;
77const CID_DATA_FRAGMENT: u8 = 0x08;
78
79/// Steps the 23-bit pseudo-random sequence the parity matrix is drawn from, appendix A.1.
80///
81/// # Arguments
82///
83/// * `x` - the current value.
84///
85/// # Returns
86///
87/// The next one.
88///
89/// # Examples
90///
91/// ```
92/// use pamoja_lorawan::packages::fragment::prbs23;
93///
94/// assert_eq!(prbs23(1), 1 << 22, "a one shifts out and feeds back");
95/// ```
96#[must_use]
97pub const fn prbs23(x: u32) -> u32 {
98 let b0 = x & 1;
99 let b1 = (x >> 5) & 1;
100 (x >> 1) + ((b0 ^ b1) << 22)
101}
102
103/// Builds one row of the parity matrix: which uncoded fragments a coded one is made of.
104///
105/// # Arguments
106///
107/// * `coded` - which coded fragment this is, counting from one. A session's fragment `n`
108/// past its `nb_frag` uncoded ones is coded fragment `n`.
109/// * `nb_frag` - how many uncoded fragments the block was cut into.
110/// * `line` - a bit for every uncoded fragment, cleared and then filled in, little end
111/// first. It must hold at least `nb_frag` bits.
112///
113/// # Returns
114///
115/// How many fragments the coded one is the exclusive-or of, which is half of `nb_frag`
116/// rounded down.
117///
118/// # Examples
119///
120/// ```
121/// use pamoja_lorawan::packages::fragment::parity_line;
122///
123/// let mut line = [0u8; 1];
124/// assert_eq!(parity_line(1, 6, &mut line), 3);
125/// assert_eq!(line[0] & 0b0011_1111, 0b0001_0011, "fragments 1, 2 and 5");
126/// ```
127pub fn parity_line(coded: u16, nb_frag: u16, line: &mut [u8]) -> usize {
128 let m = u32::from(nb_frag);
129 let bytes = (nb_frag as usize).div_ceil(8);
130 for byte in line.iter_mut().take(bytes) {
131 *byte = 0;
132 }
133 // A power of two makes the generator fall into patterns, so it draws from one more
134 // than the count and throws away anything out of range, appendix A.1.
135 let span = m + u32::from(m.is_power_of_two());
136 let mut x = 1 + 1001 * u32::from(coded);
137 let wanted = (nb_frag / 2) as usize;
138 let mut set = 0;
139 while set < wanted {
140 let mut r = 1 << 16;
141 while r >= m {
142 x = prbs23(x);
143 r = x % span;
144 }
145 let at = r as usize;
146 if line[at / 8] & (1 << (at % 8)) == 0 {
147 line[at / 8] |= 1 << (at % 8);
148 set += 1;
149 }
150 }
151 set
152}
153
154/// Whether a bit is set in a little-end-first bitset.
155const fn bit(bits: &[u8], at: usize) -> bool {
156 bits[at / 8] & (1 << (at % 8)) != 0
157}
158
159/// Sets or clears a bit in a little-end-first bitset.
160const fn set_bit(bits: &mut [u8], at: usize, on: bool) {
161 if on {
162 bits[at / 8] |= 1 << (at % 8);
163 } else {
164 bits[at / 8] &= !(1 << (at % 8));
165 }
166}
167
168/// Cuts a data block into the fragments of a session, and builds the coded ones.
169///
170/// The block is left where it is: nothing is copied until a fragment is asked for.
171#[derive(Clone, Copy, Debug)]
172pub struct Fragmenter<'a> {
173 block: &'a [u8],
174 frag_size: u8,
175 nb_frag: u16,
176 padding: u8,
177}
178
179impl<'a> Fragmenter<'a> {
180 /// Prepares a block for a session.
181 ///
182 /// # Arguments
183 ///
184 /// * `block` - the data to send.
185 /// * `frag_size` - how many bytes each fragment carries.
186 ///
187 /// # Returns
188 ///
189 /// The fragmenter, which says how many fragments the block takes and how much padding
190 /// the last one needs.
191 ///
192 /// # Errors
193 ///
194 /// [`LorawanError::MalformedFrame`] for a fragment size of zero or a block that needs
195 /// more than [`MAX_FRAGMENTS`] fragments.
196 pub fn new(block: &'a [u8], frag_size: u8) -> Result<Fragmenter<'a>, LorawanError> {
197 if frag_size == 0 {
198 return Err(LorawanError::MalformedFrame);
199 }
200 let size = usize::from(frag_size);
201 let nb_frag = block.len().div_ceil(size);
202 if nb_frag == 0 || nb_frag > MAX_FRAGMENTS as usize {
203 return Err(LorawanError::MalformedFrame);
204 }
205 Ok(Fragmenter {
206 block,
207 frag_size,
208 nb_frag: nb_frag as u16,
209 padding: (nb_frag * size - block.len()) as u8,
210 })
211 }
212
213 /// How many uncoded fragments the block was cut into.
214 ///
215 /// # Returns
216 ///
217 /// The count, which the session setup carries as `NbFrag`.
218 #[must_use]
219 pub const fn nb_frag(&self) -> u16 {
220 self.nb_frag
221 }
222
223 /// How many bytes of padding the last fragment carries.
224 ///
225 /// # Returns
226 ///
227 /// The count, which the session setup carries and the receiver strips.
228 #[must_use]
229 pub const fn padding(&self) -> u8 {
230 self.padding
231 }
232
233 /// How many bytes each fragment carries.
234 ///
235 /// # Returns
236 ///
237 /// The size.
238 #[must_use]
239 pub const fn frag_size(&self) -> u8 {
240 self.frag_size
241 }
242
243 /// Builds one fragment of the session.
244 ///
245 /// # Arguments
246 ///
247 /// * `n` - which fragment, counting from one. Up to `nb_frag` it is a piece of the
248 /// block; past that it is a coded fragment, the exclusive-or of a pseudo-random half
249 /// of the pieces.
250 /// * `out` - where to write it, at least `frag_size` bytes.
251 ///
252 /// # Returns
253 ///
254 /// How many bytes were written, which is always `frag_size`.
255 ///
256 /// # Errors
257 ///
258 /// [`LorawanError::MalformedFrame`] for a fragment number of zero, and
259 /// [`LorawanError::PayloadTooLong`] when the buffer is too small.
260 pub fn fragment(&self, n: u16, out: &mut [u8]) -> Result<usize, LorawanError> {
261 let size = usize::from(self.frag_size);
262 let room = out.get_mut(..size).ok_or(LorawanError::PayloadTooLong)?;
263 if n == 0 {
264 return Err(LorawanError::MalformedFrame);
265 }
266 room.fill(0);
267 if n <= self.nb_frag {
268 self.piece(usize::from(n - 1), room);
269 return Ok(size);
270 }
271 let mut line = [0u8; (MAX_FRAGMENTS as usize).div_ceil(8)];
272 parity_line(n - self.nb_frag, self.nb_frag, &mut line);
273 for index in 0..usize::from(self.nb_frag) {
274 if bit(&line, index) {
275 let mut piece = [0u8; 255];
276 self.piece(index, &mut piece[..size]);
277 for (out, byte) in room.iter_mut().zip(&piece[..size]) {
278 *out ^= byte;
279 }
280 }
281 }
282 Ok(size)
283 }
284
285 /// Copies one uncoded fragment out, padding the last one with zeros.
286 fn piece(&self, index: usize, out: &mut [u8]) {
287 let size = usize::from(self.frag_size);
288 let at = index * size;
289 let end = (at + size).min(self.block.len());
290 out.fill(0);
291 if at < self.block.len() {
292 out[..end - at].copy_from_slice(&self.block[at..end]);
293 }
294 }
295}
296
297/// Why a fragmentation session could not be put back together.
298#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
299pub enum FragError {
300 /// The session parameters do not describe a session this build can run.
301 Session,
302 /// The caller's storage is too small for the session it was given.
303 Storage,
304 /// More uncoded fragments were lost than the caller left room to solve for.
305 Memory,
306 /// A fragment's number is outside the session.
307 Fragment,
308}
309
310impl core::fmt::Display for FragError {
311 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
312 match self {
313 FragError::Session => f.write_str("the fragmentation session is not one this runs"),
314 FragError::Storage => f.write_str("the storage is too small for the session"),
315 FragError::Memory => {
316 f.write_str("more fragments were lost than there is room to solve for")
317 }
318 FragError::Fragment => f.write_str("the fragment is not part of the session"),
319 }
320 }
321}
322
323impl core::error::Error for FragError {}
324
325/// How far a fragmentation session has got, small enough to write down.
326///
327/// A device that loses power partway through a firmware download keeps its block storage and
328/// its working storage in flash; this is the handful of numbers it needs beside them to carry
329/// on where it left off, rather than asking for the whole image again.
330#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
331pub struct Progress {
332 /// How many fragments arrived, coded, uncoded and repeated.
333 pub received: u16,
334 /// How many uncoded fragments were lost.
335 pub lost: u16,
336 /// How many rows of the system are filled in.
337 pub rows: u16,
338 /// The highest uncoded fragment number seen.
339 pub highest: u16,
340 /// Whether the block is whole.
341 pub done: bool,
342}
343
344/// Puts a data block back together from the fragments that arrive, TS004-2.0.0 appendix A.2.
345///
346/// The uncoded fragments go straight into the caller's block storage. A coded fragment is
347/// reduced against everything already known and, if it still says something new, kept as one
348/// row of a triangular system over the fragments that were lost. Once there are as many rows
349/// as there are losses, the system is solved back into the block.
350///
351/// Nothing is allocated. The block storage is `nb_frag * frag_size` bytes, and the matrix
352/// storage bounds how many losses can be solved for: [`Defragmenter::matrix_len`] says how
353/// much room a given number of losses takes.
354pub struct Defragmenter<'a> {
355 nb_frag: u16,
356 frag_size: u8,
357 block: &'a mut [u8],
358 matrix: &'a mut [u8],
359 max_lost: u16,
360 lost: u16,
361 rows: u16,
362 received: u16,
363 highest: u16,
364 done: bool,
365}
366
367impl<'a> Defragmenter<'a> {
368 /// How many bytes of matrix storage a session needs.
369 ///
370 /// # Arguments
371 ///
372 /// * `nb_frag` - how many uncoded fragments the block was cut into.
373 /// * `max_lost` - the most uncoded fragments the session should survive losing.
374 ///
375 /// # Returns
376 ///
377 /// The byte count: a row for each loss, as wide as the losses; the map from row to
378 /// fragment; and a bit for each uncoded fragment saying whether it is in hand.
379 ///
380 /// # Examples
381 ///
382 /// ```
383 /// use pamoja_lorawan::packages::fragment::Defragmenter;
384 ///
385 /// assert_eq!(Defragmenter::matrix_len(100, 8), 8 + 2 * 8 + 13);
386 /// ```
387 #[must_use]
388 pub const fn matrix_len(nb_frag: u16, max_lost: u16) -> usize {
389 let lost = max_lost as usize;
390 lost * lost.div_ceil(8) + 2 * lost + (nb_frag as usize).div_ceil(8)
391 }
392
393 /// Starts a session.
394 ///
395 /// # Arguments
396 ///
397 /// * `nb_frag` - how many uncoded fragments the block was cut into.
398 /// * `frag_size` - how many bytes each fragment carries.
399 /// * `block` - where the block is put back together, `nb_frag * frag_size` bytes.
400 /// * `matrix` - the working storage, whose size decides how many losses can be solved
401 /// for; [`Defragmenter::matrix_len`] sizes it.
402 ///
403 /// # Returns
404 ///
405 /// The session, with nothing received.
406 ///
407 /// # Errors
408 ///
409 /// [`FragError::Session`] for a session this build does not run, and
410 /// [`FragError::Storage`] when the block storage is too small.
411 pub fn new(
412 nb_frag: u16,
413 frag_size: u8,
414 block: &'a mut [u8],
415 matrix: &'a mut [u8],
416 ) -> Result<Defragmenter<'a>, FragError> {
417 Defragmenter::opened(nb_frag, frag_size, block, matrix, true)
418 }
419
420 /// Opens a session, clearing the storage or taking it as it is.
421 fn opened(
422 nb_frag: u16,
423 frag_size: u8,
424 block: &'a mut [u8],
425 matrix: &'a mut [u8],
426 clear: bool,
427 ) -> Result<Defragmenter<'a>, FragError> {
428 if nb_frag == 0 || nb_frag > MAX_FRAGMENTS || frag_size == 0 {
429 return Err(FragError::Session);
430 }
431 let needed = usize::from(nb_frag) * usize::from(frag_size);
432 if block.len() < needed {
433 return Err(FragError::Storage);
434 }
435 // The largest number of losses this much matrix storage can carry.
436 let mut max_lost = 0u16;
437 while max_lost < nb_frag && Defragmenter::matrix_len(nb_frag, max_lost + 1) <= matrix.len()
438 {
439 max_lost += 1;
440 }
441 if Defragmenter::matrix_len(nb_frag, max_lost) > matrix.len() {
442 return Err(FragError::Storage);
443 }
444 if clear {
445 matrix.fill(0);
446 block[..needed].fill(0);
447 }
448 Ok(Defragmenter {
449 nb_frag,
450 frag_size,
451 block,
452 matrix,
453 max_lost,
454 lost: 0,
455 rows: 0,
456 received: 0,
457 highest: 0,
458 done: false,
459 })
460 }
461
462 /// Starts a session again where one left off.
463 ///
464 /// The storage must be the same two buffers the earlier session held, with whatever they
465 /// had in them, and the progress must be the one it reported. Nothing is cleared.
466 ///
467 /// # Arguments
468 ///
469 /// * `nb_frag` - how many uncoded fragments the block was cut into.
470 /// * `frag_size` - how many bytes each fragment carries.
471 /// * `block` - the block storage, as the earlier session left it.
472 /// * `matrix` - the working storage, as the earlier session left it.
473 /// * `progress` - what [`Defragmenter::progress`] reported.
474 ///
475 /// # Returns
476 ///
477 /// The session, carrying on.
478 ///
479 /// # Errors
480 ///
481 /// [`FragError::Session`] for a session this build does not run, and
482 /// [`FragError::Storage`] when the storage is too small.
483 ///
484 /// # Examples
485 ///
486 /// ```
487 /// use pamoja_lorawan::packages::fragment::{Defragmenter, Fragmenter};
488 ///
489 /// let block = b"a block that outlives the power";
490 /// let sender = Fragmenter::new(block, 8)?;
491 /// let mut store = [0u8; 32];
492 /// let mut matrix = [0u8; 64];
493 ///
494 /// let mut piece = [0u8; 8];
495 /// let mut session = Defragmenter::new(sender.nb_frag(), 8, &mut store, &mut matrix)?;
496 /// sender.fragment(1, &mut piece)?;
497 /// session.fragment(1, &piece)?;
498 /// let progress = session.progress();
499 ///
500 /// // The power goes. The two buffers were in flash; this is all that was beside them.
501 /// let mut session =
502 /// Defragmenter::resumed(sender.nb_frag(), 8, &mut store, &mut matrix, progress)?;
503 /// for n in 2..=sender.nb_frag() {
504 /// sender.fragment(n, &mut piece)?;
505 /// session.fragment(n, &piece)?;
506 /// }
507 /// assert!(session.done());
508 /// assert_eq!(&session.block()[..block.len()], block);
509 /// # Ok::<(), Box<dyn std::error::Error>>(())
510 /// ```
511 pub fn resumed(
512 nb_frag: u16,
513 frag_size: u8,
514 block: &'a mut [u8],
515 matrix: &'a mut [u8],
516 progress: Progress,
517 ) -> Result<Defragmenter<'a>, FragError> {
518 let mut session = Defragmenter::opened(nb_frag, frag_size, block, matrix, false)?;
519 session.received = progress.received;
520 session.lost = progress.lost;
521 session.rows = progress.rows;
522 session.highest = progress.highest;
523 session.done = progress.done;
524 Ok(session)
525 }
526
527 /// How far the session has got, to be kept beside its storage.
528 ///
529 /// # Returns
530 ///
531 /// The numbers [`Defragmenter::resumed`] takes.
532 #[must_use]
533 pub const fn progress(&self) -> Progress {
534 Progress {
535 received: self.received,
536 lost: self.lost,
537 rows: self.rows,
538 highest: self.highest,
539 done: self.done,
540 }
541 }
542
543 /// The block, as far as it has been put back together.
544 ///
545 /// # Returns
546 ///
547 /// The bytes, which are the whole block once [`Defragmenter::done`] is true, padding
548 /// and all. The padding the session setup named is at the end.
549 #[must_use]
550 pub fn block(&self) -> &[u8] {
551 let needed = usize::from(self.nb_frag) * usize::from(self.frag_size);
552 &self.block[..needed]
553 }
554
555 /// Whether the whole block is there.
556 ///
557 /// # Returns
558 ///
559 /// `true` once every uncoded fragment has arrived or been solved for.
560 #[must_use]
561 pub const fn done(&self) -> bool {
562 self.done
563 }
564
565 /// How many fragments arrived, coded and uncoded, including repeats.
566 ///
567 /// # Returns
568 ///
569 /// The count, which a status answer reports as `NbFragReceived`.
570 #[must_use]
571 pub const fn received(&self) -> u16 {
572 self.received
573 }
574
575 /// How many uncoded fragments are still missing.
576 ///
577 /// # Returns
578 ///
579 /// The count, which is also the least number of further independent fragments needed.
580 /// A status answer reports it as `MissingFrag`, capped at 255.
581 #[must_use]
582 pub const fn missing(&self) -> u16 {
583 if self.done {
584 0
585 } else {
586 self.lost - self.rows + (self.nb_frag - self.highest)
587 }
588 }
589
590 /// How many losses this session can still solve for.
591 ///
592 /// # Returns
593 ///
594 /// The count the caller's matrix storage allows.
595 #[must_use]
596 pub const fn max_lost(&self) -> u16 {
597 self.max_lost
598 }
599
600 /// Takes one fragment of the session.
601 ///
602 /// # Arguments
603 ///
604 /// * `n` - which fragment, counting from one.
605 /// * `data` - its bytes, `frag_size` of them.
606 ///
607 /// # Returns
608 ///
609 /// Whether the block is now complete.
610 ///
611 /// # Errors
612 ///
613 /// [`FragError::Fragment`] for a fragment outside the session or of the wrong size, and
614 /// [`FragError::Memory`] when more uncoded fragments were lost than there is room to
615 /// solve for.
616 pub fn fragment(&mut self, n: u16, data: &[u8]) -> Result<bool, FragError> {
617 let size = usize::from(self.frag_size);
618 if n == 0 || data.len() < size {
619 return Err(FragError::Fragment);
620 }
621 self.received = self.received.saturating_add(1);
622 if self.done {
623 return Ok(true);
624 }
625 if n <= self.nb_frag {
626 self.uncoded(n, &data[..size]);
627 return Ok(self.done);
628 }
629 self.coded(n - self.nb_frag, &data[..size])
630 }
631
632 /// Stores an uncoded fragment and notes what was lost before it.
633 fn uncoded(&mut self, n: u16, data: &[u8]) {
634 let size = usize::from(self.frag_size);
635 let index = usize::from(n - 1);
636 if !self.arrived(index) {
637 self.block[index * size..index * size + size].copy_from_slice(data);
638 self.mark(index);
639 }
640 if n > self.highest {
641 // Everything between the last one seen and this one was lost.
642 for missing in self.highest..n - 1 {
643 if !self.arrived(usize::from(missing)) {
644 self.note_lost(missing);
645 }
646 }
647 self.highest = n;
648 }
649 if self.highest == self.nb_frag && self.lost == 0 {
650 self.done = true;
651 }
652 }
653
654 /// Takes a coded fragment into the system, solving the block when it completes it.
655 fn coded(&mut self, coded: u16, data: &[u8]) -> Result<bool, FragError> {
656 // Every uncoded fragment has been sent by now, so anything not seen is lost.
657 for missing in self.highest..self.nb_frag {
658 if !self.arrived(usize::from(missing)) {
659 self.note_lost(missing);
660 }
661 }
662 self.highest = self.nb_frag;
663 if self.lost == 0 {
664 self.done = true;
665 return Ok(true);
666 }
667 if self.lost > self.max_lost {
668 return Err(FragError::Memory);
669 }
670
671 let size = usize::from(self.frag_size);
672 let mut line = [0u8; (MAX_FRAGMENTS as usize).div_ceil(8)];
673 parity_line(coded, self.nb_frag, &mut line);
674
675 // Reduce against the fragments already in hand, leaving a combination of the lost
676 // ones only, and fold their data into the coded fragment as we go.
677 let mut piece = [0u8; 255];
678 piece[..size].copy_from_slice(data);
679 let mut row = [0u8; ((MAX_FRAGMENTS as usize) / 8) + 1];
680 let lost = usize::from(self.lost);
681 for at in 0..lost {
682 let fragment = usize::from(self.lost_at(at));
683 if bit(&line, fragment) {
684 set_bit(&mut row, at, true);
685 }
686 }
687 for index in 0..usize::from(self.nb_frag) {
688 if bit(&line, index) && self.arrived(index) {
689 for (out, byte) in piece[..size]
690 .iter_mut()
691 .zip(&self.block[index * size..index * size + size])
692 {
693 *out ^= byte;
694 }
695 }
696 }
697
698 // Reduce against the rows already stored, which are in echelon form.
699 for at in 0..lost {
700 if !bit(&row, at) || !self.row_used(at) {
701 continue;
702 }
703 self.xor_row(at, &mut row);
704 let slot = usize::from(self.lost_at(at));
705 for (out, byte) in piece[..size]
706 .iter_mut()
707 .zip(&self.block[slot * size..slot * size + size])
708 {
709 *out ^= byte;
710 }
711 }
712
713 // What is left either says nothing new, or becomes a new row.
714 let Some(first) = (0..lost).find(|at| bit(&row, *at)) else {
715 return Ok(false);
716 };
717 self.store_row(first, &row);
718 let slot = usize::from(self.lost_at(first));
719 self.block[slot * size..slot * size + size].copy_from_slice(&piece[..size]);
720 self.rows += 1;
721 if self.rows == self.lost {
722 self.solve();
723 self.done = true;
724 }
725 Ok(self.done)
726 }
727
728 /// Runs the triangular system back into the block, appendix A.2 step 5.
729 fn solve(&mut self) {
730 let size = usize::from(self.frag_size);
731 let lost = usize::from(self.lost);
732 for at in (0..lost).rev() {
733 let slot = usize::from(self.lost_at(at));
734 for other in at + 1..lost {
735 if self.row_bit(at, other) {
736 let source = usize::from(self.lost_at(other));
737 for byte in 0..size {
738 self.block[slot * size + byte] ^= self.block[source * size + byte];
739 }
740 }
741 }
742 self.mark(slot);
743 }
744 }
745
746 /// The layout of the working storage: the row bits, then the lost list, then the
747 /// bitset of which uncoded fragments arrived.
748 const fn row_bytes(&self) -> usize {
749 (self.max_lost as usize).div_ceil(8)
750 }
751
752 /// Where the list of lost fragments starts.
753 const fn lost_at_offset(&self) -> usize {
754 self.max_lost as usize * self.row_bytes()
755 }
756
757 /// Where the bitset of arrived fragments starts.
758 const fn arrived_offset(&self) -> usize {
759 self.lost_at_offset() + 2 * self.max_lost as usize
760 }
761
762 /// Whether an uncoded fragment has arrived or been solved for.
763 fn arrived(&self, index: usize) -> bool {
764 let at = self.arrived_offset() + index / 8;
765 self.matrix[at] & (1 << (index % 8)) != 0
766 }
767
768 /// Notes that an uncoded fragment is in hand.
769 fn mark(&mut self, index: usize) {
770 let at = self.arrived_offset() + index / 8;
771 self.matrix[at] |= 1 << (index % 8);
772 }
773
774 /// Notes that an uncoded fragment was lost, giving it a row of the system.
775 fn note_lost(&mut self, fragment: u16) {
776 if self.lost >= self.max_lost {
777 self.lost += 1;
778 return;
779 }
780 let base = self.lost_at_offset() + 2 * usize::from(self.lost);
781 self.matrix[base] = (fragment & 0xFF) as u8;
782 self.matrix[base + 1] = (fragment >> 8) as u8;
783 self.lost += 1;
784 }
785
786 /// Which fragment a row of the system stands for.
787 fn lost_at(&self, at: usize) -> u16 {
788 let base = self.lost_at_offset() + 2 * at;
789 u16::from(self.matrix[base]) | (u16::from(self.matrix[base + 1]) << 8)
790 }
791
792 /// Whether a row of the system has been filled in.
793 fn row_used(&self, at: usize) -> bool {
794 self.row_bit(at, at)
795 }
796
797 /// One bit of a stored row.
798 fn row_bit(&self, row: usize, column: usize) -> bool {
799 let base = row * self.row_bytes();
800 self.matrix[base + column / 8] & (1 << (column % 8)) != 0
801 }
802
803 /// Exclusive-ors a stored row into a working one.
804 fn xor_row(&self, row: usize, into: &mut [u8]) {
805 let base = row * self.row_bytes();
806 let stored = &self.matrix[base..base + self.row_bytes()];
807 for (out, byte) in into.iter_mut().zip(stored) {
808 *out ^= byte;
809 }
810 }
811
812 /// Writes a working row into the system.
813 fn store_row(&mut self, row: usize, from: &[u8]) {
814 let width = self.row_bytes();
815 let base = row * width;
816 self.matrix[base..base + width].copy_from_slice(&from[..width]);
817 }
818}
819
820/// Derives the key that signs a data block, section 3.3.
821///
822/// # Arguments
823///
824/// * `root_key` - the device's `GenAppKey` on LoRaWAN 1.0.x, or its `AppKey` on 1.1.
825///
826/// # Returns
827///
828/// The `DataBlockIntKey`, which only ever signs data blocks.
829///
830/// # Examples
831///
832/// ```
833/// use pamoja_lorawan::packages::fragment::data_block_int_key;
834///
835/// let key = data_block_int_key(&[0x2B; 16]);
836/// assert_ne!(key, [0x2B; 16], "it is a key of its own");
837/// ```
838#[must_use]
839pub fn data_block_int_key(root_key: &[u8; 16]) -> [u8; 16] {
840 let mut block = [0u8; 16];
841 block[0] = 0x30;
842 Cipher::new(root_key).encrypt_block(&block)
843}
844
845/// The integrity code over a whole data block, section 3.3.
846///
847/// The block is signed a piece at a time, so a device checks an image it never holds twice.
848pub struct BlockMic<'a> {
849 stream: crate::crypto::CmacStream<'a>,
850}
851
852/// The cipher a block code is taken under, which the code borrows.
853pub struct BlockMicKey {
854 cipher: Cipher,
855}
856
857impl BlockMicKey {
858 /// Prepares the key a block's code is taken under.
859 ///
860 /// # Arguments
861 ///
862 /// * `data_block_int_key` - the key [`data_block_int_key`] derived.
863 ///
864 /// # Returns
865 ///
866 /// The prepared key.
867 #[must_use]
868 pub fn new(data_block_int_key: &[u8; 16]) -> BlockMicKey {
869 BlockMicKey {
870 cipher: Cipher::new(data_block_int_key),
871 }
872 }
873
874 /// Starts a code over one session's block.
875 ///
876 /// # Arguments
877 ///
878 /// * `session_cnt` - the session counter the setup carried.
879 /// * `frag_index` - which of the device's sessions this is.
880 /// * `descriptor` - the four bytes the setup described the block with.
881 /// * `block_len` - the length of the block in bytes, without its padding.
882 ///
883 /// # Returns
884 ///
885 /// The code, waiting for the block itself.
886 #[must_use]
887 pub fn start(
888 &self,
889 session_cnt: u16,
890 frag_index: u8,
891 descriptor: [u8; 4],
892 block_len: u32,
893 ) -> BlockMic<'_> {
894 let mut b0 = [0u8; 16];
895 b0[0] = 0x49;
896 b0[1..3].copy_from_slice(&session_cnt.to_le_bytes());
897 b0[3] = frag_index;
898 b0[4..8].copy_from_slice(&descriptor);
899 b0[12..16].copy_from_slice(&block_len.to_le_bytes());
900 let mut stream = self.cipher.cmac_stream();
901 stream.update(&b0);
902 BlockMic { stream }
903 }
904}
905
906impl BlockMic<'_> {
907 /// Adds a piece of the block.
908 ///
909 /// # Arguments
910 ///
911 /// * `data` - the next bytes of the block, in order, without any padding.
912 pub fn update(&mut self, data: &[u8]) {
913 self.stream.update(data);
914 }
915
916 /// Finishes the code.
917 ///
918 /// # Returns
919 ///
920 /// The four bytes a session setup carries.
921 #[must_use]
922 pub fn finish(self) -> [u8; 4] {
923 let full = self.stream.finish();
924 [full[0], full[1], full[2], full[3]]
925 }
926}
927
928/// What a device makes of a session setup, table 14.
929#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
930pub struct SetupStatus {
931 /// The device does not run the fragmentation algorithm the setup named.
932 pub unsupported_algorithm: bool,
933 /// The device has not the memory for a block that size.
934 pub not_enough_memory: bool,
935 /// The session index is not one the device keeps.
936 pub unsupported_index: bool,
937 /// The descriptor is not one the device accepts.
938 pub wrong_descriptor: bool,
939 /// The session counter repeats one already used for this index.
940 pub session_replay: bool,
941 /// Which session this answer is about.
942 pub frag_index: u8,
943}
944
945impl SetupStatus {
946 /// Whether the setup was accepted.
947 ///
948 /// # Returns
949 ///
950 /// `true` when no error bit is set.
951 #[must_use]
952 pub const fn accepted(&self) -> bool {
953 !self.unsupported_algorithm
954 && !self.not_enough_memory
955 && !self.unsupported_index
956 && !self.wrong_descriptor
957 && !self.session_replay
958 }
959
960 /// Reads the coded status.
961 const fn from_bits(bits: u8) -> SetupStatus {
962 SetupStatus {
963 unsupported_algorithm: bits & 0x01 != 0,
964 not_enough_memory: bits & 0x02 != 0,
965 unsupported_index: bits & 0x04 != 0,
966 wrong_descriptor: bits & 0x08 != 0,
967 session_replay: bits & 0x10 != 0,
968 frag_index: bits >> 6,
969 }
970 }
971
972 /// Writes the coded status.
973 const fn bits(&self) -> u8 {
974 (self.unsupported_algorithm as u8)
975 | ((self.not_enough_memory as u8) << 1)
976 | ((self.unsupported_index as u8) << 2)
977 | ((self.wrong_descriptor as u8) << 3)
978 | ((self.session_replay as u8) << 4)
979 | ((self.frag_index & 0x03) << 6)
980 }
981}
982
983/// One command of the fragmented data block transport package.
984#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
985pub enum FragCommand<'a> {
986 /// The server asking which version of this package the device implements.
987 PackageVersionReq,
988 /// The device's answer, section 3.1.
989 PackageVersionAns(PackageVersion),
990 /// The server asking how a session is going, section 3.2.
991 FragSessionStatusReq {
992 /// Which session.
993 frag_index: u8,
994 /// Whether every device answers, or only those still missing fragments.
995 all_participants: bool,
996 },
997 /// The device's answer, section 3.2.
998 FragSessionStatusAns {
999 /// Which session.
1000 frag_index: u8,
1001 /// How many fragments arrived, coded, uncoded and repeated.
1002 received: u16,
1003 /// How many uncoded fragments are still missing, capped at 255.
1004 missing: u8,
1005 /// Whether the block's integrity code did not check out.
1006 mic_error: bool,
1007 /// Whether the session ran out of memory to defragment with.
1008 memory_error: bool,
1009 /// Whether the session does not exist on this device.
1010 no_session: bool,
1011 },
1012 /// The server setting a session up, section 3.3.
1013 FragSessionSetupReq {
1014 /// Which session, 0 to 3.
1015 frag_index: u8,
1016 /// Which multicast groups may feed it, a bit for each.
1017 mc_group_bit_mask: u8,
1018 /// How many uncoded fragments the block was cut into.
1019 nb_frag: u16,
1020 /// How many bytes each fragment carries.
1021 frag_size: u8,
1022 /// Whether the device reports the block once it has it.
1023 ack_reception: bool,
1024 /// Which fragmentation algorithm to run.
1025 frag_algo: u8,
1026 /// The coded spread of the delay before a device answers.
1027 block_ack_delay: u8,
1028 /// How many bytes of padding the last fragment carries.
1029 padding: u8,
1030 /// The four bytes the server describes the block with.
1031 descriptor: [u8; 4],
1032 /// The session counter, which must rise for each new block.
1033 session_cnt: u16,
1034 /// The code over the block the device checks once it has it all.
1035 mic: [u8; 4],
1036 },
1037 /// The device's answer, section 3.3.
1038 FragSessionSetupAns(SetupStatus),
1039 /// The server deleting a session, section 3.4.
1040 FragSessionDeleteReq {
1041 /// Which session.
1042 frag_index: u8,
1043 },
1044 /// The device's answer, section 3.4.
1045 FragSessionDeleteAns {
1046 /// Which session.
1047 frag_index: u8,
1048 /// Whether the device had no such session.
1049 no_session: bool,
1050 },
1051 /// The device reporting a block it has completely received, section 3.5.
1052 FragDataBlockReceivedReq {
1053 /// Which session.
1054 frag_index: u8,
1055 /// Whether the block's integrity code did not check out.
1056 mic_error: bool,
1057 },
1058 /// The server's acknowledgment, section 3.5.
1059 FragDataBlockReceivedAns {
1060 /// Which session.
1061 frag_index: u8,
1062 },
1063 /// One fragment of a block, section 3.6.
1064 DataFragment {
1065 /// Which session.
1066 frag_index: u8,
1067 /// Which fragment, counting from one.
1068 n: u16,
1069 /// Its bytes.
1070 data: &'a [u8],
1071 },
1072}
1073
1074impl<'a> FragCommand<'a> {
1075 /// The identifier this command travels under.
1076 ///
1077 /// # Returns
1078 ///
1079 /// The identifier, 0x00 to 0x04 or 0x08 for a data fragment.
1080 #[must_use]
1081 pub const fn cid(&self) -> u8 {
1082 match self {
1083 FragCommand::PackageVersionReq | FragCommand::PackageVersionAns(_) => {
1084 CID_PACKAGE_VERSION
1085 }
1086 FragCommand::FragSessionStatusReq { .. } | FragCommand::FragSessionStatusAns { .. } => {
1087 CID_SESSION_STATUS
1088 }
1089 FragCommand::FragSessionSetupReq { .. } | FragCommand::FragSessionSetupAns(_) => {
1090 CID_SESSION_SETUP
1091 }
1092 FragCommand::FragSessionDeleteReq { .. } | FragCommand::FragSessionDeleteAns { .. } => {
1093 CID_SESSION_DELETE
1094 }
1095 FragCommand::FragDataBlockReceivedReq { .. }
1096 | FragCommand::FragDataBlockReceivedAns { .. } => CID_BLOCK_RECEIVED,
1097 FragCommand::DataFragment { .. } => CID_DATA_FRAGMENT,
1098 }
1099 }
1100
1101 /// Which way this command travels.
1102 ///
1103 /// # Returns
1104 ///
1105 /// [`Direction::Uplink`] for what a device sends, [`Direction::Downlink`] for what a
1106 /// server sends.
1107 #[must_use]
1108 pub const fn direction(&self) -> Direction {
1109 match self {
1110 FragCommand::PackageVersionAns(_)
1111 | FragCommand::FragSessionStatusAns { .. }
1112 | FragCommand::FragSessionSetupAns(_)
1113 | FragCommand::FragSessionDeleteAns { .. }
1114 | FragCommand::FragDataBlockReceivedReq { .. } => Direction::Uplink,
1115 _ => Direction::Downlink,
1116 }
1117 }
1118
1119 /// Reads one command from the front of a message.
1120 ///
1121 /// A data fragment takes the whole message, as section 3 asks, so it reads to the end.
1122 ///
1123 /// # Arguments
1124 ///
1125 /// * `direction` - which way the frame carrying it traveled.
1126 /// * `bytes` - the message, from this command's identifier on.
1127 ///
1128 /// # Returns
1129 ///
1130 /// The command and how many bytes it took.
1131 ///
1132 /// # Errors
1133 ///
1134 /// [`LorawanError::MalformedFrame`] when the message ends inside the command, and
1135 /// [`LorawanError::UnknownCommand`] for an identifier this package does not define.
1136 pub fn parse(
1137 direction: Direction,
1138 bytes: &'a [u8],
1139 ) -> Result<(FragCommand<'a>, usize), LorawanError> {
1140 let cid = *bytes.first().ok_or(LorawanError::MalformedFrame)?;
1141 let up = matches!(direction, Direction::Uplink);
1142 match (cid, up) {
1143 (CID_PACKAGE_VERSION, false) => Ok((FragCommand::PackageVersionReq, 1)),
1144 (CID_PACKAGE_VERSION, true) => {
1145 let (fields, taken) = payload(bytes, 2)?;
1146 Ok((
1147 FragCommand::PackageVersionAns(PackageVersion {
1148 package: fields[0],
1149 version: fields[1],
1150 }),
1151 taken,
1152 ))
1153 }
1154 (CID_SESSION_STATUS, false) => {
1155 let (fields, taken) = payload(bytes, 1)?;
1156 Ok((
1157 FragCommand::FragSessionStatusReq {
1158 frag_index: (fields[0] >> 1) & 0x03,
1159 all_participants: fields[0] & 0x01 != 0,
1160 },
1161 taken,
1162 ))
1163 }
1164 (CID_SESSION_STATUS, true) => {
1165 let (fields, taken) = payload(bytes, 4)?;
1166 let received = u16::from_le_bytes([fields[1], fields[2]]);
1167 Ok((
1168 FragCommand::FragSessionStatusAns {
1169 frag_index: (received >> 14) as u8,
1170 received: received & 0x3FFF,
1171 missing: fields[3],
1172 mic_error: fields[0] & 0x02 != 0,
1173 memory_error: fields[0] & 0x01 != 0,
1174 no_session: fields[0] & 0x04 != 0,
1175 },
1176 taken,
1177 ))
1178 }
1179 (CID_SESSION_SETUP, false) => {
1180 let (fields, taken) = payload(bytes, 16)?;
1181 Ok((
1182 FragCommand::FragSessionSetupReq {
1183 frag_index: (fields[0] >> 4) & 0x03,
1184 mc_group_bit_mask: fields[0] & 0x0F,
1185 nb_frag: u16::from_le_bytes([fields[1], fields[2]]),
1186 frag_size: fields[3],
1187 ack_reception: fields[4] & 0x40 != 0,
1188 frag_algo: (fields[4] >> 3) & 0x07,
1189 block_ack_delay: fields[4] & 0x07,
1190 padding: fields[5],
1191 descriptor: [fields[6], fields[7], fields[8], fields[9]],
1192 session_cnt: u16::from_le_bytes([fields[10], fields[11]]),
1193 mic: [fields[12], fields[13], fields[14], fields[15]],
1194 },
1195 taken,
1196 ))
1197 }
1198 (CID_SESSION_SETUP, true) => {
1199 let (fields, taken) = payload(bytes, 1)?;
1200 Ok((
1201 FragCommand::FragSessionSetupAns(SetupStatus::from_bits(fields[0])),
1202 taken,
1203 ))
1204 }
1205 (CID_SESSION_DELETE, false) => {
1206 let (fields, taken) = payload(bytes, 1)?;
1207 Ok((
1208 FragCommand::FragSessionDeleteReq {
1209 frag_index: fields[0] & 0x03,
1210 },
1211 taken,
1212 ))
1213 }
1214 (CID_SESSION_DELETE, true) => {
1215 let (fields, taken) = payload(bytes, 1)?;
1216 Ok((
1217 FragCommand::FragSessionDeleteAns {
1218 frag_index: fields[0] & 0x03,
1219 no_session: fields[0] & 0x04 != 0,
1220 },
1221 taken,
1222 ))
1223 }
1224 (CID_BLOCK_RECEIVED, true) => {
1225 let (fields, taken) = payload(bytes, 1)?;
1226 Ok((
1227 FragCommand::FragDataBlockReceivedReq {
1228 frag_index: fields[0] & 0x03,
1229 mic_error: fields[0] & 0x04 != 0,
1230 },
1231 taken,
1232 ))
1233 }
1234 (CID_BLOCK_RECEIVED, false) => {
1235 let (fields, taken) = payload(bytes, 1)?;
1236 Ok((
1237 FragCommand::FragDataBlockReceivedAns {
1238 frag_index: fields[0] & 0x03,
1239 },
1240 taken,
1241 ))
1242 }
1243 (CID_DATA_FRAGMENT, false) => {
1244 let fields = bytes.get(1..3).ok_or(LorawanError::MalformedFrame)?;
1245 let index = u16::from_le_bytes([fields[0], fields[1]]);
1246 Ok((
1247 FragCommand::DataFragment {
1248 frag_index: (index >> 14) as u8,
1249 n: index & 0x3FFF,
1250 data: &bytes[3..],
1251 },
1252 bytes.len(),
1253 ))
1254 }
1255 _ => Err(LorawanError::UnknownCommand(cid)),
1256 }
1257 }
1258
1259 /// Writes this command out.
1260 ///
1261 /// A session setup writes its integrity code, which the caller takes over the block
1262 /// with [`BlockMicKey`].
1263 ///
1264 /// # Arguments
1265 ///
1266 /// * `out` - where to write it.
1267 ///
1268 /// # Returns
1269 ///
1270 /// How many bytes were written.
1271 ///
1272 /// # Errors
1273 ///
1274 /// [`LorawanError::PayloadTooLong`] when the buffer is too small.
1275 pub fn encode(&self, out: &mut [u8]) -> Result<usize, LorawanError> {
1276 match self {
1277 FragCommand::PackageVersionReq => write(out, CID_PACKAGE_VERSION, &[]),
1278 FragCommand::PackageVersionAns(version) => write(
1279 out,
1280 CID_PACKAGE_VERSION,
1281 &[version.package, version.version],
1282 ),
1283 FragCommand::FragSessionStatusReq {
1284 frag_index,
1285 all_participants,
1286 } => write(
1287 out,
1288 CID_SESSION_STATUS,
1289 &[((frag_index & 0x03) << 1) | u8::from(*all_participants)],
1290 ),
1291 FragCommand::FragSessionStatusAns {
1292 frag_index,
1293 received,
1294 missing,
1295 mic_error,
1296 memory_error,
1297 no_session,
1298 } => {
1299 let coded = (received & 0x3FFF) | (u16::from(*frag_index) << 14);
1300 let status = u8::from(*memory_error)
1301 | (u8::from(*mic_error) << 1)
1302 | (u8::from(*no_session) << 2);
1303 let bytes = coded.to_le_bytes();
1304 write(
1305 out,
1306 CID_SESSION_STATUS,
1307 &[status, bytes[0], bytes[1], *missing],
1308 )
1309 }
1310 FragCommand::FragSessionSetupReq {
1311 frag_index,
1312 mc_group_bit_mask,
1313 nb_frag,
1314 frag_size,
1315 ack_reception,
1316 frag_algo,
1317 block_ack_delay,
1318 padding,
1319 descriptor,
1320 session_cnt,
1321 mic,
1322 } => {
1323 let nb = nb_frag.to_le_bytes();
1324 let count = session_cnt.to_le_bytes();
1325 let control = (u8::from(*ack_reception) << 6)
1326 | ((frag_algo & 0x07) << 3)
1327 | (block_ack_delay & 0x07);
1328 write(
1329 out,
1330 CID_SESSION_SETUP,
1331 &[
1332 ((frag_index & 0x03) << 4) | (mc_group_bit_mask & 0x0F),
1333 nb[0],
1334 nb[1],
1335 *frag_size,
1336 control,
1337 *padding,
1338 descriptor[0],
1339 descriptor[1],
1340 descriptor[2],
1341 descriptor[3],
1342 count[0],
1343 count[1],
1344 mic[0],
1345 mic[1],
1346 mic[2],
1347 mic[3],
1348 ],
1349 )
1350 }
1351 FragCommand::FragSessionSetupAns(status) => {
1352 write(out, CID_SESSION_SETUP, &[status.bits()])
1353 }
1354 FragCommand::FragSessionDeleteReq { frag_index } => {
1355 write(out, CID_SESSION_DELETE, &[frag_index & 0x03])
1356 }
1357 FragCommand::FragSessionDeleteAns {
1358 frag_index,
1359 no_session,
1360 } => write(
1361 out,
1362 CID_SESSION_DELETE,
1363 &[(frag_index & 0x03) | (u8::from(*no_session) << 2)],
1364 ),
1365 FragCommand::FragDataBlockReceivedReq {
1366 frag_index,
1367 mic_error,
1368 } => write(
1369 out,
1370 CID_BLOCK_RECEIVED,
1371 &[(frag_index & 0x03) | (u8::from(*mic_error) << 2)],
1372 ),
1373 FragCommand::FragDataBlockReceivedAns { frag_index } => {
1374 write(out, CID_BLOCK_RECEIVED, &[frag_index & 0x03])
1375 }
1376 FragCommand::DataFragment {
1377 frag_index,
1378 n,
1379 data,
1380 } => {
1381 let coded = (n & 0x3FFF) | (u16::from(*frag_index) << 14);
1382 let bytes = coded.to_le_bytes();
1383 let len = data.len() + 3;
1384 let room = out.get_mut(..len).ok_or(LorawanError::PayloadTooLong)?;
1385 room[0] = CID_DATA_FRAGMENT;
1386 room[1] = bytes[0];
1387 room[2] = bytes[1];
1388 room[3..].copy_from_slice(data);
1389 Ok(len)
1390 }
1391 }
1392 }
1393}