Merge "Model KeyStore security level in keymaster worker"
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 154b675..b5728a3 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -18,8 +18,10 @@
srcs: ["src/lib.rs"],
rustlibs: [
+ "libandroid_hardware_keymint",
+ "libandroid_security_keystore2",
"libanyhow",
- "libkeystore_aidl_generated",
+ "libbinder_rs",
"libkeystore2_selinux",
"liblazy_static",
"liblibsqlite3_sys",
@@ -38,8 +40,10 @@
auto_gen_config: true,
rustlibs: [
"libandroid_logger",
+ "libandroid_hardware_keymint",
+ "libandroid_security_keystore2",
"libanyhow",
- "libkeystore_aidl_generated",
+ "libbinder_rs",
"libkeystore2_selinux",
"liblazy_static",
"liblibsqlite3_sys",
@@ -52,15 +56,6 @@
// 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",
-
- srcs: ["src/aidl_generated.rs"],
-}
-
-// This is a placeholder for the libraries that will be generated from the AIDL specs
-// eventually.
-rust_library {
name: "libandroid_hardware_keymint",
crate_name: "android_hardware_keymint",
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index 05f9db8..8bc3bc4 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -39,10 +39,12 @@
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` assigned the static lifetime function pointer
+ // `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 {
@@ -50,7 +52,7 @@
}
}
-// This function must be called before any entrypoint into lib selinux.
+// 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() {
@@ -82,6 +84,7 @@
/// 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),
@@ -89,6 +92,16 @@
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"))
@@ -221,6 +234,32 @@
}
}
+/// 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
@@ -228,7 +267,7 @@
/// * 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: &Context, target: &Context, tclass: &str, perm: &str) -> Result<()> {
+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)
@@ -256,7 +295,7 @@
.with_context(|| {
format!(
concat!(
- "check_access: Failed with sctx: {} tctx: {}",
+ "check_access: Failed with sctx: {:?} tctx: {:?}",
" with target class: \"{}\" perm: \"{}\""
),
source, target, tclass, perm
@@ -325,7 +364,7 @@
/// 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 expeced to be denied to `shell` users but granted
+ /// 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
@@ -424,4 +463,12 @@
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/database.rs b/keystore2/src/database.rs
index 394b7be..b1cde6e 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -17,64 +17,72 @@
use crate::error::Error as KsError;
use anyhow::{Context, Result};
-use keystore_aidl_generated as aidl;
+
+use android_security_keystore2::aidl::android::security::keystore2::{
+ Domain, Domain::Domain as DomainType,
+};
+
#[cfg(not(test))]
use rand::prelude::random;
use rusqlite::{params, Connection, TransactionBehavior, NO_PARAMS};
+use std::sync::Once;
#[cfg(test)]
use tests::random;
+static INIT_TABLES: Once = Once::new();
+
pub struct KeystoreDB {
conn: Connection,
}
impl KeystoreDB {
- // TODO(b/160882985): Figure out the location for this file.
- #[cfg(not(test))]
- pub fn new() -> Result<KeystoreDB> {
- KeystoreDB::new_with_filename("persistent.sql")
+ 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 })
}
- #[cfg(test)]
- pub fn new() -> Result<KeystoreDB> {
- KeystoreDB::new_with_filename("")
- }
-
- fn new_with_filename(persistent_file: &str) -> Result<KeystoreDB> {
- let db = KeystoreDB {
- conn: Connection::open_in_memory()
- .context("Failed to initialize sqlite connection.")?,
- };
- db.attach_databases(persistent_file).context("Failed to create KeystoreDB.")?;
- db.init_tables().context("Failed to create KeystoreDB.")?;
- Ok(db)
- }
-
- fn attach_databases(&self, persistent_file: &str) -> Result<()> {
- self.conn
- .execute("ATTACH DATABASE ? as 'persistent';", params![persistent_file])
- .context("Failed to attach databases.")?;
- Ok(())
- }
-
- fn init_tables(&self) -> Result<()> {
- self.conn
- .execute(
- "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+ 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.")?;
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keyentry\" 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.")?;
+
Ok(())
}
- pub fn create_key_entry(&self, domain: aidl::Domain, namespace: i64) -> Result<i64> {
+ 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)
+ }
+
+ pub fn create_key_entry(&self, domain: DomainType, namespace: i64) -> Result<i64> {
match domain {
- aidl::Domain::App | aidl::Domain::SELinux => {}
+ Domain::App | Domain::SELinux => {}
_ => {
return Err(KsError::sys())
.context(format!("Domain {:?} must be either App or SELinux.", domain));
@@ -106,11 +114,11 @@
&mut self,
newid: u32,
alias: &str,
- domain: aidl::Domain,
+ domain: DomainType,
namespace: i64,
) -> Result<()> {
match domain {
- aidl::Domain::App | aidl::Domain::SELinux => {}
+ Domain::App | Domain::SELinux => {}
_ => {
return Err(KsError::sys())
.context(format!("Domain {:?} must be either App or SELinux.", domain));
@@ -155,6 +163,23 @@
use super::*;
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() {
@@ -169,35 +194,29 @@
}
}
- // Ensure we can initialize the database.
- #[test]
- fn test_new() -> Result<()> {
- KeystoreDB::new()?;
- Ok(())
- }
-
// Test that we have the correct tables.
#[test]
fn test_tables() -> Result<()> {
- let db = KeystoreDB::new()?;
+ 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(), 1);
+ assert_eq!(tables.len(), 2);
assert_eq!(tables[0], "keyentry");
+ assert_eq!(tables[1], "keyparameter");
Ok(())
}
#[test]
fn test_no_persistence_for_tests() -> Result<()> {
- let db = KeystoreDB::new()?;
+ let db = new_test_db()?;
- db.create_key_entry(aidl::Domain::App, 100)?;
+ db.create_key_entry(Domain::App, 100)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 1);
- let db = KeystoreDB::new()?;
+ let db = new_test_db()?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 0);
@@ -206,13 +225,14 @@
#[test]
fn test_persistence_for_files() -> Result<()> {
- let persistent = TempFile { filename: "/data/local/tmp/persistent.sql" };
- let db = KeystoreDB::new_with_filename(persistent.filename)?;
+ 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(aidl::Domain::App, 100)?;
+ db.create_key_entry(Domain::App, 100)?;
let entries = get_keyentry(&db)?;
assert_eq!(entries.len(), 1);
- let db = KeystoreDB::new_with_filename(persistent.filename)?;
+ let db = new_test_db_with_persistent_file()?;
let entries_new = get_keyentry(&db)?;
assert_eq!(entries, entries_new);
@@ -221,13 +241,11 @@
#[test]
fn test_create_key_entry() -> Result<()> {
- use aidl::Domain;
-
- fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>) {
+ fn extractor(ke: &KeyEntryRow) -> (DomainType, i64, Option<&str>) {
(ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
}
- let db = KeystoreDB::new()?;
+ let db = new_test_db()?;
db.create_key_entry(Domain::App, 100)?;
db.create_key_entry(Domain::SELinux, 101)?;
@@ -240,15 +258,15 @@
// Test that we must pass in a valid Domain.
check_result_is_error_containing_string(
db.create_key_entry(Domain::Grant, 102),
- "Domain Grant must be either App or SELinux.",
+ "Domain 1 must be either App or SELinux.",
);
check_result_is_error_containing_string(
db.create_key_entry(Domain::Blob, 103),
- "Domain Blob must be either App or SELinux.",
+ "Domain 3 must be either App or SELinux.",
);
check_result_is_error_containing_string(
db.create_key_entry(Domain::KeyId, 104),
- "Domain KeyId must be either App or SELinux.",
+ "Domain 4 must be either App or SELinux.",
);
Ok(())
@@ -256,13 +274,11 @@
#[test]
fn test_rebind_alias() -> Result<()> {
- use aidl::Domain;
-
- fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>) {
+ fn extractor(ke: &KeyEntryRow) -> (Option<DomainType>, Option<i64>, Option<&str>) {
(ke.domain, ke.namespace, ke.alias.as_deref())
}
- let mut db = KeystoreDB::new()?;
+ 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)?;
@@ -287,15 +303,15 @@
// Test that we must pass in a valid Domain.
check_result_is_error_containing_string(
db.rebind_alias(0, "foo", Domain::Grant, 42),
- "Domain Grant must be either App or SELinux.",
+ "Domain 1 must be either App or SELinux.",
);
check_result_is_error_containing_string(
db.rebind_alias(0, "foo", Domain::Blob, 42),
- "Domain Blob must be either App or SELinux.",
+ "Domain 3 must be either App or SELinux.",
);
check_result_is_error_containing_string(
db.rebind_alias(0, "foo", Domain::KeyId, 42),
- "Domain KeyId must be either App or SELinux.",
+ "Domain 4 must be either App or SELinux.",
);
// Test that we correctly handle setting an alias for something that does not exist.
@@ -333,7 +349,7 @@
struct KeyEntryRow {
id: u32,
creation_date: String,
- domain: Option<aidl::Domain>,
+ domain: Option<DomainType>,
namespace: Option<i64>,
alias: Option<String>,
}
@@ -342,11 +358,10 @@
db.conn
.prepare("SELECT * FROM persistent.keyentry;")?
.query_map(NO_PARAMS, |row| {
- let domain: Option<i32> = row.get(2)?;
Ok(KeyEntryRow {
id: row.get(0)?,
creation_date: row.get(1)?,
- domain: domain.map(domain_from_integer),
+ domain: row.get(2)?,
namespace: row.get(3)?,
alias: row.get(4)?,
})
@@ -355,19 +370,6 @@
.collect::<Result<Vec<_>>>()
}
- // TODO: Replace this with num_derive.
- fn domain_from_integer(value: i32) -> aidl::Domain {
- use aidl::Domain;
- match value {
- x if Domain::App as i32 == x => Domain::App,
- x if Domain::Grant as i32 == x => Domain::Grant,
- x if Domain::SELinux as i32 == x => Domain::SELinux,
- x if Domain::Blob as i32 == x => Domain::Blob,
- x if Domain::KeyId as i32 == x => Domain::KeyId,
- _ => panic!("Unexpected domain: {}", value),
- }
- }
-
// 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 {
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index e6443b7..0326610 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -31,35 +31,18 @@
//! 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 as Ec;
+pub use android_security_keystore2::aidl::android::security::keystore2::ResponseCode as Rc;
+
+use android_hardware_keymint::aidl::android::hardware::keymint::ErrorCode::ErrorCode;
+use android_security_keystore2::aidl::android::security::keystore2::ResponseCode::ResponseCode;
use keystore2_selinux as selinux;
-pub use aidl::ResponseCode;
-
-/// 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_security_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.
@@ -67,36 +50,55 @@
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)`
pub fn sys() -> Self {
- Error::Rc(AidlRc::SystemError)
+ Error::Rc(Rc::SystemError)
}
/// Short hand for `Error::Rc(ResponseCode::PermissionDenied`
pub fn perm() -> Self {
- Error::Rc(AidlRc::PermissionDenied)
+ Error::Rc(Rc::PermissionDenied)
}
}
-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 => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => AidlResult::rc(AidlRc::PermissionDenied),
- _ => 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(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
@@ -129,32 +131,46 @@
///
/// 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,
+ Some(Error::Km(ec)) => *ec,
+ // 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(_, _)) => Rc::SystemError,
+ None => match root_cause.downcast_ref::<selinux::Error>() {
+ Some(selinux::Error::PermissionDenied) => Rc::PermissionDenied,
+ _ => Rc::SystemError,
+ },
+ };
+ Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
)
}
#[cfg(test)]
-mod tests {
+pub mod tests {
+ use super::*;
+ use android_security_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")
}
@@ -166,11 +182,11 @@
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")
}
@@ -196,6 +212,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(
@@ -203,85 +227,101 @@
.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 Rc::Ok..Rc::BackendBusy {
+ assert_eq!(
+ Result::<(), i32>::Err(rc),
+ map_or_log_err(nested_rc(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 Ec::UNKNOWN_ERROR..Ec::ROOT_OF_TRUST_ALREADY_SET {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ map_or_log_err(nested_ec(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 Ec::UNKNOWN_ERROR..Ec::ROOT_OF_TRUST_ALREADY_SET {
+ 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 Rc::SystemError.
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::<(), i32>::Err(Rc::SystemError),
+ map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
+ BinderStatus::ok()
+ ))
+ .map_err(|s| 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 Rc::SystemError.
assert_eq!(
- AidlResult::ec(-7),
- map_or_log_err(nested_ec(-7), |_| AidlResult::rc(AidlRc::SystemError))
+ Result::<(), i32>::Err(Rc::SystemError),
+ map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
+ BinderStatus::ok()
+ ))
+ .map_err(|s| s.service_specific_error())
);
- // All other get mapped on System Error.
+ // selinux::Error::Perm() needs to be mapped to Rc::PermissionDenied
assert_eq!(
- AidlResult::rc(AidlRc::SystemError),
- map_or_log_err(nested_other_error(), |_| AidlResult::ec(0))
+ Result::<(), i32>::Err(Rc::PermissionDenied),
+ map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
+ .map_err(|s| s.service_specific_error())
+ );
+
+ // All other errors get mapped on System Error.
+ assert_eq!(
+ Result::<(), i32>::Err(Rc::SystemError),
+ map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
+ .map_err(|s| s.service_specific_error())
);
// Result::Ok variants get passed to the ok handler.
- assert_eq!(
- AidlResult::rc(AidlRc::OpAuthNeeded),
- map_or_log_err(nested_ok(AidlRc::OpAuthNeeded), AidlResult::rc)
- );
- assert_eq!(AidlResult::ok(), map_or_log_err(nested_ok(AidlRc::Ok), AidlResult::rc));
+ assert_eq!(Ok(Rc::OpAuthNeeded), map_or_log_err(nested_ok(Rc::OpAuthNeeded), Ok));
+ assert_eq!(Ok(Rc::Ok), map_or_log_err(nested_ok(Rc::Ok), Ok));
- // selinux::Error::Perm() needs to be mapped to AidlRc::PermissionDenied
- assert_eq!(
- AidlResult::rc(AidlRc::PermissionDenied),
- map_or_log_err(nested_selinux_perm(), |_| AidlResult::ec(0))
- );
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/key_parameter.rs b/keystore2/src/key_parameter.rs
index d9ec7f3..5266c28 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -16,15 +16,26 @@
//! and enforced by the OEMs. This module implements the internal representation of KeyParameter
//! and the methods to work with KeyParameter.
-use crate::keymint_definitions::{
- Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyBlobUsageRequirements,
- KeyOrigin, KeyPurpose, PaddingMode, SecurityLevel, Tag,
+use crate::error::Error as KeystoreError;
+use crate::error::Rc;
+pub use android_hardware_keymint::aidl::android::hardware::keymint::{
+ Algorithm, Algorithm::Algorithm as AlgorithmType, BlockMode,
+ BlockMode::BlockMode as BlockModeType, Digest, Digest::Digest as DigestType, EcCurve,
+ EcCurve::EcCurve as EcCurveType, HardwareAuthenticatorType,
+ HardwareAuthenticatorType::HardwareAuthenticatorType as HardwareAuthenticatorTypeType,
+ KeyOrigin, KeyOrigin::KeyOrigin as KeyOriginType, KeyPurpose,
+ KeyPurpose::KeyPurpose as KeyPurposeType, PaddingMode,
+ PaddingMode::PaddingMode as PaddingModeType, SecurityLevel,
+ SecurityLevel::SecurityLevel as SecurityLevelType, Tag, Tag::Tag as TagType,
};
+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.
pub struct KeyParameter {
key_parameter_value: KeyParameterValue,
- security_level: SecurityLevel,
+ security_level: SecurityLevelType,
}
/// KeyParameterValue holds a value corresponding to one of the Tags defined in
@@ -34,55 +45,55 @@
/// Associated with Tag:INVALID
Invalid,
/// Set of purposes for which the key may be used
- KeyPurpose(KeyPurpose),
+ KeyPurpose(KeyPurposeType),
/// Cryptographic algorithm with which the key is used
- Algorithm(Algorithm),
+ Algorithm(AlgorithmType),
/// Size of the key , in bits
- KeySize(u32),
+ KeySize(i32),
/// Block cipher mode(s) with which the key may be used
- BlockMode(BlockMode),
+ BlockMode(BlockModeType),
/// Digest algorithms that may be used with the key to perform signing and verification
- Digest(Digest),
+ Digest(DigestType),
/// Padding modes that may be used with the key. Relevant to RSA, AES and 3DES keys.
- PaddingMode(PaddingMode),
+ PaddingMode(PaddingModeType),
/// 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(u32),
+ MinMacLength(i32),
/// The elliptic curve
- EcCurve(EcCurve),
+ EcCurve(EcCurveType),
/// Value of the public exponent for an RSA key pair
- RSAPublicExponent(u64),
+ RSAPublicExponent(i64),
/// An attestation certificate for the generated key should contain an application-scoped
/// and time-bounded device-unique ID
IncludeUniqueID,
- /// Necessary system environment conditions for the generated key to be used
- KeyBlobUsageRequirements(KeyBlobUsageRequirements),
+ //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,
- //TODO: HARDWARE_TYPE reserved for future use
/// The date and time at which the key becomes active
- ActiveDateTime(u64),
+ ActiveDateTime(i64),
/// The date and time at which the key expires for signing and encryption
- OriginationExpireDateTime(u64),
+ OriginationExpireDateTime(i64),
/// The date and time at which the key expires for verification and decryption
- UsageExpireDateTime(u64),
+ UsageExpireDateTime(i64),
/// Minimum amount of time that elapses between allowed operations
- MinSecondsBetweenOps(u32),
+ MinSecondsBetweenOps(i32),
/// Maximum number of times that a key may be used between system reboots
- MaxUsesPerBoot(u32),
+ MaxUsesPerBoot(i32),
/// ID of the Android user that is permitted to use the key
- UserID(u32),
+ UserID(i32),
/// A key may only be used under a particular secure user authentication state
- UserSecureID(u64),
+ 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),
+ HardwareAuthenticatorType(HardwareAuthenticatorTypeType),
/// The time in seconds for which the key is authorized for use, after user authentication
- AuthTimeout(u32),
+ 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
@@ -99,15 +110,15 @@
/// that is necessary during all uses of the key
ApplicationData(Vec<u8>),
/// Specifies the date and time the key was created
- CreationDateTime(u64),
+ CreationDateTime(i64),
/// Specifies where the key was created, if known
- KeyOrigin(KeyOrigin),
+ KeyOrigin(KeyOriginType),
/// 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(u32),
+ OSVersion(i32),
/// Specifies the system security patch level with which the key may be used
- OSPatchLevel(u32),
+ OSPatchLevel(i32),
/// Specifies a unique, time-based identifier
UniqueID(Vec<u8>),
/// Used to deliver a "challenge" value to the attestKey() method
@@ -131,16 +142,16 @@
/// 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(u32),
+ VendorPatchLevel(i32),
/// Specifies the boot image (kernel) security patch level with which the key may be used
- BootPatchLevel(u32),
+ 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(u32),
+ MacLength(i32),
/// Specifies whether the device has been factory reset since the
/// last unique ID rotation. Used for key attestation
ResetSinceIdRotation,
@@ -151,12 +162,12 @@
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 {
+ pub fn new(key_parameter_value: KeyParameterValue, security_level: SecurityLevelType) -> Self {
KeyParameter { key_parameter_value, security_level }
}
/// Returns the tag given the KeyParameter instance.
- pub fn get_tag(&self) -> Tag {
+ pub fn get_tag(&self) -> TagType {
match self.key_parameter_value {
KeyParameterValue::Invalid => Tag::INVALID,
KeyParameterValue::KeyPurpose(_) => Tag::PURPOSE,
@@ -170,7 +181,6 @@
KeyParameterValue::EcCurve(_) => Tag::EC_CURVE,
KeyParameterValue::RSAPublicExponent(_) => Tag::RSA_PUBLIC_EXPONENT,
KeyParameterValue::IncludeUniqueID => Tag::INCLUDE_UNIQUE_ID,
- KeyParameterValue::KeyBlobUsageRequirements(_) => Tag::BLOB_USAGE_REQUIREMENTS,
KeyParameterValue::BootLoaderOnly => Tag::BOOTLOADER_ONLY,
KeyParameterValue::RollbackResistance => Tag::ROLLBACK_RESISTANCE,
KeyParameterValue::ActiveDateTime(_) => Tag::ACTIVE_DATETIME,
@@ -221,7 +231,7 @@
}
/// Returns the security level of a KeyParameter.
- pub fn security_level(&self) -> &SecurityLevel {
+ pub fn security_level(&self) -> &SecurityLevelType {
&self.security_level
}
}
@@ -229,7 +239,6 @@
#[cfg(test)]
mod basic_tests {
use crate::key_parameter::*;
- use crate::keymint_definitions::{SecurityLevel, Tag};
// Test basic functionality of KeyParameter.
#[test]
@@ -249,3 +258,617 @@
assert_eq!(*key_parameter.security_level(), SecurityLevel::STRONGBOX);
}
}
+
+/// 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> {
+ /// 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 as u32)),
+ KeyParameterValue::Algorithm(a) => Ok(ToSqlOutput::from(*a as u32)),
+ KeyParameterValue::KeySize(k) => Ok(ToSqlOutput::from(*k)),
+ KeyParameterValue::BlockMode(b) => Ok(ToSqlOutput::from(*b as u32)),
+ KeyParameterValue::Digest(d) => Ok(ToSqlOutput::from(*d as u32)),
+ KeyParameterValue::PaddingMode(p) => Ok(ToSqlOutput::from(*p as u32)),
+ KeyParameterValue::CallerNonce => Ok(ToSqlOutput::from(Null)),
+ KeyParameterValue::MinMacLength(m) => Ok(ToSqlOutput::from(*m)),
+ KeyParameterValue::EcCurve(e) => Ok(ToSqlOutput::from(*e 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 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 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: TagType,
+ data: &SqlField,
+ security_level_val: SecurityLevelType,
+ ) -> Result<Self> {
+ let key_param_value = match tag_val {
+ Tag::INVALID => KeyParameterValue::Invalid,
+ Tag::PURPOSE => {
+ let key_purpose: KeyPurposeType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: PURPOSE.")?;
+ KeyParameterValue::KeyPurpose(key_purpose)
+ }
+ Tag::ALGORITHM => {
+ let algorithm: AlgorithmType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: ALGORITHM.")?;
+ KeyParameterValue::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: BlockModeType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: BLOCK_MODE.")?;
+ KeyParameterValue::BlockMode(block_mode)
+ }
+ Tag::DIGEST => {
+ let digest: DigestType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: DIGEST.")?;
+ KeyParameterValue::Digest(digest)
+ }
+ Tag::PADDING => {
+ let padding: PaddingModeType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: PADDING.")?;
+ KeyParameterValue::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: EcCurveType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: EC_CURVE.")?;
+ KeyParameterValue::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: HardwareAuthenticatorTypeType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: USER_AUTH_TYPE.")?;
+ KeyParameterValue::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: KeyOriginType = data
+ .get()
+ .map_err(|_| KeystoreError::Rc(Rc::ValueCorrupted))
+ .context("Failed to read sql data for tag: ORIGIN.")?;
+ KeyParameterValue::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(Rc::ValueCorrupted))
+ .context("Failed to decode Tag enum from value.")?
+ }
+ };
+ Ok(KeyParameter::new(key_param_value, security_level_val))
+ }
+}
+
+/// 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,
+ &Algorithm::RSA,
+ SecurityLevel::STRONGBOX,
+ )?;
+ 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, &1024, SecurityLevel::STRONGBOX)?;
+ 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,
+ &(i64::MAX),
+ SecurityLevel::STRONGBOX,
+ )?;
+ 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, &Null, SecurityLevel::STRONGBOX)?;
+ 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,
+ &app_id_bytes,
+ SecurityLevel::STRONGBOX,
+ )?;
+ 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, &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(), kp.key_parameter_value(), kp.security_level()],
+ )?;
+ 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(row.get(0)?, &SqlField(1, row), row.get(2)?)?)
+ }
+}
diff --git a/keystore2/src/keymint_definitions.rs b/keystore2/src/keymint_definitions.rs
deleted file mode 100644
index 2658a01..0000000
--- a/keystore2/src/keymint_definitions.rs
+++ /dev/null
@@ -1,177 +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(non_camel_case_types)]
-#![allow(missing_docs)]
-
-/// This is the current interface for the code to-be-generated from the keymint AIDL.
-/// The AIDL spec is at" hardware/interfaces/keymint
-#[repr(u32)]
-#[derive(PartialEq, Debug)]
-pub enum TagType {
- INVALID = 0 << 28,
- ENUM = 1 << 28,
- ENUM_REP = 2 << 28,
- UINT = 3 << 28,
- UINT_REP = 4 << 28,
- ULONG = 5 << 28,
- DATE = 6 << 28,
- BOOL = 7 << 28,
- BIGNUM = 8 << 28,
- BYTES = 9 << 28,
- ULONG_REP = 10 << 28,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum Tag {
- INVALID = TagType::INVALID as u32,
- PURPOSE = TagType::ENUM_REP as u32 | 1,
- ALGORITHM = TagType::ENUM as u32 | 2,
- KEY_SIZE = TagType::UINT as u32 | 3,
- BLOCK_MODE = TagType::ENUM_REP as u32 | 4,
- DIGEST = TagType::ENUM_REP as u32 | 5,
- PADDING = TagType::ENUM_REP as u32 | 6,
- CALLER_NONCE = TagType::BOOL as u32 | 7,
- MIN_MAC_LENGTH = TagType::UINT as u32 | 8,
- EC_CURVE = TagType::ENUM as u32 | 10,
- RSA_PUBLIC_EXPONENT = TagType::ULONG as u32 | 200,
- INCLUDE_UNIQUE_ID = TagType::BOOL as u32 | 202,
- BLOB_USAGE_REQUIREMENTS = TagType::ENUM as u32 | 301,
- BOOTLOADER_ONLY = TagType::BOOL as u32 | 302,
- ROLLBACK_RESISTANCE = TagType::BOOL as u32 | 303,
- ACTIVE_DATETIME = TagType::DATE as u32 | 400,
- ORIGINATION_EXPIRE_DATETIME = TagType::DATE as u32 | 401,
- USAGE_EXPIRE_DATETIME = TagType::DATE as u32 | 402,
- MIN_SECONDS_BETWEEN_OPS = TagType::UINT as u32 | 403,
- MAX_USES_PER_BOOT = TagType::UINT as u32 | 404,
- USER_ID = TagType::UINT as u32 | 501,
- USER_SECURE_ID = TagType::ULONG_REP as u32 | 502,
- NO_AUTH_REQUIRED = TagType::BOOL as u32 | 503,
- USER_AUTH_TYPE = TagType::ENUM as u32 | 504,
- AUTH_TIMEOUT = TagType::UINT as u32 | 505,
- ALLOW_WHILE_ON_BODY = TagType::BOOL as u32 | 506,
- TRUSTED_USER_PRESENCE_REQUIRED = TagType::BOOL as u32 | 507,
- TRUSTED_CONFIRMATION_REQUIRED = TagType::BOOL as u32 | 508,
- UNLOCKED_DEVICE_REQUIRED = TagType::BOOL as u32 | 509,
- APPLICATION_ID = TagType::BYTES as u32 | 601,
- APPLICATION_DATA = TagType::BYTES as u32 | 700,
- CREATION_DATETIME = TagType::DATE as u32 | 701,
- ORIGIN = TagType::ENUM as u32 | 702,
- ROOT_OF_TRUST = TagType::BYTES as u32 | 704,
- OS_VERSION = TagType::UINT as u32 | 705,
- OS_PATCHLEVEL = TagType::UINT as u32 | 706,
- UNIQUE_ID = TagType::BYTES as u32 | 707,
- ATTESTATION_CHALLENGE = TagType::BYTES as u32 | 708,
- ATTESTATION_APPLICATION_ID = TagType::BYTES as u32 | 709,
- ATTESTATION_ID_BRAND = TagType::BYTES as u32 | 710,
- ATTESTATION_ID_DEVICE = TagType::BYTES as u32 | 711,
- ATTESTATION_ID_PRODUCT = TagType::BYTES as u32 | 712,
- ATTESTATION_ID_SERIAL = TagType::BYTES as u32 | 713,
- ATTESTATION_ID_IMEI = TagType::BYTES as u32 | 714,
- ATTESTATION_ID_MEID = TagType::BYTES as u32 | 715,
- ATTESTATION_ID_MANUFACTURER = TagType::BYTES as u32 | 716,
- ATTESTATION_ID_MODEL = TagType::BYTES as u32 | 717,
- VENDOR_PATCHLEVEL = TagType::UINT as u32 | 718,
- BOOT_PATCHLEVEL = TagType::UINT as u32 | 719,
- ASSOCIATED_DATA = TagType::BYTES as u32 | 1000,
- NONCE = TagType::BYTES as u32 | 1001,
- MAC_LENGTH = TagType::UINT as u32 | 1003,
- RESET_SINCE_ID_ROTATION = TagType::BOOL as u32 | 1004,
- CONFIRMATION_TOKEN = TagType::BYTES as u32 | 1005,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum Algorithm {
- RSA = 1,
- EC = 3,
- AES = 32,
- TRIPLE_DES = 33,
- HMAC = 128,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum BlockMode {
- ECB = 1,
- CBC = 2,
- CTR = 3,
- GCM = 32,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum PaddingMode {
- NONE = 1,
- RSA_OAEP = 2,
- RSA_PSS = 3,
- RSA_PKCS1_1_5_ENCRYPT = 4,
- RSA_PKCS1_1_5_SIGN = 5,
- PKCS7 = 64,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum Digest {
- NONE = 0,
- MD5 = 1,
- SHA1 = 2,
- SHA_2_224 = 3,
- SHA_2_256 = 4,
- SHA_2_384 = 5,
- SHA_2_512 = 6,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum EcCurve {
- P_224 = 0,
- P_256 = 1,
- P_384 = 2,
- P_521 = 3,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum KeyOrigin {
- GENERATED = 0,
- DERIVED = 1,
- IMPORTED = 2,
- UNKNOWN = 3,
- SECURELY_IMPORTED = 4,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum KeyBlobUsageRequirements {
- STANDALONE = 0,
- REQUIRES_FILE_SYSTEM = 1,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum KeyPurpose {
- ENCRYPT = 0,
- DECRYPT = 1,
- SIGN = 2,
- VERIFY = 3,
- WRAP_KEY = 5,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum HardwareAuthenticatorType {
- NONE = 0,
- PASSWORD = 1,
- FINGERPRINT = 1 << 1,
- ANY = (0xFFFFFFFF as u32) as u32,
-}
-#[repr(u32)]
-#[derive(PartialEq, Debug, Copy, Clone)]
-pub enum SecurityLevel {
- SOFTWARE = 0,
- TRUSTED_ENVIRONMENT = 1,
- STRONGBOX = 2,
-}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 13ef87d..b5fef3e 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -18,6 +18,4 @@
pub mod error;
/// Internal Representation of Key Parameter and convenience functions.
pub mod key_parameter;
-/// Internal interface for the code to-be-generated from the keymint AIDL
-pub mod keymint_definitions;
pub mod permission;
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 0db56dd..df59484 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -18,10 +18,13 @@
//! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
//! defined by keystore2 and keystore2_key respectively.
-use keystore_aidl_generated as aidl;
+use android_security_keystore2::aidl::android::security::keystore2::KeyPermission;
+
+use android_security_keystore2::aidl::android::security::keystore2::KeyDescriptor::KeyDescriptor;
use std::cmp::PartialEq;
use std::convert::From;
+use std::ffi::CStr;
use crate::error::Error as KsError;
use keystore2_selinux as selinux;
@@ -49,17 +52,25 @@
KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
}
-/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
-/// * `From<i32> for `MyPerm`, where each unknown numeric value is mapped to the given default,
-/// here `None`
-/// * `Into<MyPermission> for `MyPerm`
-/// * `MyPerm::foo()` and `MyPerm::bar()` which construct MyPerm instances representing
-/// `MyPermission::Foo` and `MyPermission::Bar` respectively.
-/// * `MyPerm.to_selinux(&self)`, which returns the selinux string representation of the
+/// ## 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.
-/// * Tests in the given test namespace for each permision that check that the numeric
-/// representations of MyPermission and MyPerm match. (TODO replace with static assert if
-/// they become available.)
///
/// ## Special behavior
/// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
@@ -68,85 +79,78 @@
///
/// ## Example
/// ```
-/// #[i32]
-/// enum MyPermission {
-/// None = 0,
-/// Foo = 1,
-/// Bar = 2,
-/// }
///
/// implement_permission!(
/// /// MyPerm documentation.
/// #[derive(Clone, Copy, Debug, PartialEq)]
-/// MyPermission as MyPerm with default (None = 0, none)
-/// and test namespace my_perm_tests {
-/// Foo = 1, selinux name: foo;
-/// Bar = 2, selinux name: bar;
+/// MyPerm from EnumName with default (None, none) {}
+/// Variant1, selinux name: variant1;
+/// Variant2, selinux name: variant1;
/// }
/// );
/// ```
-macro_rules! implement_permission {
+macro_rules! implement_permission_aidl {
// This rule provides the public interface of the macro. And starts the preprocessing
// recursion (see below).
- ($(#[$m:meta])* $t:ty as $name:ident with default ($($def:tt)*)
- and test namespace $tn:ident { $($element:tt)* })
+ ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*)
+ { $($element:tt)* })
=> {
- implement_permission!(@replace_use $($m)*, $t, $name, $tn, ($($def)*), [] , $($element)*);
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [],
+ $($element)*);
};
-
// The following three rules recurse through the elements of the form
- // `<enum variant> = <integer_literal>, selinux name: <selinux_name>;`
+ // `<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)*, $t:ty, $name:ident, $tn:ident, ($($def:tt)*), [$($out:tt)*], ) => {
- implement_permission!(@end $($m)*, $t, $name, $tn, ($($def)*) { $($out)* } );
+ (@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> = <integer_literal>, use_, use;`
+ // It produces the tuple `<enum variant>, use_, use;`
// and appends it to the out list.
- (@replace_use $($m:meta)*, $t:ty, $name:ident, $tn:ident, ($($def:tt)*), [$($out:tt)*],
- $e_name:ident = $e_val:expr, selinux name: use; $($element:tt)*)
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: use; $($element:tt)*)
=> {
- implement_permission!(@replace_use $($m)*, $t, $name, $tn, ($($def)*),
- [$($out)* $e_name = $e_val, use_, use;], $($element)*);
+ 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> = <integer_literal>, <selinux_name>, <selinux_name>;`
+ // `<enum variant>, <selinux_name>, <selinux_name>;`
// and appends the result to the out list.
- (@replace_use $($m:meta)*, $t:ty, $name:ident, $tn:ident, ($($def:tt)*), [$($out:tt)*],
- $e_name:ident = $e_val:expr, selinux name: $e_str:ident; $($element:tt)*)
+ (@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!(@replace_use $($m)*, $t, $name, $tn, ($($def)*),
- [$($out)* $e_name = $e_val, $e_str, $e_str;], $($element)*);
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, $e_str, $e_str;], $($element)*);
};
- (@end $($m:meta)*, $t:ty, $name:ident, $tn:ident,
- ($def_name:ident = $def:expr, $def_selinux_name:ident) {
- $($element_name:ident = $element_val:expr, $element_identifier:ident,
+ (@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($t);
+ pub struct $name(pub $aidl_name::$aidl_name);
- impl From<i32> for $name {
- fn from (p: i32) -> Self {
+ impl From<$aidl_name::$aidl_name> for $name {
+ fn from (p: $aidl_name::$aidl_name) -> Self {
match p {
- $def => Self(<$t>::$def_name),
- $($element_val => Self(<$t>::$element_name),)*
- _ => Self(<$t>::$def_name),
+ $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<$t> for $name {
- fn into(self) -> $t {
+ impl Into<$aidl_name::$aidl_name> for $name {
+ fn into(self) -> $aidl_name::$aidl_name {
self.0
}
}
@@ -156,39 +160,23 @@
/// `selinux::check_access`.
pub fn to_selinux(&self) -> &'static str {
match self {
- Self(<$t>::$def_name) => stringify!($def_selinux_name),
- $(Self(<$t>::$element_name) => stringify!($selinux_name),)*
+ 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(<$t>::$def_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(<$t>::$element_name) }
- )*
- }
- #[cfg(test)]
- mod $tn {
- use super::*;
-
- #[test]
- fn $def_selinux_name() {
- assert_eq!($name::$def_selinux_name(), (<$t>::$def_name as i32).into());
- }
- $(
- #[test]
- fn $element_identifier() {
- assert_eq!($name::$element_identifier(), (<$t>::$element_name as i32).into());
- }
+ pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) }
)*
}
};
-
-
}
-implement_permission!(
+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
@@ -204,56 +192,113 @@
/// KeyPerm::get_info().to_selinux());
/// ```
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
- aidl::KeyPermission as KeyPerm with default (None = 0, none)
- and test namespace key_perm_tests {
- Delete = 1, selinux name: delete;
- GenUniqueId = 2, selinux name: gen_unique_id;
- GetInfo = 4, selinux name: get_info;
- Grant = 8, selinux name: grant;
- List = 0x10, selinux name: list;
- ManageBlob = 0x20, selinux name: manage_blob;
- Rebind = 0x40, selinux name: rebind;
- ReqForcedOp = 0x80, selinux name: req_forced_op;
- Update = 0x100, selinux name: update;
- Use = 0x200, selinux name: use;
- UseDevId = 0x400, selinux name: use_dev_id;
+ KeyPerm from KeyPermission with default (None, none) {
+ Delete, selinux name: delete;
+ GenUniqueId, selinux name: gen_unique_id;
+ GetInfo, selinux name: get_info;
+ Grant, selinux name: grant;
+ List, selinux name: list;
+ ManageBlob, selinux name: manage_blob;
+ Rebind, selinux name: rebind;
+ ReqForcedOp, selinux name: req_forced_op;
+ Update, selinux name: update;
+ Use, selinux name: use;
+ UseDevId, selinux name: use_dev_id;
}
);
-/// KeystorePermission defines values for the SELinux `keystore2` security class.
-/// Countrary to `KeyPermission`, this enum is not generated by AIDL and need not be
-/// wrapped by newtype pattern. But we conveniently use the implement_permission macro
-/// to provide the same feature that we did for `KeyPermission` to this set of permissions.
-#[repr(i32)]
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub enum KeystorePermission {
- /// `None` is not a permission that can ever be granted. It is not known to the SEPolicy.
- None = 0,
- /// Checked when a new auth token is installed.
- AddAuth = 1,
- /// Checked when an app is uninstalled or wiped.
- ClearNs = 2,
- /// Checked when the locked state of Keystore 2.0 is queried.
- GetState = 4,
- /// Checked when Keystore 2.0 gets locked.
- Lock = 8,
- /// Checked when Keystore 2.0 shall be reset.
- Reset = 0x10,
- /// Checked when Keystore 2.0 shall be unlocked.
- Unlock = 0x20,
+/// 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)]
- KeystorePermission as KeystorePerm with default (None = 0, none)
- and test namespace keystore_perm_tests {
+ 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 gets locked.
Lock = 8, selinux name: lock;
+ /// Checked when Keystore 2.0 shall be reset.
Reset = 0x10, selinux name: reset;
+ /// Checked when Keystore 2.0 shall be unlocked.
Unlock = 0x20, selinux name: unlock;
}
);
@@ -281,8 +326,8 @@
/// assert_eq(Some(KeyPerm::use_()), i.next());
/// assert_eq(None, i.next());
/// ```
-#[derive(Copy, Clone)]
-pub struct KeyPermSet(i32);
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyPermSet(pub i32);
mod perm {
use super::*;
@@ -322,9 +367,22 @@
}
}
+/// 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`.
- fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool {
+ pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool {
let o: KeyPermSet = other.into();
(self.0 & o.0) == o.0
}
@@ -355,10 +413,7 @@
/// 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: &selinux::Context,
- perm: KeystorePerm,
-) -> anyhow::Result<()> {
+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())
}
@@ -377,11 +432,11 @@
/// SELinux keystore key backend, and the result is used
/// as target context.
pub fn check_grant_permission(
- caller_ctx: &selinux::Context,
+ caller_ctx: &CStr,
access_vec: KeyPermSet,
- key: &aidl::KeyDescriptor,
+ key: &KeyDescriptor,
) -> anyhow::Result<()> {
- use aidl::Domain;
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
let target_context = match key.domain {
Domain::App => getcon().context("check_grant_permission: getcon failed.")?,
@@ -427,12 +482,12 @@
/// was supplied. It is also produced if `Domain::KeyId` was selected, and
/// on various unexpected backend failures.
pub fn check_key_permission(
- caller_ctx: &selinux::Context,
+ caller_ctx: &CStr,
perm: KeyPerm,
- key: &aidl::KeyDescriptor,
+ key: &KeyDescriptor,
access_vector: &Option<KeyPermSet>,
) -> anyhow::Result<()> {
- use aidl::Domain;
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
let target_context = match key.domain {
// apps get the default keystore context
@@ -477,6 +532,10 @@
tctx
}
+ _ => {
+ return Err(KsError::sys())
+ .context(format!("Unknown domain value: \"{}\".", key.domain))
+ }
};
selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
@@ -488,7 +547,6 @@
use anyhow::anyhow;
use anyhow::Result;
use keystore2_selinux::*;
- use keystore_aidl_generated as aidl;
const ALL_PERMS: KeyPermSet = key_perm_set![
KeyPerm::manage_blob(),
@@ -591,9 +649,8 @@
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")?;
- use aidl::Domain;
- let key =
- aidl::KeyDescriptor { domain: Domain::App, namespace_: 0, alias: None, blob: None };
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
+ let key = KeyDescriptor { domain: Domain::App, namespace_: 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.
@@ -609,9 +666,9 @@
#[test]
fn check_grant_permission_selinux() -> Result<()> {
- use aidl::Domain;
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
let (sctx, namespace, is_su) = check_context()?;
- let key = aidl::KeyDescriptor {
+ let key = KeyDescriptor {
domain: Domain::SELinux,
namespace_: namespace as i64,
alias: None,
@@ -630,9 +687,8 @@
#[test]
fn check_key_permission_domain_grant() -> Result<()> {
- use aidl::Domain;
- let key =
- aidl::KeyDescriptor { domain: Domain::Grant, namespace_: 0, alias: None, blob: None };
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
+ let key = KeyDescriptor { domain: Domain::Grant, namespace_: 0, alias: None, blob: None };
assert_perm_failed!(check_key_permission(
&selinux::Context::new("ignored").unwrap(),
@@ -654,10 +710,9 @@
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")?;
- use aidl::Domain;
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
- let key =
- aidl::KeyDescriptor { domain: Domain::App, namespace_: 0, alias: None, blob: None };
+ let key = KeyDescriptor { domain: Domain::App, namespace_: 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());
@@ -698,9 +753,9 @@
#[test]
fn check_key_permission_domain_selinux() -> Result<()> {
- use aidl::Domain;
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
let (sctx, namespace, is_su) = check_context()?;
- let key = aidl::KeyDescriptor {
+ let key = KeyDescriptor {
domain: Domain::SELinux,
namespace_: namespace as i64,
alias: None,
@@ -737,9 +792,9 @@
#[test]
fn check_key_permission_domain_blob() -> Result<()> {
- use aidl::Domain;
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
let (sctx, namespace, is_su) = check_context()?;
- let key = aidl::KeyDescriptor {
+ let key = KeyDescriptor {
domain: Domain::Blob,
namespace_: namespace as i64,
alias: None,
@@ -756,9 +811,8 @@
#[test]
fn check_key_permission_domain_key_id() -> Result<()> {
- use aidl::Domain;
- let key =
- aidl::KeyDescriptor { domain: Domain::KeyId, namespace_: 0, alias: None, blob: None };
+ use android_security_keystore2::aidl::android::security::keystore2::Domain;
+ let key = KeyDescriptor { domain: Domain::KeyId, namespace_: 0, alias: None, blob: None };
assert_eq!(
Some(&KsError::sys()),