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