blob: d50091b0da653546f261b2abf7cf90e6ea43a6ad [file] [log] [blame]
// 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<ResponseCode> {
Ok(rc)
}
fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
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<T>(
result: anyhow::Result<T>,
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));
}
}