Skip to main content

cbor_core/
strictness.rs

1//! Per-decode policy for accepting non-deterministic CBOR encodings.
2//!
3//! See [`Strictness`].
4
5/// Policy for accepting non-deterministic CBOR encodings during a decode.
6///
7/// CBOR::Core requires every value to be encoded in a single canonical
8/// form. The default decoder enforces that and rejects any deviation
9/// with [`Error::NonDeterministic`](crate::Error::NonDeterministic).
10/// Some producers (legacy encoders, bridges from other formats, hand
11/// written test vectors) emit valid CBOR that violates one or more of
12/// these rules. `Strictness` selects which violations the decoder
13/// tolerates so that such input can still be read.
14///
15/// Each tolerated violation is normalized while decoding: the resulting
16/// [`Value`](crate::Value) is the same value the canonical encoder
17/// would produce, and re-encoding it always yields a CBOR::Core
18/// compliant byte sequence. The original wire bytes are not preserved.
19///
20/// The default, [`Strictness::STRICT`], matches the CBOR::Core draft
21/// exactly. [`Strictness::LENIENT`] accepts every supported deviation.
22/// Set individual fields for a custom mix.
23///
24/// # Examples
25///
26/// ```
27/// use cbor_core::{DecodeOptions, Strictness, Value};
28///
29/// // 255 wrongly encoded with a two byte argument (canonical: 0x18 0xff).
30/// let bytes = [0x19, 0x00, 0xff];
31///
32/// // Default: rejected.
33/// assert!(DecodeOptions::new().decode(&bytes).is_err());
34///
35/// // Lenient: accepted and normalized.
36/// let v = DecodeOptions::new()
37///     .strictness(Strictness::LENIENT)
38///     .decode(&bytes)
39///     .unwrap();
40/// assert_eq!(v, Value::from(255));
41/// assert_eq!(v.encode(), vec![0x18, 0xff]);
42/// ```
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44pub struct Strictness {
45    /// Accept integers, lengths, and tag numbers encoded in a wider
46    /// argument than necessary (for example `0x19 0x00 0xff` for the
47    /// value 255 instead of `0x18 0xff`).
48    pub allow_non_shortest_integers: bool,
49
50    /// Accept floating point values encoded in a wider form than
51    /// necessary (for example an f32 that fits exactly in f16). The
52    /// decoded [`Float`](crate::Float) is re-stored in the shortest
53    /// form that preserves the value bit for bit, including NaN
54    /// payloads.
55    pub allow_non_shortest_floats: bool,
56
57    /// Accept big integer tags (tag 2 / tag 3) whose payload has
58    /// leading zero bytes or fits into a `u64`. Leading zeros are
59    /// stripped; a value that fits is downcast to
60    /// [`Value::Unsigned`](crate::Value::Unsigned) or
61    /// [`Value::Negative`](crate::Value::Negative).
62    pub allow_oversized_bigints: bool,
63
64    /// Accept maps whose keys are not in CBOR canonical (length and
65    /// then bytewise) order. Keys are sorted by [`Value`](crate::Value)
66    /// after decoding, which is equivalent to canonical order once each
67    /// value has been re-encoded in shortest form. With
68    /// [`allow_non_shortest_integers`](Self::allow_non_shortest_integers)
69    /// off, the two orders coincide; with it on, the by-value order is
70    /// the only well-defined choice because the original byte lengths
71    /// are normalized away.
72    pub allow_unsorted_map_keys: bool,
73
74    /// Accept maps that contain the same key more than once. The last
75    /// occurrence wins, matching
76    /// [`Map::from_pairs`](crate::Map::from_pairs).
77    pub allow_duplicate_map_keys: bool,
78
79    /// Accept indefinite-length encodings (RFC 8949 §3.2.2) for byte
80    /// strings, text strings, arrays, and maps. The chunks of an
81    /// indefinite-length string are concatenated; an indefinite-length
82    /// array or map is read until the break code. The resulting
83    /// [`Value`](crate::Value) is the same that a definite-length
84    /// encoding would produce, so re-encoding emits canonical bytes.
85    ///
86    /// In [`Format::Diagnostic`](crate::Format::Diagnostic) input, the
87    /// flag also enables the matching RFC 8949 §8 spellings:
88    /// `[_ ...]`, `{_ ...}`, and `(_ chunk, ...)`.
89    pub allow_indefinite_length: bool,
90}
91
92impl Strictness {
93    /// Reject every form of non-deterministic encoding. Default for
94    /// [`DecodeOptions`](crate::DecodeOptions).
95    pub const STRICT: Self = Self {
96        allow_non_shortest_integers: false,
97        allow_non_shortest_floats: false,
98        allow_oversized_bigints: false,
99        allow_unsorted_map_keys: false,
100        allow_duplicate_map_keys: false,
101        allow_indefinite_length: false,
102    };
103
104    /// Accept every non-deterministic encoding the decoder knows how to
105    /// normalize. The resulting [`Value`](crate::Value) is canonical;
106    /// re-encoding it produces CBOR::Core compliant bytes.
107    pub const LENIENT: Self = Self {
108        allow_non_shortest_integers: true,
109        allow_non_shortest_floats: true,
110        allow_oversized_bigints: true,
111        allow_unsorted_map_keys: true,
112        allow_duplicate_map_keys: true,
113        allow_indefinite_length: true,
114    };
115}
116
117impl Default for Strictness {
118    fn default() -> Self {
119        Self::STRICT
120    }
121}