blob: e58d3ce7f4324a0ef66cc65f9ac4d5aa20cb196c [file] [log] [blame]
Janis Danisevskis7d77a762020-07-20 13:03:31 -07001// Copyright 2020, 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//! Keystore error provides convenience methods and types for Keystore error handling.
16//! Clients of Keystore expect one of two error codes, i.e., a Keystore ResponseCode as
17//! defined by the Keystore AIDL interface, or a Keymint ErrorCode as defined by
18//! the Keymint HAL specification.
19//! This crate provides `Error` which can wrap both. It is to be used
20//! internally by Keystore to diagnose error conditions that need to be reported to
21//! the client. To report the error condition to the client the Keystore AIDL
22//! interface defines a wire type `Result` which is distinctly different from Rust's
23//! `enum Result<T,E>`.
24//!
25//! This crate provides the convenience method `map_or_log_err` to convert `anyhow::Error`
26//! into this wire type. In addition to handling the conversion of `Error`
27//! to the `Result` wire type it handles any other error by mapping it to
28//! `ResponseCode::SystemError` and logs any error condition.
29//!
30//! Keystore functions should use `anyhow::Result` to return error conditions, and
31//! context should be added every time an error is forwarded.
32
33use std::cmp::PartialEq;
34use std::convert::From;
35
36use keystore_aidl_generated as aidl;
37use keystore_aidl_generated::ResponseCode as AidlRc;
38
39pub use aidl::ResponseCode;
40
41/// AidlResult wraps the `android.security.keystore2.Result` generated from AIDL
42#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct AidlResult(aidl::Result);
44
45impl AidlResult {
46 /// Creates an instance of AidlResult indicating no error has occurred.
47 pub fn ok() -> Self {
48 Self(aidl::Result { rc: AidlRc::Ok, km_error_code: 0 })
49 }
50
51 /// Creates an instance of AidlResult indicating the given ResponseCode.
52 pub fn rc(rc: AidlRc) -> Self {
53 Self(aidl::Result { rc, km_error_code: 0 })
54 }
55
56 /// Creates an instance of AidlResult indicating the given KM ErrorCode.
57 pub fn ec(ec: aidl::ErrorCode) -> Self {
58 Self(aidl::Result { rc: AidlRc::KeymintErrorCode, km_error_code: ec })
59 }
60}
61
62/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
63/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
64#[derive(Debug, thiserror::Error, PartialEq)]
65pub enum Error {
66 /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
67 #[error("Error::Rc({0:?})")]
68 Rc(AidlRc),
69 /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
70 #[error("Error::Km({0:?})")]
71 Km(aidl::ErrorCode), // TODO Keymint ErrorCode is a generated AIDL type.
72}
73
74impl Error {
75 /// Short hand for `Error::Rc(ResponseCode::SystemError)`
76 pub fn sys() -> Self {
77 Error::Rc(AidlRc::SystemError)
78 }
79
80 /// Short hand for `Error::Rc(ResponseCode::PermissionDenied`
81 pub fn perm() -> Self {
82 Error::Rc(AidlRc::PermissionDenied)
83 }
84}
85
86impl From<anyhow::Error> for AidlResult {
87 fn from(error: anyhow::Error) -> Self {
88 let root_cause = error.root_cause();
89 match root_cause.downcast_ref::<Error>() {
90 Some(Error::Rc(rcode)) => AidlResult::rc(*rcode),
91 Some(Error::Km(ec)) => AidlResult::ec(*ec),
92 None => AidlResult::rc(AidlRc::SystemError),
93 }
94 }
95}
96
97/// This function should be used by Keystore service calls to translate error conditions
98/// into `android.security.keystore2.Result` which is imported here as `aidl::Result`
99/// and newtyped as AidlResult.
100/// All error conditions get logged by this function.
101/// All `Error::Rc(x)` variants get mapped onto `aidl::Result{x, 0}`.
102/// All `Error::Km(x)` variants get mapped onto
103/// `aidl::Result{aidl::ResponseCode::KeymintErrorCode, x}`.
104///
105/// All non `Error` error conditions get mapped onto
106/// `aidl::Result{aidl::ResponseCode::SystemError}`.
107///
108/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
109/// as argument to `handle_ok`. `handle_ok` must generate an `AidlResult`, typically
110/// `AidlResult::ok()`, but other response codes may be used, e.g.,
111/// `aidl::ResponseCode::OpAuthNeeded` which does not required logging.
112///
113/// # Examples
114///
115/// ```
116/// fn loadKey() -> anyhow::Result<aidl::ResponseCode> {
117/// if (good_but_auth_required) {
118/// Ok(aidl::ResponseCode::OpAuthRequired)
119/// } else {
120/// Err(anyhow!(Error::Rc(aidl::ResponseCode::KeyNotFound)))
121/// }
122/// }
123///
124/// aidl_result_ = map_or_log_err(loadKey(), |r| { some_side_effect(); AidlResult::rc(r) });
125/// ```
126pub fn map_or_log_err<T>(
127 result: anyhow::Result<T>,
128 handle_ok: impl FnOnce(T) -> AidlResult,
129) -> AidlResult {
130 result.map_or_else(
131 |e| {
132 log::error!("{:?}", e);
133 e.into()
134 },
135 handle_ok,
136 )
137}
138
139#[cfg(test)]
140mod tests {
141
142 use anyhow::{anyhow, Context};
143
144 use super::aidl::ErrorCode;
145 use super::*;
146
147 fn nested_nested_rc(rc: AidlRc) -> anyhow::Result<()> {
148 Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
149 }
150
151 fn nested_rc(rc: AidlRc) -> anyhow::Result<()> {
152 nested_nested_rc(rc).context("nested rc")
153 }
154
155 fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
156 Err(anyhow!(Error::Km(ec))).context("nested nested ec")
157 }
158
159 fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
160 nested_nested_ec(ec).context("nested ec")
161 }
162
163 fn nested_nested_ok(rc: AidlRc) -> anyhow::Result<AidlRc> {
164 Ok(rc)
165 }
166
167 fn nested_ok(rc: AidlRc) -> anyhow::Result<AidlRc> {
168 nested_nested_ok(rc).context("nested ok")
169 }
170
171 #[derive(Debug, thiserror::Error)]
172 enum TestError {
173 #[error("TestError::Fail")]
174 Fail = 0,
175 }
176
177 fn nested_nested_other_error() -> anyhow::Result<()> {
178 Err(anyhow!(TestError::Fail)).context("nested nested other error")
179 }
180
181 fn nested_other_error() -> anyhow::Result<()> {
182 nested_nested_other_error().context("nested other error")
183 }
184
185 #[test]
186 fn keystore_error_test() -> anyhow::Result<(), String> {
187 android_logger::init_once(
188 android_logger::Config::default()
189 .with_tag("keystore_error_tests")
190 .with_min_level(log::Level::Debug),
191 );
192 // All Error::Rc(x) get mapped on aidl::Result{x, 0}
193 assert_eq!(
194 AidlResult::rc(AidlRc::Ok),
195 map_or_log_err(nested_rc(AidlRc::Ok), |_| AidlResult::ec(0))
196 );
197 assert_eq!(
198 AidlResult::rc(AidlRc::Locked),
199 map_or_log_err(nested_rc(AidlRc::Locked), |_| AidlResult::ec(0))
200 );
201 assert_eq!(
202 AidlResult::rc(AidlRc::Uninitialized),
203 map_or_log_err(nested_rc(AidlRc::Uninitialized), |_| AidlResult::ec(0))
204 );
205 assert_eq!(
206 AidlResult::rc(AidlRc::SystemError),
207 map_or_log_err(nested_rc(AidlRc::SystemError), |_| AidlResult::ec(0))
208 );
209 assert_eq!(
210 AidlResult::rc(AidlRc::PermissionDenied),
211 map_or_log_err(nested_rc(AidlRc::PermissionDenied), |_| AidlResult::ec(0))
212 );
213 assert_eq!(
214 AidlResult::rc(AidlRc::KeyNotFound),
215 map_or_log_err(nested_rc(AidlRc::KeyNotFound), |_| AidlResult::ec(0))
216 );
217 assert_eq!(
218 AidlResult::rc(AidlRc::ValueCorrupted),
219 map_or_log_err(nested_rc(AidlRc::ValueCorrupted), |_| AidlResult::ec(0))
220 );
221 assert_eq!(
222 AidlResult::rc(AidlRc::WrongPassword),
223 map_or_log_err(nested_rc(AidlRc::WrongPassword), |_| AidlResult::ec(0))
224 );
225 assert_eq!(
226 AidlResult::rc(AidlRc::OpAuthNeeded),
227 map_or_log_err(nested_rc(AidlRc::OpAuthNeeded), |_| AidlResult::ec(0))
228 );
229 assert_eq!(
230 AidlResult::rc(AidlRc::KeyPermanentlyInvalidated),
231 map_or_log_err(nested_rc(AidlRc::KeyPermanentlyInvalidated), |_| AidlResult::ec(0))
232 );
233 assert_eq!(
234 AidlResult::rc(AidlRc::NoSuchSecurityLevel),
235 map_or_log_err(nested_rc(AidlRc::NoSuchSecurityLevel), |_| AidlResult::ec(0))
236 );
237 assert_eq!(
238 AidlResult::rc(AidlRc::KeymintErrorCode),
239 map_or_log_err(nested_rc(AidlRc::KeymintErrorCode), |_| AidlResult::ec(0))
240 );
241 assert_eq!(
242 AidlResult::rc(AidlRc::BackendBusy),
243 map_or_log_err(nested_rc(AidlRc::BackendBusy), |_| AidlResult::ec(0))
244 );
245
246 // All KeystoreKerror::Km(x) get mapped on
247 // aidl::Result{AidlRc::KeymintErrorCode, x}
248 assert_eq!(
249 AidlResult::ec(-7),
250 map_or_log_err(nested_ec(-7), |_| AidlResult::rc(AidlRc::SystemError))
251 );
252
253 // All other get mapped on System Error.
254 assert_eq!(
255 AidlResult::rc(AidlRc::SystemError),
256 map_or_log_err(nested_other_error(), |_| AidlResult::ec(0))
257 );
258
259 // Result::Ok variants get passed to the ok handler.
260 assert_eq!(
261 AidlResult::rc(AidlRc::OpAuthNeeded),
262 map_or_log_err(nested_ok(AidlRc::OpAuthNeeded), AidlResult::rc)
263 );
264 assert_eq!(AidlResult::ok(), map_or_log_err(nested_ok(AidlRc::Ok), AidlResult::rc));
265
266 Ok(())
267 }
268} // mod tests