coapcore/helpers.rs
1//! Types sharedly used by different modules of this crate
2
3/// An own identifier for a security context.
4///
5/// This is used with EDHOC as C_I when in an initiator role, C_R when in a responder role, and
6/// recipient ID in OSCORE.
7///
8/// This type represents any of the 48 efficient identifiers that use CBOR one-byte integer
9/// encodings (see RFC9528 Section 3.3.2), or equivalently the 1-byte long OSCORE identifiers
10///
11/// Lakers supports a much larger value space for C_x, and coapcore processes larger values
12/// selected by the peer -- but on its own, will select only those that fit in this type.
13// FIXME Could even limit to positive values if MAX_CONTEXTS < 24
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15#[derive(Copy, Clone, PartialEq, Debug)]
16pub(crate) struct COwn(u8);
17
18impl COwn {
19 /// Maximum length of [`Self::as_slice`].
20 ///
21 /// This is exposed to allow sizing stack allocated buffers.
22 pub(crate) const MAX_SLICE_LEN: usize = 1;
23
24 /// Number of values that [`.not_in_iter()`][Self::not_in_iter] can generate.
25 ///
26 /// This is exposed to allow for static checks that a security context pool does not exceed
27 /// this number.
28 pub(crate) const GENERATABLE_VALUES: usize = 48;
29
30 /// Find a value of self that is not found in the iterator.
31 ///
32 /// This asserts that the iterator is (known to be) short enough that this will always succeed.
33 pub(crate) fn not_in_iter(iterator: impl Iterator<Item = Self>) -> Self {
34 // In theory, this would allow the compiler to see that the unreachable below is indeed
35 // unreachable
36 assert!(
37 iterator
38 .size_hint()
39 .1
40 .is_some_and(|v| v < Self::GENERATABLE_VALUES),
41 "Too many slots to reliably assign connection identifier"
42 );
43 let mut seen_pos = 0u32;
44 let mut seen_neg = 0u32;
45 for i in iterator {
46 let major = i.0 >> 5;
47 // Let's not make unsafe assumptions on the own value range
48 let target = if major == 0 {
49 &mut seen_pos
50 } else {
51 &mut seen_neg
52 };
53 // Convenienlty, masking to the minor part puts us in the very range that allows u32
54 // shifting
55 *target |= 1 << (i.0 & 0x1f);
56 }
57 // trailing_ones = n implies that bit 1<<n is a zero and thus COwn(n) is free
58 let pos_to = seen_pos.trailing_ones();
59 if pos_to < 24 {
60 return Self(pos_to as u8);
61 }
62 let neg_to = seen_neg.trailing_ones();
63 if neg_to < 24 {
64 return Self(0x20 | neg_to as u8);
65 }
66 unreachable!("Iterator is not long enough to set this many bits.");
67 }
68
69 /// Given an OSCORE Key ID (kid), find the corresponding context identifier value
70 pub(crate) fn from_kid(kid: &[u8]) -> Option<Self> {
71 match kid {
72 [first] if *first <= 0x17 || (*first >= 0x20 && *first <= 0x37) => Some(Self(*first)),
73 _ => None,
74 }
75 }
76
77 /// Reports the value of self as an OSCORE Key ID.
78 ///
79 /// This is currently the only slice form this type supports: There is no function to view this
80 /// as an encoded EDHOC compressed byte string (which is identical to the OSCORE key ID for
81 /// some, and has an extra byte string length prefix for others). Such a method is not needed
82 /// because the type currently only generates, and because the [`lakers::ConnId`] (being an
83 /// owned type) copies data into itself anyway, from where it can produce all forms; that type
84 /// also has a [`lakers::ConnId::as_cbor`] accessor.
85 pub(crate) fn as_slice(&self) -> &[u8] {
86 core::slice::from_ref(&self.0)
87 }
88}
89
90impl From<COwn> for lakers::ConnId {
91 fn from(cown: COwn) -> Self {
92 lakers::ConnId::from_slice(cown.as_slice())
93 .expect("ConnId is always big enough for at least COwn")
94 }
95}