1use alloc::vec::Vec;
4use core::fmt;
5use core::ops::Range;
6
7pub const MESSAGE_MAGIC: u32 = 0x4e49_5043;
9pub const VERSION_MAJOR: u16 = 1;
11pub const VERSION_MINOR: u16 = 0;
13pub const ENVELOPE_LEN: usize = 72;
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub struct Limits {
19 pub max_message_bytes: u32,
21 pub max_payload_bytes: u32,
23 pub max_records: u32,
25 pub max_allocation_bytes: u32,
27}
28
29pub struct DecodeContext<'a> {
31 limits: &'a Limits,
32 records_remaining: u32,
33 allocation_remaining: u32,
34}
35
36impl<'a> DecodeContext<'a> {
37 fn new(limits: &'a Limits) -> Self {
38 Self {
39 limits,
40 records_remaining: limits.max_records,
41 allocation_remaining: limits.max_allocation_bytes,
42 }
43 }
44
45 pub const fn limits(&self) -> &Limits {
47 self.limits
48 }
49
50 pub fn claim_records(&mut self, count: u32) -> Result<(), DecodeError> {
52 self.records_remaining = self
53 .records_remaining
54 .checked_sub(count)
55 .ok_or(DecodeError::LimitExceeded(LimitKind::Records))?;
56 Ok(())
57 }
58
59 pub fn claim_allocation(&mut self, bytes: u32) -> Result<(), DecodeError> {
61 self.allocation_remaining = self
62 .allocation_remaining
63 .checked_sub(bytes)
64 .ok_or(DecodeError::LimitExceeded(LimitKind::AllocationBytes))?;
65 Ok(())
66 }
67
68 pub fn copy_bytes(&mut self, source: &[u8]) -> Result<Vec<u8>, DecodeError> {
70 let len = u32::try_from(source.len()).map_err(|_| DecodeError::LengthOverflow)?;
71 self.claim_allocation(len)?;
72 Ok(source.to_vec())
73 }
74}
75
76impl Limits {
77 pub const fn new(
79 max_message_bytes: u32,
80 max_payload_bytes: u32,
81 max_records: u32,
82 max_allocation_bytes: u32,
83 ) -> Self {
84 Self {
85 max_message_bytes,
86 max_payload_bytes,
87 max_records,
88 max_allocation_bytes,
89 }
90 }
91}
92
93#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub struct Envelope {
96 pub kind: u32,
98 pub flags: u32,
100 pub generation: u64,
102 pub sequence: u64,
104 payload_len: u32,
105}
106
107impl Envelope {
108 pub const fn new(kind: u32, flags: u32, generation: u64, sequence: u64) -> Self {
110 Self {
111 kind,
112 flags,
113 generation,
114 sequence,
115 payload_len: 0,
116 }
117 }
118
119 pub const fn payload_len(self) -> u32 {
121 self.payload_len
122 }
123
124 pub fn total_len(self) -> Result<usize, EncodeError> {
126 ENVELOPE_LEN
127 .checked_add(self.payload_len as usize)
128 .ok_or(EncodeError::LengthOverflow)
129 }
130
131 fn with_payload_len(mut self, payload_len: usize) -> Result<Self, EncodeError> {
132 self.payload_len = u32::try_from(payload_len).map_err(|_| EncodeError::LengthOverflow)?;
133 Ok(self)
134 }
135}
136
137#[derive(Clone, Debug, Eq, PartialEq)]
139pub struct DecodedMessage<T> {
140 pub envelope: Envelope,
142 pub message: T,
144}
145
146pub trait Protocol {
152 type Message;
154
155 const SCHEMA_ID: [u8; 32];
157
158 fn encode_payload(
160 message: &Self::Message,
161 destination: &mut [u8],
162 ) -> Result<usize, EncodeError>;
163
164 fn decode_payload(
166 source: &[u8],
167 context: &mut DecodeContext<'_>,
168 ) -> Result<Self::Message, DecodeError>;
169}
170
171pub fn encode_message<P: Protocol>(
173 envelope: Envelope,
174 message: &P::Message,
175 destination: &mut [u8],
176) -> Result<usize, EncodeError> {
177 if envelope.generation == 0 {
178 return Err(EncodeError::ZeroGeneration);
179 }
180 if envelope.sequence == 0 {
181 return Err(EncodeError::ZeroSequence);
182 }
183 if destination.len() < ENVELOPE_LEN {
184 return Err(EncodeError::DestinationTooSmall {
185 required: ENVELOPE_LEN,
186 actual: destination.len(),
187 });
188 }
189
190 let payload_len = P::encode_payload(message, &mut destination[ENVELOPE_LEN..])?;
191 let envelope = envelope.with_payload_len(payload_len)?;
192 let total_len = envelope.total_len()?;
193 if total_len > destination.len() {
194 return Err(EncodeError::DestinationTooSmall {
195 required: total_len,
196 actual: destination.len(),
197 });
198 }
199 encode_envelope::<P>(envelope, &mut destination[..ENVELOPE_LEN]);
200 Ok(total_len)
201}
202
203pub fn decode_message<P: Protocol>(
205 source: &[u8],
206 limits: &Limits,
207) -> Result<DecodedMessage<P::Message>, DecodeError> {
208 if source.len() < ENVELOPE_LEN {
209 return Err(DecodeError::Truncated {
210 required: ENVELOPE_LEN,
211 actual: source.len(),
212 });
213 }
214 if source.len() > limits.max_message_bytes as usize {
215 return Err(DecodeError::LimitExceeded(LimitKind::MessageBytes));
216 }
217 let envelope = decode_envelope::<P>(&source[..ENVELOPE_LEN])?;
218 let payload_len = envelope.payload_len as usize;
219 if payload_len > limits.max_payload_bytes as usize {
220 return Err(DecodeError::LimitExceeded(LimitKind::PayloadBytes));
221 }
222 let total_len = ENVELOPE_LEN
223 .checked_add(payload_len)
224 .ok_or(DecodeError::LengthOverflow)?;
225 if total_len != source.len() {
226 return Err(DecodeError::NonCanonicalLength {
227 declared: total_len,
228 actual: source.len(),
229 });
230 }
231 let mut context = DecodeContext::new(limits);
232 let message = P::decode_payload(&source[ENVELOPE_LEN..], &mut context)?;
233 Ok(DecodedMessage { envelope, message })
234}
235
236#[derive(Clone, Debug, Eq, PartialEq)]
238pub struct RelativeRange(Range<usize>);
239
240impl RelativeRange {
241 pub fn new(offset: u32, len: u32, containing_len: usize) -> Result<Self, DecodeError> {
243 let start = offset as usize;
244 let end = start
245 .checked_add(len as usize)
246 .ok_or(DecodeError::LengthOverflow)?;
247 if end > containing_len {
248 return Err(DecodeError::RelativeRangeOutOfBounds {
249 offset,
250 len,
251 containing_len,
252 });
253 }
254 Ok(Self(start..end))
255 }
256
257 pub fn range(&self) -> Range<usize> {
259 self.0.clone()
260 }
261}
262
263#[allow(missing_docs)]
265#[derive(Clone, Copy, Debug, Eq, PartialEq)]
266pub enum EncodeError {
267 DestinationTooSmall { required: usize, actual: usize },
269 LengthOverflow,
271 ZeroGeneration,
273 ZeroSequence,
275 Protocol(u16),
277}
278
279#[derive(Clone, Copy, Debug, Eq, PartialEq)]
281pub enum LimitKind {
282 MessageBytes,
284 PayloadBytes,
286 Records,
288 AllocationBytes,
290}
291
292#[allow(missing_docs)]
294#[derive(Clone, Copy, Debug, Eq, PartialEq)]
295pub enum DecodeError {
296 Truncated { required: usize, actual: usize },
298 BadMagic(u32),
300 BadVersion { major: u16, minor: u16 },
302 SchemaMismatch,
304 ZeroGeneration,
306 ZeroSequence,
308 NonCanonicalLength { declared: usize, actual: usize },
310 LengthOverflow,
312 RelativeRangeOutOfBounds {
314 offset: u32,
315 len: u32,
316 containing_len: usize,
317 },
318 LimitExceeded(LimitKind),
320 Protocol(u16),
322}
323
324impl fmt::Display for EncodeError {
325 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
326 write!(formatter, "message encoding failed: {self:?}")
327 }
328}
329
330impl fmt::Display for DecodeError {
331 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
332 write!(formatter, "message decoding failed: {self:?}")
333 }
334}
335
336#[cfg(feature = "std")]
337impl std::error::Error for EncodeError {}
338#[cfg(feature = "std")]
339impl std::error::Error for DecodeError {}
340
341fn encode_envelope<P: Protocol>(envelope: Envelope, bytes: &mut [u8]) {
342 put_u32(bytes, 0, MESSAGE_MAGIC);
343 put_u16(bytes, 4, VERSION_MAJOR);
344 put_u16(bytes, 6, VERSION_MINOR);
345 put_u32(bytes, 8, envelope.kind);
346 put_u32(bytes, 12, envelope.flags);
347 put_u32(bytes, 16, envelope.payload_len);
348 put_u32(bytes, 20, ENVELOPE_LEN as u32);
349 put_u64(bytes, 24, envelope.generation);
350 put_u64(bytes, 32, envelope.sequence);
351 bytes[40..72].copy_from_slice(&P::SCHEMA_ID);
352}
353
354fn decode_envelope<P: Protocol>(bytes: &[u8]) -> Result<Envelope, DecodeError> {
355 let magic = get_u32(bytes, 0);
356 if magic != MESSAGE_MAGIC {
357 return Err(DecodeError::BadMagic(magic));
358 }
359 let major = get_u16(bytes, 4);
360 let minor = get_u16(bytes, 6);
361 if major != VERSION_MAJOR || minor != VERSION_MINOR {
362 return Err(DecodeError::BadVersion { major, minor });
363 }
364 let header_len = get_u32(bytes, 20);
365 if header_len != ENVELOPE_LEN as u32 {
366 return Err(DecodeError::NonCanonicalLength {
367 declared: header_len as usize,
368 actual: ENVELOPE_LEN,
369 });
370 }
371 if bytes[40..72] != P::SCHEMA_ID {
372 return Err(DecodeError::SchemaMismatch);
373 }
374 let generation = get_u64(bytes, 24);
375 if generation == 0 {
376 return Err(DecodeError::ZeroGeneration);
377 }
378 let sequence = get_u64(bytes, 32);
379 if sequence == 0 {
380 return Err(DecodeError::ZeroSequence);
381 }
382 Ok(Envelope {
383 kind: get_u32(bytes, 8),
384 flags: get_u32(bytes, 12),
385 generation,
386 sequence,
387 payload_len: get_u32(bytes, 16),
388 })
389}
390
391fn put_u16(bytes: &mut [u8], offset: usize, value: u16) {
392 bytes[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
393}
394
395fn put_u32(bytes: &mut [u8], offset: usize, value: u32) {
396 bytes[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
397}
398
399fn put_u64(bytes: &mut [u8], offset: usize, value: u64) {
400 bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
401}
402
403fn get_u16(bytes: &[u8], offset: usize) -> u16 {
404 u16::from_le_bytes(
405 bytes[offset..offset + 2]
406 .try_into()
407 .expect("fixed checked range"),
408 )
409}
410
411fn get_u32(bytes: &[u8], offset: usize) -> u32 {
412 u32::from_le_bytes(
413 bytes[offset..offset + 4]
414 .try_into()
415 .expect("fixed checked range"),
416 )
417}
418
419fn get_u64(bytes: &[u8], offset: usize) -> u64 {
420 u64::from_le_bytes(
421 bytes[offset..offset + 8]
422 .try_into()
423 .expect("fixed checked range"),
424 )
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430
431 struct U32Protocol;
432
433 impl Protocol for U32Protocol {
434 type Message = u32;
435 const SCHEMA_ID: [u8; 32] = [0xa5; 32];
436
437 fn encode_payload(
438 message: &Self::Message,
439 destination: &mut [u8],
440 ) -> Result<usize, EncodeError> {
441 if destination.len() < 4 {
442 return Err(EncodeError::DestinationTooSmall {
443 required: 4,
444 actual: destination.len(),
445 });
446 }
447 destination[..4].copy_from_slice(&message.to_le_bytes());
448 Ok(4)
449 }
450
451 fn decode_payload(
452 source: &[u8],
453 _context: &mut DecodeContext<'_>,
454 ) -> Result<Self::Message, DecodeError> {
455 if source.len() != 4 {
456 return Err(DecodeError::Protocol(1));
457 }
458 Ok(u32::from_le_bytes(source.try_into().unwrap()))
459 }
460 }
461
462 const LIMITS: Limits = Limits::new(1024, 512, 8, 512);
463
464 #[test]
465 fn golden_envelope_is_manual_little_endian() {
466 let mut actual = [0; ENVELOPE_LEN + 4];
467 let len =
468 encode_message::<U32Protocol>(Envelope::new(7, 0x10, 2, 3), &0x1122_3344, &mut actual)
469 .unwrap();
470 assert_eq!(len, 76);
471 let mut expected = [0; ENVELOPE_LEN + 4];
472 expected[0..4].copy_from_slice(&MESSAGE_MAGIC.to_le_bytes());
473 expected[4..6].copy_from_slice(&VERSION_MAJOR.to_le_bytes());
474 expected[6..8].copy_from_slice(&VERSION_MINOR.to_le_bytes());
475 expected[8..12].copy_from_slice(&7_u32.to_le_bytes());
476 expected[12..16].copy_from_slice(&0x10_u32.to_le_bytes());
477 expected[16..20].copy_from_slice(&4_u32.to_le_bytes());
478 expected[20..24].copy_from_slice(&(ENVELOPE_LEN as u32).to_le_bytes());
479 expected[24..32].copy_from_slice(&2_u64.to_le_bytes());
480 expected[32..40].copy_from_slice(&3_u64.to_le_bytes());
481 expected[40..72].fill(0xa5);
482 expected[72..76].copy_from_slice(&0x1122_3344_u32.to_le_bytes());
483 assert_eq!(actual, expected);
484
485 let decoded = decode_message::<U32Protocol>(&actual, &LIMITS).unwrap();
486 assert_eq!(decoded.envelope.kind, 7);
487 assert_eq!(decoded.message, 0x1122_3344);
488 }
489
490 #[test]
491 fn hostile_common_fields_are_rejected_before_payload_decode() {
492 let mut bytes = [0; ENVELOPE_LEN + 4];
493 encode_message::<U32Protocol>(Envelope::new(1, 0, 5, 1), &7, &mut bytes).unwrap();
494
495 for len in 0..ENVELOPE_LEN {
496 assert!(matches!(
497 decode_message::<U32Protocol>(&bytes[..len], &LIMITS),
498 Err(DecodeError::Truncated { .. })
499 ));
500 }
501 let mut bad = bytes;
502 bad[40] ^= 1;
503 assert_eq!(
504 decode_message::<U32Protocol>(&bad, &LIMITS).unwrap_err(),
505 DecodeError::SchemaMismatch
506 );
507 let mut bad = bytes;
508 bad[16..20].copy_from_slice(&500_u32.to_le_bytes());
509 assert!(matches!(
510 decode_message::<U32Protocol>(&bad, &LIMITS),
511 Err(DecodeError::NonCanonicalLength { .. })
512 ));
513 let strict = Limits::new(75, 512, 8, 512);
514 assert_eq!(
515 decode_message::<U32Protocol>(&bytes, &strict).unwrap_err(),
516 DecodeError::LimitExceeded(LimitKind::MessageBytes)
517 );
518 }
519
520 #[test]
521 fn relative_ranges_and_allocation_limits_are_checked() {
522 assert_eq!(RelativeRange::new(2, 3, 5).unwrap().range(), 2..5);
523 assert!(matches!(
524 RelativeRange::new(u32::MAX, 2, 8),
525 Err(DecodeError::RelativeRangeOutOfBounds { .. })
526 ));
527 let limits = Limits::new(10, 10, 1, 8);
528 let mut context = DecodeContext::new(&limits);
529 assert_eq!(
530 context.copy_bytes(&[0; 9]).unwrap_err(),
531 DecodeError::LimitExceeded(LimitKind::AllocationBytes)
532 );
533 let aggregate_limits = Limits::new(10, 10, 1, 8);
534 let mut context = DecodeContext::new(&aggregate_limits);
535 assert_eq!(context.copy_bytes(&[0; 4]).unwrap(), &[0; 4]);
536 assert_eq!(context.copy_bytes(&[0; 4]).unwrap(), &[0; 4]);
537 assert_eq!(
538 context.copy_bytes(&[0; 1]).unwrap_err(),
539 DecodeError::LimitExceeded(LimitKind::AllocationBytes)
540 );
541 }
542}