• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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