// Copyright 2020, 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 handling tests. use super::*; use android_system_keystore2::binder::{ ExceptionCode, Result as BinderResult, Status as BinderStatus, }; use anyhow::{anyhow, Context}; fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> { Err(anyhow!(Error::Rc(rc))).context("nested nested rc") } fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> { nested_nested_rc(rc).context("nested rc") } fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> { Err(anyhow!(Error::Km(ec))).context("nested nested ec") } fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> { nested_nested_ec(ec).context("nested ec") } fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result { Ok(rc) } fn nested_ok(rc: ResponseCode) -> anyhow::Result { nested_nested_ok(rc).context("nested ok") } fn nested_nested_selinux_perm() -> anyhow::Result<()> { Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied") } fn nested_selinux_perm() -> anyhow::Result<()> { nested_nested_selinux_perm().context("nested selinux permission denied") } #[derive(Debug, thiserror::Error)] enum TestError { #[error("TestError::Fail")] Fail = 0, } fn nested_nested_other_error() -> anyhow::Result<()> { Err(anyhow!(TestError::Fail)).context("nested nested other error") } fn nested_other_error() -> anyhow::Result<()> { nested_nested_other_error().context("nested other error") } fn binder_sse_error(sse: i32) -> BinderResult<()> { Err(BinderStatus::new_service_specific_error(sse, None)) } fn binder_exception(ex: ExceptionCode) -> BinderResult<()> { Err(BinderStatus::new_exception(ex, None)) } #[test] fn keystore_error_test() -> anyhow::Result<(), String> { android_logger::init_once( android_logger::Config::default() .with_tag("keystore_error_tests") .with_max_level(log::LevelFilter::Debug), ); // All Error::Rc(x) get mapped on a service specific error // code of x. for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 { assert_eq!( Result::<(), i32>::Err(rc), nested_rc(ResponseCode(rc)) .map_err(into_logged_binder) .map_err(|s| s.service_specific_error()) ); } // All Keystore Error::Km(x) get mapped on a service // specific error of x. for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 { assert_eq!( Result::<(), i32>::Err(ec), nested_ec(ErrorCode(ec)) .map_err(into_logged_binder) .map_err(|s| s.service_specific_error()) ); } // All Keymint errors x received through a Binder Result get mapped on // a service specific error of x. for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 { assert_eq!( Result::<(), i32>::Err(ec), map_km_error(binder_sse_error(ec)) .with_context(|| format!("Km error code: {}.", ec)) .map_err(into_logged_binder) .map_err(|s| s.service_specific_error()) ); } // map_km_error creates an Error::Binder variant storing // ExceptionCode::SERVICE_SPECIFIC and the given // service specific error. let sse = map_km_error(binder_sse_error(1)); assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse); // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR. assert_eq!( Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR), sse.context("Non negative service specific error.") .map_err(into_logged_binder) .map_err(|s| ResponseCode(s.service_specific_error())) ); // map_km_error creates a Error::Binder variant storing the given exception code. let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED)); assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception); // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR. assert_eq!( Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR), binder_exception .context("Binder Exception.") .map_err(into_logged_binder) .map_err(|s| ResponseCode(s.service_specific_error())) ); // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED assert_eq!( Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED), nested_selinux_perm() .map_err(into_logged_binder) .map_err(|s| ResponseCode(s.service_specific_error())) ); // All other errors get mapped on System Error. assert_eq!( Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR), nested_other_error() .map_err(into_logged_binder) .map_err(|s| ResponseCode(s.service_specific_error())) ); // Result::Ok variants get passed to the ok handler. assert_eq!( Ok(ResponseCode::LOCKED), nested_ok(ResponseCode::LOCKED).map_err(into_logged_binder) ); assert_eq!( Ok(ResponseCode::SYSTEM_ERROR), nested_ok(ResponseCode::SYSTEM_ERROR).map_err(into_logged_binder) ); Ok(()) } //Helper function to test whether error cases are handled as expected. pub fn check_result_contains_error_string( result: anyhow::Result, expected_error_string: &str, ) { let error_str = format!( "{:#?}", result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string)) ); assert!( error_str.contains(expected_error_string), "The string \"{}\" should contain \"{}\"", error_str, expected_error_string ); } #[test] fn rkpd_error_is_in_sync_with_response_code() { let error_mapping = [ (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR), (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR), ( RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN), ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR, ), ( RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT), ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR, ), ( RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY), ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY, ), ( RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH), ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE, ), (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR), (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR), (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR), ]; for (rkpd_error, expected_response_code) in error_mapping { let e: Error = rkpd_error.into(); assert_eq!(e, Error::Rc(expected_response_code)); } }