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