Skip ab/6749736 in stage.
Merged-In: I3c023063eff938812d7001d621d027b81572f4c8
Change-Id: Ied5cd2adff15c89a895bf751cecb047267931ce4
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 68df61f..9b96f36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,7 +4,7 @@
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
-rustfmt = --check --config-path=rustfmt.toml ${PREUPLOAD_FILES}
+rustfmt = --config-path=rustfmt.toml
[Hook Scripts]
aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/keystore/KeyStore.cpp b/keystore/KeyStore.cpp
index 7545397..1f80899 100644
--- a/keystore/KeyStore.cpp
+++ b/keystore/KeyStore.cpp
@@ -60,8 +60,8 @@
"KmasterDevices and KeymasterWorkers must have the same size");
for (size_t i = 0; i < kmDevices.size(); ++i) {
if (kmDevices[SecurityLevel(i)]) {
- mKmDevices[SecurityLevel(i)] =
- std::make_shared<KeymasterWorker>(kmDevices[SecurityLevel(i)], this);
+ mKmDevices[SecurityLevel(i)] = std::make_shared<KeymasterWorker>(
+ kmDevices[SecurityLevel(i)], this, SecurityLevel(i));
}
}
}
diff --git a/keystore/TEST_MAPPING b/keystore/TEST_MAPPING
new file mode 100644
index 0000000..0511967
--- /dev/null
+++ b/keystore/TEST_MAPPING
@@ -0,0 +1,74 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsKeystoreTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.RequiresDevice"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.SignatureTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.RsaSignaturePerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.RsaKeyGenPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.RsaCipherPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.MacTest#testLargeMsgKat"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.KeyPairGeneratorTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.KeyGeneratorTest#testHmacKeySupportedSizes"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.HmacMacPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.EcdsaSignaturePerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.EcKeyGenPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DesCipherPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.CipherTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AttestationPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AndroidKeyStoreTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AesCipherPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AESCipherNistCavpKatTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DESedeECBNoPaddingCipherTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsKeystoreTestCases"
+ }
+ ]
+}
diff --git a/keystore/key_attestation_log_handler.cpp b/keystore/key_attestation_log_handler.cpp
index 34c76a3..c3278cb 100644
--- a/keystore/key_attestation_log_handler.cpp
+++ b/keystore/key_attestation_log_handler.cpp
@@ -17,7 +17,11 @@
namespace keystore {
void logKeystoreKeyAttestationEvent(bool wasSuccessful, int32_t errorCode) {
- android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED,
+ // Due to a requirement in stats-write() method, the optional fields
+ // which are not required for attestation logging, are marked with -1 for
+ // non-repeated fields and 0 for repeated fields.
+ android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED, -1, -1, -1, -1, -1, 0, 0,
+ 0, 0, -1, -1,
android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_ATTESTATION,
wasSuccessful, errorCode);
}
diff --git a/keystore/keymaster_worker.cpp b/keystore/keymaster_worker.cpp
index 7481a1e..cbb184c 100644
--- a/keystore/keymaster_worker.cpp
+++ b/keystore/keymaster_worker.cpp
@@ -83,8 +83,10 @@
}
}
-KeymasterWorker::KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore)
- : keymasterDevice_(std::move(keymasterDevice)), operationMap_(keyStore), keyStore_(keyStore) {
+KeymasterWorker::KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore,
+ SecurityLevel internalSecurityLevel)
+ : keymasterDevice_(std::move(keymasterDevice)), operationMap_(keyStore), keyStore_(keyStore),
+ internalSecurityLevel_(internalSecurityLevel) {
// make sure that hal version is cached.
if (keymasterDevice_) keymasterDevice_->halVersion();
}
@@ -821,7 +823,7 @@
outCharacteristics = keyCharacteristics;
Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(securityLevel);
+ keyBlob.setSecurityLevel(internalSecurityLevel_);
keyBlob.setCriticalToDeviceEncryption(flags &
KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
@@ -929,7 +931,7 @@
outCharacteristics = keyCharacteristics;
Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(securityLevel);
+ keyBlob.setSecurityLevel(internalSecurityLevel_);
keyBlob.setCriticalToDeviceEncryption(flags &
KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
@@ -1004,8 +1006,6 @@
CAPTURE_MOVE(worker_cb)]() mutable {
auto hidlWrappingKey = blob2hidlVec(wrappingBlob);
- SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
KeyCharacteristics outCharacteristics;
KeyStoreServiceReturnCode error;
@@ -1019,7 +1019,7 @@
outCharacteristics = keyCharacteristics;
Blob keyBlob(hidlKeyBlob.data(), hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(securityLevel);
+ keyBlob.setSecurityLevel(internalSecurityLevel_);
if (isAuthenticationBound(keyCharacteristics.hardwareEnforced)) {
keyBlob.setSuperEncrypted(true);
}
diff --git a/keystore/keymaster_worker.h b/keystore/keymaster_worker.h
index f11af29..fbd52b4 100644
--- a/keystore/keymaster_worker.h
+++ b/keystore/keymaster_worker.h
@@ -135,6 +135,19 @@
OperationMap operationMap_;
KeyStore* keyStore_;
+ /**
+ * Models the security level of this worker internal to KeyStore.
+ *
+ * When the device has only a software Keymaster, KeyStore will set it on the TEE slot and
+ * instantiate a new in-process software Keymaster. In that case there is a mismatch between the
+ * security level used by KeyStore and what is reported from the HAL. This represents the level
+ * used internally by KeyStore.
+ *
+ * This value is used to associate blobs to the corresponding Keymaster backend. It does not
+ * indicate an actual Keymaster HAL security level and should never be exposed to users.
+ */
+ SecurityLevel internalSecurityLevel_;
+
template <typename KMFn, typename ErrorType, typename... Args, size_t... I>
void unwrap_tuple(KMFn kmfn, std::function<void(ErrorType)> cb,
const std::tuple<Args...>& tuple, std::index_sequence<I...>) {
@@ -200,7 +213,8 @@
hidl_vec<KeyParameter>* params);
public:
- KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore);
+ KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore,
+ SecurityLevel internalSecurityLevel);
void logIfKeymasterVendorError(ErrorCode ec) const;
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index bf78574..f4b153c 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -18,24 +18,37 @@
srcs: ["src/lib.rs"],
rustlibs: [
+ "android.system.keystore2-rust",
+ "libandroid_hardware_keymint",
"libanyhow",
- "libkeystore_aidl_generated",
+ "libbinder_rs",
+ "libkeystore2_selinux",
+ "liblazy_static",
+ "liblibsqlite3_sys",
"liblog_rust",
+ "librand",
+ "librusqlite",
"libthiserror",
],
}
rust_test {
name: "keystore2_test",
- crate_name: "keystore2_test",
+ crate_name: "keystore2",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
rustlibs: [
+ "android.system.keystore2-rust",
"libandroid_logger",
+ "libandroid_hardware_keymint",
"libanyhow",
- "libkeystore_aidl_generated",
+ "libbinder_rs",
+ "libkeystore2_selinux",
+ "liblazy_static",
+ "liblibsqlite3_sys",
"liblog_rust",
+ "librusqlite",
"libthiserror",
],
}
@@ -43,8 +56,13 @@
// This is a placeholder for the libraries that will be generated from the AIDL specs
// eventually.
rust_library {
- name: "libkeystore_aidl_generated",
- crate_name: "keystore_aidl_generated",
+ name: "libandroid_hardware_keymint",
+ crate_name: "android_hardware_keymint",
- srcs: ["src/aidl_generated.rs"],
+ srcs: ["src/android_hardware_keymint.rs"],
+
+ rustlibs: [
+ "libbinder_rs",
+ "liblazy_static",
+ ],
}
diff --git a/keystore2/selinux/Android.bp b/keystore2/selinux/Android.bp
new file mode 100644
index 0000000..acbf5ef
--- /dev/null
+++ b/keystore2/selinux/Android.bp
@@ -0,0 +1,54 @@
+// 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.
+
+rust_library {
+ name: "libkeystore2_selinux",
+ crate_name: "keystore2_selinux",
+ srcs: [
+ "src/lib.rs",
+ ],
+
+ shared_libs: [
+ "libselinux",
+ ],
+
+ rustlibs: [
+ "libanyhow",
+ "liblog_rust",
+ "libselinux_bindgen",
+ "libthiserror",
+ ],
+}
+
+rust_test {
+ name: "keystore2_selinux_test",
+ srcs: [
+ "src/lib.rs",
+ ],
+ crate_name: "keystore2_selinux_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+
+ shared_libs: [
+ "libselinux",
+ ],
+
+ rustlibs: [
+ "libandroid_logger",
+ "libanyhow",
+ "liblog_rust",
+ "libselinux_bindgen",
+ "libthiserror",
+ ],
+}
diff --git a/keystore2/selinux/TEST_MAPPING b/keystore2/selinux/TEST_MAPPING
new file mode 100644
index 0000000..0e68257
--- /dev/null
+++ b/keystore2/selinux/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "keystore2_selinux_test"
+ }
+ ]
+}
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
new file mode 100644
index 0000000..932c30e
--- /dev/null
+++ b/keystore2/selinux/src/lib.rs
@@ -0,0 +1,473 @@
+// 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.
+
+// #![allow(missing_docs)]
+
+//! This crate provides some safe wrappers around the libselinux API. It is currently limited
+//! to the API surface that Keystore 2.0 requires to perform permission checks against
+//! the SEPolicy. Notably, it provides wrappers for:
+//! * getcon
+//! * selinux_check_access
+//! * selabel_lookup for the keystore2_key backend.
+//! And it provides an owning wrapper around context strings `Context`.
+
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::io;
+use std::marker::{Send, Sync};
+pub use std::ops::Deref;
+use std::os::raw::c_char;
+use std::ptr;
+use std::sync;
+
+use selinux_bindgen as selinux;
+
+use anyhow::Context as AnyhowContext;
+use anyhow::{anyhow, Result};
+
+use selinux::SELABEL_CTX_ANDROID_KEYSTORE2_KEY;
+use selinux::SELINUX_CB_LOG;
+
+pub use selinux::pid_t;
+
+static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
+
+fn redirect_selinux_logs_to_logcat() {
+ // `selinux_set_callback` assigns the static lifetime function pointer
+ // `selinux_log_callback` to a static lifetime variable.
+ let cb = selinux::selinux_callback { func_log: Some(selinux::selinux_log_callback) };
+ unsafe {
+ selinux::selinux_set_callback(SELINUX_CB_LOG as i32, cb);
+ }
+}
+
+// This function must be called before any entry point into lib selinux.
+// Or leave a comment reasoning why calling this macro is not necessary
+// for a given entry point.
+fn init_logger_once() {
+ SELINUX_LOG_INIT.call_once(redirect_selinux_logs_to_logcat)
+}
+
+/// Selinux Error code.
+#[derive(thiserror::Error, Debug, PartialEq)]
+pub enum Error {
+ /// Indicates that an access check yielded no access.
+ #[error("Permission Denied")]
+ PermissionDenied,
+ /// Indicates an unexpected system error. Nested string provides some details.
+ #[error("Selinux SystemError: {0}")]
+ SystemError(String),
+}
+
+impl Error {
+ /// Constructs a `PermissionDenied` error.
+ pub fn perm() -> Self {
+ Error::PermissionDenied
+ }
+ fn sys<T: Into<String>>(s: T) -> Self {
+ Error::SystemError(s.into())
+ }
+}
+
+/// Context represents an SELinux context string. It can take ownership of a raw
+/// s-string as allocated by `getcon` or `selabel_lookup`. In this case it uses
+/// `freecon` to free the resources when dropped. In its second variant it stores
+/// an `std::ffi::CString` that can be initialized from a Rust string slice.
+#[derive(Debug)]
+pub enum Context {
+ /// Wraps a raw context c-string as returned by libselinux.
+ Raw(*mut ::std::os::raw::c_char),
+ /// Stores a context string as `std::ffi::CString`.
+ CString(CString),
+}
+
+impl PartialEq for Context {
+ fn eq(&self, other: &Self) -> bool {
+ // We dereference both and thereby delegate the comparison
+ // to `CStr`'s implementation of `PartialEq`.
+ **self == **other
+ }
+}
+
+impl Eq for Context {}
+
+impl fmt::Display for Context {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", (**self).to_str().unwrap_or("Invalid context"))
+ }
+}
+
+impl Drop for Context {
+ fn drop(&mut self) {
+ if let Self::Raw(p) = self {
+ // No need to initialize the logger here, because
+ // `freecon` cannot run unless `Backend::lookup` or `getcon`
+ // has run.
+ unsafe { selinux::freecon(*p) };
+ }
+ }
+}
+
+impl Deref for Context {
+ type Target = CStr;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
+ Self::CString(cstr) => &cstr,
+ }
+ }
+}
+
+impl Context {
+ /// Initializes the `Context::CString` variant from a Rust string slice.
+ pub fn new(con: &str) -> Result<Self> {
+ Ok(Self::CString(
+ CString::new(con)
+ .with_context(|| format!("Failed to create Context with \"{}\"", con))?,
+ ))
+ }
+}
+
+/// The backend trait provides a uniform interface to all libselinux context backends.
+/// Currently, we only implement the KeystoreKeyBackend though.
+pub trait Backend {
+ /// Implementers use libselinux `selabel_lookup` to lookup the context for the given `key`.
+ fn lookup(&self, key: &str) -> Result<Context>;
+}
+
+/// Keystore key backend takes onwnership of the SELinux context handle returned by
+/// `selinux_android_keystore2_key_context_handle` and uses `selabel_close` to free
+/// the handle when dropped.
+/// It implements `Backend` to provide keystore_key label lookup functionality.
+pub struct KeystoreKeyBackend {
+ handle: *mut selinux::selabel_handle,
+}
+
+// KeystoreKeyBackend is Sync because selabel_lookup is thread safe.
+unsafe impl Sync for KeystoreKeyBackend {}
+unsafe impl Send for KeystoreKeyBackend {}
+
+impl KeystoreKeyBackend {
+ const BACKEND_TYPE: i32 = SELABEL_CTX_ANDROID_KEYSTORE2_KEY as i32;
+
+ /// Creates a new instance representing an SELinux context handle as returned by
+ /// `selinux_android_keystore2_key_context_handle`.
+ pub fn new() -> Result<Self> {
+ init_logger_once();
+ let handle = unsafe { selinux::selinux_android_keystore2_key_context_handle() };
+ if handle.is_null() {
+ return Err(anyhow!(Error::sys("Failed to open KeystoreKeyBackend")));
+ }
+ Ok(KeystoreKeyBackend { handle })
+ }
+}
+
+impl Drop for KeystoreKeyBackend {
+ fn drop(&mut self) {
+ // No need to initialize the logger here because it cannot be called unless
+ // KeystoreKeyBackend::new has run.
+ unsafe { selinux::selabel_close(self.handle) };
+ }
+}
+
+// Because KeystoreKeyBackend is Sync and Send, member function must never call
+// non thread safe libselinux functions. As of this writing no non thread safe
+// functions exist that could be called on a label backend handle.
+impl Backend for KeystoreKeyBackend {
+ fn lookup(&self, key: &str) -> Result<Context> {
+ let mut con: *mut c_char = ptr::null_mut();
+ let c_key = CString::new(key).with_context(|| {
+ format!("selabel_lookup: Failed to convert key \"{}\" to CString.", key)
+ })?;
+ match unsafe {
+ // No need to initialize the logger here because it cannot run unless
+ // KeystoreKeyBackend::new has run.
+ selinux::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_TYPE)
+ } {
+ 0 => {
+ if !con.is_null() {
+ Ok(Context::Raw(con))
+ } else {
+ Err(anyhow!(Error::sys(format!(
+ "selabel_lookup returned a NULL context for key \"{}\"",
+ key
+ ))))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error()))
+ .with_context(|| format!("selabel_lookup failed for key \"{}\"", key)),
+ }
+ }
+}
+
+/// Safe wrapper around libselinux `getcon`. It initializes the `Context::Raw` variant of the
+/// returned `Context`.
+///
+/// ## Return
+/// * Ok(Context::Raw()) if successful.
+/// * Err(Error::sys()) if getcon succeeded but returned a NULL pointer.
+/// * Err(io::Error::last_os_error()) if getcon failed.
+pub fn getcon() -> Result<Context> {
+ init_logger_once();
+ let mut con: *mut c_char = ptr::null_mut();
+ match unsafe { selinux::getcon(&mut con) } {
+ 0 => {
+ if !con.is_null() {
+ Ok(Context::Raw(con))
+ } else {
+ Err(anyhow!(Error::sys("getcon returned a NULL context")))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error())).context("getcon failed"),
+ }
+}
+
+/// Safe wrapper around libselinux `getpidcon`. It initializes the `Context::Raw` variant of the
+/// returned `Context`.
+///
+/// ## Return
+/// * Ok(Context::Raw()) if successful.
+/// * Err(Error::sys()) if getpidcon succeeded but returned a NULL pointer.
+/// * Err(io::Error::last_os_error()) if getpidcon failed.
+pub fn getpidcon(pid: selinux::pid_t) -> Result<Context> {
+ init_logger_once();
+ let mut con: *mut c_char = ptr::null_mut();
+ match unsafe { selinux::getpidcon(pid, &mut con) } {
+ 0 => {
+ if !con.is_null() {
+ Ok(Context::Raw(con))
+ } else {
+ Err(anyhow!(Error::sys(format!(
+ "getpidcon returned a NULL context for pid {}",
+ pid
+ ))))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error()))
+ .context(format!("getpidcon failed for pid {}", pid)),
+ }
+}
+
+/// Safe wrapper around selinux_check_access.
+///
+/// ## Return
+/// * Ok(()) iff the requested access was granted.
+/// * Err(anyhow!(Error::perm()))) if the permission was denied.
+/// * Err(anyhow!(ioError::last_os_error())) if any other error occurred while performing
+/// the access check.
+pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
+ init_logger_once();
+ let c_tclass = CString::new(tclass).with_context(|| {
+ format!("check_access: Failed to convert tclass \"{}\" to CString.", tclass)
+ })?;
+ let c_perm = CString::new(perm).with_context(|| {
+ format!("check_access: Failed to convert perm \"{}\" to CString.", perm)
+ })?;
+
+ match unsafe {
+ selinux::selinux_check_access(
+ source.as_ptr(),
+ target.as_ptr(),
+ c_tclass.as_ptr(),
+ c_perm.as_ptr(),
+ ptr::null_mut(),
+ )
+ } {
+ 0 => Ok(()),
+ _ => {
+ let e = io::Error::last_os_error();
+ match e.kind() {
+ io::ErrorKind::PermissionDenied => Err(anyhow!(Error::perm())),
+ _ => Err(anyhow!(e)),
+ }
+ .with_context(|| {
+ format!(
+ concat!(
+ "check_access: Failed with sctx: {:?} tctx: {:?}",
+ " with target class: \"{}\" perm: \"{}\""
+ ),
+ source, target, tclass, perm
+ )
+ })
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use anyhow::Result;
+
+ /// The su_key namespace as defined in su.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ static SU_KEY_NAMESPACE: &str = "0";
+ /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ static SHELL_KEY_NAMESPACE: &str = "1";
+
+ fn check_context() -> Result<(Context, &'static str, bool)> {
+ let context = getcon()?;
+ match context.to_str().unwrap() {
+ "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+ "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+ c => Err(anyhow!(format!(
+ "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+ c
+ ))),
+ }
+ }
+
+ #[test]
+ fn test_getcon() -> Result<()> {
+ check_context()?;
+ Ok(())
+ }
+
+ #[test]
+ fn test_label_lookup() -> Result<()> {
+ let (_context, namespace, is_su) = check_context()?;
+ let backend = crate::KeystoreKeyBackend::new()?;
+ let context = backend.lookup(namespace)?;
+ if is_su {
+ assert_eq!(context.to_str(), Ok("u:object_r:su_key:s0"));
+ } else {
+ assert_eq!(context.to_str(), Ok("u:object_r:shell_key:s0"));
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn context_from_string() -> Result<()> {
+ let tctx = Context::new("u:object_r:keystore:s0").unwrap();
+ let sctx = Context::new("u:r:system_server:s0").unwrap();
+ check_access(&sctx, &tctx, "keystore2_key", "use")?;
+ Ok(())
+ }
+
+ mod perm {
+ use super::super::*;
+ use super::*;
+ use anyhow::Result;
+
+ /// check_key_perm(perm, privileged, priv_domain)
+ /// `perm` is a permission of the keystore2_key class and `privileged` is a boolean
+ /// indicating whether the permission is considered privileged.
+ /// Privileged permissions are expected to be denied to `shell` users but granted
+ /// to the given priv_domain.
+ macro_rules! check_key_perm {
+ // "use" is a keyword and cannot be used as an identifier, but we must keep
+ // the permission string intact. So we map the identifier name on use_ while using
+ // the permission string "use". In all other cases we can simply use the stringified
+ // identifier as permission string.
+ (use, $privileged:expr) => {
+ check_key_perm!(use_, $privileged, "use");
+ };
+ ($perm:ident, $privileged:expr) => {
+ check_key_perm!($perm, $privileged, stringify!($perm));
+ };
+ ($perm:ident, $privileged:expr, $p_str:expr) => {
+ #[test]
+ fn $perm() -> Result<()> {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore_selinux_tests")
+ .with_min_level(log::Level::Debug),
+ );
+ let scontext = Context::new("u:r:shell:s0")?;
+ let backend = KeystoreKeyBackend::new()?;
+ let tcontext = backend.lookup(SHELL_KEY_NAMESPACE)?;
+
+ if $privileged {
+ assert_eq!(
+ Some(&Error::perm()),
+ check_access(
+ &scontext,
+ &tcontext,
+ "keystore2_key",
+ $p_str
+ )
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<Error>()
+ );
+ } else {
+ assert!(check_access(
+ &scontext,
+ &tcontext,
+ "keystore2_key",
+ $p_str
+ )
+ .is_ok());
+ }
+ Ok(())
+ }
+ };
+ }
+
+ check_key_perm!(manage_blob, true);
+ check_key_perm!(delete, false);
+ check_key_perm!(use_dev_id, true);
+ check_key_perm!(req_forced_op, true);
+ check_key_perm!(gen_unique_id, true);
+ check_key_perm!(grant, true);
+ check_key_perm!(get_info, false);
+ check_key_perm!(rebind, false);
+ check_key_perm!(update, false);
+ check_key_perm!(use, false);
+
+ macro_rules! check_keystore_perm {
+ ($perm:ident) => {
+ #[test]
+ fn $perm() -> Result<()> {
+ let ks_context = Context::new("u:object_r:keystore:s0")?;
+ let priv_context = Context::new("u:r:system_server:s0")?;
+ let unpriv_context = Context::new("u:r:shell:s0")?;
+ assert!(check_access(
+ &priv_context,
+ &ks_context,
+ "keystore2",
+ stringify!($perm)
+ )
+ .is_ok());
+ assert_eq!(
+ Some(&Error::perm()),
+ check_access(&unpriv_context, &ks_context, "keystore2", stringify!($perm))
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<Error>()
+ );
+ Ok(())
+ }
+ };
+ }
+
+ check_keystore_perm!(add_auth);
+ check_keystore_perm!(clear_ns);
+ check_keystore_perm!(get_state);
+ check_keystore_perm!(lock);
+ check_keystore_perm!(reset);
+ check_keystore_perm!(unlock);
+ }
+
+ #[test]
+ fn test_getpidcon() {
+ // Check that `getpidcon` of our pid is equal to what `getcon` returns.
+ // And by using `unwrap` we make sure that both also have to return successfully
+ // fully to pass the test.
+ assert_eq!(getpidcon(std::process::id() as i32).unwrap(), getcon().unwrap());
+ }
+}
diff --git a/keystore2/src/aidl_generated.rs b/keystore2/src/aidl_generated.rs
deleted file mode 100644
index 4b6a844..0000000
--- a/keystore2/src/aidl_generated.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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.
-
-#![allow(missing_docs)]
-
-//! This crate holds types that we are depending on and will be generated by the AIDL
-//! compiler.
-
-use std::cmp::PartialEq;
-use std::fmt;
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Result {
- pub rc: ResponseCode,
- pub km_error_code: i32,
-}
-
-impl fmt::Display for Result {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self)
- }
-}
-
-#[repr(i32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum ResponseCode {
- Ok = 0,
- // 1 Reserved - formerly NO_ERROR
- Locked = 2,
- Uninitialized = 3,
- SystemError = 4,
- // 5 Reserved - formerly "protocol error" was never used
- PermissionDenied = 6,
- KeyNotFound = 7,
- ValueCorrupted = 8,
- // 9 Reserved - formerly "undefined action" was never used
- WrongPassword = 10,
- // 11 - 13 Reserved - formerly password retry count indicators: obsolete
- //
- // 14 Reserved - formerly SIGNATURE_INVALID: Keystore does not perform public key
- // operations any more.
-
- // Indicates to the caller that user authorization is required before the operation may
- // commence.
- OpAuthNeeded = 15,
- // 16 Reserved
- KeyPermanentlyInvalidated = 17,
- NoSuchSecurityLevel = 18,
- KeymintErrorCode = 19,
- BackendBusy = 20,
-}
-
-pub type ErrorCode = i32;
-
-#[repr(i32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum KeyPermission {
- None = 0,
- Delete = 1,
- GenUniqueId = 2,
- GetInfo = 4,
- Grant = 8,
- List = 0x10,
- ManageBlob = 0x20,
- Rebind = 0x40,
- ReqForcedOp = 0x80,
- Update = 0x100,
- Use = 0x200,
- UseDevId = 0x400,
-}
-
-#[repr(i32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Domain {
- App = 0,
- Grant = 1,
- SELinux = 2,
- Blob = 3,
- KeyId = 4,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct KeyDescriptor {
- pub domain: Domain,
- pub namespace_: i64,
- pub alias: Option<String>,
- pub blob: Option<Vec<u8>>,
-}
diff --git a/keystore2/src/android_hardware_keymint.rs b/keystore2/src/android_hardware_keymint.rs
new file mode 100644
index 0000000..103b9b9
--- /dev/null
+++ b/keystore2/src/android_hardware_keymint.rs
@@ -0,0 +1,1656 @@
+#![allow(non_snake_case)]
+#![allow(missing_docs)]
+#![allow(clippy::identity_op)]
+#![allow(clippy::excessive_precision)]
+#![allow(clippy::too_many_arguments)]
+pub use binder::public_api as binder;
+pub mod aidl {
+ pub mod android {
+ pub mod hardware {
+ pub mod keymint {
+ pub mod Algorithm {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { Algorithm : i32 {
+ RSA = 1,
+ EC = 3,
+ AES = 32,
+ TRIPLE_DES = 33,
+ HMAC = 128,
+ } }
+ pub(crate) mod mangled { pub use super::Algorithm as _7_android_8_hardware_7_keymint_9_Algorithm; }
+ }
+ pub mod BeginResult {
+ #[derive(Debug)]
+ pub struct BeginResult {
+ pub challenge: i64,
+ pub params: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>,
+ pub operation: Option<Box<dyn crate::mangled::_7_android_8_hardware_7_keymint_17_IKeyMintOperation>>,
+ }
+ pub(crate) mod mangled { pub use super::BeginResult as _7_android_8_hardware_7_keymint_11_BeginResult; }
+ impl Default for BeginResult {
+ fn default() -> Self {
+ Self {
+ challenge: 0,
+ params: Default::default(),
+ operation: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for BeginResult {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for BeginResult {}
+ impl binder::parcel::SerializeOption for BeginResult {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.challenge)?;
+ parcel.write(&this.params)?;
+ let __field_ref = this.operation.as_ref().ok_or(binder::StatusCode::UNEXPECTED_NULL)?;
+ parcel.write(__field_ref)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for BeginResult {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for BeginResult {}
+ impl binder::parcel::DeserializeOption for BeginResult {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.challenge = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.params = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.operation = Some(parcel.read()?);
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod BlockMode {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { BlockMode : i32 {
+ ECB = 1,
+ CBC = 2,
+ CTR = 3,
+ GCM = 32,
+ } }
+ pub(crate) mod mangled { pub use super::BlockMode as _7_android_8_hardware_7_keymint_9_BlockMode; }
+ }
+ pub mod Certificate {
+ #[derive(Debug)]
+ pub struct Certificate {
+ pub encodedCertificate: Vec<u8>,
+ }
+ pub(crate) mod mangled { pub use super::Certificate as _7_android_8_hardware_7_keymint_11_Certificate; }
+ impl Default for Certificate {
+ fn default() -> Self {
+ Self {
+ encodedCertificate: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for Certificate {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for Certificate {}
+ impl binder::parcel::SerializeOption for Certificate {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.encodedCertificate)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for Certificate {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for Certificate {}
+ impl binder::parcel::DeserializeOption for Certificate {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.encodedCertificate = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod Constants {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { Constants : i32 {
+ AUTH_TOKEN_MAC_LENGTH = 32,
+ } }
+ pub(crate) mod mangled { pub use super::Constants as _7_android_8_hardware_7_keymint_9_Constants; }
+ }
+ pub mod Digest {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { Digest : i32 {
+ NONE = 0,
+ MD5 = 1,
+ SHA1 = 2,
+ SHA_2_224 = 3,
+ SHA_2_256 = 4,
+ SHA_2_384 = 5,
+ SHA_2_512 = 6,
+ } }
+ pub(crate) mod mangled { pub use super::Digest as _7_android_8_hardware_7_keymint_6_Digest; }
+ }
+ pub mod EcCurve {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { EcCurve : i32 {
+ P_224 = 0,
+ P_256 = 1,
+ P_384 = 2,
+ P_521 = 3,
+ } }
+ pub(crate) mod mangled { pub use super::EcCurve as _7_android_8_hardware_7_keymint_7_EcCurve; }
+ }
+ pub mod ErrorCode {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { ErrorCode : i32 {
+ OK = 0,
+ ROOT_OF_TRUST_ALREADY_SET = -1,
+ UNSUPPORTED_PURPOSE = -2,
+ INCOMPATIBLE_PURPOSE = -3,
+ UNSUPPORTED_ALGORITHM = -4,
+ INCOMPATIBLE_ALGORITHM = -5,
+ UNSUPPORTED_KEY_SIZE = -6,
+ UNSUPPORTED_BLOCK_MODE = -7,
+ INCOMPATIBLE_BLOCK_MODE = -8,
+ UNSUPPORTED_MAC_LENGTH = -9,
+ UNSUPPORTED_PADDING_MODE = -10,
+ INCOMPATIBLE_PADDING_MODE = -11,
+ UNSUPPORTED_DIGEST = -12,
+ INCOMPATIBLE_DIGEST = -13,
+ INVALID_EXPIRATION_TIME = -14,
+ INVALID_USER_ID = -15,
+ INVALID_AUTHORIZATION_TIMEOUT = -16,
+ UNSUPPORTED_KEY_FORMAT = -17,
+ INCOMPATIBLE_KEY_FORMAT = -18,
+ UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19,
+ UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20,
+ INVALID_INPUT_LENGTH = -21,
+ KEY_EXPORT_OPTIONS_INVALID = -22,
+ DELEGATION_NOT_ALLOWED = -23,
+ KEY_NOT_YET_VALID = -24,
+ KEY_EXPIRED = -25,
+ KEY_USER_NOT_AUTHENTICATED = -26,
+ OUTPUT_PARAMETER_NULL = -27,
+ INVALID_OPERATION_HANDLE = -28,
+ INSUFFICIENT_BUFFER_SPACE = -29,
+ VERIFICATION_FAILED = -30,
+ TOO_MANY_OPERATIONS = -31,
+ UNEXPECTED_NULL_POINTER = -32,
+ INVALID_KEY_BLOB = -33,
+ IMPORTED_KEY_NOT_ENCRYPTED = -34,
+ IMPORTED_KEY_DECRYPTION_FAILED = -35,
+ IMPORTED_KEY_NOT_SIGNED = -36,
+ IMPORTED_KEY_VERIFICATION_FAILED = -37,
+ INVALID_ARGUMENT = -38,
+ UNSUPPORTED_TAG = -39,
+ INVALID_TAG = -40,
+ MEMORY_ALLOCATION_FAILED = -41,
+ IMPORT_PARAMETER_MISMATCH = -44,
+ SECURE_HW_ACCESS_DENIED = -45,
+ OPERATION_CANCELLED = -46,
+ CONCURRENT_ACCESS_CONFLICT = -47,
+ SECURE_HW_BUSY = -48,
+ SECURE_HW_COMMUNICATION_FAILED = -49,
+ UNSUPPORTED_EC_FIELD = -50,
+ MISSING_NONCE = -51,
+ INVALID_NONCE = -52,
+ MISSING_MAC_LENGTH = -53,
+ KEY_RATE_LIMIT_EXCEEDED = -54,
+ CALLER_NONCE_PROHIBITED = -55,
+ KEY_MAX_OPS_EXCEEDED = -56,
+ INVALID_MAC_LENGTH = -57,
+ MISSING_MIN_MAC_LENGTH = -58,
+ UNSUPPORTED_MIN_MAC_LENGTH = -59,
+ UNSUPPORTED_KDF = -60,
+ UNSUPPORTED_EC_CURVE = -61,
+ KEY_REQUIRES_UPGRADE = -62,
+ ATTESTATION_CHALLENGE_MISSING = -63,
+ KEYMINT_NOT_CONFIGURED = -64,
+ ATTESTATION_APPLICATION_ID_MISSING = -65,
+ CANNOT_ATTEST_IDS = -66,
+ ROLLBACK_RESISTANCE_UNAVAILABLE = -67,
+ HARDWARE_TYPE_UNAVAILABLE = -68,
+ PROOF_OF_PRESENCE_REQUIRED = -69,
+ CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+ NO_USER_CONFIRMATION = -71,
+ DEVICE_LOCKED = -72,
+ EARLY_BOOT_ENDED = -73,
+ ATTESTATION_KEYS_NOT_PROVISIONED = -74,
+ ATTESTATION_IDS_NOT_PROVISIONED = -75,
+ INVALID_OPERATION = -76,
+ STORAGE_KEY_UNSUPPORTED = -77,
+ UNIMPLEMENTED = -100,
+ VERSION_MISMATCH = -101,
+ UNKNOWN_ERROR = -1000,
+ } }
+ pub(crate) mod mangled { pub use super::ErrorCode as _7_android_8_hardware_7_keymint_9_ErrorCode; }
+ }
+ pub mod HardwareAuthToken {
+ #[derive(Debug)]
+ pub struct HardwareAuthToken {
+ pub challenge: i64,
+ pub userId: i64,
+ pub authenticatorId: i64,
+ pub authenticatorType: crate::mangled::_7_android_8_hardware_7_keymint_25_HardwareAuthenticatorType,
+ pub timestamp: crate::mangled::_7_android_8_hardware_7_keymint_9_Timestamp,
+ pub mac: Vec<u8>,
+ }
+ pub(crate) mod mangled { pub use super::HardwareAuthToken as _7_android_8_hardware_7_keymint_17_HardwareAuthToken; }
+ impl Default for HardwareAuthToken {
+ fn default() -> Self {
+ Self {
+ challenge: 0,
+ userId: 0,
+ authenticatorId: 0,
+ authenticatorType: Default::default(),
+ timestamp: Default::default(),
+ mac: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for HardwareAuthToken {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for HardwareAuthToken {}
+ impl binder::parcel::SerializeOption for HardwareAuthToken {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.challenge)?;
+ parcel.write(&this.userId)?;
+ parcel.write(&this.authenticatorId)?;
+ parcel.write(&this.authenticatorType)?;
+ parcel.write(&this.timestamp)?;
+ parcel.write(&this.mac)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for HardwareAuthToken {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for HardwareAuthToken {}
+ impl binder::parcel::DeserializeOption for HardwareAuthToken {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.challenge = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.userId = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.authenticatorId = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.authenticatorType = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.timestamp = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.mac = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod HardwareAuthenticatorType {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { HardwareAuthenticatorType : i32 {
+ NONE = 0,
+ PASSWORD = 1,
+ FINGERPRINT = 2,
+ ANY = -1,
+ } }
+ pub(crate) mod mangled { pub use super::HardwareAuthenticatorType as _7_android_8_hardware_7_keymint_25_HardwareAuthenticatorType; }
+ }
+ pub mod HmacSharingParameters {
+ #[derive(Debug)]
+ pub struct HmacSharingParameters {
+ pub seed: Vec<u8>,
+ pub nonce: Vec<u8>,
+ }
+ pub(crate) mod mangled { pub use super::HmacSharingParameters as _7_android_8_hardware_7_keymint_21_HmacSharingParameters; }
+ impl Default for HmacSharingParameters {
+ fn default() -> Self {
+ Self {
+ seed: Default::default(),
+ nonce: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for HmacSharingParameters {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for HmacSharingParameters {}
+ impl binder::parcel::SerializeOption for HmacSharingParameters {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.seed)?;
+ parcel.write(&this.nonce)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for HmacSharingParameters {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for HmacSharingParameters {}
+ impl binder::parcel::DeserializeOption for HmacSharingParameters {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.seed = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.nonce = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod IKeyMintDevice {
+ #![allow(non_upper_case_globals)]
+ #![allow(non_snake_case)]
+ #[allow(unused_imports)] use binder::IBinder;
+ use binder::declare_binder_interface;
+ declare_binder_interface! {
+ IKeyMintDevice["android.hardware.keymint.IKeyMintDevice"] {
+ native: BnKeyMintDevice(on_transact),
+ proxy: BpKeyMintDevice {
+ },
+ }
+ }
+ pub trait IKeyMintDevice: binder::Interface + Send {
+ fn get_descriptor() -> &'static str where Self: Sized { "android.hardware.keymint.IKeyMintDevice" }
+ fn getHardwareInfo(&self) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_19_KeyMintHardwareInfo> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn verifyAuthorization(&self, _arg_challenge: i64, _arg_parametersToVerify: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_token: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn addRngEntropy(&self, _arg_data: &[u8]) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn generateKey(&self, _arg_keyParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_generatedKeyBlob: &mut Vec<u8>, _arg_generatedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics, _arg_outCertChain: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate>) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn importKey(&self, _arg_inKeyParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inKeyFormat: crate::mangled::_7_android_8_hardware_7_keymint_9_KeyFormat, _arg_inKeyData: &[u8], _arg_outImportedKeyBlob: &mut Vec<u8>, _arg_outImportedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics, _arg_outCertChain: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate>) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn importWrappedKey(&self, _arg_inWrappedKeyData: &[u8], _arg_inWrappingKeyBlob: &[u8], _arg_inMaskingKey: &[u8], _arg_inUnwrappingParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inPasswordSid: i64, _arg_inBiometricSid: i64, _arg_outImportedKeyBlob: &mut Vec<u8>, _arg_outImportedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn upgradeKey(&self, _arg_inKeyBlobToUpgrade: &[u8], _arg_inUpgradeParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter]) -> binder::public_api::Result<Vec<u8>> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn deleteKey(&self, _arg_inKeyBlob: &[u8]) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn deleteAllKeys(&self) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn destroyAttestationIds(&self) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn begin(&self, _arg_inPurpose: crate::mangled::_7_android_8_hardware_7_keymint_10_KeyPurpose, _arg_inKeyBlob: &[u8], _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inAuthToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_11_BeginResult> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn getDefaultImpl() -> IKeyMintDeviceDefault where Self: Sized {
+ DEFAULT_IMPL.lock().unwrap().clone()
+ }
+ fn setDefaultImpl(d: IKeyMintDeviceDefault) -> IKeyMintDeviceDefault where Self: Sized {
+ std::mem::replace(&mut *DEFAULT_IMPL.lock().unwrap(), d)
+ }
+ }
+ pub mod transactions {
+ #[allow(unused_imports)] use binder::IBinder;
+ pub const getHardwareInfo: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 0;
+ pub const verifyAuthorization: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 1;
+ pub const addRngEntropy: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 2;
+ pub const generateKey: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 3;
+ pub const importKey: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 4;
+ pub const importWrappedKey: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 5;
+ pub const upgradeKey: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 6;
+ pub const deleteKey: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 7;
+ pub const deleteAllKeys: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 8;
+ pub const destroyAttestationIds: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 9;
+ pub const begin: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 10;
+ }
+ pub type IKeyMintDeviceDefault = Option<std::sync::Arc<dyn IKeyMintDevice + Sync>>;
+ use lazy_static::lazy_static;
+ lazy_static! {
+ static ref DEFAULT_IMPL: std::sync::Mutex<IKeyMintDeviceDefault> = std::sync::Mutex::new(None);
+ }
+ pub(crate) mod mangled { pub use super::IKeyMintDevice as _7_android_8_hardware_7_keymint_14_IKeyMintDevice; }
+ impl IKeyMintDevice for BpKeyMintDevice {
+ fn getHardwareInfo(&self) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_19_KeyMintHardwareInfo> {
+ let _aidl_reply = self.binder.transact(transactions::getHardwareInfo, 0, |_aidl_data| {
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.getHardwareInfo();
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ let _aidl_return: crate::mangled::_7_android_8_hardware_7_keymint_19_KeyMintHardwareInfo = _aidl_reply.read()?;
+ Ok(_aidl_return)
+ }
+ fn verifyAuthorization(&self, _arg_challenge: i64, _arg_parametersToVerify: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_token: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken> {
+ let _aidl_reply = self.binder.transact(transactions::verifyAuthorization, 0, |_aidl_data| {
+ _aidl_data.write(&_arg_challenge)?;
+ _aidl_data.write(_arg_parametersToVerify)?;
+ _aidl_data.write(_arg_token)?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.verifyAuthorization(_arg_challenge, _arg_parametersToVerify, _arg_token);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ let _aidl_return: crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken = _aidl_reply.read()?;
+ Ok(_aidl_return)
+ }
+ fn addRngEntropy(&self, _arg_data: &[u8]) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::addRngEntropy, 0, |_aidl_data| {
+ _aidl_data.write(_arg_data)?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.addRngEntropy(_arg_data);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ Ok(())
+ }
+ fn generateKey(&self, _arg_keyParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_generatedKeyBlob: &mut Vec<u8>, _arg_generatedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics, _arg_outCertChain: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate>) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::generateKey, 0, |_aidl_data| {
+ _aidl_data.write(_arg_keyParams)?;
+ _aidl_data.write_slice_size(Some(_arg_generatedKeyBlob))?;
+ _aidl_data.write_slice_size(Some(_arg_outCertChain))?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.generateKey(_arg_keyParams, _arg_generatedKeyBlob, _arg_generatedKeyCharacteristics, _arg_outCertChain);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ *_arg_generatedKeyBlob = _aidl_reply.read()?;
+ *_arg_generatedKeyCharacteristics = _aidl_reply.read()?;
+ *_arg_outCertChain = _aidl_reply.read()?;
+ Ok(())
+ }
+ fn importKey(&self, _arg_inKeyParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inKeyFormat: crate::mangled::_7_android_8_hardware_7_keymint_9_KeyFormat, _arg_inKeyData: &[u8], _arg_outImportedKeyBlob: &mut Vec<u8>, _arg_outImportedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics, _arg_outCertChain: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate>) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::importKey, 0, |_aidl_data| {
+ _aidl_data.write(_arg_inKeyParams)?;
+ _aidl_data.write(&_arg_inKeyFormat)?;
+ _aidl_data.write(_arg_inKeyData)?;
+ _aidl_data.write_slice_size(Some(_arg_outImportedKeyBlob))?;
+ _aidl_data.write_slice_size(Some(_arg_outCertChain))?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.importKey(_arg_inKeyParams, _arg_inKeyFormat, _arg_inKeyData, _arg_outImportedKeyBlob, _arg_outImportedKeyCharacteristics, _arg_outCertChain);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ *_arg_outImportedKeyBlob = _aidl_reply.read()?;
+ *_arg_outImportedKeyCharacteristics = _aidl_reply.read()?;
+ *_arg_outCertChain = _aidl_reply.read()?;
+ Ok(())
+ }
+ fn importWrappedKey(&self, _arg_inWrappedKeyData: &[u8], _arg_inWrappingKeyBlob: &[u8], _arg_inMaskingKey: &[u8], _arg_inUnwrappingParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inPasswordSid: i64, _arg_inBiometricSid: i64, _arg_outImportedKeyBlob: &mut Vec<u8>, _arg_outImportedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::importWrappedKey, 0, |_aidl_data| {
+ _aidl_data.write(_arg_inWrappedKeyData)?;
+ _aidl_data.write(_arg_inWrappingKeyBlob)?;
+ _aidl_data.write(_arg_inMaskingKey)?;
+ _aidl_data.write(_arg_inUnwrappingParams)?;
+ _aidl_data.write(&_arg_inPasswordSid)?;
+ _aidl_data.write(&_arg_inBiometricSid)?;
+ _aidl_data.write_slice_size(Some(_arg_outImportedKeyBlob))?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.importWrappedKey(_arg_inWrappedKeyData, _arg_inWrappingKeyBlob, _arg_inMaskingKey, _arg_inUnwrappingParams, _arg_inPasswordSid, _arg_inBiometricSid, _arg_outImportedKeyBlob, _arg_outImportedKeyCharacteristics);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ *_arg_outImportedKeyBlob = _aidl_reply.read()?;
+ *_arg_outImportedKeyCharacteristics = _aidl_reply.read()?;
+ Ok(())
+ }
+ fn upgradeKey(&self, _arg_inKeyBlobToUpgrade: &[u8], _arg_inUpgradeParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter]) -> binder::public_api::Result<Vec<u8>> {
+ let _aidl_reply = self.binder.transact(transactions::upgradeKey, 0, |_aidl_data| {
+ _aidl_data.write(_arg_inKeyBlobToUpgrade)?;
+ _aidl_data.write(_arg_inUpgradeParams)?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.upgradeKey(_arg_inKeyBlobToUpgrade, _arg_inUpgradeParams);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ let _aidl_return: Vec<u8> = _aidl_reply.read()?;
+ Ok(_aidl_return)
+ }
+ fn deleteKey(&self, _arg_inKeyBlob: &[u8]) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::deleteKey, 0, |_aidl_data| {
+ _aidl_data.write(_arg_inKeyBlob)?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.deleteKey(_arg_inKeyBlob);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ Ok(())
+ }
+ fn deleteAllKeys(&self) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::deleteAllKeys, 0, |_aidl_data| {
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.deleteAllKeys();
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ Ok(())
+ }
+ fn destroyAttestationIds(&self) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::destroyAttestationIds, 0, |_aidl_data| {
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.destroyAttestationIds();
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ Ok(())
+ }
+ fn begin(&self, _arg_inPurpose: crate::mangled::_7_android_8_hardware_7_keymint_10_KeyPurpose, _arg_inKeyBlob: &[u8], _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inAuthToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_11_BeginResult> {
+ let _aidl_reply = self.binder.transact(transactions::begin, 0, |_aidl_data| {
+ _aidl_data.write(&_arg_inPurpose)?;
+ _aidl_data.write(_arg_inKeyBlob)?;
+ _aidl_data.write(_arg_inParams)?;
+ _aidl_data.write(_arg_inAuthToken)?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintDevice>::getDefaultImpl() {
+ return _aidl_default_impl.begin(_arg_inPurpose, _arg_inKeyBlob, _arg_inParams, _arg_inAuthToken);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ let _aidl_return: crate::mangled::_7_android_8_hardware_7_keymint_11_BeginResult = _aidl_reply.read()?;
+ Ok(_aidl_return)
+ }
+ }
+ impl IKeyMintDevice for binder::Binder<BnKeyMintDevice> {
+ fn getHardwareInfo(&self) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_19_KeyMintHardwareInfo> { self.0.getHardwareInfo() }
+ fn verifyAuthorization(&self, _arg_challenge: i64, _arg_parametersToVerify: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_token: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken> { self.0.verifyAuthorization(_arg_challenge, _arg_parametersToVerify, _arg_token) }
+ fn addRngEntropy(&self, _arg_data: &[u8]) -> binder::public_api::Result<()> { self.0.addRngEntropy(_arg_data) }
+ fn generateKey(&self, _arg_keyParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_generatedKeyBlob: &mut Vec<u8>, _arg_generatedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics, _arg_outCertChain: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate>) -> binder::public_api::Result<()> { self.0.generateKey(_arg_keyParams, _arg_generatedKeyBlob, _arg_generatedKeyCharacteristics, _arg_outCertChain) }
+ fn importKey(&self, _arg_inKeyParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inKeyFormat: crate::mangled::_7_android_8_hardware_7_keymint_9_KeyFormat, _arg_inKeyData: &[u8], _arg_outImportedKeyBlob: &mut Vec<u8>, _arg_outImportedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics, _arg_outCertChain: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate>) -> binder::public_api::Result<()> { self.0.importKey(_arg_inKeyParams, _arg_inKeyFormat, _arg_inKeyData, _arg_outImportedKeyBlob, _arg_outImportedKeyCharacteristics, _arg_outCertChain) }
+ fn importWrappedKey(&self, _arg_inWrappedKeyData: &[u8], _arg_inWrappingKeyBlob: &[u8], _arg_inMaskingKey: &[u8], _arg_inUnwrappingParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inPasswordSid: i64, _arg_inBiometricSid: i64, _arg_outImportedKeyBlob: &mut Vec<u8>, _arg_outImportedKeyCharacteristics: &mut crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics) -> binder::public_api::Result<()> { self.0.importWrappedKey(_arg_inWrappedKeyData, _arg_inWrappingKeyBlob, _arg_inMaskingKey, _arg_inUnwrappingParams, _arg_inPasswordSid, _arg_inBiometricSid, _arg_outImportedKeyBlob, _arg_outImportedKeyCharacteristics) }
+ fn upgradeKey(&self, _arg_inKeyBlobToUpgrade: &[u8], _arg_inUpgradeParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter]) -> binder::public_api::Result<Vec<u8>> { self.0.upgradeKey(_arg_inKeyBlobToUpgrade, _arg_inUpgradeParams) }
+ fn deleteKey(&self, _arg_inKeyBlob: &[u8]) -> binder::public_api::Result<()> { self.0.deleteKey(_arg_inKeyBlob) }
+ fn deleteAllKeys(&self) -> binder::public_api::Result<()> { self.0.deleteAllKeys() }
+ fn destroyAttestationIds(&self) -> binder::public_api::Result<()> { self.0.destroyAttestationIds() }
+ fn begin(&self, _arg_inPurpose: crate::mangled::_7_android_8_hardware_7_keymint_10_KeyPurpose, _arg_inKeyBlob: &[u8], _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_inAuthToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken) -> binder::public_api::Result<crate::mangled::_7_android_8_hardware_7_keymint_11_BeginResult> { self.0.begin(_arg_inPurpose, _arg_inKeyBlob, _arg_inParams, _arg_inAuthToken) }
+ }
+ fn on_transact(_aidl_service: &dyn IKeyMintDevice, _aidl_code: binder::TransactionCode, _aidl_data: &binder::parcel::Parcel, _aidl_reply: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ match _aidl_code {
+ transactions::getHardwareInfo => {
+ let _aidl_return = _aidl_service.getHardwareInfo();
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(_aidl_return)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::verifyAuthorization => {
+ let _arg_challenge: i64 = _aidl_data.read()?;
+ let _arg_parametersToVerify: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _arg_token: crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken = _aidl_data.read()?;
+ let _aidl_return = _aidl_service.verifyAuthorization(_arg_challenge, &_arg_parametersToVerify, &_arg_token);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(_aidl_return)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::addRngEntropy => {
+ let _arg_data: Vec<u8> = _aidl_data.read()?;
+ let _aidl_return = _aidl_service.addRngEntropy(&_arg_data);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::generateKey => {
+ let _arg_keyParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let mut _arg_generatedKeyBlob: Vec<u8> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_generatedKeyBlob)?;
+ let mut _arg_generatedKeyCharacteristics: crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics = Default::default();
+ let mut _arg_outCertChain: Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_outCertChain)?;
+ let _aidl_return = _aidl_service.generateKey(&_arg_keyParams, &mut _arg_generatedKeyBlob, &mut _arg_generatedKeyCharacteristics, &mut _arg_outCertChain);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(&_arg_generatedKeyBlob)?;
+ _aidl_reply.write(&_arg_generatedKeyCharacteristics)?;
+ _aidl_reply.write(&_arg_outCertChain)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::importKey => {
+ let _arg_inKeyParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _arg_inKeyFormat: crate::mangled::_7_android_8_hardware_7_keymint_9_KeyFormat = _aidl_data.read()?;
+ let _arg_inKeyData: Vec<u8> = _aidl_data.read()?;
+ let mut _arg_outImportedKeyBlob: Vec<u8> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_outImportedKeyBlob)?;
+ let mut _arg_outImportedKeyCharacteristics: crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics = Default::default();
+ let mut _arg_outCertChain: Vec<crate::mangled::_7_android_8_hardware_7_keymint_11_Certificate> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_outCertChain)?;
+ let _aidl_return = _aidl_service.importKey(&_arg_inKeyParams, _arg_inKeyFormat, &_arg_inKeyData, &mut _arg_outImportedKeyBlob, &mut _arg_outImportedKeyCharacteristics, &mut _arg_outCertChain);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(&_arg_outImportedKeyBlob)?;
+ _aidl_reply.write(&_arg_outImportedKeyCharacteristics)?;
+ _aidl_reply.write(&_arg_outCertChain)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::importWrappedKey => {
+ let _arg_inWrappedKeyData: Vec<u8> = _aidl_data.read()?;
+ let _arg_inWrappingKeyBlob: Vec<u8> = _aidl_data.read()?;
+ let _arg_inMaskingKey: Vec<u8> = _aidl_data.read()?;
+ let _arg_inUnwrappingParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _arg_inPasswordSid: i64 = _aidl_data.read()?;
+ let _arg_inBiometricSid: i64 = _aidl_data.read()?;
+ let mut _arg_outImportedKeyBlob: Vec<u8> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_outImportedKeyBlob)?;
+ let mut _arg_outImportedKeyCharacteristics: crate::mangled::_7_android_8_hardware_7_keymint_18_KeyCharacteristics = Default::default();
+ let _aidl_return = _aidl_service.importWrappedKey(&_arg_inWrappedKeyData, &_arg_inWrappingKeyBlob, &_arg_inMaskingKey, &_arg_inUnwrappingParams, _arg_inPasswordSid, _arg_inBiometricSid, &mut _arg_outImportedKeyBlob, &mut _arg_outImportedKeyCharacteristics);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(&_arg_outImportedKeyBlob)?;
+ _aidl_reply.write(&_arg_outImportedKeyCharacteristics)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::upgradeKey => {
+ let _arg_inKeyBlobToUpgrade: Vec<u8> = _aidl_data.read()?;
+ let _arg_inUpgradeParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _aidl_return = _aidl_service.upgradeKey(&_arg_inKeyBlobToUpgrade, &_arg_inUpgradeParams);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(_aidl_return)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::deleteKey => {
+ let _arg_inKeyBlob: Vec<u8> = _aidl_data.read()?;
+ let _aidl_return = _aidl_service.deleteKey(&_arg_inKeyBlob);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::deleteAllKeys => {
+ let _aidl_return = _aidl_service.deleteAllKeys();
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::destroyAttestationIds => {
+ let _aidl_return = _aidl_service.destroyAttestationIds();
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::begin => {
+ let _arg_inPurpose: crate::mangled::_7_android_8_hardware_7_keymint_10_KeyPurpose = _aidl_data.read()?;
+ let _arg_inKeyBlob: Vec<u8> = _aidl_data.read()?;
+ let _arg_inParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _arg_inAuthToken: crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken = _aidl_data.read()?;
+ let _aidl_return = _aidl_service.begin(_arg_inPurpose, &_arg_inKeyBlob, &_arg_inParams, &_arg_inAuthToken);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(_aidl_return)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ _ => Err(binder::StatusCode::UNKNOWN_TRANSACTION)
+ }
+ }
+ }
+ pub mod IKeyMintOperation {
+ #![allow(non_upper_case_globals)]
+ #![allow(non_snake_case)]
+ #[allow(unused_imports)] use binder::IBinder;
+ use binder::declare_binder_interface;
+ declare_binder_interface! {
+ IKeyMintOperation["android.hardware.keymint.IKeyMintOperation"] {
+ native: BnKeyMintOperation(on_transact),
+ proxy: BpKeyMintOperation {
+ },
+ }
+ }
+ pub trait IKeyMintOperation: binder::Interface + Send {
+ fn get_descriptor() -> &'static str where Self: Sized { "android.hardware.keymint.IKeyMintOperation" }
+ fn update(&self, _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_input: &[u8], _arg_inVerificationToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken, _arg_outParams: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>, _arg_output: &mut Vec<u8>) -> binder::public_api::Result<i32> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn finish(&self, _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_input: &[u8], _arg_inSignature: &[u8], _arg_authToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken, _arg_inVerificationToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken, _arg_outParams: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>, _arg_output: &mut Vec<u8>) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn abort(&self) -> binder::public_api::Result<()> {
+ Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+ }
+ fn getDefaultImpl() -> IKeyMintOperationDefault where Self: Sized {
+ DEFAULT_IMPL.lock().unwrap().clone()
+ }
+ fn setDefaultImpl(d: IKeyMintOperationDefault) -> IKeyMintOperationDefault where Self: Sized {
+ std::mem::replace(&mut *DEFAULT_IMPL.lock().unwrap(), d)
+ }
+ }
+ pub mod transactions {
+ #[allow(unused_imports)] use binder::IBinder;
+ pub const update: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 0;
+ pub const finish: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 1;
+ pub const abort: binder::TransactionCode = binder::SpIBinder::FIRST_CALL_TRANSACTION + 2;
+ }
+ pub type IKeyMintOperationDefault = Option<std::sync::Arc<dyn IKeyMintOperation + Sync>>;
+ use lazy_static::lazy_static;
+ lazy_static! {
+ static ref DEFAULT_IMPL: std::sync::Mutex<IKeyMintOperationDefault> = std::sync::Mutex::new(None);
+ }
+ pub(crate) mod mangled { pub use super::IKeyMintOperation as _7_android_8_hardware_7_keymint_17_IKeyMintOperation; }
+ impl IKeyMintOperation for BpKeyMintOperation {
+ fn update(&self, _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_input: &[u8], _arg_inVerificationToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken, _arg_outParams: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>, _arg_output: &mut Vec<u8>) -> binder::public_api::Result<i32> {
+ let _aidl_reply = self.binder.transact(transactions::update, 0, |_aidl_data| {
+ _aidl_data.write(_arg_inParams)?;
+ _aidl_data.write(_arg_input)?;
+ _aidl_data.write(_arg_inVerificationToken)?;
+ _aidl_data.write_slice_size(Some(_arg_outParams))?;
+ _aidl_data.write_slice_size(Some(_arg_output))?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintOperation>::getDefaultImpl() {
+ return _aidl_default_impl.update(_arg_inParams, _arg_input, _arg_inVerificationToken, _arg_outParams, _arg_output);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ let _aidl_return: i32 = _aidl_reply.read()?;
+ *_arg_outParams = _aidl_reply.read()?;
+ *_arg_output = _aidl_reply.read()?;
+ Ok(_aidl_return)
+ }
+ fn finish(&self, _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_input: &[u8], _arg_inSignature: &[u8], _arg_authToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken, _arg_inVerificationToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken, _arg_outParams: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>, _arg_output: &mut Vec<u8>) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::finish, 0, |_aidl_data| {
+ _aidl_data.write(_arg_inParams)?;
+ _aidl_data.write(_arg_input)?;
+ _aidl_data.write(_arg_inSignature)?;
+ _aidl_data.write(_arg_authToken)?;
+ _aidl_data.write(_arg_inVerificationToken)?;
+ _aidl_data.write_slice_size(Some(_arg_outParams))?;
+ _aidl_data.write_slice_size(Some(_arg_output))?;
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintOperation>::getDefaultImpl() {
+ return _aidl_default_impl.finish(_arg_inParams, _arg_input, _arg_inSignature, _arg_authToken, _arg_inVerificationToken, _arg_outParams, _arg_output);
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ *_arg_outParams = _aidl_reply.read()?;
+ *_arg_output = _aidl_reply.read()?;
+ Ok(())
+ }
+ fn abort(&self) -> binder::public_api::Result<()> {
+ let _aidl_reply = self.binder.transact(transactions::abort, 0, |_aidl_data| {
+ Ok(())
+ });
+ if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+ if let Some(_aidl_default_impl) = <Self as IKeyMintOperation>::getDefaultImpl() {
+ return _aidl_default_impl.abort();
+ }
+ }
+ let _aidl_reply = _aidl_reply?;
+ let _aidl_status: binder::Status = _aidl_reply.read()?;
+ if !_aidl_status.is_ok() { return Err(_aidl_status); }
+ Ok(())
+ }
+ }
+ impl IKeyMintOperation for binder::Binder<BnKeyMintOperation> {
+ fn update(&self, _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_input: &[u8], _arg_inVerificationToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken, _arg_outParams: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>, _arg_output: &mut Vec<u8>) -> binder::public_api::Result<i32> { self.0.update(_arg_inParams, _arg_input, _arg_inVerificationToken, _arg_outParams, _arg_output) }
+ fn finish(&self, _arg_inParams: &[crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter], _arg_input: &[u8], _arg_inSignature: &[u8], _arg_authToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken, _arg_inVerificationToken: &crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken, _arg_outParams: &mut Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>, _arg_output: &mut Vec<u8>) -> binder::public_api::Result<()> { self.0.finish(_arg_inParams, _arg_input, _arg_inSignature, _arg_authToken, _arg_inVerificationToken, _arg_outParams, _arg_output) }
+ fn abort(&self) -> binder::public_api::Result<()> { self.0.abort() }
+ }
+ fn on_transact(_aidl_service: &dyn IKeyMintOperation, _aidl_code: binder::TransactionCode, _aidl_data: &binder::parcel::Parcel, _aidl_reply: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ match _aidl_code {
+ transactions::update => {
+ let _arg_inParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _arg_input: Vec<u8> = _aidl_data.read()?;
+ let _arg_inVerificationToken: crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken = _aidl_data.read()?;
+ let mut _arg_outParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_outParams)?;
+ let mut _arg_output: Vec<u8> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_output)?;
+ let _aidl_return = _aidl_service.update(&_arg_inParams, &_arg_input, &_arg_inVerificationToken, &mut _arg_outParams, &mut _arg_output);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(_aidl_return)?;
+ _aidl_reply.write(&_arg_outParams)?;
+ _aidl_reply.write(&_arg_output)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::finish => {
+ let _arg_inParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = _aidl_data.read()?;
+ let _arg_input: Vec<u8> = _aidl_data.read()?;
+ let _arg_inSignature: Vec<u8> = _aidl_data.read()?;
+ let _arg_authToken: crate::mangled::_7_android_8_hardware_7_keymint_17_HardwareAuthToken = _aidl_data.read()?;
+ let _arg_inVerificationToken: crate::mangled::_7_android_8_hardware_7_keymint_17_VerificationToken = _aidl_data.read()?;
+ let mut _arg_outParams: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_outParams)?;
+ let mut _arg_output: Vec<u8> = Default::default();
+ _aidl_data.resize_out_vec(&mut _arg_output)?;
+ let _aidl_return = _aidl_service.finish(&_arg_inParams, &_arg_input, &_arg_inSignature, &_arg_authToken, &_arg_inVerificationToken, &mut _arg_outParams, &mut _arg_output);
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ _aidl_reply.write(&_arg_outParams)?;
+ _aidl_reply.write(&_arg_output)?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ transactions::abort => {
+ let _aidl_return = _aidl_service.abort();
+ match &_aidl_return {
+ Ok(_aidl_return) => {
+ _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+ }
+ Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+ }
+ Ok(())
+ }
+ _ => Err(binder::StatusCode::UNKNOWN_TRANSACTION)
+ }
+ }
+ }
+ pub mod KeyCharacteristics {
+ #[derive(Debug)]
+ pub struct KeyCharacteristics {
+ pub softwareEnforced: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>,
+ pub hardwareEnforced: Vec<crate::mangled::_7_android_8_hardware_7_keymint_12_KeyParameter>,
+ }
+ pub(crate) mod mangled { pub use super::KeyCharacteristics as _7_android_8_hardware_7_keymint_18_KeyCharacteristics; }
+ impl Default for KeyCharacteristics {
+ fn default() -> Self {
+ Self {
+ softwareEnforced: Default::default(),
+ hardwareEnforced: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for KeyCharacteristics {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for KeyCharacteristics {}
+ impl binder::parcel::SerializeOption for KeyCharacteristics {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.softwareEnforced)?;
+ parcel.write(&this.hardwareEnforced)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for KeyCharacteristics {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for KeyCharacteristics {}
+ impl binder::parcel::DeserializeOption for KeyCharacteristics {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.softwareEnforced = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.hardwareEnforced = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod KeyDerivationFunction {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { KeyDerivationFunction : i32 {
+ NONE = 0,
+ RFC5869_SHA256 = 1,
+ ISO18033_2_KDF1_SHA1 = 2,
+ ISO18033_2_KDF1_SHA256 = 3,
+ ISO18033_2_KDF2_SHA1 = 4,
+ ISO18033_2_KDF2_SHA256 = 5,
+ } }
+ pub(crate) mod mangled { pub use super::KeyDerivationFunction as _7_android_8_hardware_7_keymint_21_KeyDerivationFunction; }
+ }
+ pub mod KeyFormat {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { KeyFormat : i32 {
+ X509 = 0,
+ PKCS8 = 1,
+ RAW = 3,
+ } }
+ pub(crate) mod mangled { pub use super::KeyFormat as _7_android_8_hardware_7_keymint_9_KeyFormat; }
+ }
+ pub mod KeyMintHardwareInfo {
+ #[derive(Debug)]
+ pub struct KeyMintHardwareInfo {
+ pub versionNumber: i32,
+ pub securityLevel: crate::mangled::_7_android_8_hardware_7_keymint_13_SecurityLevel,
+ pub keyMintName: String,
+ pub keyMintAuthorName: String,
+ }
+ pub(crate) mod mangled { pub use super::KeyMintHardwareInfo as _7_android_8_hardware_7_keymint_19_KeyMintHardwareInfo; }
+ impl Default for KeyMintHardwareInfo {
+ fn default() -> Self {
+ Self {
+ versionNumber: 0,
+ securityLevel: Default::default(),
+ keyMintName: Default::default(),
+ keyMintAuthorName: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for KeyMintHardwareInfo {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for KeyMintHardwareInfo {}
+ impl binder::parcel::SerializeOption for KeyMintHardwareInfo {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.versionNumber)?;
+ parcel.write(&this.securityLevel)?;
+ parcel.write(&this.keyMintName)?;
+ parcel.write(&this.keyMintAuthorName)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for KeyMintHardwareInfo {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for KeyMintHardwareInfo {}
+ impl binder::parcel::DeserializeOption for KeyMintHardwareInfo {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.versionNumber = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.securityLevel = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.keyMintName = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.keyMintAuthorName = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod KeyOrigin {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { KeyOrigin : i32 {
+ GENERATED = 0,
+ DERIVED = 1,
+ IMPORTED = 2,
+ RESERVED = 3,
+ SECURELY_IMPORTED = 4,
+ } }
+ pub(crate) mod mangled { pub use super::KeyOrigin as _7_android_8_hardware_7_keymint_9_KeyOrigin; }
+ }
+ pub mod KeyParameter {
+ #[derive(Debug)]
+ pub struct KeyParameter {
+ pub tag: crate::mangled::_7_android_8_hardware_7_keymint_3_Tag,
+ pub boolValue: bool,
+ pub integer: i32,
+ pub longInteger: i64,
+ pub dateTime: i64,
+ pub blob: Vec<u8>,
+ }
+ pub(crate) mod mangled { pub use super::KeyParameter as _7_android_8_hardware_7_keymint_12_KeyParameter; }
+ impl Default for KeyParameter {
+ fn default() -> Self {
+ Self {
+ tag: Default::default(),
+ boolValue: false,
+ integer: 0,
+ longInteger: 0,
+ dateTime: 0,
+ blob: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for KeyParameter {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for KeyParameter {}
+ impl binder::parcel::SerializeOption for KeyParameter {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.tag)?;
+ parcel.write(&this.boolValue)?;
+ parcel.write(&this.integer)?;
+ parcel.write(&this.longInteger)?;
+ parcel.write(&this.dateTime)?;
+ parcel.write(&this.blob)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for KeyParameter {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for KeyParameter {}
+ impl binder::parcel::DeserializeOption for KeyParameter {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.tag = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.boolValue = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.integer = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.longInteger = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.dateTime = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.blob = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod KeyPurpose {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { KeyPurpose : i32 {
+ ENCRYPT = 0,
+ DECRYPT = 1,
+ SIGN = 2,
+ VERIFY = 3,
+ WRAP_KEY = 5,
+ } }
+ pub(crate) mod mangled { pub use super::KeyPurpose as _7_android_8_hardware_7_keymint_10_KeyPurpose; }
+ }
+ pub mod PaddingMode {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { PaddingMode : i32 {
+ NONE = 1,
+ RSA_OAEP = 2,
+ RSA_PSS = 3,
+ RSA_PKCS1_1_5_ENCRYPT = 4,
+ RSA_PKCS1_1_5_SIGN = 5,
+ PKCS7 = 64,
+ } }
+ pub(crate) mod mangled { pub use super::PaddingMode as _7_android_8_hardware_7_keymint_11_PaddingMode; }
+ }
+ pub mod SecurityLevel {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { SecurityLevel : i32 {
+ SOFTWARE = 0,
+ TRUSTED_ENVIRONMENT = 1,
+ STRONGBOX = 2,
+ } }
+ pub(crate) mod mangled { pub use super::SecurityLevel as _7_android_8_hardware_7_keymint_13_SecurityLevel; }
+ }
+ pub mod Tag {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { Tag : i32 {
+ INVALID = 0,
+ PURPOSE = 536870913,
+ ALGORITHM = 268435458,
+ KEY_SIZE = 805306371,
+ BLOCK_MODE = 536870916,
+ DIGEST = 536870917,
+ PADDING = 536870918,
+ CALLER_NONCE = 1879048199,
+ MIN_MAC_LENGTH = 805306376,
+ EC_CURVE = 268435466,
+ RSA_PUBLIC_EXPONENT = 1342177480,
+ INCLUDE_UNIQUE_ID = 1879048394,
+ BLOB_USAGE_REQUIREMENTS = 268435757,
+ BOOTLOADER_ONLY = 1879048494,
+ ROLLBACK_RESISTANCE = 1879048495,
+ HARDWARE_TYPE = 268435760,
+ EARLY_BOOT_ONLY = 1879048497,
+ ACTIVE_DATETIME = 1610613136,
+ ORIGINATION_EXPIRE_DATETIME = 1610613137,
+ USAGE_EXPIRE_DATETIME = 1610613138,
+ MIN_SECONDS_BETWEEN_OPS = 805306771,
+ MAX_USES_PER_BOOT = 805306772,
+ USER_ID = 805306869,
+ USER_SECURE_ID = 1073742326,
+ NO_AUTH_REQUIRED = 1879048695,
+ USER_AUTH_TYPE = 268435960,
+ AUTH_TIMEOUT = 805306873,
+ ALLOW_WHILE_ON_BODY = 1879048698,
+ TRUSTED_USER_PRESENCE_REQUIRED = 1879048699,
+ TRUSTED_CONFIRMATION_REQUIRED = 1879048700,
+ UNLOCKED_DEVICE_REQUIRED = 1879048701,
+ APPLICATION_ID = -1879047591,
+ APPLICATION_DATA = -1879047492,
+ CREATION_DATETIME = 1610613437,
+ ORIGIN = 268436158,
+ ROOT_OF_TRUST = -1879047488,
+ OS_VERSION = 805307073,
+ OS_PATCHLEVEL = 805307074,
+ UNIQUE_ID = -1879047485,
+ ATTESTATION_CHALLENGE = -1879047484,
+ ATTESTATION_APPLICATION_ID = -1879047483,
+ ATTESTATION_ID_BRAND = -1879047482,
+ ATTESTATION_ID_DEVICE = -1879047481,
+ ATTESTATION_ID_PRODUCT = -1879047480,
+ ATTESTATION_ID_SERIAL = -1879047479,
+ ATTESTATION_ID_IMEI = -1879047478,
+ ATTESTATION_ID_MEID = -1879047477,
+ ATTESTATION_ID_MANUFACTURER = -1879047476,
+ ATTESTATION_ID_MODEL = -1879047475,
+ VENDOR_PATCHLEVEL = 805307086,
+ BOOT_PATCHLEVEL = 805307087,
+ DEVICE_UNIQUE_ATTESTATION = 1879048912,
+ IDENTITY_CREDENTIAL_KEY = 1879048913,
+ STORAGE_KEY = 1879048914,
+ ASSOCIATED_DATA = -1879047192,
+ NONCE = -1879047191,
+ MAC_LENGTH = 805307371,
+ RESET_SINCE_ID_ROTATION = 1879049196,
+ CONFIRMATION_TOKEN = -1879047187,
+ } }
+ pub(crate) mod mangled { pub use super::Tag as _7_android_8_hardware_7_keymint_3_Tag; }
+ }
+ pub mod TagType {
+ #![allow(non_upper_case_globals)]
+ use binder::declare_binder_enum;
+ declare_binder_enum! { TagType : i32 {
+ INVALID = 0,
+ ENUM = 268435456,
+ ENUM_REP = 536870912,
+ UINT = 805306368,
+ UINT_REP = 1073741824,
+ ULONG = 1342177280,
+ DATE = 1610612736,
+ BOOL = 1879048192,
+ BIGNUM = -2147483648,
+ BYTES = -1879048192,
+ ULONG_REP = -1610612736,
+ } }
+ pub(crate) mod mangled { pub use super::TagType as _7_android_8_hardware_7_keymint_7_TagType; }
+ }
+ pub mod Timestamp {
+ #[derive(Debug)]
+ pub struct Timestamp {
+ pub milliSeconds: i64,
+ }
+ pub(crate) mod mangled { pub use super::Timestamp as _7_android_8_hardware_7_keymint_9_Timestamp; }
+ impl Default for Timestamp {
+ fn default() -> Self {
+ Self {
+ milliSeconds: 0,
+ }
+ }
+ }
+ impl binder::parcel::Serialize for Timestamp {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for Timestamp {}
+ impl binder::parcel::SerializeOption for Timestamp {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.milliSeconds)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for Timestamp {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for Timestamp {}
+ impl binder::parcel::DeserializeOption for Timestamp {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.milliSeconds = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ pub mod VerificationToken {
+ #[derive(Debug)]
+ pub struct VerificationToken {
+ pub challenge: i64,
+ pub timestamp: crate::mangled::_7_android_8_hardware_7_keymint_9_Timestamp,
+ pub securityLevel: crate::mangled::_7_android_8_hardware_7_keymint_13_SecurityLevel,
+ pub mac: Vec<u8>,
+ }
+ pub(crate) mod mangled { pub use super::VerificationToken as _7_android_8_hardware_7_keymint_17_VerificationToken; }
+ impl Default for VerificationToken {
+ fn default() -> Self {
+ Self {
+ challenge: 0,
+ timestamp: Default::default(),
+ securityLevel: Default::default(),
+ mac: Default::default(),
+ }
+ }
+ }
+ impl binder::parcel::Serialize for VerificationToken {
+ fn serialize(&self, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ <Self as binder::parcel::SerializeOption>::serialize_option(Some(self), parcel)
+ }
+ }
+ impl binder::parcel::SerializeArray for VerificationToken {}
+ impl binder::parcel::SerializeOption for VerificationToken {
+ fn serialize_option(this: Option<&Self>, parcel: &mut binder::parcel::Parcel) -> binder::Result<()> {
+ let this = if let Some(this) = this {
+ parcel.write(&1i32)?;
+ this
+ } else {
+ return parcel.write(&0i32);
+ };
+ let start_pos = parcel.get_data_position();
+ parcel.write(&0i32)?;
+ parcel.write(&this.challenge)?;
+ parcel.write(&this.timestamp)?;
+ parcel.write(&this.securityLevel)?;
+ parcel.write(&this.mac)?;
+ let end_pos = parcel.get_data_position();
+ let parcelable_size = (end_pos - start_pos) as i32;
+ unsafe { parcel.set_data_position(start_pos)?; }
+ parcel.write(&parcelable_size)?;
+ unsafe { parcel.set_data_position(end_pos)?; }
+ Ok(())
+ }
+ }
+ impl binder::parcel::Deserialize for VerificationToken {
+ fn deserialize(parcel: &binder::parcel::Parcel) -> binder::Result<Self> {
+ <Self as binder::parcel::DeserializeOption>::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(binder::StatusCode::UNEXPECTED_NULL))
+ }
+ }
+ impl binder::parcel::DeserializeArray for VerificationToken {}
+ impl binder::parcel::DeserializeOption for VerificationToken {
+ fn deserialize_option(parcel: &binder::parcel::Parcel) -> binder::Result<Option<Self>> {
+ let status: i32 = parcel.read()?;
+ if status == 0 { return Ok(None); }
+ let start_pos = parcel.get_data_position();
+ let parcelable_size: i32 = parcel.read()?;
+ if parcelable_size < 0 { return Err(binder::StatusCode::BAD_VALUE); }
+ let mut result = Self::default();
+ result.challenge = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.timestamp = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.securityLevel = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ result.mac = parcel.read()?;
+ if (parcel.get_data_position() - start_pos) == parcelable_size {
+ return Ok(Some(result));
+ }
+ Ok(Some(result))
+ }
+ }
+ }
+ }
+ }
+ }
+}
+pub mod mangled {
+ pub use super::aidl::android::hardware::keymint::Algorithm::mangled::*;
+ pub use super::aidl::android::hardware::keymint::BeginResult::mangled::*;
+ pub use super::aidl::android::hardware::keymint::BlockMode::mangled::*;
+ pub use super::aidl::android::hardware::keymint::Certificate::mangled::*;
+ pub use super::aidl::android::hardware::keymint::Constants::mangled::*;
+ pub use super::aidl::android::hardware::keymint::Digest::mangled::*;
+ pub use super::aidl::android::hardware::keymint::EcCurve::mangled::*;
+ pub use super::aidl::android::hardware::keymint::ErrorCode::mangled::*;
+ pub use super::aidl::android::hardware::keymint::HardwareAuthToken::mangled::*;
+ pub use super::aidl::android::hardware::keymint::HardwareAuthenticatorType::mangled::*;
+ pub use super::aidl::android::hardware::keymint::HmacSharingParameters::mangled::*;
+ pub use super::aidl::android::hardware::keymint::IKeyMintDevice::mangled::*;
+ pub use super::aidl::android::hardware::keymint::IKeyMintOperation::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyCharacteristics::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyDerivationFunction::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyFormat::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyMintHardwareInfo::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyOrigin::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyParameter::mangled::*;
+ pub use super::aidl::android::hardware::keymint::KeyPurpose::mangled::*;
+ pub use super::aidl::android::hardware::keymint::PaddingMode::mangled::*;
+ pub use super::aidl::android::hardware::keymint::SecurityLevel::mangled::*;
+ pub use super::aidl::android::hardware::keymint::Tag::mangled::*;
+ pub use super::aidl::android::hardware::keymint::TagType::mangled::*;
+ pub use super::aidl::android::hardware::keymint::Timestamp::mangled::*;
+ pub use super::aidl::android::hardware::keymint::VerificationToken::mangled::*;
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
new file mode 100644
index 0000000..df71d94
--- /dev/null
+++ b/keystore2/src/database.rs
@@ -0,0 +1,1654 @@
+// 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.
+
+//! This is the Keystore 2.0 database module.
+//! The database module provides a connection to the backing SQLite store.
+//! We have two databases one for persistent key blob storage and one for
+//! items that have a per boot life cycle.
+//!
+//! ## Persistent database
+//! The persistent database has tables for key blobs. They are organized
+//! as follows:
+//! The `keyentry` table is the primary table for key entries. It is
+//! accompanied by two tables for blobs and parameters.
+//! Each key entry occupies exactly one row in the `keyentry` table and
+//! zero or more rows in the tables `blobentry` and `keyparameter`.
+//!
+//! ## Per boot database
+//! The per boot database stores items with a per boot lifecycle.
+//! Currently, there is only the `grant` table in this database.
+//! Grants are references to a key that can be used to access a key by
+//! clients that don't own that key. Grants can only be created by the
+//! owner of a key. And only certain components can create grants.
+//! This is governed by SEPolicy.
+//!
+//! ## Access control
+//! Some database functions that load keys or create grants perform
+//! access control. This is because in some cases access control
+//! can only be performed after some information about the designated
+//! key was loaded from the database. To decouple the permission checks
+//! from the database module these functions take permission check
+//! callbacks.
+
+use crate::error::{Error as KsError, ResponseCode};
+use crate::key_parameter::{KeyParameter, SqlField, Tag};
+use crate::permission::KeyPermSet;
+use anyhow::{anyhow, Context, Result};
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, SecurityLevel::SecurityLevel,
+};
+
+#[cfg(not(test))]
+use rand::prelude::random;
+use rusqlite::{
+ params, types::FromSql, types::FromSqlResult, types::ToSqlOutput, types::ValueRef, Connection,
+ OptionalExtension, Row, Rows, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+};
+use std::sync::Once;
+#[cfg(test)]
+use tests::random;
+
+/// Keys have a KeyMint blob component and optional public certificate and
+/// certificate chain components.
+/// KeyEntryLoadBits is a bitmap that indicates to `KeystoreDB::load_key_entry`
+/// which components shall be loaded from the database if present.
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyEntryLoadBits(u32);
+
+impl KeyEntryLoadBits {
+ /// Indicate to `KeystoreDB::load_key_entry` that no component shall be loaded.
+ pub const NONE: KeyEntryLoadBits = Self(0);
+ /// Indicate to `KeystoreDB::load_key_entry` that the KeyMint component shall be loaded.
+ pub const KM: KeyEntryLoadBits = Self(1);
+ /// Indicate to `KeystoreDB::load_key_entry` that the Public components shall be loaded.
+ pub const PUBLIC: KeyEntryLoadBits = Self(2);
+ /// Indicate to `KeystoreDB::load_key_entry` that both components shall be loaded.
+ pub const BOTH: KeyEntryLoadBits = Self(3);
+
+ /// Returns true if this object indicates that the public components shall be loaded.
+ pub const fn load_public(&self) -> bool {
+ self.0 & Self::PUBLIC.0 != 0
+ }
+
+ /// Returns true if the object indicates that the KeyMint component shall be loaded.
+ pub const fn load_km(&self) -> bool {
+ self.0 & Self::KM.0 != 0
+ }
+}
+
+/// This type represents a Keystore 2.0 key entry.
+/// An entry has a unique `id` by which it can be found in the database.
+/// It has a security level field, key parameters, and three optional fields
+/// for the KeyMint blob, public certificate and a public certificate chain.
+#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyEntry {
+ id: i64,
+ km_blob: Option<Vec<u8>>,
+ cert: Option<Vec<u8>>,
+ cert_chain: Option<Vec<u8>>,
+ sec_level: SecurityLevel,
+ parameters: Vec<KeyParameter>,
+}
+
+impl KeyEntry {
+ /// Returns the unique id of the Key entry.
+ pub fn id(&self) -> i64 {
+ self.id
+ }
+ /// Exposes the optional KeyMint blob.
+ pub fn km_blob(&self) -> &Option<Vec<u8>> {
+ &self.km_blob
+ }
+ /// Extracts the Optional KeyMint blob.
+ pub fn take_km_blob(&mut self) -> Option<Vec<u8>> {
+ self.km_blob.take()
+ }
+ /// Exposes the optional public certificate.
+ pub fn cert(&self) -> &Option<Vec<u8>> {
+ &self.cert
+ }
+ /// Extracts the optional public certificate.
+ pub fn take_cert(&mut self) -> Option<Vec<u8>> {
+ self.cert.take()
+ }
+ /// Exposes the optional public certificate chain.
+ pub fn cert_chain(&self) -> &Option<Vec<u8>> {
+ &self.cert_chain
+ }
+ /// Extracts the optional public certificate_chain.
+ pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
+ self.cert_chain.take()
+ }
+ /// Returns the security level of the key entry.
+ pub fn sec_level(&self) -> SecurityLevel {
+ self.sec_level
+ }
+}
+
+/// Indicates the sub component of a key entry for persistent storage.
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct SubComponentType(u32);
+impl SubComponentType {
+ /// Persistent identifier for a KeyMint blob.
+ pub const KM_BLOB: SubComponentType = Self(0);
+ /// Persistent identifier for a certificate blob.
+ pub const CERT: SubComponentType = Self(1);
+ /// Persistent identifier for a certificate chain blob.
+ pub const CERT_CHAIN: SubComponentType = Self(2);
+}
+
+impl ToSql for SubComponentType {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ self.0.to_sql()
+ }
+}
+
+impl FromSql for SubComponentType {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ Ok(Self(u32::column_result(value)?))
+ }
+}
+
+static INIT_TABLES: Once = Once::new();
+
+/// KeystoreDB wraps a connection to an SQLite database and tracks its
+/// ownership. It also implements all of Keystore 2.0's database functionality.
+pub struct KeystoreDB {
+ conn: Connection,
+}
+
+impl KeystoreDB {
+ /// This will create a new database connection connecting the two
+ /// files persistent.sqlite and perboot.sqlite in the current working
+ /// directory, which is usually `/data/misc/keystore/`.
+ /// It also attempts to initialize all of the tables on the first instantiation
+ /// per service startup. KeystoreDB cannot be used by multiple threads.
+ /// Each thread should open their own connection using `thread_local!`.
+ pub fn new() -> Result<Self> {
+ let conn = Self::make_connection("file:persistent.sqlite", "file:perboot.sqlite")?;
+
+ INIT_TABLES.call_once(|| Self::init_tables(&conn).expect("Failed to initialize tables."));
+ Ok(Self { conn })
+ }
+
+ fn init_tables(conn: &Connection) -> Result<()> {
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+ id INTEGER UNIQUE,
+ creation_date DATETIME,
+ domain INTEGER,
+ namespace INTEGER,
+ alias TEXT);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keyentry\" table.")?;
+
+ conn.execute(
+ "CREATE VIEW IF NOT EXISTS persistent.orphaned AS
+ SELECT id FROM persistent.keyentry WHERE domain IS NULL;",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"orphaned\" view")?;
+
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.blobentry (
+ id INTEGER PRIMARY KEY,
+ subcomponent_type INTEGER,
+ keyentryid INTEGER,
+ blob BLOB,
+ sec_level INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"blobentry\" table.")?;
+
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+ keyentryid INTEGER,
+ tag INTEGER,
+ data ANY,
+ security_level INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keyparameter\" table.")?;
+
+ // TODO only drop the perboot table if we start up for the first time per boot.
+ // Right now this is done once per startup which will lose some information
+ // upon a crash.
+ // Note: This is no regression with respect to the legacy Keystore.
+ conn.execute("DROP TABLE IF EXISTS perboot.grant;", NO_PARAMS)
+ .context("Failed to drop perboot.grant table")?;
+ conn.execute(
+ "CREATE TABLE perboot.grant (
+ id INTEGER UNIQUE,
+ grantee INTEGER,
+ keyentryid INTEGER,
+ access_vector INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"grant\" table.")?;
+
+ Ok(())
+ }
+
+ fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> {
+ let conn =
+ Connection::open_in_memory().context("Failed to initialize SQLite connection.")?;
+
+ conn.execute("ATTACH DATABASE ? as persistent;", params![persistent_file])
+ .context("Failed to attach database persistent.")?;
+ conn.execute("ATTACH DATABASE ? as perboot;", params![perboot_file])
+ .context("Failed to attach database perboot.")?;
+
+ Ok(conn)
+ }
+
+ /// Creates a new key entry and allocates a new randomized id for the new key.
+ /// The key id gets associated with a domain and namespace but not with an alias.
+ /// To complete key generation `rebind_alias` should be called after all of the
+ /// key artifacts, i.e., blobs and parameters have been associated with the new
+ /// key id. Finalizing with `rebind_alias` makes the creation of a new key entry
+ /// atomic even if key generation is not.
+ pub fn create_key_entry(&self, domain: Domain, namespace: i64) -> Result<i64> {
+ match domain {
+ Domain::APP | Domain::SELINUX => {}
+ _ => {
+ return Err(KsError::sys())
+ .context(format!("Domain {:?} must be either App or SELinux.", domain));
+ }
+ }
+ Self::insert_with_retry(|id| {
+ self.conn.execute(
+ "INSERT into persistent.keyentry (id, creation_date, domain, namespace, alias)
+ VALUES(?, datetime('now'), ?, ?, NULL);",
+ params![id, domain.0 as u32, namespace],
+ )
+ })
+ .context("In create_key_entry")
+ }
+
+ /// Inserts a new blob and associates it with the given key id. Each blob
+ /// has a sub component type and a security level.
+ /// Each key can have one of each sub component type associated. If more
+ /// are added only the most recent can be retrieved, and superseded blobs
+ /// will get garbage collected. The security level field of components
+ /// other than `SubComponentType::KM_BLOB` are ignored.
+ pub fn insert_blob(
+ &mut self,
+ key_id: i64,
+ sc_type: SubComponentType,
+ blob: &[u8],
+ sec_level: SecurityLevel,
+ ) -> Result<()> {
+ self.conn
+ .execute(
+ "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob, sec_level)
+ VALUES (?, ?, ?, ?);",
+ params![sc_type, key_id, blob, sec_level.0],
+ )
+ .context("Failed to insert blob.")?;
+ Ok(())
+ }
+
+ /// Inserts a collection of key parameters into the `persistent.keyparameter` table
+ /// and associates them with the given `key_id`.
+ pub fn insert_keyparameter<'a>(
+ &mut self,
+ key_id: i64,
+ params: impl IntoIterator<Item = &'a KeyParameter>,
+ ) -> Result<()> {
+ let mut stmt = self
+ .conn
+ .prepare(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES (?, ?, ?, ?);",
+ )
+ .context("In insert_keyparameter: Failed to prepare statement.")?;
+
+ let iter = params.into_iter();
+ for p in iter {
+ stmt.insert(params![
+ key_id,
+ p.get_tag().0,
+ p.key_parameter_value(),
+ p.security_level().0
+ ])
+ .with_context(|| format!("In insert_keyparameter: Failed to insert {:?}", p))?;
+ }
+ Ok(())
+ }
+
+ /// Updates the alias column of the given key id `newid` with the given alias,
+ /// and atomically, removes the alias, domain, and namespace from another row
+ /// with the same alias-domain-namespace tuple if such row exits.
+ pub fn rebind_alias(
+ &mut self,
+ newid: i64,
+ alias: &str,
+ domain: Domain,
+ namespace: i64,
+ ) -> Result<()> {
+ match domain {
+ Domain::APP | Domain::SELINUX => {}
+ _ => {
+ return Err(KsError::sys()).context(format!(
+ "In rebind_alias: Domain {:?} must be either App or SELinux.",
+ domain
+ ));
+ }
+ }
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Immediate)
+ .context("In rebind_alias: Failed to initialize transaction.")?;
+ tx.execute(
+ "UPDATE persistent.keyentry
+ SET alias = NULL, domain = NULL, namespace = NULL
+ WHERE alias = ? AND domain = ? AND namespace = ?;",
+ params![alias, domain.0 as u32, namespace],
+ )
+ .context("In rebind_alias: Failed to rebind existing entry.")?;
+ let result = tx
+ .execute(
+ "UPDATE persistent.keyentry
+ SET alias = ?
+ WHERE id = ? AND domain = ? AND namespace = ?;",
+ params![alias, newid, domain.0 as u32, namespace],
+ )
+ .context("In rebind_alias: Failed to set alias.")?;
+ if result != 1 {
+ // Note that this explicit rollback is not required, as
+ // the transaction should rollback if we do not commit it.
+ // We leave it here for readability.
+ tx.rollback().context("In rebind_alias: Failed to rollback a failed transaction.")?;
+ return Err(KsError::sys()).context(format!(
+ "In rebind_alias: Expected to update a single entry but instead updated {}.",
+ result
+ ));
+ }
+ tx.commit().context("In rebind_alias: Failed to commit transaction.")
+ }
+
+ // Helper function loading the key_id given the key descriptor
+ // tuple comprising domain, namespace, and alias.
+ // Requires a valid transaction.
+ fn load_key_entry_id(key: &KeyDescriptor, tx: &Transaction) -> Result<i64> {
+ let alias = key
+ .alias
+ .as_ref()
+ .map_or_else(|| Err(KsError::sys()), Ok)
+ .context("In load_key_entry_id: Alias must be specified.")?;
+ let mut stmt = tx
+ .prepare(
+ "SELECT id FROM persistent.keyentry
+ WHERE
+ domain = ?
+ AND namespace = ?
+ AND alias = ?;",
+ )
+ .context("In load_key_entry_id: Failed to select from keyentry table.")?;
+ let mut rows = stmt
+ .query(params![key.domain.0 as u32, key.nspace, alias])
+ .context("In load_key_entry_id: Failed to read from keyentry table.")?;
+ Self::with_rows_extract_one(&mut rows, |row| {
+ row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?
+ .get(0)
+ .context("Failed to unpack id.")
+ })
+ .context("In load_key_entry_id.")
+ }
+
+ /// This helper function completes the access tuple of a key, which is required
+ /// to perform access control. The strategy depends on the `domain` field in the
+ /// key descriptor.
+ /// * Domain::SELINUX: The access tuple is complete and this function only loads
+ /// the key_id for further processing.
+ /// * Domain::APP: Like Domain::SELINUX, but the tuple is completed by `caller_uid`
+ /// which serves as the namespace.
+ /// * Domain::GRANT: The grant table is queried for the `key_id` and the
+ /// `access_vector`.
+ /// * Domain::KEY_ID: The keyentry table is queried for the owning `domain` and
+ /// `namespace`.
+ /// In each case the information returned is sufficient to perform the access
+ /// check and the key id can be used to load further key artifacts.
+ fn load_access_tuple(
+ tx: &Transaction,
+ key: KeyDescriptor,
+ caller_uid: u32,
+ ) -> Result<(i64, KeyDescriptor, Option<KeyPermSet>)> {
+ match key.domain {
+ // Domain App or SELinux. In this case we load the key_id from
+ // the keyentry database for further loading of key components.
+ // We already have the full access tuple to perform access control.
+ // The only distinction is that we use the caller_uid instead
+ // of the caller supplied namespace if the domain field is
+ // Domain::APP.
+ Domain::APP | Domain::SELINUX => {
+ let mut access_key = key;
+ if access_key.domain == Domain::APP {
+ access_key.nspace = caller_uid as i64;
+ }
+ let key_id = Self::load_key_entry_id(&access_key, &tx)
+ .with_context(|| format!("With key.domain = {:?}.", access_key.domain))?;
+
+ Ok((key_id, access_key, None))
+ }
+
+ // Domain::GRANT. In this case we load the key_id and the access_vector
+ // from the grant table.
+ Domain::GRANT => {
+ let mut stmt = tx
+ .prepare(
+ "SELECT keyentryid, access_vector FROM perboot.grant
+ WHERE grantee = ? AND id = ?;",
+ )
+ .context("Domain::GRANT prepare statement failed")?;
+ let mut rows = stmt
+ .query(params![caller_uid as i64, key.nspace])
+ .context("Domain:Grant: query failed.")?;
+ let (key_id, access_vector): (i64, i32) =
+ Self::with_rows_extract_one(&mut rows, |row| {
+ let r =
+ row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
+ Ok((
+ r.get(0).context("Failed to unpack key_id.")?,
+ r.get(1).context("Failed to unpack access_vector.")?,
+ ))
+ })
+ .context("Domain::GRANT.")?;
+ Ok((key_id, key, Some(access_vector.into())))
+ }
+
+ // Domain::KEY_ID. In this case we load the domain and namespace from the
+ // keyentry database because we need them for access control.
+ Domain::KEY_ID => {
+ let mut stmt = tx
+ .prepare(
+ "SELECT domain, namespace FROM persistent.keyentry
+ WHERE
+ id = ?;",
+ )
+ .context("Domain::KEY_ID: prepare statement failed")?;
+ let mut rows =
+ stmt.query(params![key.nspace]).context("Domain::KEY_ID: query failed.")?;
+ let (domain, namespace): (Domain, i64) =
+ Self::with_rows_extract_one(&mut rows, |row| {
+ let r =
+ row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
+ Ok((
+ Domain(r.get(0).context("Failed to unpack domain.")?),
+ r.get(1).context("Failed to unpack namespace.")?,
+ ))
+ })
+ .context("Domain::KEY_ID.")?;
+ let key_id = key.nspace;
+ let mut access_key = key;
+ access_key.domain = domain;
+ access_key.nspace = namespace;
+
+ Ok((key_id, access_key, None))
+ }
+ _ => Err(anyhow!(KsError::sys())),
+ }
+ }
+
+ fn load_blob_components(
+ key_id: i64,
+ load_bits: KeyEntryLoadBits,
+ tx: &Transaction,
+ ) -> Result<(SecurityLevel, Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+ let mut stmt = tx
+ .prepare(
+ "SELECT MAX(id), sec_level, subcomponent_type, blob FROM persistent.blobentry
+ WHERE keyentryid = ? GROUP BY subcomponent_type;",
+ )
+ .context("In load_blob_components: prepare statement failed.")?;
+
+ let mut rows =
+ stmt.query(params![key_id]).context("In load_blob_components: query failed.")?;
+
+ let mut sec_level: SecurityLevel = Default::default();
+ let mut km_blob: Option<Vec<u8>> = None;
+ let mut cert_blob: Option<Vec<u8>> = None;
+ let mut cert_chain_blob: Option<Vec<u8>> = None;
+ Self::with_rows_extract_all(&mut rows, |row| {
+ let sub_type: SubComponentType =
+ row.get(2).context("Failed to extract subcomponent_type.")?;
+ match (sub_type, load_bits.load_public()) {
+ (SubComponentType::KM_BLOB, _) => {
+ sec_level =
+ SecurityLevel(row.get(1).context("Failed to extract security level.")?);
+ if load_bits.load_km() {
+ km_blob = Some(row.get(3).context("Failed to extract KM blob.")?);
+ }
+ }
+ (SubComponentType::CERT, true) => {
+ cert_blob =
+ Some(row.get(3).context("Failed to extract public certificate blob.")?);
+ }
+ (SubComponentType::CERT_CHAIN, true) => {
+ cert_chain_blob =
+ Some(row.get(3).context("Failed to extract certificate chain blob.")?);
+ }
+ (SubComponentType::CERT, _) | (SubComponentType::CERT_CHAIN, _) => {}
+ _ => Err(KsError::sys()).context("Unknown subcomponent type.")?,
+ }
+ Ok(())
+ })
+ .context("In load_blob_components.")?;
+
+ Ok((sec_level, km_blob, cert_blob, cert_chain_blob))
+ }
+
+ fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> {
+ let mut stmt = tx
+ .prepare(
+ "SELECT tag, data, security_level from persistent.keyparameter
+ WHERE keyentryid = ?;",
+ )
+ .context("In load_key_parameters: prepare statement failed.")?;
+
+ let mut parameters: Vec<KeyParameter> = Vec::new();
+
+ let mut rows =
+ stmt.query(params![key_id]).context("In load_key_parameters: query failed.")?;
+ Self::with_rows_extract_all(&mut rows, |row| {
+ let tag = Tag(row.get(0).context("Failed to read tag.")?);
+ let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?);
+ parameters.push(
+ KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level)
+ .context("Failed to read KeyParameter.")?,
+ );
+ Ok(())
+ })
+ .context("In load_key_parameters.")?;
+
+ Ok(parameters)
+ }
+
+ /// Load a key entry by the given key descriptor.
+ /// It uses the `check_permission` callback to verify if the access is allowed
+ /// given the key access tuple read from the database using `load_access_tuple`.
+ /// With `load_bits` the caller may specify which blobs shall be loaded from
+ /// the blob database.
+ pub fn load_key_entry(
+ &mut self,
+ key: KeyDescriptor,
+ load_bits: KeyEntryLoadBits,
+ caller_uid: u32,
+ check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
+ ) -> Result<KeyEntry> {
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Deferred)
+ .context("In load_key_entry: Failed to initialize transaction.")?;
+
+ // Load the key_id and complete the access control tuple.
+ let (key_id, access_key_descriptor, access_vector) =
+ Self::load_access_tuple(&tx, key, caller_uid).context("In load_key_entry.")?;
+
+ // Perform access control. It is vital that we return here if the permission is denied.
+ // So do not touch that '?' at the end.
+ check_permission(&access_key_descriptor, access_vector).context("In load_key_entry.")?;
+
+ let (sec_level, km_blob, cert_blob, cert_chain_blob) =
+ Self::load_blob_components(key_id, load_bits, &tx).context("In load_key_entry.")?;
+
+ let parameters = Self::load_key_parameters(key_id, &tx).context("In load_key_entry.")?;
+
+ tx.commit().context("In load_key_entry: Failed to commit transaction.")?;
+
+ Ok(KeyEntry {
+ id: key_id,
+ km_blob,
+ cert: cert_blob,
+ cert_chain: cert_chain_blob,
+ sec_level,
+ parameters,
+ })
+ }
+
+ /// Adds a grant to the grant table.
+ /// Like `load_key_entry` this function loads the access tuple before
+ /// it uses the callback for a permission check. Upon success,
+ /// it inserts the `grantee_uid`, `key_id`, and `access_vector` into the
+ /// grant table. The new row will have a randomized id, which is used as
+ /// grant id in the namespace field of the resulting KeyDescriptor.
+ pub fn grant(
+ &mut self,
+ key: KeyDescriptor,
+ caller_uid: u32,
+ grantee_uid: u32,
+ access_vector: KeyPermSet,
+ check_permission: impl FnOnce(&KeyDescriptor, &KeyPermSet) -> Result<()>,
+ ) -> Result<KeyDescriptor> {
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Immediate)
+ .context("In grant: Failed to initialize transaction.")?;
+
+ // Load the key_id and complete the access control tuple.
+ // We ignore the access vector here because grants cannot be granted.
+ // The access vector returned here expresses the permissions the
+ // grantee has if key.domain == Domain::GRANT. But this vector
+ // cannot include the grant permission by design, so there is no way the
+ // subsequent permission check can pass.
+ // We could check key.domain == Domain::GRANT and fail early.
+ // But even if we load the access tuple by grant here, the permission
+ // check denies the attempt to create a grant by grant descriptor.
+ let (key_id, access_key_descriptor, _) =
+ Self::load_access_tuple(&tx, key, caller_uid).context("In grant")?;
+
+ // Perform access control. It is vital that we return here if the permission
+ // was denied. So do not touch that '?' at the end of the line.
+ // This permission check checks if the caller has the grant permission
+ // for the given key and in addition to all of the permissions
+ // expressed in `access_vector`.
+ check_permission(&access_key_descriptor, &access_vector)
+ .context("In grant: check_permission failed.")?;
+
+ let grant_id = if let Some(grant_id) = tx
+ .query_row(
+ "SELECT id FROM perboot.grant
+ WHERE keyentryid = ? AND grantee = ?;",
+ params![key_id, grantee_uid],
+ |row| row.get(0),
+ )
+ .optional()
+ .context("In grant: Failed get optional existing grant id.")?
+ {
+ tx.execute(
+ "UPDATE perboot.grant
+ SET access_vector = ?
+ WHERE id = ?;",
+ params![i32::from(access_vector), grant_id],
+ )
+ .context("In grant: Failed to update existing grant.")?;
+ grant_id
+ } else {
+ Self::insert_with_retry(|id| {
+ tx.execute(
+ "INSERT INTO perboot.grant (id, grantee, keyentryid, access_vector)
+ VALUES (?, ?, ?, ?);",
+ params![id, grantee_uid, key_id, i32::from(access_vector)],
+ )
+ })
+ .context("In grant")?
+ };
+ tx.commit().context("In grant: failed to commit transaction.")?;
+
+ Ok(KeyDescriptor { domain: Domain::GRANT, nspace: grant_id, alias: None, blob: None })
+ }
+
+ /// This function checks permissions like `grant` and `load_key_entry`
+ /// before removing a grant from the grant table.
+ pub fn ungrant(
+ &mut self,
+ key: KeyDescriptor,
+ caller_uid: u32,
+ grantee_uid: u32,
+ check_permission: impl FnOnce(&KeyDescriptor) -> Result<()>,
+ ) -> Result<()> {
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Immediate)
+ .context("In ungrant: Failed to initialize transaction.")?;
+
+ // Load the key_id and complete the access control tuple.
+ // We ignore the access vector here because grants cannot be granted.
+ let (key_id, access_key_descriptor, _) =
+ Self::load_access_tuple(&tx, key, caller_uid).context("In ungrant.")?;
+
+ // Perform access control. We must return here if the permission
+ // was denied. So do not touch the '?' at the end of this line.
+ check_permission(&access_key_descriptor).context("In grant: check_permission failed.")?;
+
+ tx.execute(
+ "DELETE FROM perboot.grant
+ WHERE keyentryid = ? AND grantee = ?;",
+ params![key_id, grantee_uid],
+ )
+ .context("Failed to delete grant.")?;
+
+ tx.commit().context("In ungrant: failed to commit transaction.")?;
+
+ Ok(())
+ }
+
+ // Generates a random id and passes it to the given function, which will
+ // try to insert it into a database. If that insertion fails, retry;
+ // otherwise return the id.
+ fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> {
+ loop {
+ let newid: i64 = random();
+ match inserter(newid) {
+ // If the id already existed, try again.
+ Err(rusqlite::Error::SqliteFailure(
+ libsqlite3_sys::Error {
+ code: libsqlite3_sys::ErrorCode::ConstraintViolation,
+ extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
+ },
+ _,
+ )) => (),
+ Err(e) => {
+ return Err(e).context("In insert_with_retry: failed to insert into database.")
+ }
+ _ => return Ok(newid),
+ }
+ }
+ }
+
+ // Takes Rows as returned by a query call on prepared statement.
+ // Extracts exactly one row with the `row_extractor` and fails if more
+ // rows are available.
+ // If no row was found, `None` is passed to the `row_extractor`.
+ // This allows the row extractor to decide on an error condition or
+ // a different default behavior.
+ fn with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T>
+ where
+ F: FnOnce(Option<&Row<'a>>) -> Result<T>,
+ {
+ let result =
+ row_extractor(rows.next().context("with_rows_extract_one: Failed to unpack row.")?);
+
+ rows.next()
+ .context("In with_rows_extract_one: Failed to unpack unexpected row.")?
+ .map_or_else(|| Ok(()), |_| Err(KsError::sys()))
+ .context("In with_rows_extract_one: Unexpected row.")?;
+
+ result
+ }
+
+ fn with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()>
+ where
+ F: FnMut(&Row<'a>) -> Result<()>,
+ {
+ loop {
+ match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
+ Some(row) => {
+ row_extractor(&row).context("In with_rows_extract_all.")?;
+ }
+ None => break Ok(()),
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use crate::key_parameter::{
+ Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
+ KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
+ };
+ use crate::key_perm_set;
+ use crate::permission::{KeyPerm, KeyPermSet};
+ use rusqlite::NO_PARAMS;
+ use std::cell::RefCell;
+
+ static PERSISTENT_TEST_SQL: &str = "/data/local/tmp/persistent.sqlite";
+ static PERBOOT_TEST_SQL: &str = "/data/local/tmp/perboot.sqlite";
+
+ fn new_test_db() -> Result<KeystoreDB> {
+ let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
+
+ KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
+ Ok(KeystoreDB { conn })
+ }
+
+ fn new_test_db_with_persistent_file() -> Result<KeystoreDB> {
+ let conn = KeystoreDB::make_connection(PERSISTENT_TEST_SQL, PERBOOT_TEST_SQL)?;
+
+ KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
+ Ok(KeystoreDB { conn })
+ }
+
+ // Ensure that we're using the "injected" random function, not the real one.
+ #[test]
+ fn test_mocked_random() {
+ let rand1 = random();
+ let rand2 = random();
+ let rand3 = random();
+ if rand1 == rand2 {
+ assert_eq!(rand2 + 1, rand3);
+ } else {
+ assert_eq!(rand1 + 1, rand2);
+ assert_eq!(rand2, rand3);
+ }
+ }
+
+ // Test that we have the correct tables.
+ #[test]
+ fn test_tables() -> Result<()> {
+ let db = new_test_db()?;
+ let tables = db
+ .conn
+ .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
+ .query_map(params![], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<String>>>()?;
+ assert_eq!(tables.len(), 3);
+ assert_eq!(tables[0], "blobentry");
+ assert_eq!(tables[1], "keyentry");
+ assert_eq!(tables[2], "keyparameter");
+ let tables = db
+ .conn
+ .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")?
+ .query_map(params![], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<String>>>()?;
+ assert_eq!(tables.len(), 1);
+ assert_eq!(tables[0], "grant");
+ Ok(())
+ }
+
+ #[test]
+ fn test_no_persistence_for_tests() -> Result<()> {
+ let db = new_test_db()?;
+
+ db.create_key_entry(Domain::APP, 100)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 1);
+ let db = new_test_db()?;
+
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 0);
+ Ok(())
+ }
+
+ #[test]
+ fn test_persistence_for_files() -> Result<()> {
+ let _file_guard_persistent = TempFile { filename: PERSISTENT_TEST_SQL };
+ let _file_guard_perboot = TempFile { filename: PERBOOT_TEST_SQL };
+ let db = new_test_db_with_persistent_file()?;
+
+ db.create_key_entry(Domain::APP, 100)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 1);
+ let db = new_test_db_with_persistent_file()?;
+
+ let entries_new = get_keyentry(&db)?;
+ assert_eq!(entries, entries_new);
+ Ok(())
+ }
+
+ #[test]
+ fn test_create_key_entry() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>) {
+ (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
+ }
+
+ let db = new_test_db()?;
+
+ db.create_key_entry(Domain::APP, 100)?;
+ db.create_key_entry(Domain::SELINUX, 101)?;
+
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None));
+ assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None));
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ db.create_key_entry(Domain::GRANT, 102),
+ "Domain Domain(1) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.create_key_entry(Domain::BLOB, 103),
+ "Domain Domain(3) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.create_key_entry(Domain::KEY_ID, 104),
+ "Domain Domain(4) must be either App or SELinux.",
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_rebind_alias() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>) {
+ (ke.domain, ke.namespace, ke.alias.as_deref())
+ }
+
+ let mut db = new_test_db()?;
+ db.create_key_entry(Domain::APP, 42)?;
+ db.create_key_entry(Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None));
+
+ // Test that the first call to rebind_alias sets the alias.
+ db.rebind_alias(entries[0].id, "foo", Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), Some("foo")));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None));
+
+ // Test that the second call to rebind_alias also empties the old one.
+ db.rebind_alias(entries[1].id, "foo", Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (None, None, None));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo")));
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ db.rebind_alias(0, "foo", Domain::GRANT, 42),
+ "Domain Domain(1) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.rebind_alias(0, "foo", Domain::BLOB, 42),
+ "Domain Domain(3) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.rebind_alias(0, "foo", Domain::KEY_ID, 42),
+ "Domain Domain(4) must be either App or SELinux.",
+ );
+
+ // Test that we correctly handle setting an alias for something that does not exist.
+ check_result_is_error_containing_string(
+ db.rebind_alias(0, "foo", Domain::SELINUX, 42),
+ "Expected to update a single entry but instead updated 0",
+ );
+ // Test that we correctly abort the transaction in this case.
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (None, None, None));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo")));
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_grant_ungrant() -> Result<()> {
+ const CALLER_UID: u32 = 15;
+ const GRANTEE_UID: u32 = 12;
+ const SELINUX_NAMESPACE: i64 = 7;
+
+ let mut db = new_test_db()?;
+ db.conn.execute(
+ "INSERT INTO persistent.keyentry (id, creation_date, domain, namespace, alias)
+ VALUES (1, '1980', 0, 15, 'key'), (2, '1980', 2, 7, 'yek');",
+ NO_PARAMS,
+ )?;
+ let app_key = KeyDescriptor {
+ domain: super::Domain::APP,
+ nspace: 0,
+ alias: Some("key".to_string()),
+ blob: None,
+ };
+ const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()];
+ const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()];
+
+ // Reset totally predictable random number generator in case we
+ // are not the first test running on this thread.
+ reset_random();
+ let next_random = 0i64;
+
+ let app_granted_key =
+ db.grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
+ assert_eq!(*a, PVEC1);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::APP,
+ // namespace must be set to the caller_uid.
+ nspace: CALLER_UID as i64,
+ alias: Some("key".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })?;
+
+ assert_eq!(
+ app_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // The grantid is next_random due to the mock random number generator.
+ nspace: next_random,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ let selinux_key = KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ };
+
+ let selinux_granted_key =
+ db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| {
+ assert_eq!(*a, PVEC1);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ // namespace must be the supplied SELinux
+ // namespace.
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })?;
+
+ assert_eq!(
+ selinux_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // The grantid is next_random + 1 due to the mock random number generator.
+ nspace: next_random + 1,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ // This should update the existing grant with PVEC2.
+ let selinux_granted_key =
+ db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| {
+ assert_eq!(*a, PVEC2);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ // namespace must be the supplied SELinux
+ // namespace.
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })?;
+
+ assert_eq!(
+ selinux_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // Same grant id as before. The entry was only updated.
+ nspace: next_random + 1,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ {
+ // Limiting scope of stmt, because it borrows db.
+ let mut stmt = db
+ .conn
+ .prepare("SELECT id, grantee, keyentryid, access_vector FROM perboot.grant;")?;
+ let mut rows =
+ stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>(NO_PARAMS, |row| {
+ Ok((
+ row.get(0)?,
+ row.get(1)?,
+ row.get(2)?,
+ KeyPermSet::from(row.get::<_, i32>(3)?),
+ ))
+ })?;
+
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
+ assert!(rows.next().is_none());
+ }
+
+ debug_dump_keyentry_table(&mut db)?;
+ println!("app_key {:?}", app_key);
+ println!("selinux_key {:?}", selinux_key);
+
+ db.ungrant(app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+ db.ungrant(selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+
+ Ok(())
+ }
+
+ static TEST_KM_BLOB: &[u8] = b"my test blob";
+ static TEST_CERT_BLOB: &[u8] = b"my test cert";
+ static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
+
+ #[test]
+ fn test_insert_blob() -> Result<()> {
+ let mut db = new_test_db()?;
+ db.insert_blob(1, SubComponentType::KM_BLOB, TEST_KM_BLOB, SecurityLevel::SOFTWARE)?;
+ db.insert_blob(
+ 1,
+ SubComponentType::CERT,
+ TEST_CERT_BLOB,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ )?;
+ db.insert_blob(
+ 1,
+ SubComponentType::CERT_CHAIN,
+ TEST_CERT_CHAIN_BLOB,
+ SecurityLevel::STRONGBOX,
+ )?;
+
+ let mut stmt = db.conn.prepare(
+ "SELECT subcomponent_type, keyentryid, blob, sec_level FROM persistent.blobentry
+ ORDER BY sec_level ASC;",
+ )?;
+ let mut rows = stmt
+ .query_map::<(SubComponentType, i64, Vec<u8>, i64), _, _>(NO_PARAMS, |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+ })?;
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::KM_BLOB, 1, TEST_KM_BLOB.to_vec(), 0));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT, 1, TEST_CERT_BLOB.to_vec(), 1));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT_CHAIN, 1, TEST_CERT_CHAIN_BLOB.to_vec(), 2));
+
+ Ok(())
+ }
+
+ static TEST_ALIAS: &str = "my super duper key";
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_domain_app")?;
+ let key_entry = db.load_key_entry(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+ assert_eq!(
+ key_entry,
+ KeyEntry {
+ id: key_id,
+ km_blob: Some(TEST_KM_BLOB.to_vec()),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
+ parameters: make_test_params()
+ }
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_domain_selinux")?;
+ let key_entry = db.load_key_entry(
+ KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+ assert_eq!(
+ key_entry,
+ KeyEntry {
+ id: key_id,
+ km_blob: Some(TEST_KM_BLOB.to_vec()),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
+ parameters: make_test_params()
+ }
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_domain_key_id")?;
+ let key_entry = db.load_key_entry(
+ KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+ assert_eq!(
+ key_entry,
+ KeyEntry {
+ id: key_id,
+ km_blob: Some(TEST_KM_BLOB.to_vec()),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
+ parameters: make_test_params()
+ }
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_from_grant")?;
+
+ let granted_key = db.grant(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ 1,
+ 2,
+ key_perm_set![KeyPerm::use_()],
+ |_k, _av| Ok(()),
+ )?;
+
+ debug_dump_grant_table(&mut db)?;
+
+ let key_entry = db.load_key_entry(granted_key, KeyEntryLoadBits::BOTH, 2, |k, av| {
+ assert_eq!(Domain::GRANT, k.domain);
+ assert!(av.unwrap().includes(KeyPerm::use_()));
+ Ok(())
+ })?;
+
+ assert_eq!(
+ key_entry,
+ KeyEntry {
+ id: key_id,
+ km_blob: Some(TEST_KM_BLOB.to_vec()),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
+ parameters: make_test_params()
+ }
+ );
+ Ok(())
+ }
+
+ // Helpers
+
+ // Checks that the given result is an error containing the given string.
+ fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
+ let error_str = format!(
+ "{:#?}",
+ result.err().unwrap_or_else(|| panic!("Expected the error: {}", target))
+ );
+ assert!(
+ error_str.contains(target),
+ "The string \"{}\" should contain \"{}\"",
+ error_str,
+ target
+ );
+ }
+
+ #[derive(Debug, PartialEq)]
+ #[allow(dead_code)]
+ struct KeyEntryRow {
+ id: i64,
+ creation_date: String,
+ domain: Option<Domain>,
+ namespace: Option<i64>,
+ alias: Option<String>,
+ }
+
+ fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
+ db.conn
+ .prepare("SELECT * FROM persistent.keyentry;")?
+ .query_map(NO_PARAMS, |row| {
+ Ok(KeyEntryRow {
+ id: row.get(0)?,
+ creation_date: row.get(1)?,
+ domain: match row.get(2)? {
+ Some(i) => Some(Domain(i)),
+ None => None,
+ },
+ namespace: row.get(3)?,
+ alias: row.get(4)?,
+ })
+ })?
+ .map(|r| r.context("Could not read keyentry row."))
+ .collect::<Result<Vec<_>>>()
+ }
+
+ // Note: The parameters and SecurityLevel associations are nonsensical. This
+ // collection is only used to check if the parameters are preserved as expected by the
+ // database.
+ fn make_test_params() -> Vec<KeyParameter> {
+ vec![
+ KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::BlockMode(BlockMode::ECB),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::MD5),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::SHA_2_224),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::SHA_2_256),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_224),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_384),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_521),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::RSAPublicExponent(3),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::IncludeUniqueID,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::ActiveDateTime(1234567890),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::OriginationExpireDateTime(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::UsageExpireDateTime(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MinSecondsBetweenOps(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MaxUsesPerBoot(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::NoAuthRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
+ KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
+ KeyParameter::new(
+ KeyParameterValue::TrustedUserPresenceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::TrustedConfirmationRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::UnlockedDeviceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::CreationDateTime(12345677890),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
+ KeyParameter::new(
+ KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::VendorPatchLevel(3),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::BootPatchLevel(4),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MacLength(256),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ResetSinceIdRotation,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ ]
+ }
+
+ fn make_test_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ ) -> Result<i64> {
+ let key_id = db.create_key_entry(domain, namespace)?;
+ db.insert_blob(
+ key_id,
+ SubComponentType::KM_BLOB,
+ TEST_KM_BLOB,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ )?;
+ db.insert_blob(
+ key_id,
+ SubComponentType::CERT,
+ TEST_CERT_BLOB,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ )?;
+ db.insert_blob(
+ key_id,
+ SubComponentType::CERT_CHAIN,
+ TEST_CERT_CHAIN_BLOB,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ )?;
+ db.insert_keyparameter(key_id, &make_test_params())?;
+ db.rebind_alias(key_id, alias, domain, namespace)?;
+ Ok(key_id)
+ }
+
+ fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt = db.conn.prepare(
+ "SELECT id, creation_date, domain, namespace, alias FROM persistent.keyentry;",
+ )?;
+ let rows = stmt.query_map::<(i64, i64, i32, i64, String), _, _>(NO_PARAMS, |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?))
+ })?;
+
+ println!("Key entry table rows:");
+ for r in rows {
+ let (id, cdate, domain, namespace, alias) = r.unwrap();
+ println!(
+ " id: {} Creation date: {} Domain: {} Namespace: {} Alias: {}",
+ id, cdate, domain, namespace, alias
+ );
+ }
+ Ok(())
+ }
+
+ fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt =
+ db.conn.prepare("SELECT id, grantee, keyentryid, access_vector FROM perboot.grant;")?;
+ let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+ })?;
+
+ println!("Grant table rows:");
+ for r in rows {
+ let (id, gt, ki, av) = r.unwrap();
+ println!(" id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
+ }
+ Ok(())
+ }
+
+ // A class that deletes a file when it is dropped.
+ // TODO: If we ever add a crate that does this, we can use it instead.
+ struct TempFile {
+ filename: &'static str,
+ }
+
+ impl Drop for TempFile {
+ fn drop(&mut self) {
+ std::fs::remove_file(self.filename).expect("Cannot delete temporary file");
+ }
+ }
+
+ // Use a custom random number generator that repeats each number once.
+ // This allows us to test repeated elements.
+
+ thread_local! {
+ static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
+ }
+
+ fn reset_random() {
+ RANDOM_COUNTER.with(|counter| {
+ *counter.borrow_mut() = 0;
+ })
+ }
+
+ pub fn random() -> i64 {
+ RANDOM_COUNTER.with(|counter| {
+ let result = *counter.borrow() / 2;
+ *counter.borrow_mut() += 1;
+ result
+ })
+ }
+}
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index e58d3ce..63ebe62 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -25,39 +25,21 @@
//! This crate provides the convenience method `map_or_log_err` to convert `anyhow::Error`
//! into this wire type. In addition to handling the conversion of `Error`
//! to the `Result` wire type it handles any other error by mapping it to
-//! `ResponseCode::SystemError` and logs any error condition.
+//! `ResponseCode::SYSTEM_ERROR` and logs any error condition.
//!
//! Keystore functions should use `anyhow::Result` to return error conditions, and
//! context should be added every time an error is forwarded.
use std::cmp::PartialEq;
-use std::convert::From;
-use keystore_aidl_generated as aidl;
-use keystore_aidl_generated::ResponseCode as AidlRc;
+pub use android_hardware_keymint::aidl::android::hardware::keymint::ErrorCode::ErrorCode;
+pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
-pub use aidl::ResponseCode;
+use keystore2_selinux as selinux;
-/// AidlResult wraps the `android.security.keystore2.Result` generated from AIDL
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct AidlResult(aidl::Result);
-
-impl AidlResult {
- /// Creates an instance of AidlResult indicating no error has occurred.
- pub fn ok() -> Self {
- Self(aidl::Result { rc: AidlRc::Ok, km_error_code: 0 })
- }
-
- /// Creates an instance of AidlResult indicating the given ResponseCode.
- pub fn rc(rc: AidlRc) -> Self {
- Self(aidl::Result { rc, km_error_code: 0 })
- }
-
- /// Creates an instance of AidlResult indicating the given KM ErrorCode.
- pub fn ec(ec: aidl::ErrorCode) -> Self {
- Self(aidl::Result { rc: AidlRc::KeymintErrorCode, km_error_code: ec })
- }
-}
+use android_system_keystore2::binder::{
+ ExceptionCode, Result as BinderResult, Status as BinderStatus,
+};
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
@@ -65,45 +47,68 @@
pub enum Error {
/// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
#[error("Error::Rc({0:?})")]
- Rc(AidlRc),
+ Rc(ResponseCode),
/// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
#[error("Error::Km({0:?})")]
- Km(aidl::ErrorCode), // TODO Keymint ErrorCode is a generated AIDL type.
+ Km(ErrorCode),
+ /// 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::SystemError)`
+ /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
pub fn sys() -> Self {
- Error::Rc(AidlRc::SystemError)
+ Error::Rc(ResponseCode::SYSTEM_ERROR)
}
- /// Short hand for `Error::Rc(ResponseCode::PermissionDenied`
+ /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED`
pub fn perm() -> Self {
- Error::Rc(AidlRc::PermissionDenied)
+ Error::Rc(ResponseCode::PERMISSION_DENIED)
}
}
-impl From<anyhow::Error> for AidlResult {
- fn from(error: anyhow::Error) -> Self {
- let root_cause = error.root_cause();
- match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => AidlResult::rc(*rcode),
- Some(Error::Km(ec)) => AidlResult::ec(*ec),
- None => AidlResult::rc(AidlRc::SystemError),
+/// Helper function to map the binder status we get from calls into KeyMint
+/// to a Keystore Error. We don't create an anyhow error here to make
+/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
+/// when diagnosing authentication requirements, update requirements, and running
+/// out of operation slots.
+pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
+ r.map_err(|s| {
+ match s.exception_code() {
+ ExceptionCode::SERVICE_SPECIFIC => {
+ let se = s.service_specific_error();
+ if se < 0 {
+ // Negative service specific errors are KM error codes.
+ Error::Km(ErrorCode(s.service_specific_error()))
+ } else {
+ // Non negative error codes cannot be KM error codes.
+ // So we create an `Error::Binder` variant to preserve
+ // the service specific error code for logging.
+ // `map_or_log_err` will map this on a system error,
+ // but not before logging the details to logcat.
+ Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
+ }
+ }
+ // We create `Error::Binder` to preserve the exception code
+ // for logging.
+ // `map_or_log_err` will map this on a system error.
+ e_code => Error::Binder(e_code, 0),
}
- }
+ })
}
/// This function should be used by Keystore service calls to translate error conditions
-/// into `android.security.keystore2.Result` which is imported here as `aidl::Result`
+/// into `android.system.keystore2.Result` which is imported here as `aidl::Result`
/// and newtyped as AidlResult.
/// All error conditions get logged by this function.
/// All `Error::Rc(x)` variants get mapped onto `aidl::Result{x, 0}`.
/// All `Error::Km(x)` variants get mapped onto
/// `aidl::Result{aidl::ResponseCode::KeymintErrorCode, x}`.
+/// `selinux::Error::perm()` is mapped on `aidl::Result{aidl::ResponseCode::PERMISSION_DENIED, 0}`.
///
/// All non `Error` error conditions get mapped onto
-/// `aidl::Result{aidl::ResponseCode::SystemError}`.
+/// `aidl::Result{aidl::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 an `AidlResult`, typically
@@ -117,38 +122,52 @@
/// if (good_but_auth_required) {
/// Ok(aidl::ResponseCode::OpAuthRequired)
/// } else {
-/// Err(anyhow!(Error::Rc(aidl::ResponseCode::KeyNotFound)))
+/// Err(anyhow!(Error::Rc(aidl::ResponseCode::KEY_NOT_FOUND)))
/// }
/// }
///
/// aidl_result_ = map_or_log_err(loadKey(), |r| { some_side_effect(); AidlResult::rc(r) });
/// ```
-pub fn map_or_log_err<T>(
- result: anyhow::Result<T>,
- handle_ok: impl FnOnce(T) -> AidlResult,
-) -> AidlResult {
+pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
+where
+ F: FnOnce(U) -> BinderResult<T>,
+{
result.map_or_else(
|e| {
log::error!("{:?}", e);
- e.into()
+ let root_cause = e.root_cause();
+ let rc = match root_cause.downcast_ref::<Error>() {
+ Some(Error::Rc(rcode)) => rcode.0,
+ Some(Error::Km(ec)) => ec.0,
+ // If an Error::Binder reaches this stage we report a system error.
+ // The exception code and possible service specific error will be
+ // printed in the error log above.
+ 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,
)
}
#[cfg(test)]
-mod tests {
+pub mod tests {
+ use super::*;
+ use android_system_keystore2::binder::{
+ ExceptionCode, Result as BinderResult, Status as BinderStatus,
+ };
use anyhow::{anyhow, Context};
- use super::aidl::ErrorCode;
- use super::*;
-
- fn nested_nested_rc(rc: AidlRc) -> anyhow::Result<()> {
+ fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
}
- fn nested_rc(rc: AidlRc) -> anyhow::Result<()> {
+ fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
nested_nested_rc(rc).context("nested rc")
}
@@ -160,14 +179,22 @@
nested_nested_ec(ec).context("nested ec")
}
- fn nested_nested_ok(rc: AidlRc) -> anyhow::Result<AidlRc> {
+ fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Ok(rc)
}
- fn nested_ok(rc: AidlRc) -> anyhow::Result<AidlRc> {
+ 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")]
@@ -182,6 +209,14 @@
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(
@@ -189,80 +224,104 @@
.with_tag("keystore_error_tests")
.with_min_level(log::Level::Debug),
);
- // All Error::Rc(x) get mapped on aidl::Result{x, 0}
+ // 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),
+ map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
+ .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),
+ map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
+ .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_or_log_err(
+ map_km_error(binder_sse_error(ec))
+ .with_context(|| format!("Km error code: {}.", ec)),
+ |_| Err(BinderStatus::ok())
+ )
+ .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);
+ // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
assert_eq!(
- AidlResult::rc(AidlRc::Ok),
- map_or_log_err(nested_rc(AidlRc::Ok), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::Locked),
- map_or_log_err(nested_rc(AidlRc::Locked), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::Uninitialized),
- map_or_log_err(nested_rc(AidlRc::Uninitialized), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::SystemError),
- map_or_log_err(nested_rc(AidlRc::SystemError), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::PermissionDenied),
- map_or_log_err(nested_rc(AidlRc::PermissionDenied), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::KeyNotFound),
- map_or_log_err(nested_rc(AidlRc::KeyNotFound), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::ValueCorrupted),
- map_or_log_err(nested_rc(AidlRc::ValueCorrupted), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::WrongPassword),
- map_or_log_err(nested_rc(AidlRc::WrongPassword), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::OpAuthNeeded),
- map_or_log_err(nested_rc(AidlRc::OpAuthNeeded), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::KeyPermanentlyInvalidated),
- map_or_log_err(nested_rc(AidlRc::KeyPermanentlyInvalidated), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::NoSuchSecurityLevel),
- map_or_log_err(nested_rc(AidlRc::NoSuchSecurityLevel), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::KeymintErrorCode),
- map_or_log_err(nested_rc(AidlRc::KeymintErrorCode), |_| AidlResult::ec(0))
- );
- assert_eq!(
- AidlResult::rc(AidlRc::BackendBusy),
- map_or_log_err(nested_rc(AidlRc::BackendBusy), |_| AidlResult::ec(0))
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
+ BinderStatus::ok()
+ ))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
);
- // All KeystoreKerror::Km(x) get mapped on
- // aidl::Result{AidlRc::KeymintErrorCode, x}
+ // 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);
+ // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
assert_eq!(
- AidlResult::ec(-7),
- map_or_log_err(nested_ec(-7), |_| AidlResult::rc(AidlRc::SystemError))
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
+ BinderStatus::ok()
+ ))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
);
- // All other get mapped on System Error.
+ // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
assert_eq!(
- AidlResult::rc(AidlRc::SystemError),
- map_or_log_err(nested_other_error(), |_| AidlResult::ec(0))
+ Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
+ map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // All other errors get mapped on System Error.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
);
// Result::Ok variants get passed to the ok handler.
+ assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
assert_eq!(
- AidlResult::rc(AidlRc::OpAuthNeeded),
- map_or_log_err(nested_ok(AidlRc::OpAuthNeeded), AidlResult::rc)
+ Ok(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
);
- assert_eq!(AidlResult::ok(), map_or_log_err(nested_ok(AidlRc::Ok), AidlResult::rc));
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
+ );
+ }
} // mod tests
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
new file mode 100644
index 0000000..0654b29
--- /dev/null
+++ b/keystore2/src/globals.rs
@@ -0,0 +1,29 @@
+// 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.
+
+//! This module holds global state of Keystore such as the thread local
+//! database connections and connections to services that Keystore needs
+//! to talk to.
+
+use crate::database::KeystoreDB;
+use std::cell::RefCell;
+
+thread_local! {
+ /// Database connections are not thread safe, but connecting to the
+ /// same database multiple times is safe as long as each connection is
+ /// used by only one thread. So we store one database connection per
+ /// thread in this thread local key.
+ pub static DB: RefCell<KeystoreDB> =
+ RefCell::new(KeystoreDB::new().expect("Failed to open database."));
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
new file mode 100644
index 0000000..8825fc9
--- /dev/null
+++ b/keystore2/src/key_parameter.rs
@@ -0,0 +1,1363 @@
+// 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.
+
+//! KeyParameter is used to express different characteristics of a key requested by the user
+//! and enforced by the OEMs. This module implements the internal representation of KeyParameter
+//! and the methods to work with KeyParameter.
+
+use crate::error::Error as KeystoreError;
+use crate::error::ResponseCode;
+
+pub use android_hardware_keymint::aidl::android::hardware::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+ KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ Tag::Tag,
+};
+pub use android_system_keystore2::aidl::android::system::keystore2::SecurityLevel::SecurityLevel;
+use anyhow::{Context, Result};
+use rusqlite::types::{FromSql, Null, ToSql, ToSqlOutput};
+use rusqlite::{Result as SqlResult, Row};
+
+/// KeyParameter wraps the KeyParameterValue and the security level at which it is enforced.
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyParameter {
+ key_parameter_value: KeyParameterValue,
+ security_level: SecurityLevel,
+}
+
+/// KeyParameterValue holds a value corresponding to one of the Tags defined in
+/// the AIDL spec at hardware/interfaces/keymint
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum KeyParameterValue {
+ /// Associated with Tag:INVALID
+ Invalid,
+ /// Set of purposes for which the key may be used
+ KeyPurpose(KeyPurpose),
+ /// Cryptographic algorithm with which the key is used
+ Algorithm(Algorithm),
+ /// Size of the key , in bits
+ KeySize(i32),
+ /// Block cipher mode(s) with which the key may be used
+ BlockMode(BlockMode),
+ /// Digest algorithms that may be used with the key to perform signing and verification
+ Digest(Digest),
+ /// Padding modes that may be used with the key. Relevant to RSA, AES and 3DES keys.
+ PaddingMode(PaddingMode),
+ /// Can the caller provide a nonce for nonce-requiring operations
+ CallerNonce,
+ /// Minimum length of MAC for HMAC keys and AES keys that support GCM mode
+ MinMacLength(i32),
+ /// The elliptic curve
+ EcCurve(EcCurve),
+ /// Value of the public exponent for an RSA key pair
+ RSAPublicExponent(i64),
+ /// An attestation certificate for the generated key should contain an application-scoped
+ /// and time-bounded device-unique ID
+ IncludeUniqueID,
+ //TODO: find out about this
+ // /// Necessary system environment conditions for the generated key to be used
+ // KeyBlobUsageRequirements(KeyBlobUsageRequirements),
+ /// Only the boot loader can use the key
+ BootLoaderOnly,
+ /// When deleted, the key is guaranteed to be permanently deleted and unusable
+ RollbackResistance,
+ /// The date and time at which the key becomes active
+ ActiveDateTime(i64),
+ /// The date and time at which the key expires for signing and encryption
+ OriginationExpireDateTime(i64),
+ /// The date and time at which the key expires for verification and decryption
+ UsageExpireDateTime(i64),
+ /// Minimum amount of time that elapses between allowed operations
+ MinSecondsBetweenOps(i32),
+ /// Maximum number of times that a key may be used between system reboots
+ MaxUsesPerBoot(i32),
+ /// ID of the Android user that is permitted to use the key
+ UserID(i32),
+ /// A key may only be used under a particular secure user authentication state
+ UserSecureID(i64),
+ /// No authentication is required to use this key
+ NoAuthRequired,
+ /// The types of user authenticators that may be used to authorize this key
+ HardwareAuthenticatorType(HardwareAuthenticatorType),
+ /// The time in seconds for which the key is authorized for use, after user authentication
+ AuthTimeout(i32),
+ /// The key may be used after authentication timeout if device is still on-body
+ AllowWhileOnBody,
+ /// The key must be unusable except when the user has provided proof of physical presence
+ TrustedUserPresenceRequired,
+ /// Applicable to keys with KeyPurpose SIGN, and specifies that this key must not be usable
+ /// unless the user provides confirmation of the data to be signed
+ TrustedConfirmationRequired,
+ /// The key may only be used when the device is unlocked
+ UnlockedDeviceRequired,
+ /// When provided to generateKey or importKey, this tag specifies data
+ /// that is necessary during all uses of the key
+ ApplicationID(Vec<u8>),
+ /// When provided to generateKey or importKey, this tag specifies data
+ /// that is necessary during all uses of the key
+ ApplicationData(Vec<u8>),
+ /// Specifies the date and time the key was created
+ CreationDateTime(i64),
+ /// Specifies where the key was created, if known
+ KeyOrigin(KeyOrigin),
+ /// The key used by verified boot to validate the operating system booted
+ RootOfTrust(Vec<u8>),
+ /// System OS version with which the key may be used
+ OSVersion(i32),
+ /// Specifies the system security patch level with which the key may be used
+ OSPatchLevel(i32),
+ /// Specifies a unique, time-based identifier
+ UniqueID(Vec<u8>),
+ /// Used to deliver a "challenge" value to the attestKey() method
+ AttestationChallenge(Vec<u8>),
+ /// The set of applications which may use a key, used only with attestKey()
+ AttestationApplicationID(Vec<u8>),
+ /// Provides the device's brand name, to attestKey()
+ AttestationIdBrand(Vec<u8>),
+ /// Provides the device's device name, to attestKey()
+ AttestationIdDevice(Vec<u8>),
+ /// Provides the device's product name, to attestKey()
+ AttestationIdProduct(Vec<u8>),
+ /// Provides the device's serial number, to attestKey()
+ AttestationIdSerial(Vec<u8>),
+ /// Provides the IMEIs for all radios on the device, to attestKey()
+ AttestationIdIMEI(Vec<u8>),
+ /// Provides the MEIDs for all radios on the device, to attestKey()
+ AttestationIdMEID(Vec<u8>),
+ /// Provides the device's manufacturer name, to attestKey()
+ AttestationIdManufacturer(Vec<u8>),
+ /// Provides the device's model name, to attestKey()
+ AttestationIdModel(Vec<u8>),
+ /// Specifies the vendor image security patch level with which the key may be used
+ VendorPatchLevel(i32),
+ /// Specifies the boot image (kernel) security patch level with which the key may be used
+ BootPatchLevel(i32),
+ /// Provides "associated data" for AES-GCM encryption or decryption
+ AssociatedData(Vec<u8>),
+ /// Provides or returns a nonce or Initialization Vector (IV) for AES-GCM,
+ /// AES-CBC, AES-CTR, or 3DES-CBC encryption or decryption
+ Nonce(Vec<u8>),
+ /// Provides the requested length of a MAC or GCM authentication tag, in bits
+ MacLength(i32),
+ /// Specifies whether the device has been factory reset since the
+ /// last unique ID rotation. Used for key attestation
+ ResetSinceIdRotation,
+ /// Used to deliver a cryptographic token proving that the user
+ /// confirmed a signing request
+ ConfirmationToken(Vec<u8>),
+}
+
+impl KeyParameter {
+ /// Create an instance of KeyParameter, given the value and the security level.
+ pub fn new(key_parameter_value: KeyParameterValue, security_level: SecurityLevel) -> Self {
+ KeyParameter { key_parameter_value, security_level }
+ }
+
+ /// Returns the tag given the KeyParameter instance.
+ pub fn get_tag(&self) -> Tag {
+ match self.key_parameter_value {
+ KeyParameterValue::Invalid => Tag::INVALID,
+ KeyParameterValue::KeyPurpose(_) => Tag::PURPOSE,
+ KeyParameterValue::Algorithm(_) => Tag::ALGORITHM,
+ KeyParameterValue::KeySize(_) => Tag::KEY_SIZE,
+ KeyParameterValue::BlockMode(_) => Tag::BLOCK_MODE,
+ KeyParameterValue::Digest(_) => Tag::DIGEST,
+ KeyParameterValue::PaddingMode(_) => Tag::PADDING,
+ KeyParameterValue::CallerNonce => Tag::CALLER_NONCE,
+ KeyParameterValue::MinMacLength(_) => Tag::MIN_MAC_LENGTH,
+ KeyParameterValue::EcCurve(_) => Tag::EC_CURVE,
+ KeyParameterValue::RSAPublicExponent(_) => Tag::RSA_PUBLIC_EXPONENT,
+ KeyParameterValue::IncludeUniqueID => Tag::INCLUDE_UNIQUE_ID,
+ KeyParameterValue::BootLoaderOnly => Tag::BOOTLOADER_ONLY,
+ KeyParameterValue::RollbackResistance => Tag::ROLLBACK_RESISTANCE,
+ KeyParameterValue::ActiveDateTime(_) => Tag::ACTIVE_DATETIME,
+ KeyParameterValue::OriginationExpireDateTime(_) => Tag::ORIGINATION_EXPIRE_DATETIME,
+ KeyParameterValue::UsageExpireDateTime(_) => Tag::USAGE_EXPIRE_DATETIME,
+ KeyParameterValue::MinSecondsBetweenOps(_) => Tag::MIN_SECONDS_BETWEEN_OPS,
+ KeyParameterValue::MaxUsesPerBoot(_) => Tag::MAX_USES_PER_BOOT,
+ KeyParameterValue::UserID(_) => Tag::USER_ID,
+ KeyParameterValue::UserSecureID(_) => Tag::USER_SECURE_ID,
+ KeyParameterValue::NoAuthRequired => Tag::NO_AUTH_REQUIRED,
+ KeyParameterValue::HardwareAuthenticatorType(_) => Tag::USER_AUTH_TYPE,
+ KeyParameterValue::AuthTimeout(_) => Tag::AUTH_TIMEOUT,
+ KeyParameterValue::AllowWhileOnBody => Tag::ALLOW_WHILE_ON_BODY,
+ KeyParameterValue::TrustedUserPresenceRequired => Tag::TRUSTED_USER_PRESENCE_REQUIRED,
+ KeyParameterValue::TrustedConfirmationRequired => Tag::TRUSTED_CONFIRMATION_REQUIRED,
+ KeyParameterValue::UnlockedDeviceRequired => Tag::UNLOCKED_DEVICE_REQUIRED,
+ KeyParameterValue::ApplicationID(_) => Tag::APPLICATION_ID,
+ KeyParameterValue::ApplicationData(_) => Tag::APPLICATION_DATA,
+ KeyParameterValue::CreationDateTime(_) => Tag::CREATION_DATETIME,
+ KeyParameterValue::KeyOrigin(_) => Tag::ORIGIN,
+ KeyParameterValue::RootOfTrust(_) => Tag::ROOT_OF_TRUST,
+ KeyParameterValue::OSVersion(_) => Tag::OS_VERSION,
+ KeyParameterValue::OSPatchLevel(_) => Tag::OS_PATCHLEVEL,
+ KeyParameterValue::UniqueID(_) => Tag::UNIQUE_ID,
+ KeyParameterValue::AttestationChallenge(_) => Tag::ATTESTATION_CHALLENGE,
+ KeyParameterValue::AttestationApplicationID(_) => Tag::ATTESTATION_APPLICATION_ID,
+ KeyParameterValue::AttestationIdBrand(_) => Tag::ATTESTATION_ID_BRAND,
+ KeyParameterValue::AttestationIdDevice(_) => Tag::ATTESTATION_ID_DEVICE,
+ KeyParameterValue::AttestationIdProduct(_) => Tag::ATTESTATION_ID_PRODUCT,
+ KeyParameterValue::AttestationIdSerial(_) => Tag::ATTESTATION_ID_SERIAL,
+ KeyParameterValue::AttestationIdIMEI(_) => Tag::ATTESTATION_ID_IMEI,
+ KeyParameterValue::AttestationIdMEID(_) => Tag::ATTESTATION_ID_MEID,
+ KeyParameterValue::AttestationIdManufacturer(_) => Tag::ATTESTATION_ID_MANUFACTURER,
+ KeyParameterValue::AttestationIdModel(_) => Tag::ATTESTATION_ID_MODEL,
+ KeyParameterValue::VendorPatchLevel(_) => Tag::VENDOR_PATCHLEVEL,
+ KeyParameterValue::BootPatchLevel(_) => Tag::BOOT_PATCHLEVEL,
+ KeyParameterValue::AssociatedData(_) => Tag::ASSOCIATED_DATA,
+ KeyParameterValue::Nonce(_) => Tag::NONCE,
+ KeyParameterValue::MacLength(_) => Tag::MAC_LENGTH,
+ KeyParameterValue::ResetSinceIdRotation => Tag::RESET_SINCE_ID_ROTATION,
+ KeyParameterValue::ConfirmationToken(_) => Tag::CONFIRMATION_TOKEN,
+ }
+ }
+
+ /// Returns key parameter value.
+ pub fn key_parameter_value(&self) -> &KeyParameterValue {
+ &self.key_parameter_value
+ }
+
+ /// Returns the security level of a KeyParameter.
+ pub fn security_level(&self) -> &SecurityLevel {
+ &self.security_level
+ }
+}
+
+/// This struct is defined to postpone converting rusqlite column value to the
+/// appropriate key parameter value until we know the corresponding tag value.
+/// Wraps the column index and a rusqlite row.
+pub struct SqlField<'a>(usize, &'a Row<'a>);
+
+impl<'a> SqlField<'a> {
+ /// Creates a new SqlField with the given index and row.
+ pub fn new(index: usize, row: &'a Row<'a>) -> Self {
+ Self(index, row)
+ }
+ /// Returns the column value from the row, when we know the expected type.
+ pub fn get<T: FromSql>(&self) -> SqlResult<T> {
+ self.1.get(self.0)
+ }
+}
+
+impl ToSql for KeyParameterValue {
+ /// Converts KeyParameterValue to be stored in rusqlite database.
+ /// Note that following variants of KeyParameterValue should not be stored:
+ /// IncludeUniqueID, ApplicationID, ApplicationData, RootOfTrust, UniqueID,
+ /// Attestation*, AssociatedData, Nonce, MacLength, ResetSinceIdRotation, ConfirmationToken.
+ /// This filtering is enforced at a higher level (i.e. enforcement module) and here we support
+ /// conversion for all the variants, to keep error handling simple.
+ fn to_sql(&self) -> SqlResult<ToSqlOutput> {
+ match self {
+ KeyParameterValue::Invalid => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::KeyPurpose(k) => Ok(ToSqlOutput::from(k.0 as u32)),
+ KeyParameterValue::Algorithm(a) => Ok(ToSqlOutput::from(a.0 as u32)),
+ KeyParameterValue::KeySize(k) => Ok(ToSqlOutput::from(*k)),
+ KeyParameterValue::BlockMode(b) => Ok(ToSqlOutput::from(b.0 as u32)),
+ KeyParameterValue::Digest(d) => Ok(ToSqlOutput::from(d.0 as u32)),
+ KeyParameterValue::PaddingMode(p) => Ok(ToSqlOutput::from(p.0 as u32)),
+ KeyParameterValue::CallerNonce => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::MinMacLength(m) => Ok(ToSqlOutput::from(*m)),
+ KeyParameterValue::EcCurve(e) => Ok(ToSqlOutput::from(e.0 as u32)),
+ KeyParameterValue::RSAPublicExponent(r) => Ok(ToSqlOutput::from(*r as i64)),
+ KeyParameterValue::IncludeUniqueID => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::BootLoaderOnly => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::RollbackResistance => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::ActiveDateTime(a) => Ok(ToSqlOutput::from(*a as i64)),
+ KeyParameterValue::OriginationExpireDateTime(o) => Ok(ToSqlOutput::from(*o as i64)),
+ KeyParameterValue::UsageExpireDateTime(u) => Ok(ToSqlOutput::from(*u as i64)),
+ KeyParameterValue::MinSecondsBetweenOps(m) => Ok(ToSqlOutput::from(*m)),
+ KeyParameterValue::MaxUsesPerBoot(m) => Ok(ToSqlOutput::from(*m)),
+ KeyParameterValue::UserID(u) => Ok(ToSqlOutput::from(*u)),
+ KeyParameterValue::UserSecureID(u) => Ok(ToSqlOutput::from(*u as i64)),
+ KeyParameterValue::NoAuthRequired => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::HardwareAuthenticatorType(h) => Ok(ToSqlOutput::from(h.0 as u32)),
+ KeyParameterValue::AuthTimeout(m) => Ok(ToSqlOutput::from(*m)),
+ KeyParameterValue::AllowWhileOnBody => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::TrustedUserPresenceRequired => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::TrustedConfirmationRequired => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::UnlockedDeviceRequired => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::ApplicationID(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::ApplicationData(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::CreationDateTime(c) => Ok(ToSqlOutput::from(*c as i64)),
+ KeyParameterValue::KeyOrigin(k) => Ok(ToSqlOutput::from(k.0 as u32)),
+ KeyParameterValue::RootOfTrust(r) => Ok(ToSqlOutput::from(r.to_vec())),
+ KeyParameterValue::OSVersion(o) => Ok(ToSqlOutput::from(*o)),
+ KeyParameterValue::OSPatchLevel(o) => Ok(ToSqlOutput::from(*o)),
+ KeyParameterValue::UniqueID(u) => Ok(ToSqlOutput::from(u.to_vec())),
+ KeyParameterValue::AttestationChallenge(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationApplicationID(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdBrand(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdDevice(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdProduct(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdSerial(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdIMEI(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdMEID(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdManufacturer(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::AttestationIdModel(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::VendorPatchLevel(v) => Ok(ToSqlOutput::from(*v)),
+ KeyParameterValue::BootPatchLevel(b) => Ok(ToSqlOutput::from(*b)),
+ KeyParameterValue::AssociatedData(a) => Ok(ToSqlOutput::from(a.to_vec())),
+ KeyParameterValue::Nonce(n) => Ok(ToSqlOutput::from(n.to_vec())),
+ KeyParameterValue::MacLength(m) => Ok(ToSqlOutput::from(*m)),
+ KeyParameterValue::ResetSinceIdRotation => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::ConfirmationToken(c) => Ok(ToSqlOutput::from(c.to_vec())),
+ }
+ }
+}
+
+impl KeyParameter {
+ /// Construct a KeyParameter from the data from a rusqlite row.
+ /// Note that following variants of KeyParameterValue should not be stored:
+ /// IncludeUniqueID, ApplicationID, ApplicationData, RootOfTrust, UniqueID,
+ /// Attestation*, AssociatedData, Nonce, MacLength, ResetSinceIdRotation, ConfirmationToken.
+ /// This filtering is enforced at a higher level and here we support conversion for all the
+ /// variants.
+ pub fn new_from_sql(
+ tag_val: Tag,
+ data: &SqlField,
+ security_level_val: SecurityLevel,
+ ) -> Result<Self> {
+ let key_param_value = match tag_val {
+ Tag::INVALID => KeyParameterValue::Invalid,
+ Tag::PURPOSE => {
+ let key_purpose: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: PURPOSE.")?;
+ KeyParameterValue::KeyPurpose(KeyPurpose(key_purpose))
+ }
+ Tag::ALGORITHM => {
+ let algorithm: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: ALGORITHM.")?;
+ KeyParameterValue::Algorithm(Algorithm(algorithm))
+ }
+ Tag::KEY_SIZE => {
+ let key_size: i32 =
+ data.get().context("Failed to read sql data for tag: KEY_SIZE.")?;
+ KeyParameterValue::KeySize(key_size)
+ }
+ Tag::BLOCK_MODE => {
+ let block_mode: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: BLOCK_MODE.")?;
+ KeyParameterValue::BlockMode(BlockMode(block_mode))
+ }
+ Tag::DIGEST => {
+ let digest: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: DIGEST.")?;
+ KeyParameterValue::Digest(Digest(digest))
+ }
+ Tag::PADDING => {
+ let padding: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: PADDING.")?;
+ KeyParameterValue::PaddingMode(PaddingMode(padding))
+ }
+ Tag::CALLER_NONCE => KeyParameterValue::CallerNonce,
+ Tag::MIN_MAC_LENGTH => {
+ let min_mac_length: i32 =
+ data.get().context("Failed to read sql data for tag: MIN_MAC_LENGTH.")?;
+ KeyParameterValue::MinMacLength(min_mac_length)
+ }
+ Tag::EC_CURVE => {
+ let ec_curve: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: EC_CURVE.")?;
+ KeyParameterValue::EcCurve(EcCurve(ec_curve))
+ }
+ Tag::RSA_PUBLIC_EXPONENT => {
+ let rsa_pub_exponent: i64 =
+ data.get().context("Failed to read sql data for tag: RSA_PUBLIC_EXPONENT.")?;
+
+ KeyParameterValue::RSAPublicExponent(rsa_pub_exponent)
+ }
+ Tag::INCLUDE_UNIQUE_ID => KeyParameterValue::IncludeUniqueID,
+ Tag::BOOTLOADER_ONLY => KeyParameterValue::BootLoaderOnly,
+ Tag::ROLLBACK_RESISTANCE => KeyParameterValue::RollbackResistance,
+ Tag::ACTIVE_DATETIME => {
+ let active_datetime: i64 =
+ data.get().context("Failed to read sql data for tag: ACTIVE_DATETIME.")?;
+ KeyParameterValue::ActiveDateTime(active_datetime)
+ }
+ Tag::ORIGINATION_EXPIRE_DATETIME => {
+ let origination_expire_datetime: i64 = data
+ .get()
+ .context("Failed to read sql data for tag: ORIGINATION_EXPIRE_DATETIME.")?;
+ KeyParameterValue::OriginationExpireDateTime(origination_expire_datetime)
+ }
+ Tag::USAGE_EXPIRE_DATETIME => {
+ let usage_expire_datetime: i64 = data
+ .get()
+ .context("Failed to read sql data for tag: USAGE_EXPIRE_DATETIME.")?;
+ KeyParameterValue::UsageExpireDateTime(usage_expire_datetime)
+ }
+ Tag::MIN_SECONDS_BETWEEN_OPS => {
+ let min_secs_between_ops: i32 = data
+ .get()
+ .context("Failed to read sql data for tag: MIN_SECONDS_BETWEEN_OPS.")?;
+ KeyParameterValue::MinSecondsBetweenOps(min_secs_between_ops)
+ }
+ Tag::MAX_USES_PER_BOOT => {
+ let max_uses_per_boot: i32 =
+ data.get().context("Failed to read sql data for tag: MAX_USES_PER_BOOT.")?;
+ KeyParameterValue::MaxUsesPerBoot(max_uses_per_boot)
+ }
+ Tag::USER_ID => {
+ let user_id: i32 =
+ data.get().context("Failed to read sql data for tag: USER_ID.")?;
+ KeyParameterValue::UserID(user_id)
+ }
+ Tag::USER_SECURE_ID => {
+ let user_secure_id: i64 =
+ data.get().context("Failed to read sql data for tag: USER_SECURE_ID.")?;
+ KeyParameterValue::UserSecureID(user_secure_id)
+ }
+ Tag::NO_AUTH_REQUIRED => KeyParameterValue::NoAuthRequired,
+ Tag::USER_AUTH_TYPE => {
+ let user_auth_type: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: USER_AUTH_TYPE.")?;
+ KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(
+ user_auth_type,
+ ))
+ }
+ Tag::AUTH_TIMEOUT => {
+ let auth_timeout: i32 =
+ data.get().context("Failed to read sql data for tag: AUTH_TIMEOUT.")?;
+ KeyParameterValue::AuthTimeout(auth_timeout)
+ }
+ Tag::ALLOW_WHILE_ON_BODY => KeyParameterValue::AllowWhileOnBody,
+ Tag::TRUSTED_USER_PRESENCE_REQUIRED => KeyParameterValue::TrustedUserPresenceRequired,
+ Tag::TRUSTED_CONFIRMATION_REQUIRED => KeyParameterValue::TrustedConfirmationRequired,
+ Tag::UNLOCKED_DEVICE_REQUIRED => KeyParameterValue::UnlockedDeviceRequired,
+ Tag::APPLICATION_ID => {
+ let app_id: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: APPLICATION_ID.")?;
+ KeyParameterValue::ApplicationID(app_id)
+ }
+ Tag::APPLICATION_DATA => {
+ let app_data: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: APPLICATION_DATA.")?;
+ KeyParameterValue::ApplicationData(app_data)
+ }
+ Tag::CREATION_DATETIME => {
+ let creation_datetime: i64 =
+ data.get().context("Failed to read sql data for tag: CREATION_DATETIME.")?;
+ KeyParameterValue::CreationDateTime(creation_datetime)
+ }
+ Tag::ORIGIN => {
+ let origin: i32 = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to read sql data for tag: ORIGIN.")?;
+ KeyParameterValue::KeyOrigin(KeyOrigin(origin))
+ }
+ Tag::ROOT_OF_TRUST => {
+ let root_of_trust: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: ROOT_OF_TRUST.")?;
+ KeyParameterValue::RootOfTrust(root_of_trust)
+ }
+ Tag::OS_VERSION => {
+ let os_version: i32 =
+ data.get().context("Failed to read sql data for tag: OS_VERSION.")?;
+ KeyParameterValue::OSVersion(os_version)
+ }
+ Tag::OS_PATCHLEVEL => {
+ let os_patch_level: i32 =
+ data.get().context("Failed to read sql data for tag: OS_PATCHLEVEL.")?;
+ KeyParameterValue::OSPatchLevel(os_patch_level)
+ }
+ Tag::UNIQUE_ID => {
+ let unique_id: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: UNIQUE_ID.")?;
+ KeyParameterValue::UniqueID(unique_id)
+ }
+ Tag::ATTESTATION_CHALLENGE => {
+ let attestation_challenge: Vec<u8> = data
+ .get()
+ .context("Failed to read sql data for tag: ATTESTATION_CHALLENGE.")?;
+ KeyParameterValue::AttestationChallenge(attestation_challenge)
+ }
+ Tag::ATTESTATION_APPLICATION_ID => {
+ let attestation_app_id: Vec<u8> = data
+ .get()
+ .context("Failed to read sql data for tag: ATTESTATION_APPLICATION_ID.")?;
+ KeyParameterValue::AttestationApplicationID(attestation_app_id)
+ }
+ Tag::ATTESTATION_ID_BRAND => {
+ let attestation_id_brand: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: ATTESTATION_ID_BRAND.")?;
+ KeyParameterValue::AttestationIdBrand(attestation_id_brand)
+ }
+ Tag::ATTESTATION_ID_DEVICE => {
+ let attestation_id_device: Vec<u8> = data
+ .get()
+ .context("Failed to read sql data for tag: ATTESTATION_ID_DEVICE.")?;
+ KeyParameterValue::AttestationIdDevice(attestation_id_device)
+ }
+ Tag::ATTESTATION_ID_PRODUCT => {
+ let attestation_id_product: Vec<u8> = data
+ .get()
+ .context("Failed to read sql data for tag: ATTESTATION_ID_PRODUCT.")?;
+ KeyParameterValue::AttestationIdProduct(attestation_id_product)
+ }
+ Tag::ATTESTATION_ID_SERIAL => {
+ let attestation_id_serial: Vec<u8> = data
+ .get()
+ .context("Failed to read sql data for tag: ATTESTATION_ID_SERIAL.")?;
+ KeyParameterValue::AttestationIdSerial(attestation_id_serial)
+ }
+ Tag::ATTESTATION_ID_IMEI => {
+ let attestation_id_imei: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: ATTESTATION_ID_IMEI.")?;
+ KeyParameterValue::AttestationIdIMEI(attestation_id_imei)
+ }
+ Tag::ATTESTATION_ID_MEID => {
+ let attestation_id_meid: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: ATTESTATION_ID_MEID.")?;
+ KeyParameterValue::AttestationIdMEID(attestation_id_meid)
+ }
+ Tag::ATTESTATION_ID_MANUFACTURER => {
+ let attestation_id_manufacturer: Vec<u8> = data
+ .get()
+ .context("Failed to read sql data for tag: ATTESTATION_ID_MANUFACTURER.")?;
+ KeyParameterValue::AttestationIdManufacturer(attestation_id_manufacturer)
+ }
+ Tag::ATTESTATION_ID_MODEL => {
+ let attestation_id_model: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: ATTESTATION_ID_MODEL.")?;
+ KeyParameterValue::AttestationIdModel(attestation_id_model)
+ }
+ Tag::VENDOR_PATCHLEVEL => {
+ let vendor_patch_level: i32 =
+ data.get().context("Failed to read sql data for tag: VENDOR_PATCHLEVEL.")?;
+ KeyParameterValue::VendorPatchLevel(vendor_patch_level)
+ }
+ Tag::BOOT_PATCHLEVEL => {
+ let boot_patch_level: i32 =
+ data.get().context("Failed to read sql data for tag: BOOT_PATCHLEVEL.")?;
+ KeyParameterValue::BootPatchLevel(boot_patch_level)
+ }
+ Tag::ASSOCIATED_DATA => {
+ let associated_data: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: ASSOCIATED_DATA.")?;
+ KeyParameterValue::AssociatedData(associated_data)
+ }
+ Tag::NONCE => {
+ let nonce: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: NONCE.")?;
+ KeyParameterValue::Nonce(nonce)
+ }
+ Tag::MAC_LENGTH => {
+ let mac_length: i32 =
+ data.get().context("Failed to read sql data for tag: MAC_LENGTH.")?;
+ KeyParameterValue::MacLength(mac_length)
+ }
+ Tag::RESET_SINCE_ID_ROTATION => KeyParameterValue::ResetSinceIdRotation,
+ Tag::CONFIRMATION_TOKEN => {
+ let confirmation_token: Vec<u8> =
+ data.get().context("Failed to read sql data for tag: CONFIRMATION_TOKEN.")?;
+ KeyParameterValue::ConfirmationToken(confirmation_token)
+ }
+ _ => {
+ return Err(KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("Failed to decode Tag enum from value.")?
+ }
+ };
+ Ok(KeyParameter::new(key_param_value, security_level_val))
+ }
+}
+
+/// Macro rules for converting key parameter to/from wire type.
+/// This macro takes between three and four different pieces of information about each
+/// of the KeyParameterValue variants:
+/// 1. The KeyParameterValue variant name,
+/// 2. the tag name corresponding to the variant,
+/// 3. the field name in the KmKeyParameter struct, in which information about this variant is
+/// stored when converted, and
+/// 4. an optional enum type name when the nested value is of enum type.
+/// The macro takes a set of lines corresponding to each KeyParameterValue variant and generates
+/// the two conversion methods: convert_to_wire() and convert_from_wire().
+/// ## Example
+/// ```
+/// implement_key_parameter_conversion_to_from_wire! {
+/// Invalid, INVALID, na;
+/// KeyPurpose, PURPOSE, integer, KeyPurpose;
+/// CallerNonce, CALLER_NONCE, boolValue;
+/// UserSecureID, USER_SECURE_ID, longInteger;
+/// ApplicationID, APPLICATION_ID, blob;
+/// ActiveDateTime, ACTIVE_DATETIME, dateTime;
+/// }
+/// ```
+/// expands to:
+/// ```
+/// pub fn convert_to_wire(self) -> KmKeyParameter {
+/// match self {
+/// KeyParameterValue::Invalid => KmKeyParameter {
+/// tag: Tag::INVALID,
+/// ..Default::default()
+/// },
+/// KeyParameterValue::KeyPurpose(v) => KmKeyParameter {
+/// tag: Tag::PURPOSE,
+/// integer: v.0,
+/// ..Default::default()
+/// },
+/// KeyParameterValue::CallerNonce => KmKeyParameter {
+/// tag: Tag::CALLER_NONCE,
+/// boolValue: true,
+/// ..Default::default()
+/// },
+/// KeyParameterValue::UserSecureID(v) => KmKeyParameter {
+/// tag: Tag::USER_SECURE_ID,
+/// longInteger: v,
+/// ..Default::default()
+/// },
+/// KeyParameterValue::ApplicationID(v) => KmKeyParameter {
+/// tag: Tag::APPLICATION_ID,
+/// blob: v,
+/// ..Default::default()
+/// },
+/// KeyParameterValue::ActiveDateTime(v) => KmKeyParameter {
+/// tag: Tag::ACTIVE_DATETIME,
+/// dateTime: v,
+/// ..Default::default()
+/// },
+/// }
+/// }
+/// ```
+/// and
+/// ```
+/// pub fn convert_from_wire(aidl_kp: KmKeyParameter) -> KeyParameterValue {
+/// match aidl_kp {
+/// KmKeyParameter {
+/// tag: Tag::INVALID,
+/// ..
+/// } => KeyParameterValue::Invalid,
+/// KmKeyParameter {
+/// tag: Tag::PURPOSE,
+/// integer: v,
+/// ..
+/// } => KeyParameterValue::KeyPurpose(KeyPurpose(v)),
+/// KmKeyParameter {
+/// tag: Tag::CALLER_NONCE,
+/// boolValue: true,
+/// ..
+/// } => KeyParameterValue::CallerNonce,
+/// KmKeyParameter {
+/// tag: Tag::USER_SECURE_ID,
+/// longInteger: v,
+/// ..
+/// } => KeyParameterValue::UserSecureID(v),
+/// KmKeyParameter {
+/// tag: Tag::APPLICATION_ID,
+/// blob: v,
+/// ..
+/// } => KeyParameterValue::ApplicationID(v),
+/// KmKeyParameter {
+/// tag: Tag::ACTIVE_DATETIME,
+/// dateTime: v,
+/// ..
+/// } => KeyParameterValue::ActiveDateTime(v),
+/// _ => KeyParameterValue::Invalid,
+/// }
+/// }
+///
+macro_rules! implement_key_parameter_conversion_to_from_wire {
+ // There are three groups of rules in this macro.
+ // 1. The first group contains the rule which acts as the public interface. It takes the input
+ // given to this macro and prepares it to be given as input to the two groups of rules
+ // mentioned below.
+ // 2. The second group starts with the prefix @to and generates convert_to_wire() method.
+ // 3. The third group starts with the prefix @from and generates convert_from_wire() method.
+ //
+ // Input to this macro is first handled by the first macro rule (belonging to the first
+ // group above), which pre-processes the input such that rules in the other two groups
+ // generate the code for the two methods, when called recursively.
+ // Each of convert_to_wire() and convert_from_wire() methods are generated using a set of
+ // four macro rules in the second two groups. These four rules intend to do the following
+ // tasks respectively:
+ // i) generates match arms related to Invalid KeyParameterValue variant.
+ // ii) generates match arms related to boolValue field in KmKeyParameter struct.
+ // iii) generates match arms related to all the other fields in KmKeyParameter struct.
+ // iv) generates the method definition including the match arms generated from the above
+ // three recursive macro rules.
+
+ // This rule is applied on the input given to the macro invocations from outside the macro.
+ ($($variant:ident, $tag_name:ident, $field_name:ident $(,$enum_type:ident)?;)*) => {
+ // pre-processes input to target the rules that generate convert_to_wire() method.
+ implement_key_parameter_conversion_to_from_wire! {@to
+ [], $($variant, $tag_name, $field_name $(,$enum_type)?;)*
+ }
+ // pre-processes input to target the rules that generate convert_from_wire() method.
+ implement_key_parameter_conversion_to_from_wire! {@from
+ [], $($variant, $tag_name, $field_name $(,$enum_type)?;)*
+ }
+ };
+
+ // Following four rules (belonging to the aforementioned second group) generate
+ // convert_to_wire() conversion method.
+ // -----------------------------------------------------------------------
+ // This rule handles Invalid variant.
+ // On an input: 'Invalid, INVALID, na;' it generates a match arm like:
+ // KeyParameterValue::Invalid => KmKeyParameter {
+ // tag: Tag::INVALID,
+ // ..Default::default()
+ // },
+ (@to [$($out:tt)*], Invalid, INVALID, na; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@to
+ [$($out)*
+ KeyParameterValue::Invalid => KmKeyParameter {
+ tag: Tag::INVALID,
+ ..Default::default()
+ },
+ ], $($in)*
+ }
+ };
+ // This rule handles all variants that correspond to bool values.
+ // On an input like: 'CallerNonce, CALLER_NONCE, boolValue;' it generates
+ // a match arm like:
+ // KeyParameterValue::CallerNonce => KmKeyParameter {
+ // tag: Tag::CALLER_NONCE,
+ // boolValue: true,
+ // ..Default::default()
+ // },
+ (@to [$($out:tt)*], $variant:ident, $tag_val:ident, boolValue; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@to
+ [$($out)*
+ KeyParameterValue::$variant => KmKeyParameter {
+ tag: Tag::$tag_val,
+ boolValue: true,
+ ..Default::default()
+ },
+ ], $($in)*
+ }
+ };
+ // This rule handles all enum variants.
+ // On an input like: 'KeyPurpose, PURPOSE, integer, KeyPurpose;' it generates a match arm
+ // like: KeyParameterValue::KeyPurpose(v) => KmKeyParameter {
+ // tag: Tag::PURPOSE,
+ // integer: v.0,
+ // ..Default::default(),
+ // },
+ (@to [$($out:tt)*], $variant:ident, $tag_val:ident, $field:ident, $enum_type:ident; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@to
+ [$($out)*
+ KeyParameterValue::$variant(v) => KmKeyParameter {
+ tag: Tag::$tag_val,
+ $field: v.0,
+ ..Default::default()
+ },
+ ], $($in)*
+ }
+ };
+ // This rule handles all variants that are neither invalid nor bool values nor enums
+ // (i.e. all variants which correspond to integer, longInteger, dateTime and blob fields in
+ // KmKeyParameter).
+ // On an input like: 'ConfirmationToken, CONFIRMATION_TOKEN, blob;' it generates a match arm
+ // like: KeyParameterValue::ConfirmationToken(v) => KmKeyParameter {
+ // tag: Tag::CONFIRMATION_TOKEN,
+ // blob: v,
+ // ..Default::default(),
+ // },
+ (@to [$($out:tt)*], $variant:ident, $tag_val:ident, $field:ident; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@to
+ [$($out)*
+ KeyParameterValue::$variant(v) => KmKeyParameter {
+ tag: Tag::$tag_val,
+ $field: v,
+ ..Default::default()
+ },
+ ], $($in)*
+ }
+ };
+ // After all the match arms are generated by the above three rules, this rule combines them
+ // into the convert_to_wire() method.
+ (@to [$($out:tt)*], ) => {
+ /// Conversion of key parameter to wire type
+ pub fn convert_to_wire(self) -> KmKeyParameter {
+ match self {
+ $($out)*
+ }
+ }
+ };
+
+ // Following four rules (belonging to the aforementioned third group) generate
+ // convert_from_wire() conversion method.
+ // ------------------------------------------------------------------------
+ // This rule handles Invalid variant.
+ // On an input: 'Invalid, INVALID, na;' it generates a match arm like:
+ // KmKeyParameter { tag: Tag::INVALID, .. } => KeyParameterValue::Invalid,
+ (@from [$($out:tt)*], Invalid, INVALID, na; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@from
+ [$($out)*
+ KmKeyParameter {
+ tag: Tag::INVALID,
+ ..
+ } => KeyParameterValue::Invalid,
+ ], $($in)*
+ }
+ };
+ // This rule handles all variants that correspond to bool values.
+ // On an input like: 'CallerNonce, CALLER_NONCE, boolValue;' it generates a match arm like:
+ // KmKeyParameter {
+ // tag: Tag::CALLER_NONCE,
+ // boolValue: true,
+ // ..
+ // } => KeyParameterValue::CallerNonce,
+ (@from [$($out:tt)*], $variant:ident, $tag_val:ident, boolValue; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@from
+ [$($out)*
+ KmKeyParameter {
+ tag: Tag::$tag_val,
+ boolValue: true,
+ ..
+ } => KeyParameterValue::$variant,
+ ], $($in)*
+ }
+ };
+ // This rule handles all enum variants.
+ // On an input like: 'KeyPurpose, PURPOSE, integer, KeyPurpose;' it generates a match arm
+ // like:
+ // KmKeyParameter {
+ // tag: Tag::PURPOSE,
+ // integer: v,
+ // ..,
+ // } => KeyParameterValue::KeyPurpose(KeyPurpose(v)),
+ (@from [$($out:tt)*], $variant:ident, $tag_val:ident, $field:ident, $enum_type:ident; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@from
+ [$($out)*
+ KmKeyParameter {
+ tag: Tag::$tag_val,
+ $field: v,
+ ..
+ } => KeyParameterValue::$variant($enum_type(v)),
+ ], $($in)*
+ }
+ };
+ // This rule handles all variants that are neither invalid nor bool values nor enums
+ // (i.e. all variants which correspond to integer, longInteger, dateTime and blob fields in
+ // KmKeyParameter).
+ // On an input like: 'ConfirmationToken, CONFIRMATION_TOKEN, blob;' it generates a match arm
+ // like:
+ // KmKeyParameter {
+ // tag: Tag::CONFIRMATION_TOKEN,
+ // blob: v,
+ // ..,
+ // } => KeyParameterValue::ConfirmationToken(v),
+ (@from [$($out:tt)*], $variant:ident, $tag_val:ident, $field:ident; $($in:tt)*) => {
+ implement_key_parameter_conversion_to_from_wire! {@from
+ [$($out)*
+ KmKeyParameter {
+ tag: Tag::$tag_val,
+ $field: v,
+ ..
+ } => KeyParameterValue::$variant(v),
+ ], $($in)*
+ }
+ };
+ // After all the match arms are generated by the above three rules, this rule combines them
+ // into the convert_from_wire() method.
+ (@from [$($out:tt)*], ) => {
+ /// Conversion of key parameter from wire type
+ pub fn convert_from_wire(aidl_kp: KmKeyParameter) -> KeyParameterValue {
+ match aidl_kp {
+ $($out)*
+ _ => KeyParameterValue::Invalid,
+ }
+ }
+ };
+}
+
+impl KeyParameterValue {
+ // Invoke the macro that generates the code for key parameter conversion to/from wire type
+ // with all possible variants of KeyParameterValue. Each line corresponding to a variant
+ // contains: variant identifier, tag value, and the related field name (i.e.
+ // boolValue/integer/longInteger/dateTime/blob) in the KmKeyParameter.
+ implement_key_parameter_conversion_to_from_wire! {
+ Invalid, INVALID, na;
+ KeyPurpose, PURPOSE, integer, KeyPurpose;
+ Algorithm, ALGORITHM, integer, Algorithm;
+ KeySize, KEY_SIZE, integer;
+ BlockMode, BLOCK_MODE, integer, BlockMode;
+ Digest, DIGEST, integer, Digest;
+ PaddingMode, PADDING, integer, PaddingMode;
+ CallerNonce, CALLER_NONCE, boolValue;
+ MinMacLength, MIN_MAC_LENGTH, integer;
+ EcCurve, EC_CURVE, integer, EcCurve;
+ RSAPublicExponent, RSA_PUBLIC_EXPONENT, longInteger;
+ IncludeUniqueID, INCLUDE_UNIQUE_ID, boolValue;
+ BootLoaderOnly, BOOTLOADER_ONLY, boolValue;
+ RollbackResistance, ROLLBACK_RESISTANCE, boolValue;
+ ActiveDateTime, ACTIVE_DATETIME, dateTime;
+ OriginationExpireDateTime, ORIGINATION_EXPIRE_DATETIME, dateTime;
+ UsageExpireDateTime, USAGE_EXPIRE_DATETIME, dateTime;
+ MinSecondsBetweenOps, MIN_SECONDS_BETWEEN_OPS, integer;
+ MaxUsesPerBoot, MAX_USES_PER_BOOT, integer;
+ UserID, USER_ID, integer;
+ UserSecureID, USER_SECURE_ID, longInteger;
+ NoAuthRequired, NO_AUTH_REQUIRED, boolValue;
+ HardwareAuthenticatorType, USER_AUTH_TYPE, integer, HardwareAuthenticatorType;
+ AuthTimeout, AUTH_TIMEOUT, integer;
+ AllowWhileOnBody, ALLOW_WHILE_ON_BODY, boolValue;
+ TrustedUserPresenceRequired, TRUSTED_USER_PRESENCE_REQUIRED, boolValue;
+ TrustedConfirmationRequired, TRUSTED_CONFIRMATION_REQUIRED, boolValue;
+ UnlockedDeviceRequired, UNLOCKED_DEVICE_REQUIRED, boolValue;
+ ApplicationID, APPLICATION_ID, blob;
+ ApplicationData, APPLICATION_DATA, blob;
+ CreationDateTime, CREATION_DATETIME, dateTime;
+ KeyOrigin, ORIGIN, integer, KeyOrigin;
+ RootOfTrust, ROOT_OF_TRUST, blob;
+ OSVersion, OS_VERSION, integer;
+ OSPatchLevel, OS_PATCHLEVEL, integer;
+ UniqueID, UNIQUE_ID, blob;
+ AttestationChallenge, ATTESTATION_CHALLENGE, blob;
+ AttestationApplicationID, ATTESTATION_APPLICATION_ID, blob;
+ AttestationIdBrand, ATTESTATION_ID_BRAND, blob;
+ AttestationIdDevice, ATTESTATION_ID_DEVICE, blob;
+ AttestationIdProduct, ATTESTATION_ID_PRODUCT, blob;
+ AttestationIdSerial, ATTESTATION_ID_SERIAL, blob;
+ AttestationIdIMEI, ATTESTATION_ID_IMEI, blob;
+ AttestationIdMEID, ATTESTATION_ID_MEID, blob;
+ AttestationIdManufacturer, ATTESTATION_ID_MANUFACTURER, blob;
+ AttestationIdModel, ATTESTATION_ID_MODEL, blob;
+ VendorPatchLevel, VENDOR_PATCHLEVEL, integer;
+ BootPatchLevel, BOOT_PATCHLEVEL, integer;
+ AssociatedData, ASSOCIATED_DATA, blob;
+ Nonce, NONCE, blob;
+ MacLength, MAC_LENGTH, integer;
+ ResetSinceIdRotation, RESET_SINCE_ID_ROTATION, boolValue;
+ ConfirmationToken, CONFIRMATION_TOKEN, blob;
+ }
+}
+
+#[cfg(test)]
+mod basic_tests {
+ use crate::key_parameter::*;
+
+ // Test basic functionality of KeyParameter.
+ #[test]
+ fn test_key_parameter() {
+ let key_parameter = KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::STRONGBOX,
+ );
+
+ assert_eq!(key_parameter.get_tag(), Tag::ALGORITHM);
+
+ assert_eq!(
+ *key_parameter.key_parameter_value(),
+ KeyParameterValue::Algorithm(Algorithm::RSA)
+ );
+
+ assert_eq!(*key_parameter.security_level(), SecurityLevel::STRONGBOX);
+ }
+}
+
+/// The storage_tests module first tests the 'new_from_sql' method for KeyParameters of different
+/// data types and then tests 'to_sql' method for KeyParameters of those
+/// different data types. The five different data types for KeyParameter values are:
+/// i) enums of u32
+/// ii) u32
+/// iii) u64
+/// iv) Vec<u8>
+/// v) bool
+#[cfg(test)]
+mod storage_tests {
+ use crate::error::*;
+ use crate::key_parameter::*;
+ use anyhow::Result;
+ use rusqlite::types::ToSql;
+ use rusqlite::{params, Connection, NO_PARAMS};
+
+ /// Test initializing a KeyParameter (with key parameter value corresponding to an enum of i32)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_enum_i32() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::ALGORITHM.0,
+ &Algorithm::RSA.0,
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::ALGORITHM, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::Algorithm(Algorithm::RSA));
+ assert_eq!(*key_param.security_level(), SecurityLevel::STRONGBOX);
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of i32)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_i32() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::KEY_SIZE.0, &1024, SecurityLevel::STRONGBOX.0)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::KEY_SIZE, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::KeySize(1024));
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of i64)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_i64() -> Result<()> {
+ let db = init_db()?;
+ // max value for i64, just to test corner cases
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::RSA_PUBLIC_EXPONENT.0,
+ &(i64::MAX),
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::RSA_PUBLIC_EXPONENT, key_param.get_tag());
+ assert_eq!(
+ *key_param.key_parameter_value(),
+ KeyParameterValue::RSAPublicExponent(i64::MAX)
+ );
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of bool)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_bool() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::CALLER_NONCE.0, &Null, SecurityLevel::STRONGBOX.0)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::CALLER_NONCE, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::CallerNonce);
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of Vec<u8>)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_vec_u8() -> Result<()> {
+ let db = init_db()?;
+ let app_id = String::from("MyAppID");
+ let app_id_bytes = app_id.into_bytes();
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::APPLICATION_ID.0,
+ &app_id_bytes,
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::APPLICATION_ID, key_param.get_tag());
+ assert_eq!(
+ *key_param.key_parameter_value(),
+ KeyParameterValue::ApplicationID(app_id_bytes)
+ );
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which corresponds to an enum of i32)
+ /// in the database
+ #[test]
+ fn test_to_sql_enum_i32() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+ #[test]
+ fn test_to_sql_i32() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of i64) in the database
+ #[test]
+ fn test_to_sql_i64() -> Result<()> {
+ let db = init_db()?;
+ // max value for i64, just to test corner cases
+ let kp = KeyParameter::new(
+ KeyParameterValue::RSAPublicExponent(i64::MAX),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of Vec<u8>) in the database
+ #[test]
+ fn test_to_sql_vec_u8() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(
+ KeyParameterValue::ApplicationID(String::from("MyAppID").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+ #[test]
+ fn test_to_sql_bool() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ #[test]
+ /// Test Tag::Invalid
+ fn test_invalid_tag() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, 0, &123, 1)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::INVALID, key_param.get_tag());
+ Ok(())
+ }
+
+ #[test]
+ fn test_non_existing_enum_variant() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, 100, &123, 1)?;
+ tests::check_result_contains_error_string(
+ query_from_keyparameter(&db),
+ "Failed to decode Tag enum from value.",
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn test_invalid_conversion_from_sql() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::ALGORITHM.0, &Null, 1)?;
+ tests::check_result_contains_error_string(
+ query_from_keyparameter(&db),
+ "Failed to read sql data for tag: ALGORITHM.",
+ );
+ Ok(())
+ }
+
+ /// Helper method to init database table for key parameter
+ fn init_db() -> Result<Connection> {
+ let db = Connection::open_in_memory().context("Failed to initialize sqlite connection.")?;
+ db.execute("ATTACH DATABASE ? as 'persistent';", params![""])
+ .context("Failed to attach databases.")?;
+ db.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+ keyentryid INTEGER,
+ tag INTEGER,
+ data ANY,
+ security_level INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keyparameter\" table.")?;
+ Ok(db)
+ }
+
+ /// Helper method to insert an entry into key parameter table, with individual parameters
+ fn insert_into_keyparameter<T: ToSql>(
+ db: &Connection,
+ key_id: i64,
+ tag: i32,
+ value: &T,
+ security_level: i32,
+ ) -> Result<()> {
+ db.execute(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES(?, ?, ?, ?);",
+ params![key_id, tag, *value, security_level],
+ )?;
+ Ok(())
+ }
+
+ /// Helper method to store a key parameter instance.
+ fn store_keyparameter(db: &Connection, key_id: i64, kp: &KeyParameter) -> Result<()> {
+ db.execute(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES(?, ?, ?, ?);",
+ params![key_id, kp.get_tag().0, kp.key_parameter_value(), kp.security_level().0],
+ )?;
+ Ok(())
+ }
+
+ /// Helper method to query a row from keyparameter table
+ fn query_from_keyparameter(db: &Connection) -> Result<KeyParameter> {
+ let mut stmt =
+ db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
+ let mut rows = stmt.query(NO_PARAMS)?;
+ let row = rows.next()?.unwrap();
+ Ok(KeyParameter::new_from_sql(
+ Tag(row.get(0)?),
+ &SqlField(1, row),
+ SecurityLevel(row.get(2)?),
+ )?)
+ }
+}
+
+/// The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
+/// KeyParameter, for the five different types used in KmKeyParameter, in addition to Invalid
+/// key parameter.
+/// i) bool
+/// ii) integer
+/// iii) longInteger
+/// iv) dateTime
+/// v) blob
+#[cfg(test)]
+mod wire_tests {
+ use crate::key_parameter::*;
+ /// unit tests for to conversions
+ #[test]
+ fn test_convert_to_wire_invalid() {
+ let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
+ let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
+ assert_eq!(Tag::INVALID, actual.tag);
+ }
+ #[test]
+ fn test_convert_to_wire_bool() {
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
+ assert_eq!(Tag::CALLER_NONCE, actual.tag);
+ assert_eq!(true, actual.boolValue);
+ }
+ #[test]
+ fn test_convert_to_wire_integer() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ SecurityLevel::STRONGBOX,
+ );
+ let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
+ assert_eq!(Tag::PURPOSE, actual.tag);
+ assert_eq!(KeyPurpose::ENCRYPT.0, actual.integer);
+ }
+ #[test]
+ fn test_convert_to_wire_long_integer() {
+ let kp =
+ KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
+ let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
+ assert_eq!(Tag::USER_SECURE_ID, actual.tag);
+ assert_eq!(i64::MAX, actual.longInteger);
+ }
+ #[test]
+ fn test_convert_to_wire_date_time() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::ActiveDateTime(i64::MAX),
+ SecurityLevel::STRONGBOX,
+ );
+ let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
+ assert_eq!(Tag::ACTIVE_DATETIME, actual.tag);
+ assert_eq!(i64::MAX, actual.dateTime);
+ }
+ #[test]
+ fn test_convert_to_wire_blob() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
+ assert_eq!(Tag::CONFIRMATION_TOKEN, actual.tag);
+ assert_eq!(String::from("ConfirmationToken").into_bytes(), actual.blob);
+ }
+
+ /// unit tests for from conversion
+ #[test]
+ fn test_convert_from_wire_invalid() {
+ let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
+ let actual = KeyParameterValue::convert_from_wire(aidl_kp);
+ assert_eq!(KeyParameterValue::Invalid, actual);
+ }
+ #[test]
+ fn test_convert_from_wire_bool() {
+ let aidl_kp =
+ KmKeyParameter { tag: Tag::CALLER_NONCE, boolValue: true, ..Default::default() };
+ let actual = KeyParameterValue::convert_from_wire(aidl_kp);
+ assert_eq!(KeyParameterValue::CallerNonce, actual);
+ }
+ #[test]
+ fn test_convert_from_wire_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::ENCRYPT.0,
+ ..Default::default()
+ };
+ let actual = KeyParameterValue::convert_from_wire(aidl_kp);
+ assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), actual);
+ }
+ #[test]
+ fn test_convert_from_wire_long_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ longInteger: i64::MAX,
+ ..Default::default()
+ };
+ let actual = KeyParameterValue::convert_from_wire(aidl_kp);
+ assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), actual);
+ }
+ #[test]
+ fn test_convert_from_wire_date_time() {
+ let aidl_kp =
+ KmKeyParameter { tag: Tag::ACTIVE_DATETIME, dateTime: i64::MAX, ..Default::default() };
+ let actual = KeyParameterValue::convert_from_wire(aidl_kp);
+ assert_eq!(KeyParameterValue::ActiveDateTime(i64::MAX), actual);
+ }
+ #[test]
+ fn test_convert_from_wire_blob() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ blob: String::from("ConfirmationToken").into_bytes(),
+ ..Default::default()
+ };
+ let actual = KeyParameterValue::convert_from_wire(aidl_kp);
+ assert_eq!(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ actual
+ );
+ }
+}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index d2c55a7..7439a5b 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -14,4 +14,10 @@
//! This crate implements the Android Keystore 2.0 service.
+pub mod database;
pub mod error;
+pub mod globals;
+/// Internal Representation of Key Parameter and convenience functions.
+pub mod key_parameter;
+pub mod permission;
+pub mod utils;
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
new file mode 100644
index 0000000..0917256
--- /dev/null
+++ b/keystore2/src/permission.rs
@@ -0,0 +1,945 @@
+// 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.
+
+//! This crate provides access control primitives for Keystore 2.0.
+//! It provides high level functions for checking permissions in the keystore2 and keystore2_key
+//! SELinux classes based on the keystore2_selinux backend.
+//! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
+//! defined by keystore2 and keystore2_key respectively.
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+};
+
+use std::cmp::PartialEq;
+use std::convert::From;
+use std::ffi::CStr;
+
+use crate::error::Error as KsError;
+use keystore2_selinux as selinux;
+
+use anyhow::Context as AnyhowContext;
+
+use selinux::Backend;
+
+use lazy_static::lazy_static;
+
+// Replace getcon with a mock in the test situation
+#[cfg(not(test))]
+use selinux::getcon;
+#[cfg(test)]
+use tests::test_getcon as getcon;
+
+lazy_static! {
+ // Panicking here is allowed because keystore cannot function without this backend
+ // and it would happen early and indicate a gross misconfiguration of the device.
+ static ref KEYSTORE2_KEY_LABEL_BACKEND: selinux::KeystoreKeyBackend =
+ selinux::KeystoreKeyBackend::new().unwrap();
+}
+
+fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Context> {
+ KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
+}
+
+/// ## Background
+///
+/// AIDL enums are represented as constants of the form:
+/// ```
+/// mod EnumName {
+/// pub type EnumName = i32;
+/// pub const Variant1: EnumName = <value1>;
+/// pub const Variant2: EnumName = <value2>;
+/// ...
+/// }
+///```
+/// This macro wraps the enum in a new type, e.g., `MyPerm` and maps each variant to an SELinux
+/// permission while providing the following interface:
+/// * From<EnumName> and Into<EnumName> are implemented. Where the implementation of From maps
+/// any variant not specified to the default.
+/// * Every variant has a constructor with a name corresponding to its lower case SELinux string
+/// representation.
+/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+/// represented permission.
+///
+/// ## Special behavior
+/// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
+/// constructor function (e.g. `MePerm::use_()`) but the string returned by `to_selinux` will
+/// still be `"use"`.
+///
+/// ## Example
+/// ```
+///
+/// implement_permission!(
+/// /// MyPerm documentation.
+/// #[derive(Clone, Copy, Debug, PartialEq)]
+/// MyPerm from EnumName with default (None, none) {}
+/// Variant1, selinux name: variant1;
+/// Variant2, selinux name: variant1;
+/// }
+/// );
+/// ```
+macro_rules! implement_permission_aidl {
+ // This rule provides the public interface of the macro. And starts the preprocessing
+ // recursion (see below).
+ ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*)
+ { $($element:tt)* })
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [],
+ $($element)*);
+ };
+
+ // The following three rules recurse through the elements of the form
+ // `<enum variant>, selinux name: <selinux_name>;`
+ // preprocessing the input.
+
+ // The first rule terminates the recursion and passes the processed arguments to the final
+ // rule that spills out the implementation.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], ) => {
+ implement_permission_aidl!(@end $($m)*, $name, $aidl_name, ($($def)*) { $($out)* } );
+ };
+
+ // The second rule is triggered if the selinux name of an element is literally `use`.
+ // It produces the tuple `<enum variant>, use_, use;`
+ // and appends it to the out list.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: use; $($element:tt)*)
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, use_, use;], $($element)*);
+ };
+
+ // The third rule is the default rule which replaces every input tuple with
+ // `<enum variant>, <selinux_name>, <selinux_name>;`
+ // and appends the result to the out list.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: $e_str:ident; $($element:tt)*)
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, $e_str, $e_str;], $($element)*);
+ };
+
+ (@end $($m:meta)*, $name:ident, $aidl_name:ident,
+ ($def_name:ident, $def_selinux_name:ident) {
+ $($element_name:ident, $element_identifier:ident,
+ $selinux_name:ident;)*
+ })
+ =>
+ {
+ $(#[$m])*
+ pub struct $name(pub $aidl_name);
+
+ impl From<$aidl_name> for $name {
+ fn from (p: $aidl_name) -> Self {
+ match p {
+ $aidl_name::$def_name => Self($aidl_name::$def_name),
+ $($aidl_name::$element_name => Self($aidl_name::$element_name),)*
+ _ => Self($aidl_name::$def_name),
+ }
+ }
+ }
+
+ impl Into<$aidl_name> for $name {
+ fn into(self) -> $aidl_name {
+ self.0
+ }
+ }
+
+ impl $name {
+ /// Returns a string representation of the permission as required by
+ /// `selinux::check_access`.
+ pub fn to_selinux(&self) -> &'static str {
+ match self {
+ Self($aidl_name::$def_name) => stringify!($def_selinux_name),
+ $(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
+ _ => stringify!($def_selinux_name),
+ }
+ }
+
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) }
+ $(
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) }
+ )*
+ }
+ };
+}
+
+implement_permission_aidl!(
+ /// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`.
+ /// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to
+ /// the SELinux permissions. With the implement_permission macro, we conveniently
+ /// provide mappings between the wire type bit field values, the rust enum and the SELinux
+ /// string representation.
+ ///
+ /// ## Example
+ ///
+ /// In this access check `KeyPerm::get_info().to_selinux()` would return the SELinux representation
+ /// "info".
+ /// ```
+ /// selinux::check_access(source_context, target_context, "keystore2_key",
+ /// KeyPerm::get_info().to_selinux());
+ /// ```
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ KeyPerm from KeyPermission with default (NONE, none) {
+ DELETE, selinux name: delete;
+ GEN_UNIQUE_ID, selinux name: gen_unique_id;
+ GET_INFO, selinux name: get_info;
+ GRANT, selinux name: grant;
+ MANAGE_BLOB, selinux name: manage_blob;
+ REBIND, selinux name: rebind;
+ REQ_FORCED_OP, selinux name: req_forced_op;
+ UPDATE, selinux name: update;
+ USE, selinux name: use;
+ USE_DEV_ID, selinux name: use_dev_id;
+ }
+);
+
+/// This macro implements an enum with values mapped to SELinux permission names.
+/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
+/// * From<i32> and Into<i32> are implemented. Where the implementation of From maps
+/// any variant not specified to the default.
+/// * Every variant has a constructor with a name corresponding to its lower case SELinux string
+/// representation.
+/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+/// represented permission.
+///
+/// ## Example
+/// ```
+/// implement_permission!(
+/// /// MyPerm documentation.
+/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// MyPerm with default (None = 0, none) {
+/// Foo = 1, selinux name: foo;
+/// Bar = 2, selinux name: bar;
+/// }
+/// );
+/// ```
+macro_rules! implement_permission {
+ // This rule provides the public interface of the macro. And starts the preprocessing
+ // recursion (see below).
+ ($(#[$m:meta])* $name:ident with default
+ ($def_name:ident = $def_val:expr, $def_selinux_name:ident)
+ {
+ $($(#[$element_meta:meta])*
+ $element_name:ident = $element_val:expr, selinux name: $selinux_name:ident;)*
+ })
+ => {
+ $(#[$m])*
+ pub enum $name {
+ /// The default variant of an enum.
+ $def_name = $def_val,
+ $(
+ $(#[$element_meta])*
+ $element_name = $element_val,
+ )*
+ }
+
+ impl From<i32> for $name {
+ fn from (p: i32) -> Self {
+ match p {
+ $def_val => Self::$def_name,
+ $($element_val => Self::$element_name,)*
+ _ => Self::$def_name,
+ }
+ }
+ }
+
+ impl Into<i32> for $name {
+ fn into(self) -> i32 {
+ self as i32
+ }
+ }
+
+ impl $name {
+ /// Returns a string representation of the permission as required by
+ /// `selinux::check_access`.
+ pub fn to_selinux(&self) -> &'static str {
+ match self {
+ Self::$def_name => stringify!($def_selinux_name),
+ $(Self::$element_name => stringify!($selinux_name),)*
+ }
+ }
+
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $def_selinux_name() -> Self { Self::$def_name }
+ $(
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $selinux_name() -> Self { Self::$element_name }
+ )*
+ }
+ };
+}
+
+implement_permission!(
+ /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
+ /// Using the implement_permission macro we get the same features as `KeyPerm`.
+ #[derive(Clone, Copy, Debug, PartialEq)]
+ KeystorePerm with default (None = 0, none) {
+ /// Checked when a new auth token is installed.
+ AddAuth = 1, selinux name: add_auth;
+ /// Checked when an app is uninstalled or wiped.
+ ClearNs = 2, selinux name: clear_ns;
+ /// Checked when Keystore 2.0 gets locked.
+ GetState = 4, selinux name: get_state;
+ /// Checked when Keystore 2.0 is asked to list a namespace that the caller
+ /// does not have the get_info permission for.
+ List = 8, selinux name: list;
+ /// Checked when Keystore 2.0 gets locked.
+ Lock = 0x10, selinux name: lock;
+ /// Checked when Keystore 2.0 shall be reset.
+ Reset = 0x20, selinux name: reset;
+ /// Checked when Keystore 2.0 shall be unlocked.
+ Unlock = 0x40, selinux name: unlock;
+ }
+);
+
+/// Represents a set of `KeyPerm` permissions.
+/// `IntoIterator` is implemented for this struct allowing the iteration through all the
+/// permissions in the set.
+/// It also implements a function `includes(self, other)` that checks if the permissions
+/// in `other` are included in `self`.
+///
+/// KeyPermSet can be created with the macro `key_perm_set![]`.
+///
+/// ## Example
+/// ```
+/// let perms1 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob(), KeyPerm::grant()];
+/// let perms2 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob()];
+///
+/// assert!(perms1.includes(perms2))
+/// assert!(!perms2.includes(perms1))
+///
+/// let i = perms1.into_iter();
+/// // iteration in ascending order of the permission's numeric representation.
+/// assert_eq(Some(KeyPerm::manage_blob()), i.next());
+/// assert_eq(Some(KeyPerm::grant()), i.next());
+/// assert_eq(Some(KeyPerm::use_()), i.next());
+/// assert_eq(None, i.next());
+/// ```
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyPermSet(pub i32);
+
+mod perm {
+ use super::*;
+
+ pub struct IntoIter {
+ vec: KeyPermSet,
+ pos: u8,
+ }
+
+ impl IntoIter {
+ pub fn new(v: KeyPermSet) -> Self {
+ Self { vec: v, pos: 0 }
+ }
+ }
+
+ impl std::iter::Iterator for IntoIter {
+ type Item = KeyPerm;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ if self.pos == 32 {
+ return None;
+ }
+ let p = self.vec.0 & (1 << self.pos);
+ self.pos += 1;
+ if p != 0 {
+ return Some(KeyPerm::from(KeyPermission(p)));
+ }
+ }
+ }
+ }
+}
+
+impl From<KeyPerm> for KeyPermSet {
+ fn from(p: KeyPerm) -> Self {
+ Self((p.0).0 as i32)
+ }
+}
+
+/// allow conversion from the AIDL wire type i32 to a permission set.
+impl From<i32> for KeyPermSet {
+ fn from(p: i32) -> Self {
+ Self(p)
+ }
+}
+
+impl From<KeyPermSet> for i32 {
+ fn from(p: KeyPermSet) -> i32 {
+ p.0
+ }
+}
+
+impl KeyPermSet {
+ /// Returns true iff this permission set has all of the permissions that are in `other`.
+ pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool {
+ let o: KeyPermSet = other.into();
+ (self.0 & o.0) == o.0
+ }
+}
+
+/// This macro can be used to create a `KeyPermSet` from a list of `KeyPerm` values.
+///
+/// ## Example
+/// ```
+/// let v = key_perm_set![Perm::delete(), Perm::manage_blob()];
+/// ```
+#[macro_export]
+macro_rules! key_perm_set {
+ () => { KeyPermSet(0) };
+ ($head:expr $(, $tail:expr)* $(,)?) => {
+ KeyPermSet(($head.0).0 $(| ($tail.0).0)*)
+ };
+}
+
+impl IntoIterator for KeyPermSet {
+ type Item = KeyPerm;
+ type IntoIter = perm::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ Self::IntoIter::new(self)
+ }
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` may access
+/// the given permision `perm` of the `keystore2` security class.
+pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
+ let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
+ selinux::check_access(caller_ctx, &target_context, "keystore2", perm.to_selinux())
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` has
+/// all the permissions indicated in `access_vec` for the target domain indicated by the key
+/// descriptor `key` in the security class `keystore2_key`.
+///
+/// Also checks if the caller has the grant permission for the given target domain.
+///
+/// Attempts to grant the grant permission are always denied.
+///
+/// The only viable target domains are
+/// * `Domain::APP` in which case u:r:keystore:s0 is used as target context and
+/// * `Domain::SELINUX` in which case the `key.nspace` parameter is looked up in
+/// SELinux keystore key backend, and the result is used
+/// as target context.
+pub fn check_grant_permission(
+ caller_ctx: &CStr,
+ access_vec: KeyPermSet,
+ key: &KeyDescriptor,
+) -> anyhow::Result<()> {
+ let target_context = match key.domain {
+ Domain::APP => getcon().context("check_grant_permission: getcon failed.")?,
+ Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
+ .context("check_grant_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+ _ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
+ };
+
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", "grant")
+ .context("Grant permission is required when granting.")?;
+
+ if access_vec.includes(KeyPerm::grant()) {
+ return Err(selinux::Error::perm()).context("Grant permission cannot be granted.");
+ }
+
+ for p in access_vec.into_iter() {
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
+ .context(concat!(
+ "check_grant_permission: check_access failed. ",
+ "The caller may have tried to grant a permission that they don't possess."
+ ))?
+ }
+ Ok(())
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt`
+/// has the permissions indicated by `perm` for the target domain indicated by the key
+/// descriptor `key` in the security class `keystore2_key`.
+///
+/// The behavior differs slightly depending on the selected target domain:
+/// * `Domain::APP` u:r:keystore:s0 is used as target context.
+/// * `Domain::SELINUX` `key.nspace` parameter is looked up in the SELinux keystore key
+/// backend, and the result is used as target context.
+/// * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
+/// to the one supplied in `perm`.
+/// * `Domain::GRANT` Does not use selinux::check_access. Instead the `access_vector`
+/// parameter is queried for permission, which must be supplied in this case.
+///
+/// ## Return values.
+/// * Ok(()) If the requested permissions were granted.
+/// * Err(selinux::Error::perm()) If the requested permissions were denied.
+/// * Err(KsError::sys()) This error is produced if `Domain::GRANT` is selected but no `access_vec`
+/// was supplied. It is also produced if `Domain::KEY_ID` was selected, and
+/// on various unexpected backend failures.
+pub fn check_key_permission(
+ caller_ctx: &CStr,
+ perm: KeyPerm,
+ key: &KeyDescriptor,
+ access_vector: &Option<KeyPermSet>,
+) -> anyhow::Result<()> {
+ let target_context = match key.domain {
+ // apps get the default keystore context
+ Domain::APP => getcon().context("check_key_permission: getcon failed.")?,
+ Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
+ .context("check_key_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+ Domain::GRANT => {
+ match access_vector {
+ Some(pv) => {
+ if pv.includes(perm) {
+ return Ok(());
+ } else {
+ return Err(selinux::Error::perm())
+ .context(format!("\"{}\" not granted", perm.to_selinux()));
+ }
+ }
+ None => {
+ // If DOMAIN_GRANT was selected an access vector must be supplied.
+ return Err(KsError::sys()).context(
+ "Cannot check permission for Domain::GRANT without access vector.",
+ );
+ }
+ }
+ }
+ Domain::KEY_ID => {
+ // We should never be called with `Domain::KEY_ID. The database
+ // lookup should have converted this into one of `Domain::APP`
+ // or `Domain::SELINUX`.
+ return Err(KsError::sys()).context("Cannot check permission for Domain::KEY_ID.");
+ }
+ Domain::BLOB => {
+ let tctx = lookup_keystore2_key_context(key.nspace)
+ .context("Domain::BLOB: Failed to lookup namespace.")?;
+ // If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
+ // permission in addition to the requested permission.
+ selinux::check_access(
+ caller_ctx,
+ &tctx,
+ "keystore2_key",
+ KeyPerm::manage_blob().to_selinux(),
+ )?;
+
+ tctx
+ }
+ _ => {
+ return Err(KsError::sys())
+ .context(format!("Unknown domain value: \"{:?}\".", key.domain))
+ }
+ };
+
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use anyhow::anyhow;
+ use anyhow::Result;
+ use keystore2_selinux::*;
+
+ const ALL_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+
+ const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ // No KeyPerm::grant()
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+
+ const UNPRIV_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::delete(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+
+ /// The su_key namespace as defined in su.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ const SU_KEY_NAMESPACE: i32 = 0;
+ /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ const SHELL_KEY_NAMESPACE: i32 = 1;
+
+ pub fn test_getcon() -> Result<Context> {
+ Context::new("u:object_r:keystore:s0")
+ }
+
+ // This macro evaluates the given expression and checks that
+ // a) evaluated to Result::Err() and that
+ // b) the wrapped error is selinux::Error::perm() (permission denied).
+ // We use a macro here because a function would mask which invocation caused the failure.
+ //
+ // TODO b/164121720 Replace this macro with a function when `track_caller` is available.
+ macro_rules! assert_perm_failed {
+ ($test_function:expr) => {
+ let result = $test_function;
+ assert!(result.is_err(), "Permission check should have failed.");
+ assert_eq!(
+ Some(&selinux::Error::perm()),
+ result.err().unwrap().root_cause().downcast_ref::<selinux::Error>()
+ );
+ };
+ }
+
+ fn check_context() -> Result<(selinux::Context, i32, bool)> {
+ // Calling the non mocked selinux::getcon here intended.
+ let context = selinux::getcon()?;
+ match context.to_str().unwrap() {
+ "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+ "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+ c => Err(anyhow!(format!(
+ "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+ c
+ ))),
+ }
+ }
+
+ #[test]
+ fn check_keystore_permission_test() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::add_auth()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_ns()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::get_state()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::list()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::lock()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::reset()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::unlock()).is_ok());
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::add_auth()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_ns()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::list()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::lock()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::reset()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::unlock()));
+ Ok(())
+ }
+
+ #[test]
+ fn check_grant_permission_app() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+ assert!(check_grant_permission(&system_server_ctx, NOT_GRANT_PERMS, &key).is_ok());
+ // attempts to grant the grant permission must always fail even when privileged.
+
+ assert_perm_failed!(check_grant_permission(
+ &system_server_ctx,
+ KeyPerm::grant().into(),
+ &key
+ ));
+ // unprivileged grant attempts always fail. shell does not have the grant permission.
+ assert_perm_failed!(check_grant_permission(&shell_ctx, UNPRIV_PERMS, &key));
+ Ok(())
+ }
+
+ #[test]
+ fn check_grant_permission_selinux() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+ if is_su {
+ assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
+ // attempts to grant the grant permission must always fail even when privileged.
+ assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::grant().into(), &key));
+ } else {
+ // unprivileged grant attempts always fail. shell does not have the grant permission.
+ assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn check_key_permission_domain_grant() -> Result<()> {
+ let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
+
+ assert_perm_failed!(check_key_permission(
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::grant(),
+ &key,
+ &Some(UNPRIV_PERMS)
+ ));
+
+ check_key_permission(
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::use_(),
+ &key,
+ &Some(ALL_PERMS),
+ )
+ }
+
+ #[test]
+ fn check_key_permission_domain_app() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ let gmscore_app = Context::new("u:r:gmscore_app:s0")?;
+
+ let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
+ assert!(
+ check_key_permission(&system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
+ );
+ assert!(check_key_permission(&gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+
+ assert!(check_key_permission(&shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::update(), &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::grant(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ &shell_ctx,
+ KeyPerm::req_forced_op(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::manage_blob(), &key, &None));
+ assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::use_dev_id(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ &shell_ctx,
+ KeyPerm::gen_unique_id(),
+ &key,
+ &None
+ ));
+
+ Ok(())
+ }
+
+ #[test]
+ fn check_key_permission_domain_selinux() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+
+ if is_su {
+ assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
+ } else {
+ assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None));
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn check_key_permission_domain_blob() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::BLOB,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+
+ if is_su {
+ check_key_permission(&sctx, KeyPerm::use_(), &key, &None)
+ } else {
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None));
+ Ok(())
+ }
+ }
+
+ #[test]
+ fn check_key_permission_domain_key_id() -> Result<()> {
+ let key = KeyDescriptor { domain: Domain::KEY_ID, nspace: 0, alias: None, blob: None };
+
+ assert_eq!(
+ Some(&KsError::sys()),
+ check_key_permission(
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::use_(),
+ &key,
+ &None
+ )
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn key_perm_set_all_test() {
+ let v = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_() // Test if the macro accepts missing comma at the end of the list.
+ ];
+ let mut i = v.into_iter();
+ assert_eq!(i.next().unwrap().to_selinux(), "delete");
+ assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "get_info");
+ assert_eq!(i.next().unwrap().to_selinux(), "grant");
+ assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+ assert_eq!(i.next().unwrap().to_selinux(), "rebind");
+ assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+ assert_eq!(i.next().unwrap().to_selinux(), "update");
+ assert_eq!(i.next().unwrap().to_selinux(), "use");
+ assert_eq!(i.next().unwrap().to_selinux(), "use_dev_id");
+ assert_eq!(None, i.next());
+ }
+ #[test]
+ fn key_perm_set_sparse_test() {
+ let v = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::update(),
+ KeyPerm::use_(), // Test if macro accepts the comma at the end of the list.
+ ];
+ let mut i = v.into_iter();
+ assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+ assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+ assert_eq!(i.next().unwrap().to_selinux(), "update");
+ assert_eq!(i.next().unwrap().to_selinux(), "use");
+ assert_eq!(None, i.next());
+ }
+ #[test]
+ fn key_perm_set_empty_test() {
+ let v = key_perm_set![];
+ let mut i = v.into_iter();
+ assert_eq!(None, i.next());
+ }
+ #[test]
+ fn key_perm_set_include_subset_test() {
+ let v1 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(v1.includes(v2));
+ assert!(!v2.includes(v1));
+ }
+ #[test]
+ fn key_perm_set_include_equal_test() {
+ let v1 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(v1.includes(v2));
+ assert!(v2.includes(v1));
+ }
+ #[test]
+ fn key_perm_set_include_overlap_test() {
+ let v1 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::grant(), // only in v1
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::req_forced_op(), // only in v2
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+ }
+ #[test]
+ fn key_perm_set_include_no_overlap_test() {
+ let v1 = key_perm_set![KeyPerm::manage_blob(), KeyPerm::delete(), KeyPerm::grant(),];
+ let v2 = key_perm_set![
+ KeyPerm::req_forced_op(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+ }
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
new file mode 100644
index 0000000..825b34a
--- /dev/null
+++ b/keystore2/src/utils.rs
@@ -0,0 +1,122 @@
+// 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.
+
+//! This module implements utility functions used by the Keystore 2.0 service
+//! implementation.
+
+use crate::error::Error;
+use crate::permission;
+use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
+use android_hardware_keymint::aidl::android::hardware::keymint::{
+ KeyParameter::KeyParameter as KmParam, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ KeyDescriptor::KeyDescriptor, KeyParameter::KeyParameter,
+};
+use anyhow::{anyhow, Context};
+use binder::{FromIBinder, SpIBinder, ThreadState};
+use std::sync::Mutex;
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given keystore permission.
+pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
+ ThreadState::with_calling_sid(|calling_sid| {
+ permission::check_keystore_permission(
+ &calling_sid.ok_or_else(Error::sys).context(
+ "In check_keystore_permission: Cannot check permission without calling_sid.",
+ )?,
+ perm,
+ )
+ })
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given grant permission.
+pub fn check_grant_permission(access_vec: KeyPermSet, key: &KeyDescriptor) -> anyhow::Result<()> {
+ ThreadState::with_calling_sid(|calling_sid| {
+ permission::check_grant_permission(
+ &calling_sid.ok_or_else(Error::sys).context(
+ "In check_grant_permission: Cannot check permission without calling_sid.",
+ )?,
+ access_vec,
+ key,
+ )
+ })
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given key permission.
+pub fn check_key_permission(
+ perm: KeyPerm,
+ key: &KeyDescriptor,
+ access_vector: &Option<KeyPermSet>,
+) -> anyhow::Result<()> {
+ ThreadState::with_calling_sid(|calling_sid| {
+ permission::check_key_permission(
+ &calling_sid
+ .ok_or_else(Error::sys)
+ .context("In check_key_permission: Cannot check permission without calling_sid.")?,
+ perm,
+ key,
+ access_vector,
+ )
+ })
+}
+
+/// This function converts a `KeyParameter` from the keystore2 AIDL
+/// bindings into a `KeyParameter` from the keymint AIDL bindings.
+/// TODO This is a temporary workaround until the keymint AIDL spec
+/// lands.
+pub fn keyparam_ks_to_km(p: &KeyParameter) -> KmParam {
+ KmParam {
+ tag: Tag(p.tag),
+ boolValue: p.boolValue,
+ integer: p.integer,
+ longInteger: p.longInteger,
+ dateTime: p.dateTime,
+ blob: match &p.blob {
+ Some(b) => b.clone(),
+ None => vec![],
+ },
+ }
+}
+
+/// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
+/// same object in multiple threads, but cloning a SpIBinder is not thread safe.
+/// Keystore frequently hands out binder tokens to the security level interface. If this
+/// is to happen from a multi threaded thread pool, the SpIBinder needs to be protected by a
+/// Mutex.
+#[derive(Debug)]
+pub struct Asp(Mutex<SpIBinder>);
+
+impl Asp {
+ /// Creates a new instance owning a SpIBinder wrapped in a Mutex.
+ pub fn new(i: SpIBinder) -> Self {
+ Self(Mutex::new(i))
+ }
+
+ /// Clones the owned SpIBinder and attempts to convert it into the requested interface.
+ pub fn get_interface<T: FromIBinder + ?Sized>(&self) -> anyhow::Result<Box<T>> {
+ // We can use unwrap here because we never panic when locked, so the mutex
+ // can never be poisoned.
+ let lock = self.0.lock().unwrap();
+ (*lock)
+ .clone()
+ .into_interface()
+ .map_err(|e| anyhow!(format!("get_interface failed with error code {:?}", e)))
+ }
+}