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}