lakers_shared/
lib.rs

1//! Common data structures used by [lakers] and its dependent crates
2//!
3//! This crate is separate from lakers to avoid circular dependencies that would otherwise arise
4//! from the pattern in which [lakers-ead] combined the main crate with variations of the
5//! protocol's EAD handling. As its types will then likely move over into the main lakers crate, it
6//! is recommended to use them through the public re-export there wherever possible.
7//!
8//! [lakers]: https://docs.rs/lakers/
9//! [lakers-ead]: https://docs.rs/lakers-ead/latest/lakers_ead/
10// NOTE: if there is no python-bindings feature, which will be the case for embedded builds,
11//       then the crate will be no_std
12#![cfg_attr(not(feature = "python-bindings"), no_std)]
13
14pub use cbor_decoder::*;
15pub use edhoc_parser::*;
16pub use helpers::*;
17
18use core::num::NonZeroI16;
19use defmt_or_log::trace;
20
21mod crypto;
22pub use crypto::*;
23
24mod cred;
25pub use cred::*;
26
27mod buffer;
28pub use buffer::*;
29
30#[cfg(feature = "python-bindings")]
31use pyo3::prelude::*;
32#[cfg(feature = "python-bindings")]
33mod python_bindings;
34
35// When changing this, beware that it is re-implemented in cbindgen.toml
36pub const MAX_MESSAGE_SIZE_LEN: usize = if cfg!(feature = "max_message_size_len_1024") {
37    1024
38} else if cfg!(feature = "max_message_size_len_512") {
39    512
40} else if cfg!(feature = "max_message_size_len_448") {
41    448
42} else if cfg!(feature = "max_message_size_len_384") {
43    384
44} else if cfg!(feature = "max_message_size_len_320") {
45    320
46} else if cfg!(feature = "max_message_size_len_256") {
47    256
48} else {
49    // need 128 to handle EAD fields, and 192 for the EAD_1 voucher
50    128 + 64
51};
52
53pub const ID_CRED_LEN: usize = 4;
54pub const SUITES_LEN: usize = 9;
55pub const SUPPORTED_SUITES_LEN: usize = 1;
56pub const EDHOC_METHOD: u8 = 3u8; // stat-stat is the only supported method
57pub const P256_ELEM_LEN: usize = 32;
58pub const SHA256_DIGEST_LEN: usize = 32;
59pub const AES_CCM_KEY_LEN: usize = 16;
60pub const AES_CCM_IV_LEN: usize = 13;
61pub const AES_CCM_TAG_LEN: usize = 8;
62pub const MAC_LENGTH: usize = 8; // used for EAD Zeroconf
63pub const MAC_LENGTH_2: usize = MAC_LENGTH;
64pub const MAC_LENGTH_3: usize = MAC_LENGTH_2;
65pub const ENCODED_VOUCHER_LEN: usize = 1 + MAC_LENGTH; // 1 byte for the length of the bstr-encoded voucher
66
67// maximum supported length of connection identifier for R
68//
69// When changing this, beware that it is re-implemented in cbindgen.toml
70pub const MAX_KDF_CONTEXT_LEN: usize = if cfg!(feature = "max_kdf_content_len_1024") {
71    1024
72} else if cfg!(feature = "max_kdf_content_len_512") {
73    512
74} else if cfg!(feature = "max_kdf_content_len_448") {
75    448
76} else if cfg!(feature = "max_kdf_content_len_384") {
77    384
78} else if cfg!(feature = "max_kdf_content_len_320") {
79    320
80} else {
81    256
82};
83pub const MAX_KDF_LABEL_LEN: usize = 15; // for "KEYSTREAM_2"
84
85// When changing this, beware that it is re-implemented in cbindgen.toml
86pub const MAX_BUFFER_LEN: usize = if cfg!(feature = "max_buffer_len_1024") {
87    1024
88} else if cfg!(feature = "max_buffer_len_512") {
89    512
90} else if cfg!(feature = "max_buffer_len_448") {
91    448
92} else if cfg!(feature = "max_buffer_len_384") {
93    384
94} else {
95    256 + 64
96};
97pub const CBOR_BYTE_STRING: u8 = 0x58u8;
98pub const CBOR_TEXT_STRING: u8 = 0x78u8;
99pub const CBOR_UINT_1BYTE: u8 = 0x18u8;
100pub const CBOR_NEG_INT_1BYTE_START: u8 = 0x20u8;
101pub const CBOR_NEG_INT_1BYTE_END: u8 = 0x37u8;
102pub const CBOR_UINT_1BYTE_START: u8 = 0x0u8;
103pub const CBOR_UINT_1BYTE_END: u8 = 0x17u8;
104pub const CBOR_MAJOR_TEXT_STRING: u8 = 0x60u8;
105pub const CBOR_MAJOR_BYTE_STRING: u8 = 0x40u8;
106pub const CBOR_MAJOR_BYTE_STRING_MAX: u8 = 0x57u8;
107pub const CBOR_MAJOR_ARRAY: u8 = 0x80u8;
108pub const CBOR_MAJOR_ARRAY_MAX: u8 = 0x97u8;
109pub const CBOR_MAJOR_MAP: u8 = 0xA0;
110pub const MAX_INFO_LEN: usize = 2 + SHA256_DIGEST_LEN + // 32-byte digest as bstr
111				            1 + MAX_KDF_LABEL_LEN +     // label <24 bytes as tstr
112						    1 + MAX_KDF_CONTEXT_LEN +   // context <24 bytes as bstr
113						    1; // length as u8
114
115pub const KCCS_LABEL: u8 = 14;
116#[deprecated(note = "Typo for KCCS_LABEL")]
117pub const KCSS_LABEL: u8 = KCCS_LABEL;
118pub const KID_LABEL: u8 = 4;
119
120pub const ENC_STRUCTURE_LEN: usize = 8 + 5 + SHA256_DIGEST_LEN; // 8 for ENCRYPT0
121
122pub const MAX_EAD_SIZE_LEN: usize = 64;
123
124/// Maximum length of a [`ConnId`] (`C_x`).
125///
126/// This length includes the leading CBOR encoding byte(s).
127// Note that when implementing larger sizes than 24, the encoding will need to use actual CBOR
128// rather than masking a known short length into a byte.
129//
130// When changing this, beware that it is re-implemented in cbindgen.toml
131const MAX_CONNID_ENCODED_LEN: usize = if cfg!(feature = "max_connid_encoded_len_24") {
132    24
133} else {
134    8
135};
136
137pub type BytesSuites = [u8; SUITES_LEN];
138pub type BytesSupportedSuites = [u8; SUPPORTED_SUITES_LEN];
139pub const EDHOC_SUITES: BytesSuites = [0, 1, 2, 3, 4, 5, 6, 24, 25]; // all but private cipher suites
140pub const EDHOC_SUPPORTED_SUITES: BytesSupportedSuites = [0x2u8];
141
142pub type BytesEad2 = [u8; 0];
143pub type BytesIdCred = [u8; ID_CRED_LEN];
144pub type Bytes8 = [u8; 8];
145pub type BytesCcmKeyLen = [u8; AES_CCM_KEY_LEN];
146pub type BytesCcmIvLen = [u8; AES_CCM_IV_LEN];
147pub type BufferPlaintext2 = EdhocMessageBuffer;
148pub type BufferPlaintext3 = EdhocMessageBuffer;
149pub type BufferPlaintext4 = EdhocMessageBuffer;
150pub type BytesMac2 = [u8; MAC_LENGTH_2];
151pub type BytesMac3 = [u8; MAC_LENGTH_3];
152pub type BufferMessage1 = EdhocMessageBuffer;
153pub type BufferMessage3 = EdhocMessageBuffer;
154pub type BufferMessage4 = EdhocMessageBuffer;
155pub type BufferCiphertext2 = EdhocMessageBuffer;
156pub type BufferCiphertext3 = EdhocMessageBuffer;
157pub type BufferCiphertext4 = EdhocMessageBuffer;
158pub type BytesHashLen = [u8; SHA256_DIGEST_LEN];
159pub type BytesP256ElemLen = [u8; P256_ELEM_LEN];
160pub type BufferMessage2 = EdhocMessageBuffer;
161pub type BytesMaxBuffer = [u8; MAX_BUFFER_LEN];
162pub type BytesMaxContextBuffer = [u8; MAX_KDF_CONTEXT_LEN];
163pub type BytesMaxInfoBuffer = [u8; MAX_INFO_LEN];
164pub type BytesMaxLabelBuffeer = [u8; MAX_KDF_LABEL_LEN];
165pub type BytesEncStructureLen = [u8; ENC_STRUCTURE_LEN];
166
167pub type BytesMac = [u8; MAC_LENGTH];
168pub type BytesEncodedVoucher = [u8; ENCODED_VOUCHER_LEN];
169pub type EADMessageBuffer = EdhocMessageBuffer; // TODO: make it of size MAX_EAD_SIZE_LEN
170
171/// Value of C_R or C_I, as chosen by ourself or the peer.
172///
173/// Semantically, this is a byte string of some length.
174///
175/// Its legal values are constrained to only contain a single CBOR item that is either a byte
176/// string or a number in -24..=23, all in preferred encoding.
177#[derive(Debug, PartialEq, Eq, Copy, Clone)]
178// TODO: This should not be needed, there is nothing special about the value 0.
179#[derive(Default)]
180pub struct ConnId([u8; MAX_CONNID_ENCODED_LEN]);
181
182/// Classifier for the content of [`ConnId`]; used internally in its implementation.
183enum ConnIdType {
184    /// The ID contains a single positive or negative number, expressed in its first byte.
185    SingleByte,
186    /// The ID contains a byte string, and the first byte of the ID indicates its length.
187    ///
188    /// It is expected that if longer connection IDs than 1+0+n are ever supported, this will be
189    /// renamed to ByteString10n, and longer variants get their own class.
190    ByteString(u8),
191}
192
193impl ConnIdType {
194    const _IMPL_CONSTRAINTS: () = assert!(
195        MAX_CONNID_ENCODED_LEN <= 1 + 23,
196        "Longer connection IDs require more elaborate decoding here"
197    );
198
199    /// Returns a classifier based on an initial byte.
200    ///
201    /// Its signature will need to change if ever connection IDs longer than 1+0+n are supported.
202    fn classify(byte: u8) -> Option<Self> {
203        if byte >> 5 <= 1 && byte & 0x1f < 24 {
204            return Some(ConnIdType::SingleByte);
205        } else if byte >> 5 == 2 && byte & 0x1f < 24 {
206            return Some(ConnIdType::ByteString(byte & 0x1f));
207        }
208        None
209    }
210
211    /// Returns the number of bytes in the [`ConnId`]'s buffer.
212    fn length(&self) -> usize {
213        match self {
214            ConnIdType::SingleByte => 1,
215            ConnIdType::ByteString(n) => (1 + n).into(),
216        }
217    }
218}
219
220impl ConnId {
221    /// Construct a ConnId from the result of [`cbor_decoder::int_raw`], which is a
222    /// byte that represents a single positive or negative CBOR integer encoded in the 5 bits minor
223    /// type.
224    ///
225    /// Evolving from u8-only values, this could later interact with the decoder directly.
226    #[deprecated(
227        note = "This API is only capable of generating a limited sub-set of the supported identifiers."
228    )]
229    pub const fn from_int_raw(raw: u8) -> Self {
230        debug_assert!(raw >> 5 <= 1, "Major type is not an integer");
231        debug_assert!(raw & 0x1f < 24, "Value is not immediate");
232        // We might allow '' (the empty bytes tring, byte 40) as well, but the again, this API is
233        // already deprecated.
234        let mut s = [0; MAX_CONNID_ENCODED_LEN];
235        s[0] = raw;
236        Self(s)
237    }
238
239    /// The connection ID classification of this connection ID
240    ///
241    /// Due to the invariants of this type, this classification infallible.
242    fn classify(&self) -> ConnIdType {
243        let Some(t) = ConnIdType::classify(self.0[0]) else {
244            unreachable!("Type invariant requires valid classification")
245        };
246        t
247    }
248
249    /// Read a connection identifier from a given decoder.
250    ///
251    /// It is an error for the decoder to read anything but a small integer or a byte string, to
252    /// exceed the maximum allowed ConnId length, or to contain a byte string that should have been
253    /// encoded as a small integer.
254    pub fn from_decoder(decoder: &mut CBORDecoder<'_>) -> Result<Self, CBORError> {
255        let mut s = [0; MAX_CONNID_ENCODED_LEN];
256        let len = ConnIdType::classify(decoder.current()?)
257            .ok_or(CBORError::DecodingError)?
258            .length();
259        s[..len].copy_from_slice(decoder.read_slice(len)?);
260        Ok(Self(s))
261    }
262
263    /// The bytes that form the identifier (an arbitrary byte string)
264    pub fn as_slice(&self) -> &[u8] {
265        match self.classify() {
266            ConnIdType::SingleByte => &self.0[..1],
267            ConnIdType::ByteString(n) => &self.0[1..1 + usize::from(n)],
268        }
269    }
270
271    /// The CBOR encoding of the identifier.
272    ///
273    /// For the 48 compact connection identifiers -24..=23, this is identical to the slice
274    /// representation:
275    ///
276    /// ```
277    /// # use lakers_shared::ConnId;
278    /// let c_i = ConnId::from_slice(&[0x04]).unwrap();
279    /// assert_eq!(c_i.as_cbor(), &[0x04]);
280    /// ```
281    ///
282    /// For other IDs, this contains an extra byte header:
283    ///
284    /// ```
285    /// # use lakers_shared::ConnId;
286    /// let c_i = ConnId::from_slice(&[0xff]).unwrap();
287    /// assert_eq!(c_i.as_cbor(), &[0x41, 0xff]);
288    /// ```
289    pub fn as_cbor(&self) -> &[u8] {
290        &self.0[..self.classify().length()]
291    }
292
293    /// Try to construct a [`ConnId`] from a slice that represents its string value.
294    ///
295    /// This is the inverse of [Self::as_slice], and returns None if the identifier is too long
296    /// (or, if only the compact 48 values are supported, outside of that range).
297    ///
298    /// ```
299    /// # use lakers_shared::ConnId;
300    /// let c_i = &[0x04];
301    /// let c_i = ConnId::from_slice(c_i).unwrap();
302    /// assert!(c_i.as_slice() == &[0x04]);
303    ///
304    /// let c_i = ConnId::from_slice(&[0x12, 0x34]).unwrap();
305    /// assert!(c_i.as_slice() == &[0x12, 0x34]);
306    /// ```
307    pub fn from_slice(input: &[u8]) -> Option<Self> {
308        if input.len() > MAX_CONNID_ENCODED_LEN - 1 {
309            None
310        } else {
311            let mut s = [0; MAX_CONNID_ENCODED_LEN];
312            if input.len() == 1
313                && matches!(ConnIdType::classify(input[0]), Some(ConnIdType::SingleByte))
314            {
315                s[0] = input[0];
316            } else {
317                s[0] = input.len() as u8 | 0x40;
318                s[1..1 + input.len()].copy_from_slice(input);
319            }
320            Some(Self(s))
321        }
322    }
323}
324
325#[derive(PartialEq, Debug)]
326pub enum EDHOCMethod {
327    StatStat = 3,
328    // add others, such as:
329    // PSK1 = ?,
330    // PSK2 = ?,
331}
332
333impl From<EDHOCMethod> for u8 {
334    fn from(method: EDHOCMethod) -> u8 {
335        method as u8
336    }
337}
338
339#[derive(PartialEq, Debug)]
340pub enum EDHOCSuite {
341    CipherSuite2 = 2,
342    // add others, such as:
343    // CiherSuite3 = 3,
344}
345
346impl From<EDHOCSuite> for u8 {
347    fn from(suite: EDHOCSuite) -> u8 {
348        suite as u8
349    }
350}
351
352#[derive(PartialEq, Debug)]
353#[non_exhaustive]
354pub enum EDHOCError {
355    /// In an exchange, a credential was set as "expected", but the credential configured by the
356    /// peer did not match what was presented. This is more an application internal than an EDHOC
357    /// error: When the application sets the expected credential, that process should be informed
358    /// by the known details.
359    UnexpectedCredential,
360    MissingIdentity,
361    IdentityAlreadySet,
362    MacVerificationFailed,
363    UnsupportedMethod,
364    UnsupportedCipherSuite,
365    ParsingError,
366    EncodingError,
367    CredentialTooLongError,
368    EadLabelTooLongError,
369    EadTooLongError,
370    /// An EAD was received that was either not known (and critical), or not understood, or
371    /// otherwise erroneous.
372    EADUnprocessable,
373    /// The credential or EADs could be processed (possibly by a third party), but the decision
374    /// based on that was to not to continue the EDHOC session.
375    ///
376    /// See also
377    /// <https://datatracker.ietf.org/doc/html/draft-ietf-lake-authz#name-edhoc-error-access-denied>
378    AccessDenied,
379}
380
381impl EDHOCError {
382    /// The ERR_CODE corresponding to the error
383    ///
384    /// Errors that refer to internal limitations (such as EadTooLongError) are treated the same
385    /// way as parsing errors, and return an unspecified error: Those are equivalent to limitations
386    /// of the parser, and a constrained system can not be expected to differentiate between "the
387    /// standard allows this but my number space is too small" and "this violates the standard".
388    ///
389    /// If an EDHOCError is returned through EDHOC, it will use this in its EDHOC error message.
390    ///
391    /// Note that this on its own is insufficient to create an error message: Additional ERR_INFO
392    /// is needed, which may or may not be available with the EDHOCError alone.
393    ///
394    /// TODO: Evolve the EDHOCError type such that all information needed is available.
395    pub fn err_code(&self) -> ErrCode {
396        use EDHOCError::*;
397        match self {
398            UnexpectedCredential => ErrCode::UNSPECIFIED,
399            MissingIdentity => ErrCode::UNSPECIFIED,
400            IdentityAlreadySet => ErrCode::UNSPECIFIED,
401            MacVerificationFailed => ErrCode::UNSPECIFIED,
402            UnsupportedMethod => ErrCode::UNSPECIFIED,
403            UnsupportedCipherSuite => ErrCode::WRONG_SELECTED_CIPHER_SUITE,
404            ParsingError => ErrCode::UNSPECIFIED,
405            EncodingError => ErrCode::UNSPECIFIED,
406            CredentialTooLongError => ErrCode::UNSPECIFIED,
407            EadLabelTooLongError => ErrCode::UNSPECIFIED,
408            EadTooLongError => ErrCode::UNSPECIFIED,
409            EADUnprocessable => ErrCode::UNSPECIFIED,
410            AccessDenied => ErrCode::ACCESS_DENIED,
411        }
412    }
413}
414
415/// Representation of an EDHOC ERR_CODE
416#[repr(C)]
417pub struct ErrCode(pub NonZeroI16);
418
419impl ErrCode {
420    // The way these are initialized will be simplified once const_option is stable
421
422    pub const UNSPECIFIED: Self = ErrCode(match NonZeroI16::new(1) {
423        Some(v) => v,
424        _ => unreachable!(),
425    });
426    pub const WRONG_SELECTED_CIPHER_SUITE: Self = ErrCode(match NonZeroI16::new(2) {
427        Some(v) => v,
428        _ => unreachable!(),
429    });
430    pub const UNKNOWN_CREDENTIAL: Self = ErrCode(match NonZeroI16::new(3) {
431        Some(v) => v,
432        _ => unreachable!(),
433    });
434    // Code requested in https://datatracker.ietf.org/doc/html/draft-ietf-lake-authz
435    pub const ACCESS_DENIED: Self = ErrCode(match NonZeroI16::new(3333) {
436        Some(v) => v,
437        _ => unreachable!(),
438    });
439}
440
441#[derive(Debug)]
442#[repr(C)]
443pub struct InitiatorStart {
444    pub suites_i: EdhocBuffer<MAX_SUITES_LEN>,
445    pub method: u8,
446    pub x: BytesP256ElemLen,   // ephemeral private key of myself
447    pub g_x: BytesP256ElemLen, // ephemeral public key of myself
448}
449
450#[derive(Debug)]
451pub struct ResponderStart {
452    pub method: u8,
453    pub y: BytesP256ElemLen,   // ephemeral private key of myself
454    pub g_y: BytesP256ElemLen, // ephemeral public key of myself
455}
456
457#[derive(Default, Debug)]
458pub struct ProcessingM1 {
459    pub y: BytesP256ElemLen,
460    pub g_y: BytesP256ElemLen,
461    pub c_i: ConnId,
462    pub g_x: BytesP256ElemLen, // ephemeral public key of the initiator
463    pub h_message_1: BytesHashLen,
464}
465
466#[derive(Default, Clone, Debug)]
467#[repr(C)]
468pub struct WaitM2 {
469    pub x: BytesP256ElemLen, // ephemeral private key of the initiator
470    pub h_message_1: BytesHashLen,
471}
472
473#[derive(Default, Debug)]
474pub struct WaitM3 {
475    pub y: BytesP256ElemLen, // ephemeral private key of the responder
476    pub prk_3e2m: BytesHashLen,
477    pub th_3: BytesHashLen,
478}
479
480#[derive(Debug, Default)]
481#[repr(C)]
482pub struct ProcessingM2 {
483    pub mac_2: BytesMac2,
484    pub prk_2e: BytesHashLen,
485    pub th_2: BytesHashLen,
486    pub x: BytesP256ElemLen,
487    pub g_y: BytesP256ElemLen,
488    pub plaintext_2: EdhocMessageBuffer,
489    pub c_r: ConnId,
490    pub id_cred_r: IdCred,
491    pub ead_2: Option<EADItem>,
492}
493
494#[derive(Default, Debug)]
495#[repr(C)]
496pub struct ProcessedM2 {
497    pub prk_3e2m: BytesHashLen,
498    pub prk_4e3m: BytesHashLen,
499    pub th_3: BytesHashLen,
500}
501
502#[derive(Default, Debug)]
503pub struct ProcessingM3 {
504    pub mac_3: BytesMac3,
505    pub y: BytesP256ElemLen, // ephemeral private key of the responder
506    pub prk_3e2m: BytesHashLen,
507    pub th_3: BytesHashLen,
508    pub id_cred_i: IdCred,
509    pub plaintext_3: EdhocMessageBuffer,
510    pub ead_3: Option<EADItem>,
511}
512
513#[derive(Default, Debug)]
514pub struct PreparingM3 {
515    pub prk_3e2m: BytesHashLen,
516    pub prk_4e3m: BytesHashLen,
517    pub th_3: BytesHashLen,
518    pub mac_3: BytesMac3,
519}
520
521#[derive(Default, Debug)]
522pub struct ProcessedM3 {
523    pub prk_4e3m: BytesHashLen,
524    pub th_4: BytesHashLen,
525    pub prk_out: BytesHashLen,
526    pub prk_exporter: BytesHashLen,
527}
528
529#[derive(Default, Debug)]
530#[repr(C)]
531pub struct WaitM4 {
532    pub prk_4e3m: BytesHashLen,
533    pub th_4: BytesHashLen,
534    pub prk_out: BytesHashLen,
535    pub prk_exporter: BytesHashLen,
536}
537
538#[derive(Default, Debug)]
539#[repr(C)]
540pub struct Completed {
541    pub prk_out: BytesHashLen,
542    pub prk_exporter: BytesHashLen,
543}
544
545#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
546#[derive(Copy, Clone, Debug, PartialEq)]
547#[repr(C)]
548pub enum CredentialTransfer {
549    ByReference,
550    ByValue,
551}
552
553#[derive(PartialEq, Debug)]
554#[repr(C)]
555pub enum MessageBufferError {
556    BufferAlreadyFull,
557    SliceTooLong,
558}
559
560/// An owned u8 vector of a limited length
561///
562/// It is used to represent the various messages in encrypted and in decrypted form, as well as
563/// other data items. Its maximum length is [MAX_MESSAGE_SIZE_LEN].
564#[repr(C)]
565#[derive(PartialEq, Debug, Copy, Clone)]
566pub struct EdhocMessageBuffer {
567    pub content: [u8; MAX_MESSAGE_SIZE_LEN],
568    pub len: usize,
569}
570
571impl Default for EdhocMessageBuffer {
572    fn default() -> Self {
573        EdhocMessageBuffer {
574            content: [0; MAX_MESSAGE_SIZE_LEN],
575            len: 0,
576        }
577    }
578}
579
580impl EdhocMessageBuffer {
581    pub fn new() -> Self {
582        EdhocMessageBuffer {
583            content: [0u8; MAX_MESSAGE_SIZE_LEN],
584            len: 0,
585        }
586    }
587
588    pub fn new_from_slice(slice: &[u8]) -> Result<Self, MessageBufferError> {
589        let mut buffer = Self::new();
590        if buffer.fill_with_slice(slice).is_ok() {
591            Ok(buffer)
592        } else {
593            Err(MessageBufferError::SliceTooLong)
594        }
595    }
596
597    pub fn get(self, index: usize) -> Option<u8> {
598        self.content.get(index).copied()
599    }
600
601    pub fn push(&mut self, item: u8) -> Result<(), MessageBufferError> {
602        if self.len < self.content.len() {
603            self.content[self.len] = item;
604            self.len += 1;
605            Ok(())
606        } else {
607            Err(MessageBufferError::BufferAlreadyFull)
608        }
609    }
610
611    pub fn get_slice(&self, start: usize, len: usize) -> Option<&[u8]> {
612        self.content.get(start..start + len)
613    }
614
615    pub fn as_slice(&self) -> &[u8] {
616        &self.content[0..self.len]
617    }
618
619    pub fn fill_with_slice(&mut self, slice: &[u8]) -> Result<(), MessageBufferError> {
620        if slice.len() <= self.content.len() {
621            self.len = slice.len();
622            self.content[..self.len].copy_from_slice(slice);
623            Ok(())
624        } else {
625            Err(MessageBufferError::SliceTooLong)
626        }
627    }
628
629    pub fn extend_from_slice(&mut self, slice: &[u8]) -> Result<(), MessageBufferError> {
630        if self.len + slice.len() <= self.content.len() {
631            self.content[self.len..self.len + slice.len()].copy_from_slice(slice);
632            self.len += slice.len();
633            Ok(())
634        } else {
635            Err(MessageBufferError::SliceTooLong)
636        }
637    }
638
639    pub fn from_hex(hex: &str) -> Self {
640        let mut buffer = EdhocMessageBuffer::new();
641        buffer.len = hex.len() / 2;
642        for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
643            let chunk_str = core::str::from_utf8(chunk).unwrap();
644            buffer.content[i] = u8::from_str_radix(chunk_str, 16).unwrap();
645        }
646        buffer
647    }
648}
649
650impl TryInto<EdhocMessageBuffer> for &[u8] {
651    type Error = ();
652
653    fn try_into(self) -> Result<EdhocMessageBuffer, Self::Error> {
654        let mut buffer = [0u8; MAX_MESSAGE_SIZE_LEN];
655        if self.len() <= buffer.len() {
656            buffer[..self.len()].copy_from_slice(self);
657
658            Ok(EdhocMessageBuffer {
659                content: buffer,
660                len: self.len(),
661            })
662        } else {
663            Err(())
664        }
665    }
666}
667
668#[cfg_attr(feature = "python-bindings", pyclass)]
669#[derive(Clone, Debug)]
670pub struct EADItem {
671    /// EAD label of the item
672    ///
673    /// # Caveats
674    ///
675    /// Currently, only values up to 23 are supported.
676    pub label: u16,
677    pub is_critical: bool,
678    // TODO[ead]: have adjustable (smaller) length for this buffer
679    pub value: Option<EdhocMessageBuffer>,
680}
681
682impl EADItem {
683    pub fn new() -> Self {
684        EADItem {
685            label: 0,
686            is_critical: false,
687            value: None,
688        }
689    }
690}
691
692mod helpers {
693    use super::*;
694
695    pub fn encode_info(
696        label: u8,
697        context: &BytesMaxContextBuffer,
698        context_len: usize,
699        length: usize,
700    ) -> (BytesMaxInfoBuffer, usize) {
701        let mut info: BytesMaxInfoBuffer = [0x00; MAX_INFO_LEN];
702
703        // construct info with inline cbor encoding
704        info[0] = label;
705        let mut info_len = if context_len < 24 {
706            info[1] = context_len as u8 | CBOR_MAJOR_BYTE_STRING;
707            info[2..2 + context_len].copy_from_slice(&context[..context_len]);
708            2 + context_len
709        } else {
710            info[1] = CBOR_BYTE_STRING;
711            info[2] = context_len as u8;
712            info[3..3 + context_len].copy_from_slice(&context[..context_len]);
713            3 + context_len
714        };
715
716        info_len = if length < 24 {
717            info[info_len] = length as u8;
718            info_len + 1
719        } else {
720            info[info_len] = CBOR_UINT_1BYTE;
721            info[info_len + 1] = length as u8;
722            info_len + 2
723        };
724
725        (info, info_len)
726    }
727}
728
729// TODO: move to own file (or even to the main crate, once EAD is extracted as an external dependency)
730mod edhoc_parser {
731    use super::*;
732
733    pub fn parse_ead(buffer: &[u8]) -> Result<Option<EADItem>, EDHOCError> {
734        trace!("Enter parse_ead");
735        // assuming label is a single byte integer (negative or positive)
736        if let Some((&label, tail)) = buffer.split_first() {
737            let label_res = if CBORDecoder::is_u8(label) {
738                // CBOR unsigned integer (0..=23)
739                Ok((label, false))
740            } else if CBORDecoder::is_i8(label) {
741                // CBOR negative integer (-1..=-24)
742                Ok((label - (CBOR_NEG_INT_1BYTE_START - 1), true))
743            } else {
744                Err(EDHOCError::ParsingError)
745            };
746
747            if let Ok((label, is_critical)) = label_res {
748                let ead_value = if tail.len() > 0 {
749                    // EAD value is present
750                    let mut buffer = EdhocMessageBuffer::new();
751                    buffer.fill_with_slice(tail).unwrap(); // TODO(hax): this *should* not panic due to the buffer sizes passed from upstream functions. can we prove it with hax?
752                    buffer.len = tail.len();
753                    Some(buffer)
754                } else {
755                    None
756                };
757                let ead_item = Some(EADItem {
758                    label: label.into(),
759                    is_critical,
760                    value: ead_value,
761                });
762                Ok(ead_item)
763            } else {
764                Err(EDHOCError::ParsingError)
765            }
766        } else {
767            Err(EDHOCError::ParsingError)
768        }
769    }
770
771    pub fn parse_suites_i(
772        mut decoder: CBORDecoder,
773    ) -> Result<(EdhocBuffer<MAX_SUITES_LEN>, CBORDecoder), EDHOCError> {
774        trace!("Enter parse_suites_i");
775        let mut suites_i: EdhocBuffer<MAX_SUITES_LEN> = Default::default();
776        if let Ok(curr) = decoder.current() {
777            if CBOR_UINT_1BYTE_START == CBORDecoder::type_of(curr) {
778                let Ok(_) = suites_i.push(decoder.u8()?) else {
779                    return Err(EDHOCError::ParsingError);
780                };
781                Ok((suites_i, decoder))
782            } else if CBOR_MAJOR_ARRAY == CBORDecoder::type_of(curr)
783                && CBORDecoder::info_of(curr) >= 2
784            {
785                // NOTE: arrays must be at least 2 items long, otherwise the compact encoding (int) must be used
786                let received_suites_i_len = decoder.array()?;
787                if received_suites_i_len <= suites_i.capacity() {
788                    for i in 0..received_suites_i_len {
789                        // NOTE: could use suites_i.push, but hax complains about mutable references in loops
790                        suites_i.content[i] = decoder.u8()?;
791                    }
792                    suites_i.len = received_suites_i_len;
793                    Ok((suites_i, decoder))
794                } else {
795                    Err(EDHOCError::ParsingError)
796                }
797            } else {
798                Err(EDHOCError::ParsingError)
799            }
800        } else {
801            Err(EDHOCError::ParsingError)
802        }
803    }
804
805    pub fn parse_message_1(
806        rcvd_message_1: &BufferMessage1,
807    ) -> Result<
808        (
809            u8,
810            EdhocBuffer<MAX_SUITES_LEN>,
811            BytesP256ElemLen,
812            ConnId,
813            Option<EADItem>,
814        ),
815        EDHOCError,
816    > {
817        trace!("Enter parse_message_1");
818        let mut decoder = CBORDecoder::new(rcvd_message_1.as_slice());
819        let method = decoder.u8()?;
820
821        if let Ok((suites_i, mut decoder)) = parse_suites_i(decoder) {
822            let mut g_x: BytesP256ElemLen = [0x00; P256_ELEM_LEN];
823            g_x.copy_from_slice(decoder.bytes_sized(P256_ELEM_LEN)?);
824
825            // consume c_i encoded as single-byte int (we still do not support bstr encoding)
826            let c_i = ConnId::from_decoder(&mut decoder)?;
827
828            // if there is still more to parse, the rest will be the EAD_1
829            if rcvd_message_1.len > decoder.position() {
830                // NOTE: since the current implementation only supports one EAD handler,
831                // we assume only one EAD item
832                let ead_res = parse_ead(decoder.remaining_buffer()?);
833                if let Ok(ead_1) = ead_res {
834                    Ok((method, suites_i, g_x, c_i, ead_1))
835                } else {
836                    Err(ead_res.unwrap_err())
837                }
838            } else if decoder.finished() {
839                Ok((method, suites_i, g_x, c_i, None))
840            } else {
841                Err(EDHOCError::ParsingError)
842            }
843        } else {
844            Err(EDHOCError::ParsingError)
845        }
846    }
847
848    pub fn parse_message_2(
849        rcvd_message_2: &BufferMessage2,
850    ) -> Result<(BytesP256ElemLen, BufferCiphertext2), EDHOCError> {
851        trace!("Enter parse_message_2");
852        // FIXME decode negative integers as well
853        let mut ciphertext_2: BufferCiphertext2 = BufferCiphertext2::new();
854
855        let mut decoder = CBORDecoder::new(rcvd_message_2.as_slice());
856
857        // message_2 consists of 1 bstr element; this element in turn contains the concatenation of g_y and ciphertext_2
858        let decoded = decoder.bytes()?;
859        if decoder.finished() {
860            if let Some(key) = decoded.get(0..P256_ELEM_LEN) {
861                let mut g_y: BytesP256ElemLen = [0x00; P256_ELEM_LEN];
862                g_y.copy_from_slice(key);
863                if let Some(c2) = decoded.get(P256_ELEM_LEN..) {
864                    if ciphertext_2.fill_with_slice(c2).is_ok() {
865                        Ok((g_y, ciphertext_2))
866                    } else {
867                        Err(EDHOCError::ParsingError)
868                    }
869                } else {
870                    Err(EDHOCError::ParsingError)
871                }
872            } else {
873                Err(EDHOCError::ParsingError)
874            }
875        } else {
876            Err(EDHOCError::ParsingError)
877        }
878    }
879
880    pub fn decode_plaintext_2(
881        plaintext_2: &BufferCiphertext2,
882    ) -> Result<(ConnId, IdCred, BytesMac2, Option<EADItem>), EDHOCError> {
883        trace!("Enter decode_plaintext_2");
884        let mut mac_2: BytesMac2 = [0x00; MAC_LENGTH_2];
885
886        let mut decoder = CBORDecoder::new(plaintext_2.as_slice());
887
888        let c_r = ConnId::from_decoder(&mut decoder)?;
889
890        // the id_cred may have been encoded as a single int, a byte string, or a map
891        let id_cred_r = IdCred::from_encoded_value(decoder.any_as_encoded()?)?;
892
893        mac_2[..].copy_from_slice(decoder.bytes_sized(MAC_LENGTH_2)?);
894
895        // if there is still more to parse, the rest will be the EAD_2
896        if plaintext_2.len > decoder.position() {
897            // assume only one EAD item
898            let ead_res = parse_ead(decoder.remaining_buffer()?);
899            if let Ok(ead_2) = ead_res {
900                Ok((c_r, id_cred_r, mac_2, ead_2))
901            } else {
902                Err(ead_res.unwrap_err())
903            }
904        } else if decoder.finished() {
905            Ok((c_r, id_cred_r, mac_2, None))
906        } else {
907            Err(EDHOCError::ParsingError)
908        }
909    }
910
911    pub fn decode_plaintext_3(
912        plaintext_3: &BufferPlaintext3,
913    ) -> Result<(IdCred, BytesMac3, Option<EADItem>), EDHOCError> {
914        trace!("Enter decode_plaintext_3");
915        let mut mac_3: BytesMac3 = [0x00; MAC_LENGTH_3];
916
917        let mut decoder = CBORDecoder::new(plaintext_3.as_slice());
918
919        // the id_cred may have been encoded as a single int, a byte string, or a map
920        let id_cred_i = IdCred::from_encoded_value(decoder.any_as_encoded()?)?;
921
922        mac_3[..].copy_from_slice(decoder.bytes_sized(MAC_LENGTH_3)?);
923
924        // if there is still more to parse, the rest will be the EAD_3
925        if plaintext_3.len > decoder.position() {
926            // assume only one EAD item
927            let ead_res = parse_ead(decoder.remaining_buffer()?);
928            if let Ok(ead_3) = ead_res {
929                Ok((id_cred_i, mac_3, ead_3))
930            } else {
931                Err(ead_res.unwrap_err())
932            }
933        } else if decoder.finished() {
934            Ok((id_cred_i, mac_3, None))
935        } else {
936            Err(EDHOCError::ParsingError)
937        }
938    }
939
940    pub fn decode_plaintext_4(
941        plaintext_4: &BufferPlaintext4,
942    ) -> Result<Option<EADItem>, EDHOCError> {
943        trace!("Enter decode_plaintext_4");
944        let decoder = CBORDecoder::new(plaintext_4.as_slice());
945
946        if plaintext_4.len > decoder.position() {
947            // assume only one EAD item
948            let ead_res = parse_ead(decoder.remaining_buffer()?);
949            if let Ok(ead_4) = ead_res {
950                Ok(ead_4)
951            } else {
952                Err(ead_res.unwrap_err())
953            }
954        } else if decoder.finished() {
955            Ok(None)
956        } else {
957            Err(EDHOCError::ParsingError)
958        }
959    }
960}
961
962mod cbor_decoder {
963    /// Decoder inspired by the [minicbor](https://crates.io/crates/minicbor) crate.
964    use super::*;
965
966    #[derive(Debug)]
967    pub enum CBORError {
968        DecodingError,
969    }
970
971    impl From<CBORError> for EDHOCError {
972        fn from(error: CBORError) -> Self {
973            match error {
974                CBORError::DecodingError => EDHOCError::ParsingError,
975            }
976        }
977    }
978
979    #[derive(Debug)]
980    pub struct CBORDecoder<'a> {
981        buf: &'a [u8],
982        pos: usize,
983    }
984
985    impl<'a> CBORDecoder<'a> {
986        pub fn new(bytes: &'a [u8]) -> Self {
987            CBORDecoder { buf: bytes, pos: 0 }
988        }
989
990        fn read(&mut self) -> Result<u8, CBORError> {
991            if let Some(b) = self.buf.get(self.pos) {
992                self.pos += 1;
993                Ok(*b)
994            } else {
995                Err(CBORError::DecodingError)
996            }
997        }
998
999        /// Consume and return *n* bytes starting at the current position.
1000        pub fn read_slice(&mut self, n: usize) -> Result<&'a [u8], CBORError> {
1001            if let Some(b) = self
1002                .pos
1003                .checked_add(n)
1004                .and_then(|end| self.buf.get(self.pos..end))
1005            {
1006                self.pos += n;
1007                Ok(b)
1008            } else {
1009                Err(CBORError::DecodingError)
1010            }
1011        }
1012
1013        pub fn position(&self) -> usize {
1014            self.pos
1015        }
1016
1017        pub fn finished(&self) -> bool {
1018            self.pos == self.buf.len()
1019        }
1020
1021        pub fn ensure_finished(&self) -> Result<(), CBORError> {
1022            if self.finished() {
1023                Ok(())
1024            } else {
1025                Err(CBORError::DecodingError)
1026            }
1027        }
1028
1029        pub fn remaining_buffer(&self) -> Result<&[u8], CBORError> {
1030            if let Some(buffer) = self.buf.get(self.pos..) {
1031                Ok(buffer)
1032            } else {
1033                Err(CBORError::DecodingError)
1034            }
1035        }
1036
1037        /// Get the byte at the current position.
1038        pub fn current(&self) -> Result<u8, CBORError> {
1039            if let Some(b) = self.buf.get(self.pos) {
1040                Ok(*b)
1041            } else {
1042                Err(CBORError::DecodingError)
1043            }
1044        }
1045
1046        /// Decode a `u8` value.
1047        pub fn u8(&mut self) -> Result<u8, CBORError> {
1048            let n = self.read()?;
1049            // NOTE: thid could be a `match` with `n @ 0x00..=0x17` clauses but hax doesn't support it
1050            if (0..=0x17).contains(&n) {
1051                Ok(n)
1052            } else if 0x18 == n {
1053                self.read()
1054            } else {
1055                Err(CBORError::DecodingError)
1056            }
1057        }
1058
1059        /// Decode an `i8` value.
1060        pub fn i8(&mut self) -> Result<i8, CBORError> {
1061            let n = self.read()?;
1062            if (0..=0x17).contains(&n) {
1063                Ok(n as i8)
1064            } else if (0x20..=0x37).contains(&n) {
1065                Ok(-1 - (n - 0x20) as i8)
1066            } else if 0x18 == n {
1067                Ok(self.read()? as i8)
1068            } else if 0x38 == n {
1069                Ok(-1 - (self.read()? - 0x20) as i8)
1070            } else {
1071                Err(CBORError::DecodingError)
1072            }
1073        }
1074
1075        /// Get the raw `i8` or `u8` value.
1076        pub fn int_raw(&mut self) -> Result<u8, CBORError> {
1077            let n = self.read()?;
1078            if (0..=0x17).contains(&n) || (0x20..=0x37).contains(&n) {
1079                Ok(n)
1080            } else {
1081                Err(CBORError::DecodingError)
1082            }
1083        }
1084
1085        /// Decode a string slice.
1086        pub fn str(&mut self) -> Result<&'a [u8], CBORError> {
1087            let b = self.read()?;
1088            if CBOR_MAJOR_TEXT_STRING != Self::type_of(b) || Self::info_of(b) == 31 {
1089                Err(CBORError::DecodingError)
1090            } else {
1091                let n = self.as_usize(Self::info_of(b))?;
1092                self.read_slice(n)
1093            }
1094        }
1095
1096        /// Decode a byte slice.
1097        pub fn bytes(&mut self) -> Result<&'a [u8], CBORError> {
1098            let b = self.read()?;
1099            if CBOR_MAJOR_BYTE_STRING != Self::type_of(b) || Self::info_of(b) == 31 {
1100                Err(CBORError::DecodingError)
1101            } else {
1102                let n = self.as_usize(Self::info_of(b))?;
1103                self.read_slice(n)
1104            }
1105        }
1106
1107        /// Decode a byte slice of an expected size.
1108        pub fn bytes_sized(&mut self, expected_size: usize) -> Result<&'a [u8], CBORError> {
1109            let res = self.bytes()?;
1110            if res.len() == expected_size {
1111                Ok(res)
1112            } else {
1113                Err(CBORError::DecodingError)
1114            }
1115        }
1116
1117        /// Begin decoding an array.
1118        pub fn array(&mut self) -> Result<usize, CBORError> {
1119            let b = self.read()?;
1120            if CBOR_MAJOR_ARRAY != Self::type_of(b) {
1121                Err(CBORError::DecodingError)
1122            } else {
1123                match Self::info_of(b) {
1124                    31 => Err(CBORError::DecodingError), // no support for unknown size arrays
1125                    n => Ok(self.as_usize(n)?),
1126                }
1127            }
1128        }
1129
1130        /// Begin decoding a map.
1131        pub fn map(&mut self) -> Result<usize, CBORError> {
1132            let b = self.read()?;
1133            if CBOR_MAJOR_MAP != Self::type_of(b) {
1134                Err(CBORError::DecodingError)
1135            } else {
1136                match Self::info_of(b) {
1137                    n if n < 24 => Ok(self.as_usize(n)?),
1138                    _ => Err(CBORError::DecodingError), // no support for long or indeterminate size
1139                }
1140            }
1141        }
1142
1143        /// Decode a `u8` value into usize.
1144        pub fn as_usize(&mut self, b: u8) -> Result<usize, CBORError> {
1145            if (0..=0x17).contains(&b) {
1146                Ok(usize::from(b))
1147            } else if 0x18 == b {
1148                self.read().map(usize::from)
1149            } else {
1150                Err(CBORError::DecodingError)
1151            }
1152        }
1153
1154        /// Get the major type info of the given byte (highest 3 bits).
1155        pub fn type_of(b: u8) -> u8 {
1156            b & 0b111_00000
1157        }
1158
1159        /// Get the additionl type info of the given byte (lowest 5 bits).
1160        pub fn info_of(b: u8) -> u8 {
1161            b & 0b000_11111
1162        }
1163
1164        /// Check for: an unsigned integer encoded as a single byte
1165        pub fn is_u8(byte: u8) -> bool {
1166            byte >= CBOR_UINT_1BYTE_START && byte <= CBOR_UINT_1BYTE_END
1167        }
1168
1169        /// Check for: a negative integer encoded as a single byte
1170        pub fn is_i8(byte: u8) -> bool {
1171            byte >= CBOR_NEG_INT_1BYTE_START && byte <= CBOR_NEG_INT_1BYTE_END
1172        }
1173
1174        /// Decode any (supported) CBOR item, but ignore its internal structure and just return the
1175        /// encoded data.
1176        ///
1177        /// To have bound memory requirements, this depends on the encoded data to be in
1178        /// deterministic encoding, thus not having any indeterminate length items.
1179        pub fn any_as_encoded(&mut self) -> Result<&'a [u8], CBORError> {
1180            let mut remaining_items = 1;
1181            let start = self.position();
1182
1183            // Instead of `while remaining_items > 0`, this loop helps hax to see that the loop
1184            // terminates. As every loop iteration advances the cursor by at least 1, the iteration
1185            // bound introduced by the for loop will never be reached, and the loop only terminates
1186            // through the remaining_items condition or a failure to read.
1187            //
1188            // I trust (but did not verify) that the Rust compiler can make something sensible out
1189            // of this (especially not keep looping needlessly) and doesn't do anything worse than
1190            // keep a limited loop counter.
1191            for _ in self.buf.iter() {
1192                if remaining_items > 0 {
1193                    remaining_items -= 1;
1194                    let head = self.read()?;
1195                    let major = head >> 5;
1196                    let minor = head & 0x1f;
1197                    let argument = match minor {
1198                        0..=23 => minor,
1199                        24 => self.read()?,
1200                        // We do not support values outside the range -256..256.
1201                        // FIXME: Sooner or later we should. There is probably an upper bound on
1202                        // lengths we need to support (we don't need to support 32bit integer decoding
1203                        // for map keys when our maximum buffers are 256 long); will split things up
1204                        // here into major-0/1/6/7 where we can just skip 1/2/4/8 bytes vs. the other
1205                        // majors where this is an out-of-bounds error anyway, or just have up to 64bit
1206                        // decoding available consistently for all?
1207                        25 | 26 | 27 => return Err(CBORError::DecodingError),
1208                        // Reserved, not well-formed
1209                        28 | 29 | 30 => return Err(CBORError::DecodingError),
1210                        // Indefinite length markers are forbidden in deterministic CBOR (or it's one
1211                        // of the major types where this is just not well-formed)
1212                        31 => return Err(CBORError::DecodingError),
1213                        _ => unreachable!("Value was masked to 5 bits"),
1214                    };
1215                    match major {
1216                        0..=1 => (), // Argument consumed, remaining items were already decremented
1217                        7 => (), // Same, but in separate line due to Hax FStar backend limitations
1218                        6 => {
1219                            remaining_items += 1;
1220                        }
1221                        2..=3 => {
1222                            self.read_slice(argument.into())?;
1223                        }
1224                        4 => {
1225                            remaining_items += argument;
1226                        }
1227                        5 => {
1228                            remaining_items += argument * 2;
1229                        }
1230                        _ => unreachable!("Value is result of a right shift trimming it to 3 bits"),
1231                    }
1232                }
1233            }
1234
1235            Ok(&self.buf[start..self.position()])
1236        }
1237    }
1238}
1239
1240#[cfg(test)]
1241mod test_cbor_decoder {
1242    use super::cbor_decoder::*;
1243    use hexlit::hex;
1244
1245    #[test]
1246    fn test_cbor_decoder() {
1247        // CBOR sequence: 1, -1, "hi", h'fefe'
1248        let input = [0x01, 0x20, 0x62, 0x68, 0x69, 0x42, 0xFE, 0xFE];
1249        let mut decoder = CBORDecoder::new(&input);
1250
1251        assert_eq!(1, decoder.u8().unwrap());
1252        assert_eq!(-1, decoder.i8().unwrap());
1253        assert_eq!([0x68, 0x69], decoder.str().unwrap()); // "hi"
1254        assert_eq!([0xFE, 0xFE], decoder.bytes().unwrap());
1255    }
1256
1257    #[test]
1258    fn test_cbor_decoder_any_as_decoded() {
1259        // {"bytes": 'val', "n": 123, "tagged": 255(["a", -1]), "deep": [[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]], {1: {2: {3: {4: [simple(0), true, null, simple(128)]}}}}]}
1260        // Note we can't have floats b/c we don't skip long arguments yet (and all floats have
1261        // minor 25 or longer).
1262        let input = hex!("A46562797465734376616C616E187B66746167676564D8FF82616120646465657082818181818181818181818181818181818181818180A101A102A103A10484E0F5F6F880");
1263        let mut decoder = CBORDecoder::new(&input);
1264
1265        assert_eq!(input, decoder.any_as_encoded().unwrap());
1266        assert!(decoder.finished())
1267    }
1268}