1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Error types used in libavb.
16 //!
17 //! There are a few advantages of providing these custom types rather than exposing the raw bindgen
18 //! enums directly:
19 //! * More idiomatic error handling
20 //! * C code defines a "status" enum that can contain either OK or an error, whereas Rust prefers
21 //! error-only enums to use with `Result<>` e.g. `Result<(), IoError>`. An "OK" status doesn't
22 //! make sense when used with `Result<>`.
23 //! * Better naming e.g. `IoError::Oom` vs the redundant `AvbIoResult::AVB_IO_RESULT_ERROR_OOM`
24 //! * We can implement traits such as `Display` for added convenience.
25
26 // The naming scheme can be a bit confusing due to the re-use of "result" in a few places:
27 // * `Avb*Result`: raw libavb enums generated by bindgen, containing errors and "OK". Internal-only;
28 // library users should never have to use these types.
29 // * `*Error`: `Avb*Result` wrappers which only contain error conditions, not "OK". Should be
30 // wrapped in a Rust `Result<>` in public API.
31 // * `Result<T, *Error>`: top-level `Result<>` type used in this library's public API.
32
33 use crate::SlotVerifyData;
34 use avb_bindgen::{AvbIOResult, AvbSlotVerifyResult, AvbVBMetaVerifyResult};
35 use core::{fmt, str::Utf8Error};
36
37 /// `AvbSlotVerifyResult` error wrapper.
38 ///
39 /// Some of the errors can contain the resulting `SlotVerifyData` if the `AllowVerificationError`
40 /// flag was passed into `slot_verify()`.
41 #[derive(Debug, PartialEq, Eq)]
42 pub enum SlotVerifyError<'a> {
43 /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT`
44 InvalidArgument,
45 /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA`
46 InvalidMetadata,
47 /// `AVB_SLOT_VERIFY_RESULT_ERROR_IO`
48 Io,
49 /// `AVB_SLOT_VERIFY_RESULT_ERROR_OOM`
50 Oom,
51 /// `AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED`
52 PublicKeyRejected(Option<SlotVerifyData<'a>>),
53 /// `AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX`
54 RollbackIndex(Option<SlotVerifyData<'a>>),
55 /// `AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION`
56 UnsupportedVersion,
57 /// `AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION`
58 Verification(Option<SlotVerifyData<'a>>),
59 /// Unexpected internal error. This does not have a corresponding libavb error code.
60 Internal,
61 }
62
63 /// `Result` type for `SlotVerifyError` errors.
64 pub type SlotVerifyResult<'a, T> = Result<T, SlotVerifyError<'a>>;
65
66 /// `Result` type for `SlotVerifyError` errors without any `SlotVerifyData`.
67 ///
68 /// If the contained error will never hold a `SlotVerifyData`, this is easier to work with compared
69 /// to `SlotVerifyResult` due to the static lifetime bound.
70 pub type SlotVerifyNoDataResult<T> = SlotVerifyResult<'static, T>;
71
72 impl<'a> SlotVerifyError<'a> {
73 /// Returns a copy of this error without any contained `SlotVerifyData`.
74 ///
75 /// This can simplify usage if the user doesn't care about the `SlotVerifyData` by turning the
76 /// current lifetime bound into `'static`.
without_verify_data(&self) -> SlotVerifyError<'static>77 pub fn without_verify_data(&self) -> SlotVerifyError<'static> {
78 match self {
79 Self::InvalidArgument => SlotVerifyError::InvalidArgument,
80 Self::InvalidMetadata => SlotVerifyError::InvalidMetadata,
81 Self::Io => SlotVerifyError::Io,
82 Self::Oom => SlotVerifyError::Oom,
83 Self::PublicKeyRejected(_) => SlotVerifyError::PublicKeyRejected(None),
84 Self::RollbackIndex(_) => SlotVerifyError::RollbackIndex(None),
85 Self::UnsupportedVersion => SlotVerifyError::UnsupportedVersion,
86 Self::Verification(_) => SlotVerifyError::Verification(None),
87 Self::Internal => SlotVerifyError::Internal,
88 }
89 }
90
91 /// Returns a `SlotVerifyData` which can be provided with non-fatal errors in case
92 /// `AllowVerificationError` flag was passed into `slot_verify()`.
verification_data(&self) -> Option<&SlotVerifyData<'a>>93 pub fn verification_data(&self) -> Option<&SlotVerifyData<'a>> {
94 match self {
95 SlotVerifyError::PublicKeyRejected(data)
96 | SlotVerifyError::RollbackIndex(data)
97 | SlotVerifyError::Verification(data) => data.as_ref(),
98 _ => None,
99 }
100 }
101 }
102
103 impl fmt::Display for SlotVerifyError<'_> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result104 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105 match self {
106 Self::InvalidArgument => write!(f, "Invalid parameters"),
107 Self::InvalidMetadata => write!(f, "Invalid metadata"),
108 Self::Io => write!(f, "I/O error"),
109 Self::Oom => write!(f, "Unable to allocate memory"),
110 Self::PublicKeyRejected(_) => write!(f, "Public key rejected or data not signed"),
111 Self::RollbackIndex(_) => write!(f, "Rollback index violation"),
112 Self::UnsupportedVersion => write!(f, "Unsupported vbmeta version"),
113 Self::Verification(_) => write!(f, "Verification failure"),
114 Self::Internal => write!(f, "Internal error"),
115 }
116 }
117 }
118
119 /// Converts a bindgen `AvbSlotVerifyResult` enum to a `SlotVerifyNoDataResult<>`, mapping
120 /// `AVB_SLOT_VERIFY_RESULT_OK` to the Rust equivalent `Ok(())` and errors to the corresponding
121 /// `Err(SlotVerifyError)`.
122 ///
123 /// An error returned here will always have a `None` `SlotVerifyData`; the data should be added
124 /// in later if it exists.
125 ///
126 /// This function is also important to serve as a compile-time check that we're handling all the
127 /// libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile
128 /// until it is updated to match.
slot_verify_enum_to_result( result: AvbSlotVerifyResult, ) -> SlotVerifyNoDataResult<()>129 pub(crate) fn slot_verify_enum_to_result(
130 result: AvbSlotVerifyResult,
131 ) -> SlotVerifyNoDataResult<()> {
132 match result {
133 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK => Ok(()),
134 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT => {
135 Err(SlotVerifyError::InvalidArgument)
136 }
137 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA => {
138 Err(SlotVerifyError::InvalidMetadata)
139 }
140 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO => Err(SlotVerifyError::Io),
141 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_OOM => Err(SlotVerifyError::Oom),
142 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED => {
143 Err(SlotVerifyError::PublicKeyRejected(None))
144 }
145 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX => {
146 Err(SlotVerifyError::RollbackIndex(None))
147 }
148 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION => {
149 Err(SlotVerifyError::UnsupportedVersion)
150 }
151 AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION => {
152 Err(SlotVerifyError::Verification(None))
153 }
154 }
155 }
156
157 /// `AvbIOResult` error wrapper.
158 #[derive(Clone, Debug, PartialEq, Eq)]
159 pub enum IoError {
160 /// `AVB_IO_RESULT_ERROR_OOM`
161 Oom,
162 /// `AVB_IO_RESULT_ERROR_IO`
163 Io,
164 /// `AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION`
165 NoSuchPartition,
166 /// `AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION`
167 RangeOutsidePartition,
168 /// `AVB_IO_RESULT_ERROR_NO_SUCH_VALUE`
169 NoSuchValue,
170 /// `AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE`
171 InvalidValueSize,
172 /// `AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE`. Also contains the space that would be required.
173 InsufficientSpace(usize),
174 /// Custom error code to indicate that an optional callback method has not been implemented.
175 /// If this is returned from a required callback method, it will bubble up as an `Io` error.
176 NotImplemented,
177 }
178
179 /// `Result` type for `IoError` errors.
180 pub type IoResult<T> = Result<T, IoError>;
181
182 impl fmt::Display for IoError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 match self {
185 Self::Oom => write!(f, "Unable to allocate memory"),
186 Self::Io => write!(f, "I/O error"),
187 Self::NoSuchPartition => write!(f, "No such partition exists"),
188 Self::RangeOutsidePartition => write!(f, "Range is outside the partition"),
189 Self::NoSuchValue => write!(f, "No such named persistent value"),
190 Self::InvalidValueSize => write!(f, "Invalid named persistent value size"),
191 Self::InsufficientSpace(size) => write!(f, "Buffer is too small (requires {})", size),
192 Self::NotImplemented => write!(f, "Function not implemented"),
193 }
194 }
195 }
196
197 impl From<Utf8Error> for IoError {
from(_: Utf8Error) -> Self198 fn from(_: Utf8Error) -> Self {
199 Self::Io
200 }
201 }
202
203 // Converts our `IoError` to the bindgen `AvbIOResult` enum.
204 //
205 // Unlike `SlotVerifyError` which gets generated by libavb and passed to the caller, `IoError` is
206 // created by the user callbacks and passed back into libavb so we need to be able to convert in
207 // this direction as well.
208 impl From<IoError> for AvbIOResult {
from(error: IoError) -> Self209 fn from(error: IoError) -> Self {
210 match error {
211 IoError::Oom => AvbIOResult::AVB_IO_RESULT_ERROR_OOM,
212 IoError::Io => AvbIOResult::AVB_IO_RESULT_ERROR_IO,
213 IoError::NoSuchPartition => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
214 IoError::RangeOutsidePartition => {
215 AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
216 }
217 IoError::NoSuchValue => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
218 IoError::InvalidValueSize => AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
219 IoError::InsufficientSpace(_) => AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
220 // `NotImplemented` is internal to this library and doesn't have a libavb equivalent,
221 // convert it to the default I/O error.
222 IoError::NotImplemented => AvbIOResult::AVB_IO_RESULT_ERROR_IO,
223 }
224 }
225 }
226
227 /// Converts an `IoResult<>` to the bindgen `AvbIOResult` enum.
result_to_io_enum(result: IoResult<()>) -> AvbIOResult228 pub(crate) fn result_to_io_enum(result: IoResult<()>) -> AvbIOResult {
229 result.map_or_else(|e| e.into(), |_| AvbIOResult::AVB_IO_RESULT_OK)
230 }
231
232 /// Converts a bindgen `AvbIOResult` enum to an `IoResult<>`, mapping `AVB_IO_RESULT_OK` to the Rust
233 /// equivalent `Ok(())` and errors to the corresponding `Err(IoError)`.
234 ///
235 /// This function is also important to serve as a compile-time check that we're handling all the
236 /// libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile
237 /// until it is updated to match.
io_enum_to_result(result: AvbIOResult) -> IoResult<()>238 pub(crate) fn io_enum_to_result(result: AvbIOResult) -> IoResult<()> {
239 match result {
240 AvbIOResult::AVB_IO_RESULT_OK => Ok(()),
241 AvbIOResult::AVB_IO_RESULT_ERROR_OOM => Err(IoError::Oom),
242 AvbIOResult::AVB_IO_RESULT_ERROR_IO => Err(IoError::Io),
243 AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION => Err(IoError::NoSuchPartition),
244 AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION => {
245 Err(IoError::RangeOutsidePartition)
246 }
247 AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE => Err(IoError::NoSuchValue),
248 AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE => Err(IoError::InvalidValueSize),
249 AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE => Err(IoError::InsufficientSpace(0)),
250 }
251 }
252
253 /// `AvbVBMetaVerifyResult` error wrapper.
254 #[derive(Clone, Debug, PartialEq, Eq)]
255 pub enum VbmetaVerifyError {
256 /// `AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED`
257 NotSigned,
258 /// `AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER`
259 InvalidVbmetaHeader,
260 /// `AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION`
261 UnsupportedVersion,
262 /// `AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH`
263 HashMismatch,
264 /// `AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH`
265 SignatureMismatch,
266 }
267
268 /// `Result` type for `VbmetaVerifyError` errors.
269 pub type VbmetaVerifyResult<T> = Result<T, VbmetaVerifyError>;
270
271 impl fmt::Display for VbmetaVerifyError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result272 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273 match self {
274 Self::NotSigned => write!(f, "vbmeta is unsigned"),
275 Self::InvalidVbmetaHeader => write!(f, "invalid vbmeta header"),
276 Self::UnsupportedVersion => write!(f, "unsupported vbmeta version"),
277 Self::HashMismatch => write!(f, "vbmeta hash mismatch"),
278 Self::SignatureMismatch => write!(f, "vbmeta signature mismatch"),
279 }
280 }
281 }
282
283 // Converts a bindgen `AvbVBMetaVerifyResult` enum to a `VbmetaVerifyResult<>`, mapping
284 // `AVB_VBMETA_VERIFY_RESULT_OK` to the Rust equivalent `Ok(())` and errors to the corresponding
285 // `Err(SlotVerifyError)`.
286 //
287 // This function is also important to serve as a compile-time check that we're handling all the
288 // libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile
289 // until it is updated to match.
vbmeta_verify_enum_to_result(result: AvbVBMetaVerifyResult) -> VbmetaVerifyResult<()>290 pub fn vbmeta_verify_enum_to_result(result: AvbVBMetaVerifyResult) -> VbmetaVerifyResult<()> {
291 match result {
292 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK => Ok(()),
293 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED => {
294 Err(VbmetaVerifyError::NotSigned)
295 }
296 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER => {
297 Err(VbmetaVerifyError::InvalidVbmetaHeader)
298 }
299 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION => {
300 Err(VbmetaVerifyError::UnsupportedVersion)
301 }
302 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH => {
303 Err(VbmetaVerifyError::HashMismatch)
304 }
305 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH => {
306 Err(VbmetaVerifyError::SignatureMismatch)
307 }
308 }
309 }
310
311 #[cfg(test)]
312 mod tests {
313 use super::*;
314
315 #[test]
display_slot_verify_error()316 fn display_slot_verify_error() {
317 // The actual error message can change as needed, the point of the test is just to make sure
318 // the fmt::Display trait is properly implemented.
319 assert_eq!(
320 format!("{}", SlotVerifyError::Verification(None)),
321 "Verification failure"
322 );
323 }
324
325 #[test]
convert_slot_verify_enum_to_result()326 fn convert_slot_verify_enum_to_result() {
327 assert!(matches!(
328 slot_verify_enum_to_result(AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK),
329 Ok(())
330 ));
331 assert!(matches!(
332 slot_verify_enum_to_result(AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO),
333 Err(SlotVerifyError::Io)
334 ));
335 }
336
337 #[test]
display_io_error()338 fn display_io_error() {
339 // The actual error message can change as needed, the point of the test is just to make sure
340 // the fmt::Display trait is properly implemented.
341 assert_eq!(
342 format!("{}", IoError::NoSuchPartition),
343 "No such partition exists"
344 );
345 }
346
347 #[test]
convert_io_enum_to_result()348 fn convert_io_enum_to_result() {
349 // This is a compile-time check that we handle all the `AvbIOResult` enum values. If any
350 // enums are added or removed this will break, indicating we need to update `IoError` to
351 // match.
352 assert_eq!(io_enum_to_result(AvbIOResult::AVB_IO_RESULT_OK), Ok(()));
353 assert_eq!(
354 io_enum_to_result(AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION),
355 Err(IoError::NoSuchPartition)
356 );
357 }
358
359 #[test]
convert_io_result_to_enum()360 fn convert_io_result_to_enum() {
361 assert_eq!(result_to_io_enum(Ok(())), AvbIOResult::AVB_IO_RESULT_OK);
362 assert_eq!(
363 result_to_io_enum(Err(IoError::Io)),
364 AvbIOResult::AVB_IO_RESULT_ERROR_IO
365 );
366 }
367
368 #[test]
display_vmbeta_verify_error()369 fn display_vmbeta_verify_error() {
370 // The actual error message can change as needed, the point of the test is just to make sure
371 // the fmt::Display trait is properly implemented.
372 assert_eq!(
373 format!("{}", VbmetaVerifyError::NotSigned),
374 "vbmeta is unsigned"
375 );
376 }
377
378 #[test]
convert_vbmeta_verify_enum_to_result()379 fn convert_vbmeta_verify_enum_to_result() {
380 assert_eq!(
381 vbmeta_verify_enum_to_result(AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK),
382 Ok(())
383 );
384 assert_eq!(
385 vbmeta_verify_enum_to_result(
386 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH
387 ),
388 Err(VbmetaVerifyError::HashMismatch)
389 );
390 }
391 }
392