Keystore 2.0: Implement APC service.

This patch implements the Android Protected Confirmation service in
Keystore 2.0. This includes a C++ wrapper around the HIDL confirmationui
interface which will stay a HIDL interface for now.
This patch also includes the new AIDL specification.

This patch lacks death listener registration b/176491050.

Bug: 159341464
Bug: 173546269
Test: None
Change-Id: Ida4af108e86b538ab64d1dea4809cfa3b36f74cd
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
new file mode 100644
index 0000000..105e071
--- /dev/null
+++ b/keystore2/src/apc.rs
@@ -0,0 +1,366 @@
+// 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.
+
+// TODO The confirmation token is yet unused.
+#![allow(unused_variables)]
+
+//! This module implements the Android Protected Confirmation (APC) service as defined
+//! in the android.security.apc AIDL spec.
+
+use std::{
+    cmp::PartialEq,
+    collections::HashMap,
+    sync::{Arc, Mutex},
+};
+
+use crate::utils::{compat_2_response_code, ui_opts_2_compat};
+use android_security_apc::aidl::android::security::apc::{
+    IConfirmationCallback::IConfirmationCallback,
+    IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
+    ResponseCode::ResponseCode,
+};
+use android_security_apc::binder::{
+    ExceptionCode, Interface, Result as BinderResult, SpIBinder, Status as BinderStatus,
+};
+use anyhow::{Context, Result};
+use binder::{IBinder, ThreadState};
+use keystore2_apc_compat::ApcHal;
+use keystore2_selinux as selinux;
+use std::time::{Duration, Instant};
+
+/// This is the main APC error type, it wraps binder exceptions and the
+/// APC ResponseCode.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// Wraps an Android Protected Confirmation (APC) response code as defined by the
+    /// android.security.apc AIDL interface specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+}
+
+impl Error {
+    /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
+    pub fn sys() -> Self {
+        Error::Rc(ResponseCode::SYSTEM_ERROR)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::OPERATION_PENDING)`
+    pub fn pending() -> Self {
+        Error::Rc(ResponseCode::OPERATION_PENDING)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::CANCELLED)`
+    pub fn cancelled() -> Self {
+        Error::Rc(ResponseCode::CANCELLED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::ABORTED)`
+    pub fn aborted() -> Self {
+        Error::Rc(ResponseCode::ABORTED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::IGNORED)`
+    pub fn ignored() -> Self {
+        Error::Rc(ResponseCode::IGNORED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::UNIMPLEMENTED)`
+    pub fn unimplemented() -> Self {
+        Error::Rc(ResponseCode::UNIMPLEMENTED)
+    }
+}
+
+/// This function should be used by confirmation service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
+/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions get mapped onto ResponseCode::SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
+where
+    F: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            log::error!("{:#?}", e);
+            let root_cause = e.root_cause();
+            let rc = match root_cause.downcast_ref::<Error>() {
+                Some(Error::Rc(rcode)) => rcode.0,
+                Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+                None => match root_cause.downcast_ref::<selinux::Error>() {
+                    Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+                    _ => ResponseCode::SYSTEM_ERROR.0,
+                },
+            };
+            Err(BinderStatus::new_service_specific_error(rc, None))
+        },
+        handle_ok,
+    )
+}
+
+/// Rate info records how many failed attempts a client has made to display a protected
+/// confirmation prompt. Clients are penalized for attempts that get declined by the user
+/// or attempts that get aborted by the client itself.
+///
+/// After the third failed attempt the client has to cool down for 30 seconds before it
+/// it can retry. After the sixth failed attempt, the time doubles with every failed attempt
+/// until it goes into saturation at 24h.
+///
+/// A successful user prompt resets the counter.
+#[derive(Debug, Clone)]
+struct RateInfo {
+    counter: u32,
+    timestamp: Instant,
+}
+
+impl RateInfo {
+    const ONE_DAY: Duration = Duration::from_secs(60u64 * 60u64 * 24u64);
+
+    fn get_remaining_back_off(&self) -> Option<Duration> {
+        let back_off = match self.counter {
+            // The first three attempts come without penalty.
+            0..=2 => return None,
+            // The next three attempts are are penalized with 30 seconds back off time.
+            3..=5 => Duration::from_secs(30),
+            // After that we double the back off time the with every additional attempt
+            // until we reach 1024m (~17h).
+            6..=16 => Duration::from_secs(60)
+                .checked_mul(1u32 << (self.counter - 6))
+                .unwrap_or(Self::ONE_DAY),
+            // After that we cap of at 24h between attempts.
+            _ => Self::ONE_DAY,
+        };
+        let elapsed = self.timestamp.elapsed();
+        // This does exactly what we want.
+        // `back_off - elapsed` is the remaining back off duration or None if elapsed is larger
+        // than back_off. Also, this operation cannot overflow as long as elapsed is less than
+        // back_off, which is all that we care about.
+        back_off.checked_sub(elapsed)
+    }
+}
+
+impl Default for RateInfo {
+    fn default() -> Self {
+        Self { counter: 0u32, timestamp: Instant::now() }
+    }
+}
+
+/// The APC session state represents the state of an APC session.
+struct ApcSessionState {
+    /// A reference to the APC HAL backend.
+    hal: Arc<ApcHal>,
+    /// The client callback object.
+    cb: SpIBinder,
+    /// The uid of the owner of this APC session.
+    uid: u32,
+    /// The time when this session was started.
+    start: Instant,
+    /// This is set when the client calls abort.
+    /// This is used by the rate limiting logic to determine
+    /// if the client needs to be penalized for this attempt.
+    client_aborted: bool,
+}
+
+#[derive(Default)]
+struct ApcState {
+    session: Option<ApcSessionState>,
+    rate_limiting: HashMap<u32, RateInfo>,
+}
+
+/// Implementation of the APC service.
+pub struct ApcManager {
+    state: Arc<Mutex<ApcState>>,
+}
+
+impl Interface for ApcManager {}
+
+impl ApcManager {
+    /// Create a new instance of the Android Protected Confirmation service.
+    pub fn new_native_binder() -> Result<impl IProtectedConfirmation> {
+        let result = BnProtectedConfirmation::new_binder(Self {
+            state: Arc::new(Mutex::new(Default::default())),
+        });
+        result.as_binder().set_requesting_sid(true);
+        Ok(result)
+    }
+
+    fn result(
+        state: Arc<Mutex<ApcState>>,
+        rc: u32,
+        data_confirmed: Option<&[u8]>,
+        confirmation_token: Option<&[u8]>,
+    ) {
+        let mut state = state.lock().unwrap();
+        let (callback, uid, start, client_aborted) = match state.session.take() {
+            None => return, // Nothing to do
+            Some(ApcSessionState { cb: callback, uid, start, client_aborted, .. }) => {
+                (callback, uid, start, client_aborted)
+            }
+        };
+
+        let rc = compat_2_response_code(rc);
+
+        // Update rate limiting information.
+        match (rc, client_aborted) {
+            // If the user confirmed the dialog.
+            (ResponseCode::OK, _) => {
+                // Reset counter.
+                state.rate_limiting.remove(&uid);
+                // TODO at this point we need to send the confirmation token to where keystore can
+                // use it.
+            }
+            // If cancelled by the user or if aborted by the client.
+            (ResponseCode::CANCELLED, _) | (ResponseCode::ABORTED, true) => {
+                // Penalize.
+                let mut rate_info = state.rate_limiting.entry(uid).or_default();
+                rate_info.counter += 1;
+                rate_info.timestamp = start;
+            }
+            // In any other case this try does not count at all.
+            _ => {}
+        }
+        drop(state);
+
+        if let Ok(listener) = callback.into_interface::<dyn IConfirmationCallback>() {
+            if let Err(e) = listener.onCompleted(rc, data_confirmed) {
+                log::error!(
+                    "In ApcManagerCallback::result: Reporting completion to client failed {:?}",
+                    e
+                )
+            }
+        } else {
+            log::error!("In ApcManagerCallback::result: SpIBinder is not a IConfirmationCallback.");
+        }
+    }
+
+    fn present_prompt(
+        &self,
+        listener: &dyn IConfirmationCallback,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> Result<()> {
+        let mut state = self.state.lock().unwrap();
+        if state.session.is_some() {
+            return Err(Error::pending())
+                .context("In ApcManager::present_prompt: Session pending.");
+        }
+
+        // Perform rate limiting.
+        let uid = ThreadState::get_calling_uid();
+        match state.rate_limiting.get(&uid) {
+            None => {}
+            Some(rate_info) => {
+                if let Some(back_off) = rate_info.get_remaining_back_off() {
+                    return Err(Error::sys()).context(format!(
+                        "In ApcManager::present_prompt: Cooling down. Remaining back-off: {}s",
+                        back_off.as_secs()
+                    ));
+                }
+            }
+        }
+
+        let hal = ApcHal::try_get_service();
+        let hal = match hal {
+            None => {
+                return Err(Error::unimplemented())
+                    .context("In ApcManager::present_prompt: APC not supported.")
+            }
+            Some(h) => Arc::new(h),
+        };
+
+        let ui_opts = ui_opts_2_compat(ui_option_flags);
+
+        let state_clone = self.state.clone();
+        hal.prompt_user_confirmation(
+            prompt_text,
+            extra_data,
+            locale,
+            ui_opts,
+            |rc, data_confirmed, confirmation_token| {
+                Self::result(state_clone, rc, data_confirmed, confirmation_token)
+            },
+        )
+        .map_err(|rc| Error::Rc(compat_2_response_code(rc)))
+        .context("In present_prompt: Failed to present prompt.")?;
+        state.session = Some(ApcSessionState {
+            hal,
+            cb: listener.as_binder(),
+            uid,
+            start: Instant::now(),
+            client_aborted: false,
+        });
+        Ok(())
+    }
+
+    fn cancel_prompt(&self, listener: &dyn IConfirmationCallback) -> Result<()> {
+        let mut state = self.state.lock().unwrap();
+        let hal = match &mut state.session {
+            None => {
+                return Err(Error::ignored())
+                    .context("In cancel_prompt: Attempt to cancel non existing session. Ignoring.")
+            }
+            Some(session) => {
+                if session.cb != listener.as_binder() {
+                    return Err(Error::ignored()).context(concat!(
+                        "In cancel_prompt: Attempt to cancel session not belonging to caller. ",
+                        "Ignoring."
+                    ));
+                }
+                session.client_aborted = true;
+                session.hal.clone()
+            }
+        };
+        drop(state);
+        hal.abort();
+        Ok(())
+    }
+
+    fn is_supported() -> Result<bool> {
+        Ok(ApcHal::try_get_service().is_some())
+    }
+}
+
+impl IProtectedConfirmation for ApcManager {
+    fn presentPrompt(
+        &self,
+        listener: &dyn IConfirmationCallback,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> BinderResult<()> {
+        map_or_log_err(
+            self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
+            Ok,
+        )
+    }
+    fn cancelPrompt(&self, listener: &dyn IConfirmationCallback) -> BinderResult<()> {
+        map_or_log_err(self.cancel_prompt(listener), Ok)
+    }
+    fn isSupported(&self) -> BinderResult<bool> {
+        map_or_log_err(Self::is_supported(), Ok)
+    }
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 2916549..59e5972 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -15,11 +15,13 @@
 //! This crate implements the Keystore 2.0 service entry point.
 
 use binder::Interface;
+use keystore2::apc::ApcManager;
 use keystore2::service::KeystoreService;
 use log::{error, info};
 use std::panic;
 
 static KS2_SERVICE_NAME: &str = "android.system.keystore2";
+static APC_SERVICE_NAME: &str = "android.security.apc";
 
 /// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
 fn main() {
@@ -55,6 +57,13 @@
         panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
     });
 
+    let apc_service = ApcManager::new_native_binder().unwrap_or_else(|e| {
+        panic!("Failed to create service {} because of {:?}.", APC_SERVICE_NAME, e);
+    });
+    binder::add_service(APC_SERVICE_NAME, apc_service.as_binder()).unwrap_or_else(|e| {
+        panic!("Failed to register service {} because of {:?}.", APC_SERVICE_NAME, e);
+    });
+
     info!("Successfully registered Keystore 2.0 service.");
 
     info!("Starting thread pool now.");
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 5ad77d1..3fb938c 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -14,6 +14,7 @@
 
 //! This crate implements the Android Keystore 2.0 service.
 
+pub mod apc;
 pub mod auth_token_handler;
 pub mod database;
 pub mod enforcements;
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index d50f70e..eab9b4d 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -25,11 +25,20 @@
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     KeyCharacteristics::KeyCharacteristics, SecurityLevel::SecurityLevel,
 };
+use android_security_apc::aidl::android::security::apc::{
+    IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
+    ResponseCode::ResponseCode as ApcResponseCode,
+};
 use android_system_keystore2::aidl::android::system::keystore2::{
     Authorization::Authorization, KeyDescriptor::KeyDescriptor,
 };
 use anyhow::{anyhow, Context};
 use binder::{FromIBinder, SpIBinder, ThreadState};
+use keystore2_apc_compat::{
+    ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
+    APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
+    APC_COMPAT_ERROR_SYSTEM_ERROR,
+};
 use std::convert::TryFrom;
 use std::sync::Mutex;
 
@@ -146,6 +155,31 @@
     i64::try_from(current_time.tv_sec).unwrap()
 }
 
+/// Converts a response code as returned by the Android Protected Confirmation HIDL compatibility
+/// module (keystore2_apc_compat) into a ResponseCode as defined by the APC AIDL
+/// (android.security.apc) spec.
+pub fn compat_2_response_code(rc: u32) -> ApcResponseCode {
+    match rc {
+        APC_COMPAT_ERROR_OK => ApcResponseCode::OK,
+        APC_COMPAT_ERROR_CANCELLED => ApcResponseCode::CANCELLED,
+        APC_COMPAT_ERROR_ABORTED => ApcResponseCode::ABORTED,
+        APC_COMPAT_ERROR_OPERATION_PENDING => ApcResponseCode::OPERATION_PENDING,
+        APC_COMPAT_ERROR_IGNORED => ApcResponseCode::IGNORED,
+        APC_COMPAT_ERROR_SYSTEM_ERROR => ApcResponseCode::SYSTEM_ERROR,
+        _ => ApcResponseCode::SYSTEM_ERROR,
+    }
+}
+
+/// Converts the UI Options flags as defined by the APC AIDL (android.security.apc) spec into
+/// UI Options flags as defined by the Android Protected Confirmation HIDL compatibility
+/// module (keystore2_apc_compat).
+pub fn ui_opts_2_compat(opt: i32) -> ApcCompatUiOptions {
+    ApcCompatUiOptions {
+        inverted: (opt & FLAG_UI_OPTION_INVERTED) != 0,
+        magnified: (opt & FLAG_UI_OPTION_MAGNIFIED) != 0,
+    }
+}
+
 /// AID offset for uid space partitioning.
 /// TODO: Replace with bindgen generated from libcutils. b/175619259
 pub const AID_USER_OFFSET: u32 = 100000;