xref: /aosp_15_r20/external/avb/rust/src/error.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
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 #[derive(Debug, PartialEq, Eq)]
39 pub enum SlotVerifyError<'a> {
40     /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT`
41     InvalidArgument,
42     /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA`
43     InvalidMetadata,
44     /// `AVB_SLOT_VERIFY_RESULT_ERROR_IO`
45     Io,
46     /// `AVB_SLOT_VERIFY_RESULT_ERROR_OOM`
47     Oom,
48     /// `AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED`
49     PublicKeyRejected,
50     /// `AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX`
51     RollbackIndex,
52     /// `AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION`
53     UnsupportedVersion,
54     /// `AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION`
55     ///
56     /// This verification error can contain the resulting `SlotVerifyData` if the
57     /// `AllowVerificationError` flag was passed into `slot_verify()`.
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,
84             Self::RollbackIndex => SlotVerifyError::RollbackIndex,
85             Self::UnsupportedVersion => SlotVerifyError::UnsupportedVersion,
86             Self::Verification(_) => SlotVerifyError::Verification(None),
87             Self::Internal => SlotVerifyError::Internal,
88         }
89     }
90 }
91 
92 impl<'a> fmt::Display for SlotVerifyError<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result93     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94         match self {
95             Self::InvalidArgument => write!(f, "Invalid parameters"),
96             Self::InvalidMetadata => write!(f, "Invalid metadata"),
97             Self::Io => write!(f, "I/O error"),
98             Self::Oom => write!(f, "Unable to allocate memory"),
99             Self::PublicKeyRejected => write!(f, "Public key rejected or data not signed"),
100             Self::RollbackIndex => write!(f, "Rollback index violation"),
101             Self::UnsupportedVersion => write!(f, "Unsupported vbmeta version"),
102             Self::Verification(_) => write!(f, "Verification failure"),
103             Self::Internal => write!(f, "Internal error"),
104         }
105     }
106 }
107 
108 /// Converts a bindgen `AvbSlotVerifyResult` enum to a `SlotVerifyNoDataResult<>`, mapping
109 /// `AVB_SLOT_VERIFY_RESULT_OK` to the Rust equivalent `Ok(())` and errors to the corresponding
110 /// `Err(SlotVerifyError)`.
111 ///
112 /// A `Verification` error returned here will always have a `None` `SlotVerifyData`; the data should
113 /// be added in later if it exists.
114 ///
115 /// This function is also important to serve as a compile-time check that we're handling all the
116 /// libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile
117 /// until it is updated to match.
slot_verify_enum_to_result( result: AvbSlotVerifyResult, ) -> SlotVerifyNoDataResult<()>118 pub(crate) fn slot_verify_enum_to_result(
119     result: AvbSlotVerifyResult,
120 ) -> SlotVerifyNoDataResult<()> {
121     match result {
122         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK => Ok(()),
123         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT => {
124             Err(SlotVerifyError::InvalidArgument)
125         }
126         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA => {
127             Err(SlotVerifyError::InvalidMetadata)
128         }
129         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO => Err(SlotVerifyError::Io),
130         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_OOM => Err(SlotVerifyError::Oom),
131         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED => {
132             Err(SlotVerifyError::PublicKeyRejected)
133         }
134         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX => {
135             Err(SlotVerifyError::RollbackIndex)
136         }
137         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION => {
138             Err(SlotVerifyError::UnsupportedVersion)
139         }
140         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION => {
141             Err(SlotVerifyError::Verification(None))
142         }
143     }
144 }
145 
146 /// `AvbIOResult` error wrapper.
147 #[derive(Clone, Debug, PartialEq, Eq)]
148 pub enum IoError {
149     /// `AVB_IO_RESULT_ERROR_OOM`
150     Oom,
151     /// `AVB_IO_RESULT_ERROR_IO`
152     Io,
153     /// `AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION`
154     NoSuchPartition,
155     /// `AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION`
156     RangeOutsidePartition,
157     /// `AVB_IO_RESULT_ERROR_NO_SUCH_VALUE`
158     NoSuchValue,
159     /// `AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE`
160     InvalidValueSize,
161     /// `AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE`. Also contains the space that would be required.
162     InsufficientSpace(usize),
163     /// Custom error code to indicate that an optional callback method has not been implemented.
164     /// If this is returned from a required callback method, it will bubble up as an `Io` error.
165     NotImplemented,
166 }
167 
168 /// `Result` type for `IoError` errors.
169 pub type IoResult<T> = Result<T, IoError>;
170 
171 impl fmt::Display for IoError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result172     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173         match self {
174             Self::Oom => write!(f, "Unable to allocate memory"),
175             Self::Io => write!(f, "I/O error"),
176             Self::NoSuchPartition => write!(f, "No such partition exists"),
177             Self::RangeOutsidePartition => write!(f, "Range is outside the partition"),
178             Self::NoSuchValue => write!(f, "No such named persistent value"),
179             Self::InvalidValueSize => write!(f, "Invalid named persistent value size"),
180             Self::InsufficientSpace(size) => write!(f, "Buffer is too small (requires {})", size),
181             Self::NotImplemented => write!(f, "Function not implemented"),
182         }
183     }
184 }
185 
186 impl From<Utf8Error> for IoError {
from(_: Utf8Error) -> Self187     fn from(_: Utf8Error) -> Self {
188         Self::Io
189     }
190 }
191 
192 // Converts our `IoError` to the bindgen `AvbIOResult` enum.
193 //
194 // Unlike `SlotVerifyError` which gets generated by libavb and passed to the caller, `IoError` is
195 // created by the user callbacks and passed back into libavb so we need to be able to convert in
196 // this direction as well.
197 impl From<IoError> for AvbIOResult {
from(error: IoError) -> Self198     fn from(error: IoError) -> Self {
199         match error {
200             IoError::Oom => AvbIOResult::AVB_IO_RESULT_ERROR_OOM,
201             IoError::Io => AvbIOResult::AVB_IO_RESULT_ERROR_IO,
202             IoError::NoSuchPartition => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
203             IoError::RangeOutsidePartition => {
204                 AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
205             }
206             IoError::NoSuchValue => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
207             IoError::InvalidValueSize => AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
208             IoError::InsufficientSpace(_) => AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
209             // `NotImplemented` is internal to this library and doesn't have a libavb equivalent,
210             // convert it to the default I/O error.
211             IoError::NotImplemented => AvbIOResult::AVB_IO_RESULT_ERROR_IO,
212         }
213     }
214 }
215 
216 /// Converts an `IoResult<>` to the bindgen `AvbIOResult` enum.
result_to_io_enum(result: IoResult<()>) -> AvbIOResult217 pub(crate) fn result_to_io_enum(result: IoResult<()>) -> AvbIOResult {
218     result.map_or_else(|e| e.into(), |_| AvbIOResult::AVB_IO_RESULT_OK)
219 }
220 
221 /// Converts a bindgen `AvbIOResult` enum to an `IoResult<>`, mapping `AVB_IO_RESULT_OK` to the Rust
222 /// equivalent `Ok(())` and errors to the corresponding `Err(IoError)`.
223 ///
224 /// This function is also important to serve as a compile-time check that we're handling all the
225 /// libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile
226 /// until it is updated to match.
io_enum_to_result(result: AvbIOResult) -> IoResult<()>227 pub(crate) fn io_enum_to_result(result: AvbIOResult) -> IoResult<()> {
228     match result {
229         AvbIOResult::AVB_IO_RESULT_OK => Ok(()),
230         AvbIOResult::AVB_IO_RESULT_ERROR_OOM => Err(IoError::Oom),
231         AvbIOResult::AVB_IO_RESULT_ERROR_IO => Err(IoError::Io),
232         AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION => Err(IoError::NoSuchPartition),
233         AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION => {
234             Err(IoError::RangeOutsidePartition)
235         }
236         AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE => Err(IoError::NoSuchValue),
237         AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE => Err(IoError::InvalidValueSize),
238         AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE => Err(IoError::InsufficientSpace(0)),
239     }
240 }
241 
242 /// `AvbVBMetaVerifyResult` error wrapper.
243 #[derive(Clone, Debug, PartialEq, Eq)]
244 pub enum VbmetaVerifyError {
245     /// `AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED`
246     NotSigned,
247     /// `AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER`
248     InvalidVbmetaHeader,
249     /// `AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION`
250     UnsupportedVersion,
251     /// `AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH`
252     HashMismatch,
253     /// `AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH`
254     SignatureMismatch,
255 }
256 
257 /// `Result` type for `VbmetaVerifyError` errors.
258 pub type VbmetaVerifyResult<T> = Result<T, VbmetaVerifyError>;
259 
260 impl fmt::Display for VbmetaVerifyError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result261     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262         match self {
263             Self::NotSigned => write!(f, "vbmeta is unsigned"),
264             Self::InvalidVbmetaHeader => write!(f, "invalid vbmeta header"),
265             Self::UnsupportedVersion => write!(f, "unsupported vbmeta version"),
266             Self::HashMismatch => write!(f, "vbmeta hash mismatch"),
267             Self::SignatureMismatch => write!(f, "vbmeta signature mismatch"),
268         }
269     }
270 }
271 
272 // Converts a bindgen `AvbVBMetaVerifyResult` enum to a `VbmetaVerifyResult<>`, mapping
273 // `AVB_VBMETA_VERIFY_RESULT_OK` to the Rust equivalent `Ok(())` and errors to the corresponding
274 // `Err(SlotVerifyError)`.
275 //
276 // This function is also important to serve as a compile-time check that we're handling all the
277 // libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile
278 // until it is updated to match.
vbmeta_verify_enum_to_result(result: AvbVBMetaVerifyResult) -> VbmetaVerifyResult<()>279 pub fn vbmeta_verify_enum_to_result(result: AvbVBMetaVerifyResult) -> VbmetaVerifyResult<()> {
280     match result {
281         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK => Ok(()),
282         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED => {
283             Err(VbmetaVerifyError::NotSigned)
284         }
285         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER => {
286             Err(VbmetaVerifyError::InvalidVbmetaHeader)
287         }
288         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION => {
289             Err(VbmetaVerifyError::UnsupportedVersion)
290         }
291         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH => {
292             Err(VbmetaVerifyError::HashMismatch)
293         }
294         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH => {
295             Err(VbmetaVerifyError::SignatureMismatch)
296         }
297     }
298 }
299 
300 #[cfg(test)]
301 mod tests {
302     use super::*;
303 
304     #[test]
display_slot_verify_error()305     fn display_slot_verify_error() {
306         // The actual error message can change as needed, the point of the test is just to make sure
307         // the fmt::Display trait is properly implemented.
308         assert_eq!(
309             format!("{}", SlotVerifyError::Verification(None)),
310             "Verification failure"
311         );
312     }
313 
314     #[test]
convert_slot_verify_enum_to_result()315     fn convert_slot_verify_enum_to_result() {
316         assert!(matches!(
317             slot_verify_enum_to_result(AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK),
318             Ok(())
319         ));
320         assert!(matches!(
321             slot_verify_enum_to_result(AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO),
322             Err(SlotVerifyError::Io)
323         ));
324     }
325 
326     #[test]
display_io_error()327     fn display_io_error() {
328         // The actual error message can change as needed, the point of the test is just to make sure
329         // the fmt::Display trait is properly implemented.
330         assert_eq!(
331             format!("{}", IoError::NoSuchPartition),
332             "No such partition exists"
333         );
334     }
335 
336     #[test]
convert_io_enum_to_result()337     fn convert_io_enum_to_result() {
338         // This is a compile-time check that we handle all the `AvbIOResult` enum values. If any
339         // enums are added or removed this will break, indicating we need to update `IoError` to
340         // match.
341         assert_eq!(io_enum_to_result(AvbIOResult::AVB_IO_RESULT_OK), Ok(()));
342         assert_eq!(
343             io_enum_to_result(AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION),
344             Err(IoError::NoSuchPartition)
345         );
346     }
347 
348     #[test]
convert_io_result_to_enum()349     fn convert_io_result_to_enum() {
350         assert_eq!(result_to_io_enum(Ok(())), AvbIOResult::AVB_IO_RESULT_OK);
351         assert_eq!(
352             result_to_io_enum(Err(IoError::Io)),
353             AvbIOResult::AVB_IO_RESULT_ERROR_IO
354         );
355     }
356 
357     #[test]
display_vmbeta_verify_error()358     fn display_vmbeta_verify_error() {
359         // The actual error message can change as needed, the point of the test is just to make sure
360         // the fmt::Display trait is properly implemented.
361         assert_eq!(
362             format!("{}", VbmetaVerifyError::NotSigned),
363             "vbmeta is unsigned"
364         );
365     }
366 
367     #[test]
convert_vbmeta_verify_enum_to_result()368     fn convert_vbmeta_verify_enum_to_result() {
369         assert_eq!(
370             vbmeta_verify_enum_to_result(AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK),
371             Ok(())
372         );
373         assert_eq!(
374             vbmeta_verify_enum_to_result(
375                 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH
376             ),
377             Err(VbmetaVerifyError::HashMismatch)
378         );
379     }
380 }
381