Skip to main content

cesr/core/matter/
error.rs

1use super::MatterPart;
2use crate::b64::error::Error as CesrUtilError;
3#[cfg(feature = "alloc")]
4#[allow(
5    unused_imports,
6    reason = "alloc prelude items; subset used per cfg/feature combination"
7)]
8use alloc::string::String;
9use base64::DecodeError;
10use core::str::Utf8Error;
11use thiserror::Error as ThisError;
12
13/// Errors produced while parsing a CESR Matter stream.
14#[derive(Debug, ThisError, PartialEq, Eq)]
15pub enum ParsingError {
16    /// The input stream is empty.
17    #[error("Input stream is empty.")]
18    EmptyStream,
19
20    /// The stream ended before enough bytes were available to parse the given part.
21    #[error("Input stream is too short; more bytes were expected to complete parsing of `{0}`.")]
22    StreamTooShort(MatterPart),
23
24    /// The code prefix does not match any known Matter code.
25    #[error("Unrecognized code: '{0}' does not correspond to a known Matter code.")]
26    UnknownMatterCode(String),
27
28    /// A structural component of the code was malformed.
29    #[error("Malformed code: the {part} component was invalid. Found '{found}'.")]
30    MalformedCode {
31        /// Which structural part was malformed.
32        part: MatterPart,
33        /// The invalid content that was found.
34        found: String,
35    },
36
37    /// The variable-size lead character is not a valid CESR lead byte.
38    #[error("The character '{0}' is not a valid lead character for a variable-sized primitive.")]
39    InvalidVariableSizeLead(char),
40
41    /// Variable-length logic was applied to a fixed-size code.
42    #[error("Attempted to apply variable-length logic to the fixed-size code '{0}'.")]
43    MismatchedSizingLogic(String),
44
45    /// A low-level Base64 conversion error occurred.
46    #[error("A low-level conversion error occurred")]
47    Conversion(#[from] CesrUtilError),
48
49    /// An invalid UTF-8 sequence was encountered during parsing.
50    #[error("Invalid UTF-8 sequence encountered during parsing.")]
51    InvalidUtf8(#[from] Utf8Error),
52
53    /// Base64 decoding failed.
54    #[error("Base64 decoding failed.")]
55    Base64(DecodeError),
56}
57
58impl From<DecodeError> for ParsingError {
59    fn from(e: DecodeError) -> Self {
60        Self::Base64(e)
61    }
62}
63
64/// Errors produced while validating Matter builder inputs.
65#[derive(Debug, ThisError, PartialEq, Eq)]
66pub enum ValidationError {
67    /// The code string does not correspond to a known Matter code.
68    #[error("Unrecognized code: '{0}' does not correspond to a known Matter code.")]
69    UnknownMatterCode(String),
70
71    /// A structural component of the code was malformed.
72    #[error("Malformed code: the {part} component was invalid. Found '{found}'.")]
73    MalformedCode {
74        /// Which structural part was malformed.
75        part: MatterPart,
76        /// The invalid content that was found.
77        found: String,
78    },
79
80    /// The code requires a soft field but none was provided.
81    #[error("The code '{code}' requires a 'soft' component, but it was not provided.")]
82    MissingSoft {
83        /// The CESR code that requires a soft field.
84        code: String,
85    },
86
87    /// The soft field has the wrong length for the given code.
88    #[error(
89        "The 'soft' component has an incorrect length for code '{code}': expected {expected}, but found {found}."
90    )]
91    IncorrectSoftLength {
92        /// The CESR code.
93        code: String,
94        /// Expected soft field length.
95        expected: usize,
96        /// Actual soft field length found.
97        found: usize,
98    },
99
100    /// The soft field contains non-Base64 characters.
101    #[error("The 'soft' component for code '{code}' contains invalid Base64 characters.")]
102    InvalidSoftFormat {
103        /// The CESR code.
104        code: String,
105    },
106
107    /// The code requires raw data but none was provided.
108    #[error("The code '{code}' requires a raw data payload, but it was not provided.")]
109    MissingRaw {
110        /// The CESR code.
111        code: String,
112    },
113
114    /// Raw data was provided for a code that has no raw payload.
115    #[error("The code '{code}' must not have a raw data payload.")]
116    UnexpectedRaw {
117        /// The CESR code.
118        code: String,
119    },
120
121    /// The raw data length does not match the code's expected size.
122    #[error("Incorrect raw data size for code '{code}': expected {expected}, but found {found}.")]
123    IncorrectRawSize {
124        /// The CESR code.
125        code: String,
126        /// Expected number of raw bytes.
127        expected: usize,
128        /// Actual number of raw bytes found.
129        found: usize,
130    },
131
132    /// A fixed-size code was used where a variable-size promotion is required.
133    #[error(
134        "The code '{0}' is a fixed-size code and cannot be promoted to a variable-size equivalent."
135    )]
136    IncompatiblePromotion(String),
137
138    /// The requested promotion for the given code and lead size is not a valid CESR transformation.
139    #[error(
140        "The requested promotion for code '{code}' with a lead size of {lead} is not a valid CESR transformation."
141    )]
142    InvalidPromotionTarget {
143        /// The CESR code being promoted.
144        code: String,
145        /// The lead size that was requested.
146        lead: usize,
147    },
148
149    /// The result of a code promotion was invalid or unknown.
150    #[error("Promotion from '{from}' to '{to}' resulted in an invalid or unknown code.")]
151    InvalidPromotionResult {
152        /// The source code.
153        from: String,
154        /// The target code.
155        to: String,
156    },
157
158    /// Cannot determine a fixed raw size for a variable-size code.
159    #[error("Cannot get a fixed raw size for the variable-sized code '{0}'.")]
160    InvalidSizingOperation(String),
161
162    /// Non-canonical encoding: padding bits in the given part were non-zero.
163    #[error("Non-canonical encoding: non-zero bits were found in the '{0}' padding section.")]
164    NonCanonicalEncoding(MatterPart),
165
166    /// The parsed components do not add up to the expected total length.
167    #[error(
168        "Structural integrity error: the parsed components do not match the expected total length of the primitive."
169    )]
170    StructuralIntegrityError,
171
172    /// Computing the primitive's full size from the decoded soft field overflowed
173    /// `usize`. The soft field is attacker-controlled; a declared size this large
174    /// cannot describe a real frame, so it is rejected rather than wrapped.
175    #[error(
176        "Declared variable size is too large: computing the primitive's full size overflowed the address space."
177    )]
178    SizeOverflow,
179}
180
181/// Error returned by [`MatterBuilder`](super::builder::MatterBuilder) parse and
182/// build operations.
183///
184/// The input was either structurally unparseable ([`ParsingError`]) or it
185/// parsed but violated a CESR validation rule ([`ValidationError`]).
186#[derive(Debug, ThisError, PartialEq, Eq)]
187pub enum MatterBuildError {
188    /// The input could not be parsed into a CESR primitive.
189    #[error(transparent)]
190    Parsing(#[from] ParsingError),
191
192    /// The input parsed but failed a validation constraint.
193    #[error(transparent)]
194    Validation(#[from] ValidationError),
195}
196
197#[cfg(test)]
198mod build_error_tests {
199    use super::{MatterBuildError, ParsingError, ValidationError};
200    use alloc::string::ToString;
201
202    #[test]
203    fn from_parsing_error_lands_in_parsing_variant() {
204        let e: MatterBuildError = ParsingError::EmptyStream.into();
205        assert_eq!(e, MatterBuildError::Parsing(ParsingError::EmptyStream));
206    }
207
208    #[test]
209    fn from_validation_error_lands_in_validation_variant() {
210        let e: MatterBuildError = ValidationError::StructuralIntegrityError.into();
211        assert_eq!(
212            e,
213            MatterBuildError::Validation(ValidationError::StructuralIntegrityError)
214        );
215    }
216
217    #[test]
218    fn display_is_transparent_to_source() {
219        let e: MatterBuildError = ParsingError::EmptyStream.into();
220        assert_eq!(e.to_string(), ParsingError::EmptyStream.to_string());
221    }
222
223    #[test]
224    fn validation_display_is_transparent_to_source() {
225        let e: MatterBuildError = ValidationError::StructuralIntegrityError.into();
226        assert_eq!(
227            e.to_string(),
228            ValidationError::StructuralIntegrityError.to_string()
229        );
230    }
231}