Skip to main content

fast_floe/
error.rs

1use core::fmt;
2
3/// Result type used by this crate.
4pub type Result<T> = core::result::Result<T, Error>;
5
6/// A length bound that a plaintext or ciphertext input failed to meet.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum LengthRequirement {
9    /// Exactly this many bytes are required.
10    Exactly(usize),
11    /// At least this many bytes are required.
12    AtLeast(usize),
13    /// At most this many bytes are permitted.
14    AtMost(usize),
15    /// A length in this inclusive range is required.
16    Between { minimum: usize, maximum: usize },
17}
18
19impl fmt::Display for LengthRequirement {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Self::Exactly(length) => write!(f, "exactly {length}"),
23            Self::AtLeast(length) => write!(f, "at least {length}"),
24            Self::AtMost(length) => write!(f, "at most {length}"),
25            Self::Between { minimum, maximum } => {
26                write!(f, "between {minimum} and {maximum}")
27            }
28        }
29    }
30}
31
32/// Errors returned by FLOE operations.
33#[derive(Clone, Debug, Eq, PartialEq)]
34#[non_exhaustive]
35pub enum Error {
36    /// A key did not have the required 32-byte length.
37    InvalidKeyLength { actual: usize },
38    /// An operation combined values from different FLOE parameter sets.
39    InvalidParameters,
40    /// A segment length was outside
41    /// [`Parameters::VALID_SEGMENT_LENGTHS`](crate::Parameters::VALID_SEGMENT_LENGTHS).
42    InvalidSegmentLength { actual: u32 },
43    /// A plaintext segment did not meet its required length.
44    InvalidPlaintextLength {
45        actual: usize,
46        required: LengthRequirement,
47    },
48    /// A header did not have the length required by [`crate::Header`].
49    InvalidHeaderLength { actual: usize },
50    /// The encoded parameters in a header do not match the selected parameters.
51    InvalidHeaderParameters,
52    /// Header authentication failed.
53    InvalidHeaderTag,
54    /// A ciphertext segment did not meet its required length, or its encoded
55    /// final-length prefix contradicted its actual length.
56    InvalidCiphertextLength {
57        actual: usize,
58        required: LengthRequirement,
59    },
60    /// An internal segment did not begin with `0xffff_ffff`.
61    InvalidSegmentPrefix,
62    /// AES-GCM authentication failed.
63    AuthenticationFailed,
64    /// An online encryptor or decryptor was used after its final segment.
65    Closed,
66    /// A message or online operation ended without producing or authenticating
67    /// a final segment.
68    Truncated,
69    /// A segment position exceeded the AES-GCM FLOE limit.
70    SegmentLimit,
71    /// A caller-provided output buffer was too small.
72    OutputTooSmall { actual: usize, required: usize },
73    /// A segment buffer was used in the wrong preparation state.
74    InvalidBufferState,
75    /// An operation omitted its provider while multiple providers were compiled.
76    ProviderSelectionRequired,
77    /// A length calculation overflowed.
78    LengthOverflow,
79    /// The selected provider could not obtain cryptographically secure bytes.
80    RngFailure,
81    /// The selected cryptographic backend rejected an otherwise valid operation.
82    CryptoFailure,
83}
84
85impl fmt::Display for Error {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            Self::InvalidKeyLength { actual } => {
89                write!(f, "FLOE keys must be 32 bytes, got {actual}")
90            }
91            Self::InvalidParameters => f.write_str("FLOE parameter sets do not match"),
92            Self::InvalidSegmentLength { actual } => {
93                let supported = crate::Parameters::VALID_SEGMENT_LENGTHS;
94                write!(
95                    f,
96                    "invalid FLOE segment length {actual}; supported lengths are {} through {}",
97                    supported.start,
98                    supported.end - 1
99                )
100            }
101            Self::InvalidPlaintextLength { actual, required } => write!(
102                f,
103                "invalid plaintext segment length {actual}; required {required}"
104            ),
105            Self::InvalidHeaderLength { actual } => write!(
106                f,
107                "FLOE headers must be {} bytes, got {actual}",
108                crate::HEADER_LENGTH
109            ),
110            Self::InvalidHeaderParameters => {
111                f.write_str("header parameters do not match the selected FLOE parameters")
112            }
113            Self::InvalidHeaderTag => f.write_str("invalid FLOE header tag"),
114            Self::InvalidCiphertextLength { actual, required } => write!(
115                f,
116                "invalid ciphertext segment length {actual}; required {required}"
117            ),
118            Self::InvalidSegmentPrefix => {
119                f.write_str("non-final FLOE segment has an invalid prefix")
120            }
121            Self::AuthenticationFailed => f.write_str("FLOE segment authentication failed"),
122            Self::Closed => f.write_str("FLOE online state is already closed"),
123            Self::Truncated => f.write_str("FLOE input has no authenticated final segment"),
124            Self::SegmentLimit => f.write_str("FLOE segment limit exceeded"),
125            Self::OutputTooSmall { actual, required } => {
126                write!(
127                    f,
128                    "output buffer is {actual} bytes; {required} bytes are required"
129                )
130            }
131            Self::InvalidBufferState => {
132                f.write_str("segment buffer is not prepared for this operation")
133            }
134            Self::ProviderSelectionRequired => {
135                f.write_str("multiple FLOE providers are compiled (")?;
136                for (index, provider) in crate::Provider::COMPILED.iter().enumerate() {
137                    if index != 0 {
138                        f.write_str(", ")?;
139                    }
140                    f.write_str(provider.name())?;
141                }
142                f.write_str(
143                    ") and none was named; construct keys with \
144                     Key::from_bytes_with_provider or Key::generate_with_provider",
145                )
146            }
147            Self::LengthOverflow => f.write_str("FLOE length calculation overflowed"),
148            Self::RngFailure => f.write_str("random backend generation failed"),
149            Self::CryptoFailure => f.write_str("cryptographic backend operation failed"),
150        }
151    }
152}
153
154impl std::error::Error for Error {}
155
156impl Error {
157    /// Returns the [`Error`] carried as the source of a wrapped
158    /// [`std::io::Error`].
159    ///
160    /// The [`crate::io`] and [`crate::random_access`] adapters report FLOE
161    /// failures as `io::Error` values with the original [`Error`] attached as
162    /// the source. Use this helper to distinguish authentication, truncation,
163    /// and policy failures from ordinary I/O errors:
164    ///
165    /// ```
166    /// use fast_floe::Error;
167    ///
168    /// fn is_tampered(error: &std::io::Error) -> bool {
169    ///     matches!(Error::io_source(error), Some(Error::AuthenticationFailed))
170    /// }
171    /// ```
172    ///
173    /// Returns `None` for errors that did not originate in this crate, such
174    /// as failures of the wrapped reader or writer.
175    #[must_use]
176    pub fn io_source(error: &std::io::Error) -> Option<&Self> {
177        error
178            .get_ref()
179            .and_then(|source| source.downcast_ref::<Self>())
180    }
181}