Pass AVB data for other non-fatal errors

AVB data is also may be provided for rollback index
and public key related errors.

Test: confirmed data provided
Bug: 337846185
Bug: 374684404
Change-Id: I0181feb8004e72eb761707cb7c0a06ce9f86a97b
Signed-off-by: Dmitrii Merkurev <dimorinny@google.com>
diff --git a/rust/src/error.rs b/rust/src/error.rs
index a5642c6..1222f5f 100644
--- a/rust/src/error.rs
+++ b/rust/src/error.rs
@@ -35,6 +35,9 @@
 use core::{fmt, str::Utf8Error};
 
 /// `AvbSlotVerifyResult` error wrapper.
+///
+/// Some of the errors can contain the resulting `SlotVerifyData` if the `AllowVerificationError`
+/// flag was passed into `slot_verify()`.
 #[derive(Debug, PartialEq, Eq)]
 pub enum SlotVerifyError<'a> {
     /// `AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT`
@@ -46,15 +49,12 @@
     /// `AVB_SLOT_VERIFY_RESULT_ERROR_OOM`
     Oom,
     /// `AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED`
-    PublicKeyRejected,
+    PublicKeyRejected(Option<SlotVerifyData<'a>>),
     /// `AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX`
-    RollbackIndex,
+    RollbackIndex(Option<SlotVerifyData<'a>>),
     /// `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,
@@ -80,13 +80,24 @@
             Self::InvalidMetadata => SlotVerifyError::InvalidMetadata,
             Self::Io => SlotVerifyError::Io,
             Self::Oom => SlotVerifyError::Oom,
-            Self::PublicKeyRejected => SlotVerifyError::PublicKeyRejected,
-            Self::RollbackIndex => SlotVerifyError::RollbackIndex,
+            Self::PublicKeyRejected(_) => SlotVerifyError::PublicKeyRejected(None),
+            Self::RollbackIndex(_) => SlotVerifyError::RollbackIndex(None),
             Self::UnsupportedVersion => SlotVerifyError::UnsupportedVersion,
             Self::Verification(_) => SlotVerifyError::Verification(None),
             Self::Internal => SlotVerifyError::Internal,
         }
     }
+
+    /// Returns a `SlotVerifyData` which can be provided with non-fatal errors in case
+    /// `AllowVerificationError` flag was passed into `slot_verify()`.
+    pub fn verification_data(&self) -> Option<&SlotVerifyData<'a>> {
+        match self {
+            SlotVerifyError::PublicKeyRejected(data)
+            | SlotVerifyError::RollbackIndex(data)
+            | SlotVerifyError::Verification(data) => data.as_ref(),
+            _ => None,
+        }
+    }
 }
 
 impl<'a> fmt::Display for SlotVerifyError<'a> {
@@ -96,8 +107,8 @@
             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::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"),
@@ -109,8 +120,8 @@
 /// `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.
+/// An 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
@@ -129,10 +140,10 @@
         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)
+            Err(SlotVerifyError::PublicKeyRejected(None))
         }
         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX => {
-            Err(SlotVerifyError::RollbackIndex)
+            Err(SlotVerifyError::RollbackIndex(None))
         }
         AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION => {
             Err(SlotVerifyError::UnsupportedVersion)
diff --git a/rust/src/verify.rs b/rust/src/verify.rs
index bea2275..989c8f1 100644
--- a/rust/src/verify.rs
+++ b/rust/src/verify.rs
@@ -465,7 +465,11 @@
     match result {
         // libavb will always provide verification data on success.
         Ok(()) => Ok(data.unwrap()),
-        // Data may also be provided on verification failure, fold it into the error.
+        // Data may also be provided on non-fatal failures, fold it into the error.
+        Err(SlotVerifyError::PublicKeyRejected(None)) => {
+            Err(SlotVerifyError::PublicKeyRejected(data))
+        }
+        Err(SlotVerifyError::RollbackIndex(None)) => Err(SlotVerifyError::RollbackIndex(data)),
         Err(SlotVerifyError::Verification(None)) => Err(SlotVerifyError::Verification(data)),
         // No other error provides verification data.
         Err(e) => Err(e),
diff --git a/rust/tests/cert_tests.rs b/rust/tests/cert_tests.rs
index 171fffb..2dd77e6 100644
--- a/rust/tests/cert_tests.rs
+++ b/rust/tests/cert_tests.rs
@@ -138,7 +138,7 @@
 
     let result = verify_one_image_one_vbmeta(&mut ops);
 
-    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected);
+    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected(None));
 }
 
 #[test]
@@ -153,7 +153,7 @@
 
     let result = verify_one_image_one_vbmeta(&mut ops);
 
-    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected);
+    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected(None));
 }
 
 #[test]
@@ -164,7 +164,7 @@
 
     let result = verify_one_image_one_vbmeta(&mut ops);
 
-    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected);
+    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected(None));
 }
 
 #[test]
@@ -175,7 +175,7 @@
 
     let result = verify_one_image_one_vbmeta(&mut ops);
 
-    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected);
+    assert_eq!(result.unwrap_err(), SlotVerifyError::PublicKeyRejected(None));
 }
 
 #[test]
diff --git a/rust/tests/verify_tests.rs b/rust/tests/verify_tests.rs
index a5e67c5..45c15c3 100644
--- a/rust/tests/verify_tests.rs
+++ b/rust/tests/verify_tests.rs
@@ -464,7 +464,7 @@
     let result = verify_one_image_one_vbmeta(&mut ops);
 
     let error = result.unwrap_err();
-    assert!(matches!(error, SlotVerifyError::RollbackIndex));
+    assert!(matches!(error, SlotVerifyError::RollbackIndex(None)));
 }
 
 #[test]
@@ -489,7 +489,7 @@
     let result = verify_one_image_one_vbmeta(&mut ops);
 
     let error = result.unwrap_err();
-    assert!(matches!(error, SlotVerifyError::PublicKeyRejected));
+    assert!(matches!(error, SlotVerifyError::PublicKeyRejected(None)));
 }
 
 #[test]
@@ -629,9 +629,8 @@
     );
 
     let error = result.unwrap_err();
-    let data = match error {
-        SlotVerifyError::Verification(Some(data)) => data,
-        _ => panic!("Expected verification data to exist"),
+    let SlotVerifyError::Verification(Some(data)) = error else {
+        panic!("Expected Verification with verification data");
     };
     assert_eq!(
         format!("{data}"),
@@ -640,6 +639,56 @@
 }
 
 #[test]
+fn invalid_public_key_verification_data_provided() {
+    let mut ops = build_test_ops_one_image_one_vbmeta();
+    ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb {
+        public_key: b"not_the_key".into(),
+        public_key_metadata: None,
+    });
+
+    let result = slot_verify(
+        &mut ops,
+        &[&CString::new(TEST_PARTITION_NAME).unwrap()],
+        None,
+        SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
+        HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
+    );
+
+    let error = result.unwrap_err();
+    let SlotVerifyError::PublicKeyRejected(Some(data)) = error else {
+        panic!("Expected PublicKeyRejected with verification data");
+    };
+    assert_eq!(
+        format!("{data}"),
+        r#"slot: "", vbmeta: ["vbmeta": Ok(())], images: ["test_part": Ok(())]"#
+    );
+}
+
+#[test]
+fn invalid_rollback_index_verification_data_provided() {
+    let mut ops = build_test_ops_one_image_one_vbmeta();
+    // Device with rollback = 1 should refuse to boot image with rollback = 0.
+    ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, Ok(1));
+
+    let result = slot_verify(
+        &mut ops,
+        &[&CString::new(TEST_PARTITION_NAME).unwrap()],
+        None,
+        SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
+        HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
+    );
+
+    let error = result.unwrap_err();
+    let SlotVerifyError::RollbackIndex(Some(data)) = error else {
+        panic!("Expected RollbackIndex with verification data");
+    };
+    assert_eq!(
+        format!("{data}"),
+        r#"slot: "", vbmeta: ["vbmeta": Ok(())], images: ["test_part": Ok(())]"#
+    );
+}
+
+#[test]
 fn one_image_gives_single_descriptor() {
     let mut ops = build_test_ops_one_image_one_vbmeta();