| // Copyright 2022, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! Error types used in libavb. |
| //! |
| //! There are a few advantages of providing these custom types rather than exposing the raw bindgen |
| //! enums directly: |
| //! * More idiomatic error handling |
| //! * C code defines a "status" enum that can contain either OK or an error, whereas Rust prefers |
| //! error-only enums to use with `Result<>` e.g. `Result<(), IoError>`. An "OK" status doesn't |
| //! make sense when used with `Result<>`. |
| //! * Better naming e.g. `IoError::Oom` vs the redundant `AvbIoResult::AVB_IO_RESULT_ERROR_OOM` |
| //! * We can implement traits such as `Display` for added convenience. |
| |
| // The naming scheme can be a bit confusing due to the re-use of "result" in a few places: |
| // * `Avb*Result`: raw libavb enums generated by bindgen, containing errors and "OK". Internal-only; |
| // library users should never have to use these types. |
| // * `*Error`: `Avb*Result` wrappers which only contain error conditions, not "OK". Should be |
| // wrapped in a Rust `Result<>` in public API. |
| // * `Result<T, *Error>`: top-level `Result<>` type used in this library's public API. |
| |
| use crate::SlotVerifyData; |
| use avb_bindgen::{AvbIOResult, AvbSlotVerifyResult, AvbVBMetaVerifyResult}; |
| use core::{fmt, str::Utf8Error}; |
| |
| /// `AvbSlotVerifyResult` error wrapper. |
| #[derive(Debug, PartialEq, Eq)] |
| pub enum SlotVerifyError<'a> { |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT` |
| InvalidArgument, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA` |
| InvalidMetadata, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_IO` |
| Io, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_OOM` |
| Oom, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED` |
| PublicKeyRejected, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX` |
| RollbackIndex, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION` |
| UnsupportedVersion, |
| /// `AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION` |
| /// |
| /// This verification error can contain the resulting `SlotVerifyData` if the |
| /// `AllowVerificationError` flag was passed into `slot_verify()`. |
| Verification(Option<SlotVerifyData<'a>>), |
| /// Unexpected internal error. This does not have a corresponding libavb error code. |
| Internal, |
| } |
| |
| /// `Result` type for `SlotVerifyError` errors. |
| pub type SlotVerifyResult<'a, T> = Result<T, SlotVerifyError<'a>>; |
| |
| /// `Result` type for `SlotVerifyError` errors without any `SlotVerifyData`. |
| /// |
| /// If the contained error will never hold a `SlotVerifyData`, this is easier to work with compared |
| /// to `SlotVerifyResult` due to the static lifetime bound. |
| pub type SlotVerifyNoDataResult<T> = SlotVerifyResult<'static, T>; |
| |
| impl<'a> SlotVerifyError<'a> { |
| /// Returns a copy of this error without any contained `SlotVerifyData`. |
| /// |
| /// This can simplify usage if the user doesn't care about the `SlotVerifyData` by turning the |
| /// current lifetime bound into `'static`. |
| pub fn without_verify_data(&self) -> SlotVerifyError<'static> { |
| match self { |
| Self::InvalidArgument => SlotVerifyError::InvalidArgument, |
| Self::InvalidMetadata => SlotVerifyError::InvalidMetadata, |
| Self::Io => SlotVerifyError::Io, |
| Self::Oom => SlotVerifyError::Oom, |
| Self::PublicKeyRejected => SlotVerifyError::PublicKeyRejected, |
| Self::RollbackIndex => SlotVerifyError::RollbackIndex, |
| Self::UnsupportedVersion => SlotVerifyError::UnsupportedVersion, |
| Self::Verification(_) => SlotVerifyError::Verification(None), |
| Self::Internal => SlotVerifyError::Internal, |
| } |
| } |
| } |
| |
| impl<'a> fmt::Display for SlotVerifyError<'a> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| Self::InvalidArgument => write!(f, "Invalid parameters"), |
| Self::InvalidMetadata => write!(f, "Invalid metadata"), |
| Self::Io => write!(f, "I/O error"), |
| Self::Oom => write!(f, "Unable to allocate memory"), |
| Self::PublicKeyRejected => write!(f, "Public key rejected or data not signed"), |
| Self::RollbackIndex => write!(f, "Rollback index violation"), |
| Self::UnsupportedVersion => write!(f, "Unsupported vbmeta version"), |
| Self::Verification(_) => write!(f, "Verification failure"), |
| Self::Internal => write!(f, "Internal error"), |
| } |
| } |
| } |
| |
| /// Converts a bindgen `AvbSlotVerifyResult` enum to a `Result<>`, mapping |
| /// `AVB_SLOT_VERIFY_RESULT_OK` to the Rust equivalent `Ok(())` and errors to the corresponding |
| /// `Err(SlotVerifyError)`. |
| /// |
| /// A `Verification` error returned here will always have a `None` `SlotVerifyData`; the data should |
| /// be added in later if it exists. |
| /// |
| /// This function is also important to serve as a compile-time check that we're handling all the |
| /// libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile |
| /// until it is updated to match. |
| /// |
| /// TODO(b/290110273): this can be limited to pub(crate) once we've moved the full libavb wrapper |
| /// here. |
| pub fn slot_verify_enum_to_result(result: AvbSlotVerifyResult) -> SlotVerifyNoDataResult<()> { |
| match result { |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK => Ok(()), |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT => { |
| Err(SlotVerifyError::InvalidArgument) |
| } |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA => { |
| Err(SlotVerifyError::InvalidMetadata) |
| } |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO => Err(SlotVerifyError::Io), |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_OOM => Err(SlotVerifyError::Oom), |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED => { |
| Err(SlotVerifyError::PublicKeyRejected) |
| } |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX => { |
| Err(SlotVerifyError::RollbackIndex) |
| } |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION => { |
| Err(SlotVerifyError::UnsupportedVersion) |
| } |
| AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION => { |
| Err(SlotVerifyError::Verification(None)) |
| } |
| } |
| } |
| |
| /// `AvbIOResult` error wrapper. |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub enum IoError { |
| /// `AVB_IO_RESULT_ERROR_OOM` |
| Oom, |
| /// `AVB_IO_RESULT_ERROR_IO` |
| Io, |
| /// `AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION` |
| NoSuchPartition, |
| /// `AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION` |
| RangeOutsidePartition, |
| /// `AVB_IO_RESULT_ERROR_NO_SUCH_VALUE` |
| NoSuchValue, |
| /// `AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE` |
| InvalidValueSize, |
| /// `AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE`. Also contains the space that would be required. |
| InsufficientSpace(usize), |
| /// Custom error code to indicate that an optional callback method has not been implemented. |
| /// If this is returned from a required callback method, it will bubble up as an `Io` error. |
| NotImplemented, |
| } |
| |
| /// `Result` type for `IoError` errors. |
| pub type IoResult<T> = Result<T, IoError>; |
| |
| impl fmt::Display for IoError { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| Self::Oom => write!(f, "Unable to allocate memory"), |
| Self::Io => write!(f, "I/O error"), |
| Self::NoSuchPartition => write!(f, "No such partition exists"), |
| Self::RangeOutsidePartition => write!(f, "Range is outside the partition"), |
| Self::NoSuchValue => write!(f, "No such named persistent value"), |
| Self::InvalidValueSize => write!(f, "Invalid named persistent value size"), |
| Self::InsufficientSpace(size) => write!(f, "Buffer is too small (requires {})", size), |
| Self::NotImplemented => write!(f, "Function not implemented"), |
| } |
| } |
| } |
| |
| impl From<Utf8Error> for IoError { |
| fn from(_: Utf8Error) -> Self { |
| Self::Io |
| } |
| } |
| |
| // Converts a bindgen `AvbIOResult` enum to a `Result<>`, mapping `AVB_IO_RESULT_OK` to the Rust |
| // equivalent `Ok(())` and errors to the corresponding `Err(IoError)`. |
| // |
| // This function is not currently used, but serves as a compile-time check that we're handling all |
| // the libavb enums; if a new one is added to or removed from the C code, this will fail to compile |
| // until it is updated to match. |
| fn io_enum_to_result(result: AvbIOResult) -> IoResult<()> { |
| match result { |
| AvbIOResult::AVB_IO_RESULT_OK => Ok(()), |
| AvbIOResult::AVB_IO_RESULT_ERROR_OOM => Err(IoError::Oom), |
| AvbIOResult::AVB_IO_RESULT_ERROR_IO => Err(IoError::Io), |
| AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION => Err(IoError::NoSuchPartition), |
| AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION => { |
| Err(IoError::RangeOutsidePartition) |
| } |
| AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE => Err(IoError::NoSuchValue), |
| AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE => Err(IoError::InvalidValueSize), |
| AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE => Err(IoError::InsufficientSpace(0)), |
| } |
| } |
| |
| // Converts our `IoError` to the bindgen `AvbIOResult` enum. |
| // |
| // Unlike `SlotVerifyError` which gets generated by libavb and passed to the caller, `IoError` is |
| // created by the user callbacks and passed back into libavb so we need to be able to convert in |
| // this direction as well. |
| impl From<IoError> for AvbIOResult { |
| fn from(error: IoError) -> Self { |
| match error { |
| IoError::Oom => AvbIOResult::AVB_IO_RESULT_ERROR_OOM, |
| IoError::Io => AvbIOResult::AVB_IO_RESULT_ERROR_IO, |
| IoError::NoSuchPartition => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION, |
| IoError::RangeOutsidePartition => { |
| AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION |
| } |
| IoError::NoSuchValue => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE, |
| IoError::InvalidValueSize => AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE, |
| IoError::InsufficientSpace(_) => AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE, |
| // `NotImplemented` is internal to this library and doesn't have a libavb equivalent, |
| // convert it to the default I/O error. |
| IoError::NotImplemented => AvbIOResult::AVB_IO_RESULT_ERROR_IO, |
| } |
| } |
| } |
| |
| // Converts a `Result<>` to the bindgen `AvbIOResult` enum. |
| // |
| // TODO(b/290110273): this can be limited to pub(crate) once we've moved the full libavb wrapper |
| // here. |
| pub fn result_to_io_enum(result: Result<(), IoError>) -> AvbIOResult { |
| result.map_or_else(|e| e.into(), |_| AvbIOResult::AVB_IO_RESULT_OK) |
| } |
| |
| /// `AvbVBMetaVerifyResult` error wrapper. |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub enum VbmetaVerifyError { |
| /// `AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED` |
| NotSigned, |
| /// `AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER` |
| InvalidVbmetaHeader, |
| /// `AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION` |
| UnsupportedVersion, |
| /// `AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH` |
| HashMismatch, |
| /// `AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH` |
| SignatureMismatch, |
| } |
| |
| /// `Result` type for `VbmetaVerifyError` errors. |
| pub type VbmetaVerifyResult<T> = Result<T, VbmetaVerifyError>; |
| |
| impl fmt::Display for VbmetaVerifyError { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| Self::NotSigned => write!(f, "vbmeta is unsigned"), |
| Self::InvalidVbmetaHeader => write!(f, "invalid vbmeta header"), |
| Self::UnsupportedVersion => write!(f, "unsupported vbmeta version"), |
| Self::HashMismatch => write!(f, "vbmeta hash mismatch"), |
| Self::SignatureMismatch => write!(f, "vbmeta signature mismatch"), |
| } |
| } |
| } |
| |
| // Converts a bindgen `AvbVBMetaVerifyResult` enum to a `Result<>`, mapping |
| // `AVB_VBMETA_VERIFY_RESULT_OK` to the Rust equivalent `Ok(())` and errors to the corresponding |
| // `Err(SlotVerifyError)`. |
| // |
| // This function is also important to serve as a compile-time check that we're handling all the |
| // libavb enums; if a new one is added to (or removed from) the C code, this will fail to compile |
| // until it is updated to match. |
| pub fn vbmeta_verify_enum_to_result(result: AvbVBMetaVerifyResult) -> VbmetaVerifyResult<()> { |
| match result { |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK => Ok(()), |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED => { |
| Err(VbmetaVerifyError::NotSigned) |
| } |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER => { |
| Err(VbmetaVerifyError::InvalidVbmetaHeader) |
| } |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION => { |
| Err(VbmetaVerifyError::UnsupportedVersion) |
| } |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH => { |
| Err(VbmetaVerifyError::HashMismatch) |
| } |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH => { |
| Err(VbmetaVerifyError::SignatureMismatch) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_SlotVerifyError_display() { |
| // The actual error message can change as needed, the point of the test is just to make sure |
| // the fmt::Display trait is properly implemented. |
| assert_eq!( |
| format!("{}", SlotVerifyError::Verification(None)), |
| "Verification failure" |
| ); |
| } |
| |
| #[test] |
| fn test_SlotVerifyError_from_raw() { |
| assert!(matches!( |
| slot_verify_enum_to_result(AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK), |
| Ok(()) |
| )); |
| assert!(matches!( |
| slot_verify_enum_to_result(AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO), |
| Err(SlotVerifyError::Io) |
| )); |
| } |
| |
| #[test] |
| fn test_IoError_display() { |
| // The actual error message can change as needed, the point of the test is just to make sure |
| // the fmt::Display trait is properly implemented. |
| assert_eq!( |
| format!("{}", IoError::NoSuchPartition), |
| "No such partition exists" |
| ); |
| } |
| |
| #[test] |
| fn test_IoError_from_raw() { |
| assert_eq!(io_enum_to_result(AvbIOResult::AVB_IO_RESULT_OK), Ok(())); |
| assert_eq!( |
| io_enum_to_result(AvbIOResult::AVB_IO_RESULT_ERROR_IO), |
| Err(IoError::Io) |
| ); |
| } |
| |
| #[test] |
| fn test_IoError_to_raw() { |
| assert_eq!(result_to_io_enum(Ok(())), AvbIOResult::AVB_IO_RESULT_OK); |
| assert_eq!( |
| result_to_io_enum(Err(IoError::Io)), |
| AvbIOResult::AVB_IO_RESULT_ERROR_IO |
| ); |
| } |
| |
| #[test] |
| fn test_VbmetaVerifyError_display() { |
| // The actual error message can change as needed, the point of the test is just to make sure |
| // the fmt::Display trait is properly implemented. |
| assert_eq!( |
| format!("{}", VbmetaVerifyError::NotSigned), |
| "vbmeta is unsigned" |
| ); |
| } |
| |
| #[test] |
| fn test_VbmetaVerifyError_from_raw() { |
| assert_eq!( |
| vbmeta_verify_enum_to_result(AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK), |
| Ok(()) |
| ); |
| assert_eq!( |
| vbmeta_verify_enum_to_result( |
| AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH |
| ), |
| Err(VbmetaVerifyError::HashMismatch) |
| ); |
| } |
| } |