Move large test modules into separate files
This complies better with the Android Rust style guide, and makes it
easier to navigate the code.
Test: keystore2_test libwatchdog_rs.test librkpd_client.test
Change-Id: Iceb49e309af66ec16d31da66b328936b0312061a
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 6548445..16401a4 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -27,6 +27,9 @@
thread,
};
+#[cfg(test)]
+mod tests;
+
#[derive(Debug, PartialEq, Eq)]
enum State {
Exiting,
@@ -256,279 +259,3 @@
state.state = State::Running;
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{AsyncTask, Shelf};
- use std::sync::{
- mpsc::{channel, sync_channel, RecvTimeoutError},
- Arc,
- };
- use std::time::Duration;
-
- #[test]
- fn test_shelf() {
- let mut shelf = Shelf::default();
-
- let s = "A string".to_string();
- assert_eq!(shelf.put(s), None);
-
- let s2 = "Another string".to_string();
- assert_eq!(shelf.put(s2), Some("A string".to_string()));
-
- // Put something of a different type on the shelf.
- #[derive(Debug, PartialEq, Eq)]
- struct Elf {
- pub name: String,
- }
- let e1 = Elf { name: "Glorfindel".to_string() };
- assert_eq!(shelf.put(e1), None);
-
- // The String value is still on the shelf.
- let s3 = shelf.get_downcast_ref::<String>().unwrap();
- assert_eq!(s3, "Another string");
-
- // As is the Elf.
- {
- let e2 = shelf.get_downcast_mut::<Elf>().unwrap();
- assert_eq!(e2.name, "Glorfindel");
- e2.name = "Celeborn".to_string();
- }
-
- // Take the Elf off the shelf.
- let e3 = shelf.remove_downcast_ref::<Elf>().unwrap();
- assert_eq!(e3.name, "Celeborn");
-
- assert_eq!(shelf.remove_downcast_ref::<Elf>(), None);
-
- // No u64 value has been put on the shelf, so getting one gives the default value.
- {
- let i = shelf.get_mut::<u64>();
- assert_eq!(*i, 0);
- *i = 42;
- }
- let i2 = shelf.get_downcast_ref::<u64>().unwrap();
- assert_eq!(*i2, 42);
-
- // No i32 value has ever been seen near the shelf.
- assert_eq!(shelf.get_downcast_ref::<i32>(), None);
- assert_eq!(shelf.get_downcast_mut::<i32>(), None);
- assert_eq!(shelf.remove_downcast_ref::<i32>(), None);
- }
-
- #[test]
- fn test_async_task() {
- let at = AsyncTask::default();
-
- // First queue up a job that blocks until we release it, to avoid
- // unpredictable synchronization.
- let (start_sender, start_receiver) = channel();
- at.queue_hi(move |shelf| {
- start_receiver.recv().unwrap();
- // Put a trace vector on the shelf
- shelf.put(Vec::<String>::new());
- });
-
- // Queue up some high-priority and low-priority jobs.
- for i in 0..3 {
- let j = i;
- at.queue_lo(move |shelf| {
- let trace = shelf.get_mut::<Vec<String>>();
- trace.push(format!("L{}", j));
- });
- let j = i;
- at.queue_hi(move |shelf| {
- let trace = shelf.get_mut::<Vec<String>>();
- trace.push(format!("H{}", j));
- });
- }
-
- // Finally queue up a low priority job that emits the trace.
- let (trace_sender, trace_receiver) = channel();
- at.queue_lo(move |shelf| {
- let trace = shelf.get_downcast_ref::<Vec<String>>().unwrap();
- trace_sender.send(trace.clone()).unwrap();
- });
-
- // Ready, set, go.
- start_sender.send(()).unwrap();
- let trace = trace_receiver.recv().unwrap();
-
- assert_eq!(trace, vec!["H0", "H1", "H2", "L0", "L1", "L2"]);
- }
-
- #[test]
- fn test_async_task_chain() {
- let at = Arc::new(AsyncTask::default());
- let (sender, receiver) = channel();
- // Queue up a job that will queue up another job. This confirms
- // that the job is not invoked with any internal AsyncTask locks held.
- let at_clone = at.clone();
- at.queue_hi(move |_shelf| {
- at_clone.queue_lo(move |_shelf| {
- sender.send(()).unwrap();
- });
- });
- receiver.recv().unwrap();
- }
-
- #[test]
- #[should_panic]
- fn test_async_task_panic() {
- let at = AsyncTask::default();
- at.queue_hi(|_shelf| {
- panic!("Panic from queued job");
- });
- // Queue another job afterwards to ensure that the async thread gets joined.
- let (done_sender, done_receiver) = channel();
- at.queue_hi(move |_shelf| {
- done_sender.send(()).unwrap();
- });
- done_receiver.recv().unwrap();
- }
-
- #[test]
- fn test_async_task_idle() {
- let at = AsyncTask::new(Duration::from_secs(3));
- // Need a SyncSender as it is Send+Sync.
- let (idle_done_sender, idle_done_receiver) = sync_channel::<()>(3);
- at.add_idle(move |_shelf| {
- idle_done_sender.send(()).unwrap();
- });
-
- // Queue up some high-priority and low-priority jobs that take time.
- for _i in 0..3 {
- at.queue_lo(|_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- });
- at.queue_hi(|_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- });
- }
- // Final low-priority job.
- let (done_sender, done_receiver) = channel();
- at.queue_lo(move |_shelf| {
- done_sender.send(()).unwrap();
- });
-
- // Nothing happens until the last job completes.
- assert_eq!(
- idle_done_receiver.recv_timeout(Duration::from_secs(1)),
- Err(RecvTimeoutError::Timeout)
- );
- done_receiver.recv().unwrap();
- // Now that the last low-priority job has completed, the idle task should
- // fire pretty much immediately.
- idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
-
- // Idle callback not executed again even if we wait for a while.
- assert_eq!(
- idle_done_receiver.recv_timeout(Duration::from_secs(3)),
- Err(RecvTimeoutError::Timeout)
- );
-
- // However, if more work is done then there's another chance to go idle.
- let (done_sender, done_receiver) = channel();
- at.queue_hi(move |_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- done_sender.send(()).unwrap();
- });
- // Idle callback not immediately executed, because the high priority
- // job is taking a while.
- assert_eq!(
- idle_done_receiver.recv_timeout(Duration::from_millis(1)),
- Err(RecvTimeoutError::Timeout)
- );
- done_receiver.recv().unwrap();
- idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
- }
-
- #[test]
- fn test_async_task_multiple_idle() {
- let at = AsyncTask::new(Duration::from_secs(3));
- let (idle_sender, idle_receiver) = sync_channel::<i32>(5);
- // Queue a high priority job to start things off
- at.queue_hi(|_shelf| {
- std::thread::sleep(Duration::from_millis(500));
- });
-
- // Multiple idle callbacks.
- for i in 0..3 {
- let idle_sender = idle_sender.clone();
- at.add_idle(move |_shelf| {
- idle_sender.send(i).unwrap();
- });
- }
-
- // Nothing happens immediately.
- assert_eq!(
- idle_receiver.recv_timeout(Duration::from_millis(1)),
- Err(RecvTimeoutError::Timeout)
- );
- // Wait for a moment and the idle jobs should have run.
- std::thread::sleep(Duration::from_secs(1));
-
- let mut results = Vec::new();
- while let Ok(i) = idle_receiver.recv_timeout(Duration::from_millis(1)) {
- results.push(i);
- }
- assert_eq!(results, [0, 1, 2]);
- }
-
- #[test]
- fn test_async_task_idle_queues_job() {
- let at = Arc::new(AsyncTask::new(Duration::from_secs(1)));
- let at_clone = at.clone();
- let (idle_sender, idle_receiver) = sync_channel::<i32>(100);
- // Add an idle callback that queues a low-priority job.
- at.add_idle(move |shelf| {
- at_clone.queue_lo(|_shelf| {
- // Slow things down so the channel doesn't fill up.
- std::thread::sleep(Duration::from_millis(50));
- });
- let i = shelf.get_mut::<i32>();
- idle_sender.send(*i).unwrap();
- *i += 1;
- });
-
- // Nothing happens immediately.
- assert_eq!(
- idle_receiver.recv_timeout(Duration::from_millis(1500)),
- Err(RecvTimeoutError::Timeout)
- );
-
- // Once we queue a normal job, things start.
- at.queue_hi(|_shelf| {});
- assert_eq!(0, idle_receiver.recv_timeout(Duration::from_millis(200)).unwrap());
-
- // The idle callback queues a job, and completion of that job
- // means the task is going idle again...so the idle callback will
- // be called repeatedly.
- assert_eq!(1, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
- assert_eq!(2, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
- assert_eq!(3, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
- }
-
- #[test]
- #[should_panic]
- fn test_async_task_idle_panic() {
- let at = AsyncTask::new(Duration::from_secs(1));
- let (idle_sender, idle_receiver) = sync_channel::<()>(3);
- // Add an idle callback that panics.
- at.add_idle(move |_shelf| {
- idle_sender.send(()).unwrap();
- panic!("Panic from idle callback");
- });
- // Queue a job to trigger idleness and ensuing panic.
- at.queue_hi(|_shelf| {});
- idle_receiver.recv().unwrap();
-
- // Queue another job afterwards to ensure that the async thread gets joined
- // and the panic detected.
- let (done_sender, done_receiver) = channel();
- at.queue_hi(move |_shelf| {
- done_sender.send(()).unwrap();
- });
- done_receiver.recv().unwrap();
- }
-}
diff --git a/keystore2/src/async_task/tests.rs b/keystore2/src/async_task/tests.rs
new file mode 100644
index 0000000..e67303e
--- /dev/null
+++ b/keystore2/src/async_task/tests.rs
@@ -0,0 +1,287 @@
+// 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.
+
+//! Async task tests.
+use super::{AsyncTask, Shelf};
+use std::sync::{
+ mpsc::{channel, sync_channel, RecvTimeoutError},
+ Arc,
+};
+use std::time::Duration;
+
+#[test]
+fn test_shelf() {
+ let mut shelf = Shelf::default();
+
+ let s = "A string".to_string();
+ assert_eq!(shelf.put(s), None);
+
+ let s2 = "Another string".to_string();
+ assert_eq!(shelf.put(s2), Some("A string".to_string()));
+
+ // Put something of a different type on the shelf.
+ #[derive(Debug, PartialEq, Eq)]
+ struct Elf {
+ pub name: String,
+ }
+ let e1 = Elf { name: "Glorfindel".to_string() };
+ assert_eq!(shelf.put(e1), None);
+
+ // The String value is still on the shelf.
+ let s3 = shelf.get_downcast_ref::<String>().unwrap();
+ assert_eq!(s3, "Another string");
+
+ // As is the Elf.
+ {
+ let e2 = shelf.get_downcast_mut::<Elf>().unwrap();
+ assert_eq!(e2.name, "Glorfindel");
+ e2.name = "Celeborn".to_string();
+ }
+
+ // Take the Elf off the shelf.
+ let e3 = shelf.remove_downcast_ref::<Elf>().unwrap();
+ assert_eq!(e3.name, "Celeborn");
+
+ assert_eq!(shelf.remove_downcast_ref::<Elf>(), None);
+
+ // No u64 value has been put on the shelf, so getting one gives the default value.
+ {
+ let i = shelf.get_mut::<u64>();
+ assert_eq!(*i, 0);
+ *i = 42;
+ }
+ let i2 = shelf.get_downcast_ref::<u64>().unwrap();
+ assert_eq!(*i2, 42);
+
+ // No i32 value has ever been seen near the shelf.
+ assert_eq!(shelf.get_downcast_ref::<i32>(), None);
+ assert_eq!(shelf.get_downcast_mut::<i32>(), None);
+ assert_eq!(shelf.remove_downcast_ref::<i32>(), None);
+}
+
+#[test]
+fn test_async_task() {
+ let at = AsyncTask::default();
+
+ // First queue up a job that blocks until we release it, to avoid
+ // unpredictable synchronization.
+ let (start_sender, start_receiver) = channel();
+ at.queue_hi(move |shelf| {
+ start_receiver.recv().unwrap();
+ // Put a trace vector on the shelf
+ shelf.put(Vec::<String>::new());
+ });
+
+ // Queue up some high-priority and low-priority jobs.
+ for i in 0..3 {
+ let j = i;
+ at.queue_lo(move |shelf| {
+ let trace = shelf.get_mut::<Vec<String>>();
+ trace.push(format!("L{}", j));
+ });
+ let j = i;
+ at.queue_hi(move |shelf| {
+ let trace = shelf.get_mut::<Vec<String>>();
+ trace.push(format!("H{}", j));
+ });
+ }
+
+ // Finally queue up a low priority job that emits the trace.
+ let (trace_sender, trace_receiver) = channel();
+ at.queue_lo(move |shelf| {
+ let trace = shelf.get_downcast_ref::<Vec<String>>().unwrap();
+ trace_sender.send(trace.clone()).unwrap();
+ });
+
+ // Ready, set, go.
+ start_sender.send(()).unwrap();
+ let trace = trace_receiver.recv().unwrap();
+
+ assert_eq!(trace, vec!["H0", "H1", "H2", "L0", "L1", "L2"]);
+}
+
+#[test]
+fn test_async_task_chain() {
+ let at = Arc::new(AsyncTask::default());
+ let (sender, receiver) = channel();
+ // Queue up a job that will queue up another job. This confirms
+ // that the job is not invoked with any internal AsyncTask locks held.
+ let at_clone = at.clone();
+ at.queue_hi(move |_shelf| {
+ at_clone.queue_lo(move |_shelf| {
+ sender.send(()).unwrap();
+ });
+ });
+ receiver.recv().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn test_async_task_panic() {
+ let at = AsyncTask::default();
+ at.queue_hi(|_shelf| {
+ panic!("Panic from queued job");
+ });
+ // Queue another job afterwards to ensure that the async thread gets joined.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+ done_receiver.recv().unwrap();
+}
+
+#[test]
+fn test_async_task_idle() {
+ let at = AsyncTask::new(Duration::from_secs(3));
+ // Need a SyncSender as it is Send+Sync.
+ let (idle_done_sender, idle_done_receiver) = sync_channel::<()>(3);
+ at.add_idle(move |_shelf| {
+ idle_done_sender.send(()).unwrap();
+ });
+
+ // Queue up some high-priority and low-priority jobs that take time.
+ for _i in 0..3 {
+ at.queue_lo(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+ at.queue_hi(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+ }
+ // Final low-priority job.
+ let (done_sender, done_receiver) = channel();
+ at.queue_lo(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+
+ // Nothing happens until the last job completes.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_secs(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ done_receiver.recv().unwrap();
+ // Now that the last low-priority job has completed, the idle task should
+ // fire pretty much immediately.
+ idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+
+ // Idle callback not executed again even if we wait for a while.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_secs(3)),
+ Err(RecvTimeoutError::Timeout)
+ );
+
+ // However, if more work is done then there's another chance to go idle.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ done_sender.send(()).unwrap();
+ });
+ // Idle callback not immediately executed, because the high priority
+ // job is taking a while.
+ assert_eq!(
+ idle_done_receiver.recv_timeout(Duration::from_millis(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ done_receiver.recv().unwrap();
+ idle_done_receiver.recv_timeout(Duration::from_millis(50)).unwrap();
+}
+
+#[test]
+fn test_async_task_multiple_idle() {
+ let at = AsyncTask::new(Duration::from_secs(3));
+ let (idle_sender, idle_receiver) = sync_channel::<i32>(5);
+ // Queue a high priority job to start things off
+ at.queue_hi(|_shelf| {
+ std::thread::sleep(Duration::from_millis(500));
+ });
+
+ // Multiple idle callbacks.
+ for i in 0..3 {
+ let idle_sender = idle_sender.clone();
+ at.add_idle(move |_shelf| {
+ idle_sender.send(i).unwrap();
+ });
+ }
+
+ // Nothing happens immediately.
+ assert_eq!(
+ idle_receiver.recv_timeout(Duration::from_millis(1)),
+ Err(RecvTimeoutError::Timeout)
+ );
+ // Wait for a moment and the idle jobs should have run.
+ std::thread::sleep(Duration::from_secs(1));
+
+ let mut results = Vec::new();
+ while let Ok(i) = idle_receiver.recv_timeout(Duration::from_millis(1)) {
+ results.push(i);
+ }
+ assert_eq!(results, [0, 1, 2]);
+}
+
+#[test]
+fn test_async_task_idle_queues_job() {
+ let at = Arc::new(AsyncTask::new(Duration::from_secs(1)));
+ let at_clone = at.clone();
+ let (idle_sender, idle_receiver) = sync_channel::<i32>(100);
+ // Add an idle callback that queues a low-priority job.
+ at.add_idle(move |shelf| {
+ at_clone.queue_lo(|_shelf| {
+ // Slow things down so the channel doesn't fill up.
+ std::thread::sleep(Duration::from_millis(50));
+ });
+ let i = shelf.get_mut::<i32>();
+ idle_sender.send(*i).unwrap();
+ *i += 1;
+ });
+
+ // Nothing happens immediately.
+ assert_eq!(
+ idle_receiver.recv_timeout(Duration::from_millis(1500)),
+ Err(RecvTimeoutError::Timeout)
+ );
+
+ // Once we queue a normal job, things start.
+ at.queue_hi(|_shelf| {});
+ assert_eq!(0, idle_receiver.recv_timeout(Duration::from_millis(200)).unwrap());
+
+ // The idle callback queues a job, and completion of that job
+ // means the task is going idle again...so the idle callback will
+ // be called repeatedly.
+ assert_eq!(1, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ assert_eq!(2, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+ assert_eq!(3, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+}
+
+#[test]
+#[should_panic]
+fn test_async_task_idle_panic() {
+ let at = AsyncTask::new(Duration::from_secs(1));
+ let (idle_sender, idle_receiver) = sync_channel::<()>(3);
+ // Add an idle callback that panics.
+ at.add_idle(move |_shelf| {
+ idle_sender.send(()).unwrap();
+ panic!("Panic from idle callback");
+ });
+ // Queue a job to trigger idleness and ensuing panic.
+ at.queue_hi(|_shelf| {});
+ idle_receiver.recv().unwrap();
+
+ // Queue another job afterwards to ensure that the async thread gets joined
+ // and the panic detected.
+ let (done_sender, done_receiver) = channel();
+ at.queue_hi(move |_shelf| {
+ done_sender.send(()).unwrap();
+ });
+ done_receiver.recv().unwrap();
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index a9a1c4b..754dd9c 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -45,6 +45,9 @@
pub(crate) mod utils;
mod versioning;
+#[cfg(test)]
+pub mod tests;
+
use crate::gc::Gc;
use crate::impl_metadata; // This is in database/utils.rs
use crate::key_parameter::{KeyParameter, KeyParameterValue, Tag};
@@ -2864,2620 +2867,3 @@
Ok(app_uids_vec)
}
}
-
-#[cfg(test)]
-pub 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 crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
- use keystore2_test_utils::TempDir;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
- };
- use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- Timestamp::Timestamp,
- };
- use std::cell::RefCell;
- use std::collections::BTreeMap;
- use std::fmt::Write;
- use std::sync::atomic::{AtomicU8, Ordering};
- use std::sync::Arc;
- use std::thread;
- use std::time::{Duration, SystemTime};
- use crate::utils::AesGcm;
- #[cfg(disabled)]
- use std::time::Instant;
-
- pub fn new_test_db() -> Result<KeystoreDB> {
- let conn = KeystoreDB::make_connection("file::memory:")?;
-
- let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
- db.with_transaction(Immediate("TX_new_test_db"), |tx| {
- KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
- })?;
- Ok(db)
- }
-
- fn rebind_alias(
- db: &mut KeystoreDB,
- newid: &KeyIdGuard,
- alias: &str,
- domain: Domain,
- namespace: i64,
- ) -> Result<bool> {
- db.with_transaction(Immediate("TX_rebind_alias"), |tx| {
- KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
- })
- .context(ks_err!())
- }
-
- #[test]
- fn datetime() -> Result<()> {
- let conn = Connection::open_in_memory()?;
- conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
- let now = SystemTime::now();
- let duration = Duration::from_secs(1000);
- let then = now.checked_sub(duration).unwrap();
- let soon = now.checked_add(duration).unwrap();
- conn.execute(
- "INSERT INTO test (ts) VALUES (?), (?), (?);",
- params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
- )?;
- let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
- let mut rows = stmt.query([])?;
- assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
- assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
- assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
- assert!(rows.next()?.is_none());
- assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
- assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
- assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
- Ok(())
- }
-
- // 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(), 6);
- assert_eq!(tables[0], "blobentry");
- assert_eq!(tables[1], "blobmetadata");
- assert_eq!(tables[2], "grant");
- assert_eq!(tables[3], "keyentry");
- assert_eq!(tables[4], "keymetadata");
- assert_eq!(tables[5], "keyparameter");
- Ok(())
- }
-
- #[test]
- fn test_auth_token_table_invariant() -> Result<()> {
- let mut db = new_test_db()?;
- let auth_token1 = HardwareAuthToken {
- challenge: i64::MAX,
- userId: 200,
- authenticatorId: 200,
- authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
- timestamp: Timestamp { milliSeconds: 500 },
- mac: String::from("mac").into_bytes(),
- };
- db.insert_auth_token(&auth_token1);
- let auth_tokens_returned = get_auth_tokens(&db);
- assert_eq!(auth_tokens_returned.len(), 1);
-
- // insert another auth token with the same values for the columns in the UNIQUE constraint
- // of the auth token table and different value for timestamp
- let auth_token2 = HardwareAuthToken {
- challenge: i64::MAX,
- userId: 200,
- authenticatorId: 200,
- authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
- timestamp: Timestamp { milliSeconds: 600 },
- mac: String::from("mac").into_bytes(),
- };
-
- db.insert_auth_token(&auth_token2);
- let mut auth_tokens_returned = get_auth_tokens(&db);
- assert_eq!(auth_tokens_returned.len(), 1);
-
- if let Some(auth_token) = auth_tokens_returned.pop() {
- assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
- }
-
- // insert another auth token with the different values for the columns in the UNIQUE
- // constraint of the auth token table
- let auth_token3 = HardwareAuthToken {
- challenge: i64::MAX,
- userId: 201,
- authenticatorId: 200,
- authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
- timestamp: Timestamp { milliSeconds: 600 },
- mac: String::from("mac").into_bytes(),
- };
-
- db.insert_auth_token(&auth_token3);
- let auth_tokens_returned = get_auth_tokens(&db);
- assert_eq!(auth_tokens_returned.len(), 2);
-
- Ok(())
- }
-
- // utility function for test_auth_token_table_invariant()
- fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
- db.perboot.get_all_auth_token_entries()
- }
-
- fn create_key_entry(
- db: &mut KeystoreDB,
- domain: &Domain,
- namespace: &i64,
- key_type: KeyType,
- km_uuid: &Uuid,
- ) -> Result<KeyIdGuard> {
- db.with_transaction(Immediate("TX_create_key_entry"), |tx| {
- KeystoreDB::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
- })
- }
-
- #[test]
- fn test_persistence_for_files() -> Result<()> {
- let temp_dir = TempDir::new("persistent_db_test")?;
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
-
- create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 1);
-
- let db = KeystoreDB::new(temp_dir.path(), None)?;
-
- 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>, Uuid) {
- (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
- }
-
- let mut db = new_test_db()?;
-
- create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
- create_key_entry(&mut db, &Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
-
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
- assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
-
- // Test that we must pass in a valid Domain.
- check_result_is_error_containing_string(
- create_key_entry(&mut db, &Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
- &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
- );
- check_result_is_error_containing_string(
- create_key_entry(&mut db, &Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
- &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
- );
- check_result_is_error_containing_string(
- create_key_entry(&mut db, &Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
- &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
- );
-
- Ok(())
- }
-
- #[test]
- fn test_rebind_alias() -> Result<()> {
- fn extractor(
- ke: &KeyEntryRow,
- ) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
- (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
- }
-
- let mut db = new_test_db()?;
- create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- let entries = get_keyentry(&db)?;
- assert_eq!(entries.len(), 2);
- assert_eq!(
- extractor(&entries[0]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
-
- // Test that the first call to rebind_alias sets the alias.
- rebind_alias(&mut db, &KEY_ID_LOCK.get(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"), Some(KEYSTORE_UUID))
- );
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
- );
-
- // Test that the second call to rebind_alias also empties the old one.
- rebind_alias(&mut db, &KEY_ID_LOCK.get(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, Some(KEYSTORE_UUID)));
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
-
- // Test that we must pass in a valid Domain.
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
- );
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
- );
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
- &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
- );
-
- // Test that we correctly handle setting an alias for something that does not exist.
- check_result_is_error_containing_string(
- rebind_alias(&mut db, &KEY_ID_LOCK.get(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, Some(KEYSTORE_UUID)));
- assert_eq!(
- extractor(&entries[1]),
- (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
- );
-
- 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, key_type, domain, namespace, alias, state, km_uuid)
- VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
- params![KEYSTORE_UUID, KEYSTORE_UUID],
- )?;
- 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::GetInfo];
- 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, 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(())
- })
- .unwrap();
-
- 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, 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(())
- })
- .unwrap();
-
- 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, 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(())
- })
- .unwrap();
-
- 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 persistent.grant;")?;
- let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |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_KEY_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_set_blob() -> Result<()> {
- let key_id = KEY_ID_LOCK.get(3000);
- let mut db = new_test_db()?;
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
- db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
- drop(key_id);
-
- let mut stmt = db.conn.prepare(
- "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
- ORDER BY subcomponent_type ASC;",
- )?;
- let mut rows = stmt
- .query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
- Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
- })?;
- let (r, id) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
- let (r, _) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
- let (r, _) = rows.next().unwrap().unwrap();
- assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
-
- drop(rows);
- drop(stmt);
-
- assert_eq!(
- db.with_transaction(Immediate("TX_test"), |tx| {
- BlobMetaData::load_from_db(id, tx).no_gc()
- })
- .expect("Should find blob metadata."),
- blob_metadata
- );
- 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, None)
- .context("test_insert_and_load_full_keyentry_domain_app")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
- let mut db = new_test_db()?;
-
- db.store_new_certificate(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- TEST_CERT_BLOB,
- &KEYSTORE_UUID,
- )
- .expect("Trying to insert cert.");
-
- let (_key_guard, mut key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::PUBLIC,
- 1,
- |_k, _av| Ok(()),
- )
- .expect("Trying to read certificate entry.");
-
- assert!(key_entry.pure_cert());
- assert!(key_entry.cert().is_none());
- assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- 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, None)
- .context("test_insert_and_load_full_keyentry_domain_selinux")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: 1,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- 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, None)
- .context("test_insert_and_load_full_keyentry_domain_key_id")?
- .0;
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- 1,
- |_, _| Ok(()),
- )
- .unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 1,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
- .context("test_check_and_update_key_usage_count_with_limited_use_key")?
- .0;
- // Update the usage count of the limited use key.
- db.check_and_update_key_usage_count(key_id)?;
-
- let (_key_guard, key_entry) = db.load_key_entry(
- &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 1,
- |_k, _av| Ok(()),
- )?;
-
- // The usage count is decremented now.
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
-
- Ok(())
- }
-
- #[test]
- fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
- .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
- .0;
- // Update the usage count of the limited use key.
- db.check_and_update_key_usage_count(key_id).expect(concat!(
- "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
- "This should succeed."
- ));
-
- // Try to update the exhausted limited use key.
- let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
- "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
- "This should fail."
- ));
- assert_eq!(
- &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
- e.root_cause().downcast_ref::<KsError>().unwrap()
- );
-
- 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, None)
- .context("test_insert_and_load_full_keyentry_from_grant")?
- .0;
-
- 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(()),
- )
- .unwrap();
-
- debug_dump_grant_table(&mut db)?;
-
- let (_key_guard, key_entry) = db
- .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
- assert_eq!(Domain::GRANT, k.domain);
- assert!(av.unwrap().includes(KeyPerm::Use));
- Ok(())
- })
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &granted_key,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- 2,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // This test attempts to load a key by key id while the caller is not the owner
- // but a grant exists for the given key and the caller.
- #[test]
- fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
- let mut db = new_test_db()?;
- const OWNER_UID: u32 = 1u32;
- const GRANTEE_UID: u32 = 2u32;
- const SOMEONE_ELSE_UID: u32 = 3u32;
- let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
- .0;
-
- db.grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- OWNER_UID,
- GRANTEE_UID,
- key_perm_set![KeyPerm::Use],
- |_k, _av| Ok(()),
- )
- .unwrap();
-
- debug_dump_grant_table(&mut db)?;
-
- let id_descriptor =
- KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
-
- let (_, key_entry) = db
- .load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- GRANTEE_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.unwrap().includes(KeyPerm::Use));
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- let (_, key_entry) = db
- .load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- SOMEONE_ELSE_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(OWNER_UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &id_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- GRANTEE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates a key migrates it to a different location and then tries to access it by the old
- // and new location.
- #[test]
- fn test_migrate_key_app_to_app() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let source_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(SOURCE_ALIAS.to_string()),
- blob: None,
- };
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- let key_id = key_id_guard.id();
-
- db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
- Ok(())
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &destination_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- DESTINATION_UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(DESTINATION_UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &source_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- SOURCE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates a key migrates it to a different location and then tries to access it by the old
- // and new location.
- #[test]
- fn test_migrate_key_app_to_selinux() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- const DESTINATION_NAMESPACE: i64 = 1000i64;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let source_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(SOURCE_ALIAS.to_string()),
- blob: None,
- };
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: DESTINATION_NAMESPACE,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- let key_id = key_id_guard.id();
-
- db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
- Ok(())
- })
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &destination_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- DESTINATION_UID,
- |k, av| {
- assert_eq!(Domain::SELINUX, k.domain);
- assert_eq!(DESTINATION_NAMESPACE, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
-
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &source_descriptor,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- SOURCE_UID,
- |_k, _av| Ok(()),
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- // Creates two keys and tries to migrate the first to the location of the second which
- // is expected to fail.
- #[test]
- fn test_migrate_key_destination_occupied() -> Result<()> {
- let mut db = new_test_db()?;
- const SOURCE_UID: u32 = 1u32;
- const DESTINATION_UID: u32 = 2u32;
- static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
- static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
- let key_id_guard =
- make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
- make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
-
- let destination_descriptor: KeyDescriptor = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(DESTINATION_ALIAS.to_string()),
- blob: None,
- };
-
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
- db.migrate_key_namespace(
- key_id_guard,
- &destination_descriptor,
- DESTINATION_UID,
- |_k| Ok(())
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
-
- Ok(())
- }
-
- #[test]
- fn test_upgrade_0_to_1() {
- const ALIAS1: &str = "test_upgrade_0_to_1_1";
- const ALIAS2: &str = "test_upgrade_0_to_1_2";
- const ALIAS3: &str = "test_upgrade_0_to_1_3";
- const UID: u32 = 33;
- let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
- let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
- let key_id_untouched1 =
- make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
- let key_id_untouched2 =
- make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
- let key_id_deleted =
- make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
-
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS1.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS2.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS3.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
-
- db.with_transaction(Immediate("TX_test"), |tx| KeystoreDB::from_0_to_1(tx).no_gc())
- .unwrap();
-
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS1.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
- let (_, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS2.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap();
- assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
- assert_eq!(
- Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
- db.load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(ALIAS3.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- UID,
- |k, av| {
- assert_eq!(Domain::APP, k.domain);
- assert_eq!(UID as i64, k.nspace);
- assert!(av.is_none());
- Ok(())
- },
- )
- .unwrap_err()
- .root_cause()
- .downcast_ref::<KsError>()
- );
- }
-
- static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
-
- #[test]
- fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
- let handle = {
- let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
- let temp_dir_clone = temp_dir.clone();
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
- .context("test_insert_and_load_full_keyentry_domain_app")?
- .0;
- let (_key_guard, key_entry) = db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 33,
- |_k, _av| Ok(()),
- )
- .unwrap();
- assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
- let state = Arc::new(AtomicU8::new(1));
- let state2 = state.clone();
-
- // Spawning a second thread that attempts to acquire the key id lock
- // for the same key as the primary thread. The primary thread then
- // waits, thereby forcing the secondary thread into the second stage
- // of acquiring the lock (see KEY ID LOCK 2/2 above).
- // The test succeeds if the secondary thread observes the transition
- // of `state` from 1 to 2, despite having a whole second to overtake
- // the primary thread.
- let handle = thread::spawn(move || {
- let temp_dir = temp_dir_clone;
- let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
- assert!(db
- .load_key_entry(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
- blob: None,
- },
- KeyType::Client,
- KeyEntryLoadBits::BOTH,
- 33,
- |_k, _av| Ok(()),
- )
- .is_ok());
- // We should only see a 2 here because we can only return
- // from load_key_entry when the `_key_guard` expires,
- // which happens at the end of the scope.
- assert_eq!(2, state2.load(Ordering::Relaxed));
- });
-
- thread::sleep(std::time::Duration::from_millis(1000));
-
- assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
-
- // Return the handle from this scope so we can join with the
- // secondary thread after the key id lock has expired.
- handle
- // This is where the `_key_guard` goes out of scope,
- // which is the reason for concurrent load_key_entry on the same key
- // to unblock.
- };
- // Join with the secondary thread and unwrap, to propagate failing asserts to the
- // main test thread. We will not see failing asserts in secondary threads otherwise.
- handle.join().unwrap();
- Ok(())
- }
-
- #[test]
- fn test_database_busy_error_code() {
- let temp_dir =
- TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
-
- let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
- let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
-
- let _tx1 = db1
- .conn
- .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
- .expect("Failed to create first transaction.");
-
- let error = db2
- .conn
- .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
- .context("Transaction begin failed.")
- .expect_err("This should fail.");
- let root_cause = error.root_cause();
- if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
- root_cause.downcast_ref::<rusqlite::ffi::Error>()
- {
- return;
- }
- panic!(
- "Unexpected error {:?} \n{:?} \n{:?}",
- error,
- root_cause,
- root_cause.downcast_ref::<rusqlite::ffi::Error>()
- )
- }
-
- #[cfg(disabled)]
- #[test]
- fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
- let temp_dir = Arc::new(
- TempDir::new("test_large_number_of_concurrent_db_manipulations_")
- .expect("Failed to create temp dir."),
- );
-
- let test_begin = Instant::now();
-
- const KEY_COUNT: u32 = 500u32;
- let mut db =
- new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
- const OPEN_DB_COUNT: u32 = 50u32;
-
- let mut actual_key_count = KEY_COUNT;
- // First insert KEY_COUNT keys.
- for count in 0..KEY_COUNT {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
- actual_key_count = count;
- break;
- }
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
- .expect("Failed to make key entry.");
- }
-
- // Insert more keys from a different thread and into a different namespace.
- let temp_dir1 = temp_dir.clone();
- let handle1 = thread::spawn(move || {
- let mut db = new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
- .expect("Failed to make key entry.");
- }
-
- // then unbind them again.
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(format!("test_alias_{}", count)),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- // And start unbinding the first set of keys.
- let temp_dir2 = temp_dir.clone();
- let handle2 = thread::spawn(move || {
- let mut db = new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- for count in 0..actual_key_count {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(format!("test_alias_{}", count)),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- // While a lot of inserting and deleting is going on we have to open database connections
- // successfully and use them.
- // This clone is not redundant, because temp_dir needs to be kept alive until db goes
- // out of scope.
- #[allow(clippy::redundant_clone)]
- let temp_dir4 = temp_dir.clone();
- let handle4 = thread::spawn(move || {
- for count in 0..OPEN_DB_COUNT {
- if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
- return;
- }
- let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
- .expect("Failed to open database.");
-
- let alias = format!("test_alias_{}", count);
- make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
- .expect("Failed to make key entry.");
- let key = KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(alias),
- blob: None,
- };
- db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
- }
- });
-
- handle1.join().expect("Thread 1 panicked.");
- handle2.join().expect("Thread 2 panicked.");
- handle4.join().expect("Thread 4 panicked.");
-
- Ok(())
- }
-
- #[test]
- fn list() -> Result<()> {
- let temp_dir = TempDir::new("list_test")?;
- let mut db = KeystoreDB::new(temp_dir.path(), None)?;
- static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
- (Domain::APP, 1, "test1"),
- (Domain::APP, 1, "test2"),
- (Domain::APP, 1, "test3"),
- (Domain::APP, 1, "test4"),
- (Domain::APP, 1, "test5"),
- (Domain::APP, 1, "test6"),
- (Domain::APP, 1, "test7"),
- (Domain::APP, 2, "test1"),
- (Domain::APP, 2, "test2"),
- (Domain::APP, 2, "test3"),
- (Domain::APP, 2, "test4"),
- (Domain::APP, 2, "test5"),
- (Domain::APP, 2, "test6"),
- (Domain::APP, 2, "test8"),
- (Domain::SELINUX, 100, "test1"),
- (Domain::SELINUX, 100, "test2"),
- (Domain::SELINUX, 100, "test3"),
- (Domain::SELINUX, 100, "test4"),
- (Domain::SELINUX, 100, "test5"),
- (Domain::SELINUX, 100, "test6"),
- (Domain::SELINUX, 100, "test9"),
- ];
-
- let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
- .iter()
- .map(|(domain, ns, alias)| {
- let entry =
- make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
- panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
- });
- (entry.id(), *ns)
- })
- .collect();
-
- for (domain, namespace) in
- &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
- {
- let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
- .iter()
- .filter_map(|(domain, ns, alias)| match ns {
- ns if *ns == *namespace => Some(KeyDescriptor {
- domain: *domain,
- nspace: *ns,
- alias: Some(alias.to_string()),
- blob: None,
- }),
- _ => None,
- })
- .collect();
- list_o_descriptors.sort();
- let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
- list_result.sort();
- assert_eq!(list_o_descriptors, list_result);
-
- let mut list_o_ids: Vec<i64> = list_o_descriptors
- .into_iter()
- .map(|d| {
- let (_, entry) = db
- .load_key_entry(
- &d,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- *namespace as u32,
- |_, _| Ok(()),
- )
- .unwrap();
- entry.id()
- })
- .collect();
- list_o_ids.sort_unstable();
- let mut loaded_entries: Vec<i64> = list_o_keys
- .iter()
- .filter_map(|(id, ns)| match ns {
- ns if *ns == *namespace => Some(*id),
- _ => None,
- })
- .collect();
- loaded_entries.sort_unstable();
- assert_eq!(list_o_ids, loaded_entries);
- }
- assert_eq!(
- Vec::<KeyDescriptor>::new(),
- db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
- );
-
- 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)]
- struct KeyEntryRow {
- id: i64,
- key_type: KeyType,
- domain: Option<Domain>,
- namespace: Option<i64>,
- alias: Option<String>,
- state: KeyLifeCycle,
- km_uuid: Option<Uuid>,
- }
-
- fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
- db.conn
- .prepare("SELECT * FROM persistent.keyentry;")?
- .query_map([], |row| {
- Ok(KeyEntryRow {
- id: row.get(0)?,
- key_type: row.get(1)?,
- domain: row.get::<_, Option<_>>(2)?.map(Domain),
- namespace: row.get(3)?,
- alias: row.get(4)?,
- state: row.get(5)?,
- km_uuid: row.get(6)?,
- })
- })?
- .map(|r| r.context("Could not read keyentry row."))
- .collect::<Result<Vec<_>>>()
- }
-
- fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
- make_test_params_with_sids(max_usage_count, &[42])
- }
-
- // 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_with_sids(
- max_usage_count: Option<i32>,
- user_secure_ids: &[i64],
- ) -> Vec<KeyParameter> {
- let mut params = 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::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::AttestationIdSecondIMEI(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,
- ),
- ];
- if let Some(value) = max_usage_count {
- params.push(KeyParameter::new(
- KeyParameterValue::UsageCountLimit(value),
- SecurityLevel::SOFTWARE,
- ));
- }
-
- for sid in user_secure_ids.iter() {
- params.push(KeyParameter::new(
- KeyParameterValue::UserSecureID(*sid),
- SecurityLevel::STRONGBOX,
- ));
- }
- params
- }
-
- pub fn make_test_key_entry(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- max_usage_count: Option<i32>,
- ) -> Result<KeyIdGuard> {
- make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
- }
-
- pub fn make_test_key_entry_with_sids(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- max_usage_count: Option<i32>,
- sids: &[i64],
- ) -> Result<KeyIdGuard> {
- let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
- blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
- blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
- db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
-
- let params = make_test_params_with_sids(max_usage_count, sids);
- db.insert_keyparameter(&key_id, ¶ms)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
- let params = make_test_params(max_usage_count);
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
- blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
- blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
-
- KeyEntry {
- id: key_id,
- key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
- cert: Some(TEST_CERT_BLOB.to_vec()),
- cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- km_uuid: KEYSTORE_UUID,
- parameters: params,
- metadata,
- pure_cert: false,
- }
- }
-
- pub fn make_bootlevel_key_entry(
- db: &mut KeystoreDB,
- domain: Domain,
- namespace: i64,
- alias: &str,
- logical_only: bool,
- ) -> Result<KeyIdGuard> {
- let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
- let mut blob_metadata = BlobMetaData::new();
- if !logical_only {
- blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
- }
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
- db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
- db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
-
- let mut params = make_test_params(None);
- params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
-
- db.insert_keyparameter(&key_id, ¶ms)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- // Creates an app key that is marked as being superencrypted by the given
- // super key ID and that has the given authentication and unlocked device
- // parameters. This does not actually superencrypt the key blob.
- fn make_superencrypted_key_entry(
- db: &mut KeystoreDB,
- namespace: i64,
- alias: &str,
- requires_authentication: bool,
- requires_unlocked_device: bool,
- super_key_id: i64,
- ) -> Result<KeyIdGuard> {
- let domain = Domain::APP;
- let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
- db.set_blob(
- &key_id,
- SubComponentType::KEY_BLOB,
- Some(TEST_KEY_BLOB),
- Some(&blob_metadata),
- )?;
-
- let mut params = vec![];
- if requires_unlocked_device {
- params.push(KeyParameter::new(
- KeyParameterValue::UnlockedDeviceRequired,
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ));
- }
- if requires_authentication {
- params.push(KeyParameter::new(
- KeyParameterValue::UserSecureID(42),
- SecurityLevel::TRUSTED_ENVIRONMENT,
- ));
- }
- db.insert_keyparameter(&key_id, ¶ms)?;
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
-
- rebind_alias(db, &key_id, alias, domain, namespace)?;
- Ok(key_id)
- }
-
- fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
- let mut params = make_test_params(None);
- params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
-
- let mut blob_metadata = BlobMetaData::new();
- if !logical_only {
- blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
- }
- blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
-
- KeyEntry {
- id: key_id,
- key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
- cert: Some(TEST_CERT_BLOB.to_vec()),
- cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
- km_uuid: KEYSTORE_UUID,
- parameters: params,
- metadata,
- pure_cert: false,
- }
- }
-
- fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
- let mut stmt = db.conn.prepare(
- "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
- )?;
- let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>(
- [],
- |row| {
- Ok((
- row.get(0)?,
- row.get(1)?,
- row.get(2)?,
- row.get(3)?,
- row.get(4)?,
- row.get(5)?,
- row.get(6)?,
- ))
- },
- )?;
-
- println!("Key entry table rows:");
- for r in rows {
- let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
- println!(
- " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
- id, key_type, domain, namespace, alias, state, km_uuid
- );
- }
- Ok(())
- }
-
- fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
- let mut stmt = db
- .conn
- .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |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(())
- }
-
- // 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> = const { 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
- })
- }
-
- #[test]
- fn test_unbind_keys_for_user() -> Result<()> {
- let mut db = new_test_db()?;
- db.unbind_keys_for_user(1)?;
-
- make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
- make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
- db.unbind_keys_for_user(2)?;
-
- assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
- assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
-
- db.unbind_keys_for_user(1)?;
- assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
-
- Ok(())
- }
-
- #[test]
- fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
- let mut db = new_test_db()?;
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
-
- let key_name_enc = SuperKeyType {
- alias: "test_super_key_1",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- name: "test_super_key_1",
- };
-
- let key_name_nonenc = SuperKeyType {
- alias: "test_super_key_2",
- algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
- name: "test_super_key_2",
- };
-
- // Install two super keys.
- db.store_super_key(
- 1,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that both can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
-
- // Install the same keys for a different user.
- db.store_super_key(
- 2,
- &key_name_nonenc,
- &super_key,
- &BlobMetaData::new(),
- &KeyMetaData::new(),
- )?;
- db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
-
- // Check that the second pair of keys can be found in the database.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- // Delete all keys for user 1.
- db.unbind_keys_for_user(1)?;
-
- // All of user 1's keys should be gone.
- assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
- assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
-
- // User 2's keys should not have been touched.
- assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
- assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
-
- Ok(())
- }
-
- fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
- db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
- }
-
- // Tests the unbind_auth_bound_keys_for_user() function.
- #[test]
- fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
- let mut db = new_test_db()?;
- let user_id = 1;
- let nspace: i64 = (user_id * AID_USER_OFFSET).into();
- let other_user_id = 2;
- let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
- let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
-
- // Create a superencryption key.
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let (encrypted_super_key, blob_metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(
- user_id,
- super_key_type,
- &encrypted_super_key,
- &blob_metadata,
- &KeyMetaData::new(),
- )?;
- let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
-
- // Store 4 superencrypted app keys, one for each possible combination of
- // (authentication required, unlocked device required).
- make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
- make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
- assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
- assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
-
- // Also store a key for a different user that requires authentication.
- make_superencrypted_key_entry(
- &mut db,
- other_user_nspace,
- "auth_ud",
- true,
- true,
- super_key_id,
- )?;
-
- db.unbind_auth_bound_keys_for_user(user_id)?;
-
- // Verify that only the user's app keys that require authentication were
- // deleted. Keys that require an unlocked device but not authentication
- // should *not* have been deleted, nor should the super key have been
- // deleted, nor should other users' keys have been deleted.
- assert!(db.load_super_key(super_key_type, user_id)?.is_some());
- assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
- assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
- assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
- assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
- assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
-
- Ok(())
- }
-
- #[test]
- fn test_store_super_key() -> Result<()> {
- let mut db = new_test_db()?;
- let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
- let super_key = keystore2_crypto::generate_aes256_key()?;
- let secret_bytes = b"keystore2 is great.";
- let (encrypted_secret, iv, tag) =
- keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
-
- let (encrypted_super_key, metadata) =
- SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(
- 1,
- &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
- &encrypted_super_key,
- &metadata,
- &KeyMetaData::new(),
- )?;
-
- // Check if super key exists.
- assert!(db.key_exists(
- Domain::APP,
- 1,
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
- KeyType::Super
- )?);
-
- let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
- let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
- USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
- key_entry,
- &pw,
- None,
- )?;
-
- let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
- assert_eq!(secret_bytes, &*decrypted_secret_bytes);
-
- Ok(())
- }
-
- fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
- vec![
- MetricsStorage::KEY_ENTRY,
- MetricsStorage::KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
- MetricsStorage::BLOB_ENTRY,
- MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_PARAMETER,
- MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_METADATA,
- MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
- MetricsStorage::GRANT,
- MetricsStorage::AUTH_TOKEN,
- MetricsStorage::BLOB_METADATA,
- MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
- ]
- }
-
- /// Perform a simple check to ensure that we can query all the storage types
- /// that are supported by the DB. Check for reasonable values.
- #[test]
- fn test_query_all_valid_table_sizes() -> Result<()> {
- const PAGE_SIZE: i32 = 4096;
-
- let mut db = new_test_db()?;
-
- for t in get_valid_statsd_storage_types() {
- let stat = db.get_storage_stat(t)?;
- // AuthToken can be less than a page since it's in a btree, not sqlite
- // TODO(b/187474736) stop using if-let here
- if let MetricsStorage::AUTH_TOKEN = t {
- } else {
- assert!(stat.size >= PAGE_SIZE);
- }
- assert!(stat.size >= stat.unused_size);
- }
-
- Ok(())
- }
-
- fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
- get_valid_statsd_storage_types()
- .into_iter()
- .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
- .collect()
- }
-
- fn assert_storage_increased(
- db: &mut KeystoreDB,
- increased_storage_types: Vec<MetricsStorage>,
- baseline: &mut BTreeMap<i32, StorageStats>,
- ) {
- for storage in increased_storage_types {
- // Verify the expected storage increased.
- let new = db.get_storage_stat(storage).unwrap();
- let old = &baseline[&storage.0];
- assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
- assert!(
- new.unused_size <= old.unused_size,
- "{}: {} <= {}",
- storage.0,
- new.unused_size,
- old.unused_size
- );
-
- // Update the baseline with the new value so that it succeeds in the
- // later comparison.
- baseline.insert(storage.0, new);
- }
-
- // Get an updated map of the storage and verify there were no unexpected changes.
- let updated_stats = get_storage_stats_map(db);
- assert_eq!(updated_stats.len(), baseline.len());
-
- for &k in baseline.keys() {
- let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
- let mut s = String::new();
- for &k in map.keys() {
- writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
- .expect("string concat failed");
- }
- s
- };
-
- assert!(
- updated_stats[&k].size == baseline[&k].size
- && updated_stats[&k].unused_size == baseline[&k].unused_size,
- "updated_stats:\n{}\nbaseline:\n{}",
- stringify(&updated_stats),
- stringify(baseline)
- );
- }
- }
-
- #[test]
- fn test_verify_key_table_size_reporting() -> Result<()> {
- let mut db = new_test_db()?;
- let mut working_stats = get_storage_stats_map(&mut db);
-
- let key_id = create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- assert_storage_increased(
- &mut db,
- vec![
- MetricsStorage::KEY_ENTRY,
- MetricsStorage::KEY_ENTRY_ID_INDEX,
- MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
- ],
- &mut working_stats,
- );
-
- let mut blob_metadata = BlobMetaData::new();
- blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
- db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
- assert_storage_increased(
- &mut db,
- vec![
- MetricsStorage::BLOB_ENTRY,
- MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
- MetricsStorage::BLOB_METADATA,
- MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
- ],
- &mut working_stats,
- );
-
- let params = make_test_params(None);
- db.insert_keyparameter(&key_id, ¶ms)?;
- assert_storage_increased(
- &mut db,
- vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
- &mut working_stats,
- );
-
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
- db.insert_key_metadata(&key_id, &metadata)?;
- assert_storage_increased(
- &mut db,
- vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
- &mut working_stats,
- );
-
- let mut sum = 0;
- for stat in working_stats.values() {
- sum += stat.size;
- }
- let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
- assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
-
- Ok(())
- }
-
- #[test]
- fn test_verify_auth_table_size_reporting() -> Result<()> {
- let mut db = new_test_db()?;
- let mut working_stats = get_storage_stats_map(&mut db);
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 456,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 10 },
- mac: b"mac".to_vec(),
- });
- assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
- Ok(())
- }
-
- #[test]
- fn test_verify_grant_table_size_reporting() -> Result<()> {
- const OWNER: i64 = 1;
- let mut db = new_test_db()?;
- make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
-
- let mut working_stats = get_storage_stats_map(&mut db);
- db.grant(
- &KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(TEST_ALIAS.to_string()),
- blob: None,
- },
- OWNER as u32,
- 123,
- key_perm_set![KeyPerm::Use],
- |_, _| Ok(()),
- )?;
-
- assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
-
- Ok(())
- }
-
- #[test]
- fn find_auth_token_entry_returns_latest() -> Result<()> {
- let mut db = new_test_db()?;
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 456,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 10 },
- mac: b"mac0".to_vec(),
- });
- std::thread::sleep(std::time::Duration::from_millis(1));
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 457,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 12 },
- mac: b"mac1".to_vec(),
- });
- std::thread::sleep(std::time::Duration::from_millis(1));
- db.insert_auth_token(&HardwareAuthToken {
- challenge: 123,
- userId: 458,
- authenticatorId: 789,
- authenticatorType: kmhw_authenticator_type::ANY,
- timestamp: Timestamp { milliSeconds: 3 },
- mac: b"mac2".to_vec(),
- });
- // All three entries are in the database
- assert_eq!(db.perboot.auth_tokens_len(), 3);
- // It selected the most recent timestamp
- assert_eq!(db.find_auth_token_entry(|_| true).unwrap().auth_token.mac, b"mac2".to_vec());
- Ok(())
- }
-
- #[test]
- fn test_load_key_descriptor() -> Result<()> {
- let mut db = new_test_db()?;
- let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
-
- let key = db.load_key_descriptor(key_id)?.unwrap();
-
- assert_eq!(key.domain, Domain::APP);
- assert_eq!(key.nspace, 1);
- assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
-
- // No such id
- assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
- Ok(())
- }
-
- #[test]
- fn test_get_list_app_uids_for_sid() -> Result<()> {
- let uid: i32 = 1;
- let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
- let first_sid = 667;
- let second_sid = 669;
- let first_app_id: i64 = 123 + uid_offset;
- let second_app_id: i64 = 456 + uid_offset;
- let third_app_id: i64 = 789 + uid_offset;
- let unrelated_app_id: i64 = 1011 + uid_offset;
- let mut db = new_test_db()?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- first_app_id,
- TEST_ALIAS,
- None,
- &[first_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- second_app_id,
- "alias2",
- None,
- &[first_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- second_app_id,
- TEST_ALIAS,
- None,
- &[second_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- third_app_id,
- "alias3",
- None,
- &[second_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- unrelated_app_id,
- TEST_ALIAS,
- None,
- &[],
- )
- .context("test_get_list_app_uids_for_sid")?;
-
- let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
- first_sid_apps.sort();
- assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
- let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
- second_sid_apps.sort();
- assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
- Ok(())
- }
-
- #[test]
- fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
- let uid: i32 = 1;
- let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
- let first_sid = 667;
- let second_sid = 669;
- let third_sid = 772;
- let first_app_id: i64 = 123 + uid_offset;
- let second_app_id: i64 = 456 + uid_offset;
- let mut db = new_test_db()?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- first_app_id,
- TEST_ALIAS,
- None,
- &[first_sid, second_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
- make_test_key_entry_with_sids(
- &mut db,
- Domain::APP,
- second_app_id,
- "alias2",
- None,
- &[second_sid, third_sid],
- )
- .context("test_get_list_app_uids_for_sid")?;
-
- let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
- assert_eq!(first_sid_apps, vec![first_app_id]);
-
- let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
- second_sid_apps.sort();
- assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
-
- let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
- assert_eq!(third_sid_apps, vec![second_app_id]);
- Ok(())
- }
-}
diff --git a/keystore2/src/database/tests.rs b/keystore2/src/database/tests.rs
new file mode 100644
index 0000000..031d749
--- /dev/null
+++ b/keystore2/src/database/tests.rs
@@ -0,0 +1,2528 @@
+// 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.
+
+//! Database 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 crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
+use keystore2_test_utils::TempDir;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+ Timestamp::Timestamp,
+};
+use std::cell::RefCell;
+use std::collections::BTreeMap;
+use std::fmt::Write;
+use std::sync::atomic::{AtomicU8, Ordering};
+use std::sync::Arc;
+use std::thread;
+use std::time::{Duration, SystemTime};
+use crate::utils::AesGcm;
+#[cfg(disabled)]
+use std::time::Instant;
+
+pub fn new_test_db() -> Result<KeystoreDB> {
+ let conn = KeystoreDB::make_connection("file::memory:")?;
+
+ let mut db = KeystoreDB { conn, gc: None, perboot: Arc::new(perboot::PerbootDB::new()) };
+ db.with_transaction(Immediate("TX_new_test_db"), |tx| {
+ KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
+ })?;
+ Ok(db)
+}
+
+fn rebind_alias(
+ db: &mut KeystoreDB,
+ newid: &KeyIdGuard,
+ alias: &str,
+ domain: Domain,
+ namespace: i64,
+) -> Result<bool> {
+ db.with_transaction(Immediate("TX_rebind_alias"), |tx| {
+ KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace, KeyType::Client).no_gc()
+ })
+ .context(ks_err!())
+}
+
+#[test]
+fn datetime() -> Result<()> {
+ let conn = Connection::open_in_memory()?;
+ conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
+ let now = SystemTime::now();
+ let duration = Duration::from_secs(1000);
+ let then = now.checked_sub(duration).unwrap();
+ let soon = now.checked_add(duration).unwrap();
+ conn.execute(
+ "INSERT INTO test (ts) VALUES (?), (?), (?);",
+ params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
+ )?;
+ let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
+ let mut rows = stmt.query([])?;
+ assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
+ assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
+ assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
+ assert!(rows.next()?.is_none());
+ assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
+ assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
+ assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
+ Ok(())
+}
+
+// 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(), 6);
+ assert_eq!(tables[0], "blobentry");
+ assert_eq!(tables[1], "blobmetadata");
+ assert_eq!(tables[2], "grant");
+ assert_eq!(tables[3], "keyentry");
+ assert_eq!(tables[4], "keymetadata");
+ assert_eq!(tables[5], "keyparameter");
+ Ok(())
+}
+
+#[test]
+fn test_auth_token_table_invariant() -> Result<()> {
+ let mut db = new_test_db()?;
+ let auth_token1 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 200,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 500 },
+ mac: String::from("mac").into_bytes(),
+ };
+ db.insert_auth_token(&auth_token1);
+ let auth_tokens_returned = get_auth_tokens(&db);
+ assert_eq!(auth_tokens_returned.len(), 1);
+
+ // insert another auth token with the same values for the columns in the UNIQUE constraint
+ // of the auth token table and different value for timestamp
+ let auth_token2 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 200,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 600 },
+ mac: String::from("mac").into_bytes(),
+ };
+
+ db.insert_auth_token(&auth_token2);
+ let mut auth_tokens_returned = get_auth_tokens(&db);
+ assert_eq!(auth_tokens_returned.len(), 1);
+
+ if let Some(auth_token) = auth_tokens_returned.pop() {
+ assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
+ }
+
+ // insert another auth token with the different values for the columns in the UNIQUE
+ // constraint of the auth token table
+ let auth_token3 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 201,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 600 },
+ mac: String::from("mac").into_bytes(),
+ };
+
+ db.insert_auth_token(&auth_token3);
+ let auth_tokens_returned = get_auth_tokens(&db);
+ assert_eq!(auth_tokens_returned.len(), 2);
+
+ Ok(())
+}
+
+// utility function for test_auth_token_table_invariant()
+fn get_auth_tokens(db: &KeystoreDB) -> Vec<AuthTokenEntry> {
+ db.perboot.get_all_auth_token_entries()
+}
+
+fn create_key_entry(
+ db: &mut KeystoreDB,
+ domain: &Domain,
+ namespace: &i64,
+ key_type: KeyType,
+ km_uuid: &Uuid,
+) -> Result<KeyIdGuard> {
+ db.with_transaction(Immediate("TX_create_key_entry"), |tx| {
+ KeystoreDB::create_key_entry_internal(tx, domain, namespace, key_type, km_uuid).no_gc()
+ })
+}
+
+#[test]
+fn test_persistence_for_files() -> Result<()> {
+ let temp_dir = TempDir::new("persistent_db_test")?;
+ let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+
+ create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 1);
+
+ let db = KeystoreDB::new(temp_dir.path(), None)?;
+
+ 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>, Uuid) {
+ (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
+ }
+
+ let mut db = new_test_db()?;
+
+ create_key_entry(&mut db, &Domain::APP, &100, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::SELINUX, &101, KeyType::Client, &KEYSTORE_UUID)?;
+
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
+ assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ create_key_entry(&mut db, &Domain::GRANT, &102, KeyType::Client, &KEYSTORE_UUID),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
+ );
+ check_result_is_error_containing_string(
+ create_key_entry(&mut db, &Domain::BLOB, &103, KeyType::Client, &KEYSTORE_UUID),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
+ );
+ check_result_is_error_containing_string(
+ create_key_entry(&mut db, &Domain::KEY_ID, &104, KeyType::Client, &KEYSTORE_UUID),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_rebind_alias() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
+ (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
+ }
+
+ let mut db = new_test_db()?;
+ create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+
+ // Test that the first call to rebind_alias sets the alias.
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(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"), Some(KEYSTORE_UUID))
+ );
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID)));
+
+ // Test that the second call to rebind_alias also empties the old one.
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(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, Some(KEYSTORE_UUID)));
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::GRANT),
+ );
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::BLOB),
+ );
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
+ &format!("Domain {:?} must be either App or SELinux.", Domain::KEY_ID),
+ );
+
+ // Test that we correctly handle setting an alias for something that does not exist.
+ check_result_is_error_containing_string(
+ rebind_alias(&mut db, &KEY_ID_LOCK.get(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, Some(KEYSTORE_UUID)));
+ assert_eq!(
+ extractor(&entries[1]),
+ (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+ );
+
+ 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, key_type, domain, namespace, alias, state, km_uuid)
+ VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
+ params![KEYSTORE_UUID, KEYSTORE_UUID],
+ )?;
+ 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::GetInfo];
+ 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, 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(())
+ })
+ .unwrap();
+
+ 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, 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(())
+ })
+ .unwrap();
+
+ 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, 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(())
+ })
+ .unwrap();
+
+ 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 persistent.grant;")?;
+ let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |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_KEY_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_set_blob() -> Result<()> {
+ let key_id = KEY_ID_LOCK.get(3000);
+ let mut db = new_test_db()?;
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+ drop(key_id);
+
+ let mut stmt = db.conn.prepare(
+ "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
+ ORDER BY subcomponent_type ASC;",
+ )?;
+ let mut rows = stmt.query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
+ Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
+ })?;
+ let (r, id) = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
+ let (r, _) = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
+ let (r, _) = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
+
+ drop(rows);
+ drop(stmt);
+
+ assert_eq!(
+ db.with_transaction(Immediate("TX_test"), |tx| {
+ BlobMetaData::load_from_db(id, tx).no_gc()
+ })
+ .expect("Should find blob metadata."),
+ blob_metadata
+ );
+ 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, None)
+ .context("test_insert_and_load_full_keyentry_domain_app")?
+ .0;
+ let (_key_guard, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
+ let mut db = new_test_db()?;
+
+ db.store_new_certificate(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ TEST_CERT_BLOB,
+ &KEYSTORE_UUID,
+ )
+ .expect("Trying to insert cert.");
+
+ let (_key_guard, mut key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::PUBLIC,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .expect("Trying to read certificate entry.");
+
+ assert!(key_entry.pure_cert());
+ assert!(key_entry.cert().is_none());
+ assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
+
+ db.unbind_key(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ 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, None)
+ .context("test_insert_and_load_full_keyentry_domain_selinux")?
+ .0;
+ let (_key_guard, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ 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, None)
+ .context("test_insert_and_load_full_keyentry_domain_key_id")?
+ .0;
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
+ .context("test_check_and_update_key_usage_count_with_limited_use_key")?
+ .0;
+ // Update the usage count of the limited use key.
+ db.check_and_update_key_usage_count(key_id)?;
+
+ let (_key_guard, key_entry) = db.load_key_entry(
+ &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+
+ // The usage count is decremented now.
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
+
+ Ok(())
+}
+
+#[test]
+fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
+ .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
+ .0;
+ // Update the usage count of the limited use key.
+ db.check_and_update_key_usage_count(key_id).expect(concat!(
+ "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+ "This should succeed."
+ ));
+
+ // Try to update the exhausted limited use key.
+ let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
+ "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+ "This should fail."
+ ));
+ assert_eq!(
+ &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
+ e.root_cause().downcast_ref::<KsError>().unwrap()
+ );
+
+ 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, None)
+ .context("test_insert_and_load_full_keyentry_from_grant")?
+ .0;
+
+ 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(()),
+ )
+ .unwrap();
+
+ debug_dump_grant_table(&mut db)?;
+
+ let (_key_guard, key_entry) = db
+ .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
+ assert_eq!(Domain::GRANT, k.domain);
+ assert!(av.unwrap().includes(KeyPerm::Use));
+ Ok(())
+ })
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::NONE, 2, |_k, _av| Ok(
+ ()
+ ),)
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// This test attempts to load a key by key id while the caller is not the owner
+// but a grant exists for the given key and the caller.
+#[test]
+fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
+ let mut db = new_test_db()?;
+ const OWNER_UID: u32 = 1u32;
+ const GRANTEE_UID: u32 = 2u32;
+ const SOMEONE_ELSE_UID: u32 = 3u32;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
+ .0;
+
+ db.grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ OWNER_UID,
+ GRANTEE_UID,
+ key_perm_set![KeyPerm::Use],
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+
+ debug_dump_grant_table(&mut db)?;
+
+ let id_descriptor =
+ KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &id_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ GRANTEE_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(OWNER_UID as i64, k.nspace);
+ assert!(av.unwrap().includes(KeyPerm::Use));
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &id_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ SOMEONE_ELSE_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(OWNER_UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &id_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ GRANTEE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// Creates a key migrates it to a different location and then tries to access it by the old
+// and new location.
+#[test]
+fn test_migrate_key_app_to_app() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let source_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(SOURCE_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let key_id = key_id_guard.id();
+
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(()))
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &destination_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ DESTINATION_UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(DESTINATION_UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &source_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ SOURCE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// Creates a key migrates it to a different location and then tries to access it by the old
+// and new location.
+#[test]
+fn test_migrate_key_app_to_selinux() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ const DESTINATION_NAMESPACE: i64 = 1000i64;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let source_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(SOURCE_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: DESTINATION_NAMESPACE,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ let key_id = key_id_guard.id();
+
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(()))
+ .unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &destination_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ DESTINATION_UID,
+ |k, av| {
+ assert_eq!(Domain::SELINUX, k.domain);
+ assert_eq!(DESTINATION_NAMESPACE, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &source_descriptor,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ SOURCE_UID,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+// Creates two keys and tries to migrate the first to the location of the second which
+// is expected to fail.
+#[test]
+fn test_migrate_key_destination_occupied() -> Result<()> {
+ let mut db = new_test_db()?;
+ const SOURCE_UID: u32 = 1u32;
+ const DESTINATION_UID: u32 = 2u32;
+ static SOURCE_ALIAS: &str = "SOURCE_ALIAS";
+ static DESTINATION_ALIAS: &str = "DESTINATION_ALIAS";
+ let key_id_guard =
+ make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+ make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+ let destination_descriptor: KeyDescriptor = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(DESTINATION_ALIAS.to_string()),
+ blob: None,
+ };
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
+ db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| Ok(
+ ()
+ ))
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+}
+
+#[test]
+fn test_upgrade_0_to_1() {
+ const ALIAS1: &str = "test_upgrade_0_to_1_1";
+ const ALIAS2: &str = "test_upgrade_0_to_1_2";
+ const ALIAS3: &str = "test_upgrade_0_to_1_3";
+ const UID: u32 = 33;
+ let temp_dir = Arc::new(TempDir::new("test_upgrade_0_to_1").unwrap());
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+ let key_id_untouched1 =
+ make_test_key_entry(&mut db, Domain::APP, UID as i64, ALIAS1, None).unwrap().id();
+ let key_id_untouched2 =
+ make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS2, false).unwrap().id();
+ let key_id_deleted =
+ make_bootlevel_key_entry(&mut db, Domain::APP, UID as i64, ALIAS3, true).unwrap().id();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS1.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS2.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS3.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_deleted, true));
+
+ db.with_transaction(Immediate("TX_test"), |tx| KeystoreDB::from_0_to_1(tx).no_gc()).unwrap();
+
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS1.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id_untouched1, None));
+ let (_, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS2.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_bootlevel_test_key_entry_test_vector(key_id_untouched2, false));
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(ALIAS3.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ UID,
+ |k, av| {
+ assert_eq!(Domain::APP, k.domain);
+ assert_eq!(UID as i64, k.nspace);
+ assert!(av.is_none());
+ Ok(())
+ },
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+}
+
+static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
+
+#[test]
+fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
+ let handle = {
+ let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
+ let temp_dir_clone = temp_dir.clone();
+ let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
+ .context("test_insert_and_load_full_keyentry_domain_app")?
+ .0;
+ let (_key_guard, key_entry) = db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 33,
+ |_k, _av| Ok(()),
+ )
+ .unwrap();
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+ let state = Arc::new(AtomicU8::new(1));
+ let state2 = state.clone();
+
+ // Spawning a second thread that attempts to acquire the key id lock
+ // for the same key as the primary thread. The primary thread then
+ // waits, thereby forcing the secondary thread into the second stage
+ // of acquiring the lock (see KEY ID LOCK 2/2 above).
+ // The test succeeds if the secondary thread observes the transition
+ // of `state` from 1 to 2, despite having a whole second to overtake
+ // the primary thread.
+ let handle = thread::spawn(move || {
+ let temp_dir = temp_dir_clone;
+ let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+ assert!(db
+ .load_key_entry(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 33,
+ |_k, _av| Ok(()),
+ )
+ .is_ok());
+ // We should only see a 2 here because we can only return
+ // from load_key_entry when the `_key_guard` expires,
+ // which happens at the end of the scope.
+ assert_eq!(2, state2.load(Ordering::Relaxed));
+ });
+
+ thread::sleep(std::time::Duration::from_millis(1000));
+
+ assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
+
+ // Return the handle from this scope so we can join with the
+ // secondary thread after the key id lock has expired.
+ handle
+ // This is where the `_key_guard` goes out of scope,
+ // which is the reason for concurrent load_key_entry on the same key
+ // to unblock.
+ };
+ // Join with the secondary thread and unwrap, to propagate failing asserts to the
+ // main test thread. We will not see failing asserts in secondary threads otherwise.
+ handle.join().unwrap();
+ Ok(())
+}
+
+#[test]
+fn test_database_busy_error_code() {
+ let temp_dir =
+ TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
+
+ let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
+ let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
+
+ let _tx1 = db1
+ .conn
+ .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
+ .expect("Failed to create first transaction.");
+
+ let error = db2
+ .conn
+ .transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)
+ .context("Transaction begin failed.")
+ .expect_err("This should fail.");
+ let root_cause = error.root_cause();
+ if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
+ root_cause.downcast_ref::<rusqlite::ffi::Error>()
+ {
+ return;
+ }
+ panic!(
+ "Unexpected error {:?} \n{:?} \n{:?}",
+ error,
+ root_cause,
+ root_cause.downcast_ref::<rusqlite::ffi::Error>()
+ )
+}
+
+#[cfg(disabled)]
+#[test]
+fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
+ let temp_dir = Arc::new(
+ TempDir::new("test_large_number_of_concurrent_db_manipulations_")
+ .expect("Failed to create temp dir."),
+ );
+
+ let test_begin = Instant::now();
+
+ const KEY_COUNT: u32 = 500u32;
+ let mut db =
+ new_test_db_with_gc(temp_dir.path(), |_, _| Ok(())).expect("Failed to open database.");
+ const OPEN_DB_COUNT: u32 = 50u32;
+
+ let mut actual_key_count = KEY_COUNT;
+ // First insert KEY_COUNT keys.
+ for count in 0..KEY_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+ actual_key_count = count;
+ break;
+ }
+ let alias = format!("test_alias_{}", count);
+ make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
+ .expect("Failed to make key entry.");
+ }
+
+ // Insert more keys from a different thread and into a different namespace.
+ let temp_dir1 = temp_dir.clone();
+ let handle1 = thread::spawn(move || {
+ let mut db =
+ new_test_db_with_gc(temp_dir1.path(), |_, _| Ok(())).expect("Failed to open database.");
+
+ for count in 0..actual_key_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let alias = format!("test_alias_{}", count);
+ make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
+ .expect("Failed to make key entry.");
+ }
+
+ // then unbind them again.
+ for count in 0..actual_key_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let key = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(format!("test_alias_{}", count)),
+ blob: None,
+ };
+ db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
+ }
+ });
+
+ // And start unbinding the first set of keys.
+ let temp_dir2 = temp_dir.clone();
+ let handle2 = thread::spawn(move || {
+ let mut db =
+ new_test_db_with_gc(temp_dir2.path(), |_, _| Ok(())).expect("Failed to open database.");
+
+ for count in 0..actual_key_count {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let key = KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(format!("test_alias_{}", count)),
+ blob: None,
+ };
+ db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
+ }
+ });
+
+ // While a lot of inserting and deleting is going on we have to open database connections
+ // successfully and use them.
+ // This clone is not redundant, because temp_dir needs to be kept alive until db goes
+ // out of scope.
+ #[allow(clippy::redundant_clone)]
+ let temp_dir4 = temp_dir.clone();
+ let handle4 = thread::spawn(move || {
+ for count in 0..OPEN_DB_COUNT {
+ if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+ return;
+ }
+ let mut db = new_test_db_with_gc(temp_dir4.path(), |_, _| Ok(()))
+ .expect("Failed to open database.");
+
+ let alias = format!("test_alias_{}", count);
+ make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
+ .expect("Failed to make key entry.");
+ let key =
+ KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None };
+ db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
+ }
+ });
+
+ handle1.join().expect("Thread 1 panicked.");
+ handle2.join().expect("Thread 2 panicked.");
+ handle4.join().expect("Thread 4 panicked.");
+
+ Ok(())
+}
+
+#[test]
+fn list() -> Result<()> {
+ let temp_dir = TempDir::new("list_test")?;
+ let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+ static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
+ (Domain::APP, 1, "test1"),
+ (Domain::APP, 1, "test2"),
+ (Domain::APP, 1, "test3"),
+ (Domain::APP, 1, "test4"),
+ (Domain::APP, 1, "test5"),
+ (Domain::APP, 1, "test6"),
+ (Domain::APP, 1, "test7"),
+ (Domain::APP, 2, "test1"),
+ (Domain::APP, 2, "test2"),
+ (Domain::APP, 2, "test3"),
+ (Domain::APP, 2, "test4"),
+ (Domain::APP, 2, "test5"),
+ (Domain::APP, 2, "test6"),
+ (Domain::APP, 2, "test8"),
+ (Domain::SELINUX, 100, "test1"),
+ (Domain::SELINUX, 100, "test2"),
+ (Domain::SELINUX, 100, "test3"),
+ (Domain::SELINUX, 100, "test4"),
+ (Domain::SELINUX, 100, "test5"),
+ (Domain::SELINUX, 100, "test6"),
+ (Domain::SELINUX, 100, "test9"),
+ ];
+
+ let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
+ .iter()
+ .map(|(domain, ns, alias)| {
+ let entry =
+ make_test_key_entry(&mut db, *domain, *ns, alias, None).unwrap_or_else(|e| {
+ panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
+ });
+ (entry.id(), *ns)
+ })
+ .collect();
+
+ for (domain, namespace) in
+ &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
+ {
+ let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
+ .iter()
+ .filter_map(|(domain, ns, alias)| match ns {
+ ns if *ns == *namespace => Some(KeyDescriptor {
+ domain: *domain,
+ nspace: *ns,
+ alias: Some(alias.to_string()),
+ blob: None,
+ }),
+ _ => None,
+ })
+ .collect();
+ list_o_descriptors.sort();
+ let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
+ list_result.sort();
+ assert_eq!(list_o_descriptors, list_result);
+
+ let mut list_o_ids: Vec<i64> = list_o_descriptors
+ .into_iter()
+ .map(|d| {
+ let (_, entry) = db
+ .load_key_entry(
+ &d,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ *namespace as u32,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+ entry.id()
+ })
+ .collect();
+ list_o_ids.sort_unstable();
+ let mut loaded_entries: Vec<i64> = list_o_keys
+ .iter()
+ .filter_map(|(id, ns)| match ns {
+ ns if *ns == *namespace => Some(*id),
+ _ => None,
+ })
+ .collect();
+ loaded_entries.sort_unstable();
+ assert_eq!(list_o_ids, loaded_entries);
+ }
+ assert_eq!(
+ Vec::<KeyDescriptor>::new(),
+ db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
+ );
+
+ 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)]
+struct KeyEntryRow {
+ id: i64,
+ key_type: KeyType,
+ domain: Option<Domain>,
+ namespace: Option<i64>,
+ alias: Option<String>,
+ state: KeyLifeCycle,
+ km_uuid: Option<Uuid>,
+}
+
+fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
+ db.conn
+ .prepare("SELECT * FROM persistent.keyentry;")?
+ .query_map([], |row| {
+ Ok(KeyEntryRow {
+ id: row.get(0)?,
+ key_type: row.get(1)?,
+ domain: row.get::<_, Option<_>>(2)?.map(Domain),
+ namespace: row.get(3)?,
+ alias: row.get(4)?,
+ state: row.get(5)?,
+ km_uuid: row.get(6)?,
+ })
+ })?
+ .map(|r| r.context("Could not read keyentry row."))
+ .collect::<Result<Vec<_>>>()
+}
+
+fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+ make_test_params_with_sids(max_usage_count, &[42])
+}
+
+// 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_with_sids(
+ max_usage_count: Option<i32>,
+ user_secure_ids: &[i64],
+) -> Vec<KeyParameter> {
+ let mut params = 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::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::AttestationIdSecondIMEI(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,
+ ),
+ ];
+ if let Some(value) = max_usage_count {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UsageCountLimit(value),
+ SecurityLevel::SOFTWARE,
+ ));
+ }
+
+ for sid in user_secure_ids.iter() {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(*sid),
+ SecurityLevel::STRONGBOX,
+ ));
+ }
+ params
+}
+
+pub fn make_test_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ max_usage_count: Option<i32>,
+) -> Result<KeyIdGuard> {
+ make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
+}
+
+pub fn make_test_key_entry_with_sids(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ max_usage_count: Option<i32>,
+ sids: &[i64],
+) -> Result<KeyIdGuard> {
+ let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+ blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+ blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+ let params = make_test_params_with_sids(max_usage_count, sids);
+ db.insert_keyparameter(&key_id, ¶ms)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+}
+
+fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
+ let params = make_test_params(max_usage_count);
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+ blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+ blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+ KeyEntry {
+ id: key_id,
+ key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ km_uuid: KEYSTORE_UUID,
+ parameters: params,
+ metadata,
+ pure_cert: false,
+ }
+}
+
+pub fn make_bootlevel_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ logical_only: bool,
+) -> Result<KeyIdGuard> {
+ let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+ let mut blob_metadata = BlobMetaData::new();
+ if !logical_only {
+ blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+ }
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+ let mut params = make_test_params(None);
+ params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+ db.insert_keyparameter(&key_id, ¶ms)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+}
+
+// Creates an app key that is marked as being superencrypted by the given
+// super key ID and that has the given authentication and unlocked device
+// parameters. This does not actually superencrypt the key blob.
+fn make_superencrypted_key_entry(
+ db: &mut KeystoreDB,
+ namespace: i64,
+ alias: &str,
+ requires_authentication: bool,
+ requires_unlocked_device: bool,
+ super_key_id: i64,
+) -> Result<KeyIdGuard> {
+ let domain = Domain::APP;
+ let key_id = create_key_entry(db, &domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), Some(&blob_metadata))?;
+
+ let mut params = vec![];
+ if requires_unlocked_device {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UnlockedDeviceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ));
+ }
+ if requires_authentication {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(42),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ));
+ }
+ db.insert_keyparameter(&key_id, ¶ms)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+}
+
+fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
+ let mut params = make_test_params(None);
+ params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
+
+ let mut blob_metadata = BlobMetaData::new();
+ if !logical_only {
+ blob_metadata.add(BlobMetaEntry::MaxBootLevel(3));
+ }
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+ KeyEntry {
+ id: key_id,
+ key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ km_uuid: KEYSTORE_UUID,
+ parameters: params,
+ metadata,
+ pure_cert: false,
+ }
+}
+
+fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt = db.conn.prepare(
+ "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
+ )?;
+ let rows =
+ stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>([], |row| {
+ Ok((
+ row.get(0)?,
+ row.get(1)?,
+ row.get(2)?,
+ row.get(3)?,
+ row.get(4)?,
+ row.get(5)?,
+ row.get(6)?,
+ ))
+ })?;
+
+ println!("Key entry table rows:");
+ for r in rows {
+ let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
+ println!(
+ " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
+ id, key_type, domain, namespace, alias, state, km_uuid
+ );
+ }
+ Ok(())
+}
+
+fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt =
+ db.conn.prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+ let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |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(())
+}
+
+// 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> = const { 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
+ })
+}
+
+#[test]
+fn test_unbind_keys_for_user() -> Result<()> {
+ let mut db = new_test_db()?;
+ db.unbind_keys_for_user(1)?;
+
+ make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
+ make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
+ db.unbind_keys_for_user(2)?;
+
+ assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+ assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
+
+ db.unbind_keys_for_user(1)?;
+ assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+
+ Ok(())
+}
+
+#[test]
+fn test_unbind_keys_for_user_removes_superkeys() -> Result<()> {
+ let mut db = new_test_db()?;
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let (encrypted_super_key, metadata) = SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+
+ let key_name_enc = SuperKeyType {
+ alias: "test_super_key_1",
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "test_super_key_1",
+ };
+
+ let key_name_nonenc = SuperKeyType {
+ alias: "test_super_key_2",
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "test_super_key_2",
+ };
+
+ // Install two super keys.
+ db.store_super_key(1, &key_name_nonenc, &super_key, &BlobMetaData::new(), &KeyMetaData::new())?;
+ db.store_super_key(1, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+ // Check that both can be found in the database.
+ assert!(db.load_super_key(&key_name_enc, 1)?.is_some());
+ assert!(db.load_super_key(&key_name_nonenc, 1)?.is_some());
+
+ // Install the same keys for a different user.
+ db.store_super_key(2, &key_name_nonenc, &super_key, &BlobMetaData::new(), &KeyMetaData::new())?;
+ db.store_super_key(2, &key_name_enc, &encrypted_super_key, &metadata, &KeyMetaData::new())?;
+
+ // Check that the second pair of keys can be found in the database.
+ assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+ assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+ // Delete all keys for user 1.
+ db.unbind_keys_for_user(1)?;
+
+ // All of user 1's keys should be gone.
+ assert!(db.load_super_key(&key_name_enc, 1)?.is_none());
+ assert!(db.load_super_key(&key_name_nonenc, 1)?.is_none());
+
+ // User 2's keys should not have been touched.
+ assert!(db.load_super_key(&key_name_enc, 2)?.is_some());
+ assert!(db.load_super_key(&key_name_nonenc, 2)?.is_some());
+
+ Ok(())
+}
+
+fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
+ db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
+}
+
+// Tests the unbind_auth_bound_keys_for_user() function.
+#[test]
+fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
+ let mut db = new_test_db()?;
+ let user_id = 1;
+ let nspace: i64 = (user_id * AID_USER_OFFSET).into();
+ let other_user_id = 2;
+ let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
+ let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
+
+ // Create a superencryption key.
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let (encrypted_super_key, blob_metadata) =
+ SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+ db.store_super_key(
+ user_id,
+ super_key_type,
+ &encrypted_super_key,
+ &blob_metadata,
+ &KeyMetaData::new(),
+ )?;
+ let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
+
+ // Store 4 superencrypted app keys, one for each possible combination of
+ // (authentication required, unlocked device required).
+ make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
+ assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+ assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
+
+ // Also store a key for a different user that requires authentication.
+ make_superencrypted_key_entry(&mut db, other_user_nspace, "auth_ud", true, true, super_key_id)?;
+
+ db.unbind_auth_bound_keys_for_user(user_id)?;
+
+ // Verify that only the user's app keys that require authentication were
+ // deleted. Keys that require an unlocked device but not authentication
+ // should *not* have been deleted, nor should the super key have been
+ // deleted, nor should other users' keys have been deleted.
+ assert!(db.load_super_key(super_key_type, user_id)?.is_some());
+ assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+ assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
+ assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
+ assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
+
+ Ok(())
+}
+
+#[test]
+fn test_store_super_key() -> Result<()> {
+ let mut db = new_test_db()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let secret_bytes = b"keystore2 is great.";
+ let (encrypted_secret, iv, tag) = keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
+
+ let (encrypted_super_key, metadata) = SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+ db.store_super_key(
+ 1,
+ &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
+ &encrypted_super_key,
+ &metadata,
+ &KeyMetaData::new(),
+ )?;
+
+ // Check if super key exists.
+ assert!(db.key_exists(
+ Domain::APP,
+ 1,
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
+ KeyType::Super
+ )?);
+
+ let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
+ let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
+ key_entry,
+ &pw,
+ None,
+ )?;
+
+ let decrypted_secret_bytes = loaded_super_key.decrypt(&encrypted_secret, &iv, &tag)?;
+ assert_eq!(secret_bytes, &*decrypted_secret_bytes);
+
+ Ok(())
+}
+
+fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
+ vec![
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_PARAMETER,
+ MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_METADATA,
+ MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::GRANT,
+ MetricsStorage::AUTH_TOKEN,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+ ]
+}
+
+/// Perform a simple check to ensure that we can query all the storage types
+/// that are supported by the DB. Check for reasonable values.
+#[test]
+fn test_query_all_valid_table_sizes() -> Result<()> {
+ const PAGE_SIZE: i32 = 4096;
+
+ let mut db = new_test_db()?;
+
+ for t in get_valid_statsd_storage_types() {
+ let stat = db.get_storage_stat(t)?;
+ // AuthToken can be less than a page since it's in a btree, not sqlite
+ // TODO(b/187474736) stop using if-let here
+ if let MetricsStorage::AUTH_TOKEN = t {
+ } else {
+ assert!(stat.size >= PAGE_SIZE);
+ }
+ assert!(stat.size >= stat.unused_size);
+ }
+
+ Ok(())
+}
+
+fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
+ get_valid_statsd_storage_types()
+ .into_iter()
+ .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
+ .collect()
+}
+
+fn assert_storage_increased(
+ db: &mut KeystoreDB,
+ increased_storage_types: Vec<MetricsStorage>,
+ baseline: &mut BTreeMap<i32, StorageStats>,
+) {
+ for storage in increased_storage_types {
+ // Verify the expected storage increased.
+ let new = db.get_storage_stat(storage).unwrap();
+ let old = &baseline[&storage.0];
+ assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
+ assert!(
+ new.unused_size <= old.unused_size,
+ "{}: {} <= {}",
+ storage.0,
+ new.unused_size,
+ old.unused_size
+ );
+
+ // Update the baseline with the new value so that it succeeds in the
+ // later comparison.
+ baseline.insert(storage.0, new);
+ }
+
+ // Get an updated map of the storage and verify there were no unexpected changes.
+ let updated_stats = get_storage_stats_map(db);
+ assert_eq!(updated_stats.len(), baseline.len());
+
+ for &k in baseline.keys() {
+ let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
+ let mut s = String::new();
+ for &k in map.keys() {
+ writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
+ .expect("string concat failed");
+ }
+ s
+ };
+
+ assert!(
+ updated_stats[&k].size == baseline[&k].size
+ && updated_stats[&k].unused_size == baseline[&k].unused_size,
+ "updated_stats:\n{}\nbaseline:\n{}",
+ stringify(&updated_stats),
+ stringify(baseline)
+ );
+ }
+}
+
+#[test]
+fn test_verify_key_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+
+ let key_id = create_key_entry(&mut db, &Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ ],
+ &mut working_stats,
+ );
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
+ ],
+ &mut working_stats,
+ );
+
+ let params = make_test_params(None);
+ db.insert_keyparameter(&key_id, ¶ms)?;
+ assert_storage_increased(
+ &mut db,
+ vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
+ &mut working_stats,
+ );
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ assert_storage_increased(
+ &mut db,
+ vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
+ &mut working_stats,
+ );
+
+ let mut sum = 0;
+ for stat in working_stats.values() {
+ sum += stat.size;
+ }
+ let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
+ assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
+
+ Ok(())
+}
+
+#[test]
+fn test_verify_auth_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac".to_vec(),
+ });
+ assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
+ Ok(())
+}
+
+#[test]
+fn test_verify_grant_table_size_reporting() -> Result<()> {
+ const OWNER: i64 = 1;
+ let mut db = new_test_db()?;
+ make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
+
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ OWNER as u32,
+ 123,
+ key_perm_set![KeyPerm::Use],
+ |_, _| Ok(()),
+ )?;
+
+ assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
+
+ Ok(())
+}
+
+#[test]
+fn find_auth_token_entry_returns_latest() -> Result<()> {
+ let mut db = new_test_db()?;
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac0".to_vec(),
+ });
+ std::thread::sleep(std::time::Duration::from_millis(1));
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 457,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 12 },
+ mac: b"mac1".to_vec(),
+ });
+ std::thread::sleep(std::time::Duration::from_millis(1));
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 458,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 3 },
+ mac: b"mac2".to_vec(),
+ });
+ // All three entries are in the database
+ assert_eq!(db.perboot.auth_tokens_len(), 3);
+ // It selected the most recent timestamp
+ assert_eq!(db.find_auth_token_entry(|_| true).unwrap().auth_token.mac, b"mac2".to_vec());
+ Ok(())
+}
+
+#[test]
+fn test_load_key_descriptor() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)?.0;
+
+ let key = db.load_key_descriptor(key_id)?.unwrap();
+
+ assert_eq!(key.domain, Domain::APP);
+ assert_eq!(key.nspace, 1);
+ assert_eq!(key.alias, Some(TEST_ALIAS.to_string()));
+
+ // No such id
+ assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
+ Ok(())
+}
+
+#[test]
+fn test_get_list_app_uids_for_sid() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let third_app_id: i64 = 789 + uid_offset;
+ let unrelated_app_id: i64 = 1011 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ TEST_ALIAS,
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ third_app_id,
+ "alias3",
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(&mut db, Domain::APP, unrelated_app_id, TEST_ALIAS, None, &[])
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ first_sid_apps.sort();
+ assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
+ Ok(())
+}
+
+#[test]
+fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let third_sid = 772;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid, second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[second_sid, third_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ assert_eq!(first_sid_apps, vec![first_app_id]);
+
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
+
+ let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
+ assert_eq!(third_sid_apps, vec![second_app_id]);
+ Ok(())
+}
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index cea4d6b..5e80266 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -38,6 +38,9 @@
use std::cmp::PartialEq;
use std::ffi::CString;
+#[cfg(test)]
+pub mod tests;
+
/// 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.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
@@ -232,210 +235,3 @@
},
}
}
-
-#[cfg(test)]
-pub mod tests {
-
- use super::*;
- use android_system_keystore2::binder::{
- ExceptionCode, Result as BinderResult, Status as BinderStatus,
- };
- use anyhow::{anyhow, Context};
-
- fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
- Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
- }
-
- fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
- nested_nested_rc(rc).context("nested rc")
- }
-
- fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
- Err(anyhow!(Error::Km(ec))).context("nested nested ec")
- }
-
- fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
- nested_nested_ec(ec).context("nested ec")
- }
-
- fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
- Ok(rc)
- }
-
- fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
- nested_nested_ok(rc).context("nested ok")
- }
-
- fn nested_nested_selinux_perm() -> anyhow::Result<()> {
- Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
- }
-
- fn nested_selinux_perm() -> anyhow::Result<()> {
- nested_nested_selinux_perm().context("nested selinux permission denied")
- }
-
- #[derive(Debug, thiserror::Error)]
- enum TestError {
- #[error("TestError::Fail")]
- Fail = 0,
- }
-
- fn nested_nested_other_error() -> anyhow::Result<()> {
- Err(anyhow!(TestError::Fail)).context("nested nested other error")
- }
-
- fn nested_other_error() -> anyhow::Result<()> {
- nested_nested_other_error().context("nested other error")
- }
-
- fn binder_sse_error(sse: i32) -> BinderResult<()> {
- Err(BinderStatus::new_service_specific_error(sse, None))
- }
-
- fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
- Err(BinderStatus::new_exception(ex, None))
- }
-
- #[test]
- fn keystore_error_test() -> anyhow::Result<(), String> {
- android_logger::init_once(
- android_logger::Config::default()
- .with_tag("keystore_error_tests")
- .with_max_level(log::LevelFilter::Debug),
- );
- // All Error::Rc(x) get mapped on a service specific error
- // code of x.
- for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
- assert_eq!(
- Result::<(), i32>::Err(rc),
- nested_rc(ResponseCode(rc))
- .map_err(into_logged_binder)
- .map_err(|s| s.service_specific_error())
- );
- }
-
- // All Keystore Error::Km(x) get mapped on a service
- // specific error of x.
- for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
- assert_eq!(
- Result::<(), i32>::Err(ec),
- nested_ec(ErrorCode(ec))
- .map_err(into_logged_binder)
- .map_err(|s| s.service_specific_error())
- );
- }
-
- // All Keymint errors x received through a Binder Result get mapped on
- // a service specific error of x.
- for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
- assert_eq!(
- Result::<(), i32>::Err(ec),
- map_km_error(binder_sse_error(ec))
- .with_context(|| format!("Km error code: {}.", ec))
- .map_err(into_logged_binder)
- .map_err(|s| s.service_specific_error())
- );
- }
-
- // map_km_error creates an Error::Binder variant storing
- // ExceptionCode::SERVICE_SPECIFIC and the given
- // service specific error.
- let sse = map_km_error(binder_sse_error(1));
- assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
- // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
- sse.context("Non negative service specific error.")
- .map_err(into_logged_binder)
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // map_km_error creates a Error::Binder variant storing the given exception code.
- let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
- assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
- // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
- binder_exception
- .context("Binder Exception.")
- .map_err(into_logged_binder)
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
- nested_selinux_perm()
- .map_err(into_logged_binder)
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // All other errors get mapped on System Error.
- assert_eq!(
- Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
- nested_other_error()
- .map_err(into_logged_binder)
- .map_err(|s| ResponseCode(s.service_specific_error()))
- );
-
- // Result::Ok variants get passed to the ok handler.
- assert_eq!(
- Ok(ResponseCode::LOCKED),
- nested_ok(ResponseCode::LOCKED).map_err(into_logged_binder)
- );
- assert_eq!(
- Ok(ResponseCode::SYSTEM_ERROR),
- nested_ok(ResponseCode::SYSTEM_ERROR).map_err(into_logged_binder)
- );
-
- Ok(())
- }
-
- //Helper function to test whether error cases are handled as expected.
- pub fn check_result_contains_error_string<T>(
- result: anyhow::Result<T>,
- expected_error_string: &str,
- ) {
- let error_str = format!(
- "{:#?}",
- result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
- );
- assert!(
- error_str.contains(expected_error_string),
- "The string \"{}\" should contain \"{}\"",
- error_str,
- expected_error_string
- );
- }
-
- #[test]
- fn rkpd_error_is_in_sync_with_response_code() {
- let error_mapping = [
- (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
- ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
- ),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
- ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
- ),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
- ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
- ),
- (
- RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
- ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
- ),
- (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
- (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
- ];
- for (rkpd_error, expected_response_code) in error_mapping {
- let e: Error = rkpd_error.into();
- assert_eq!(e, Error::Rc(expected_response_code));
- }
- }
-} // mod tests
diff --git a/keystore2/src/error/tests.rs b/keystore2/src/error/tests.rs
new file mode 100644
index 0000000..d50091b
--- /dev/null
+++ b/keystore2/src/error/tests.rs
@@ -0,0 +1,218 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Error handling tests.
+
+use super::*;
+use android_system_keystore2::binder::{
+ ExceptionCode, Result as BinderResult, Status as BinderStatus,
+};
+use anyhow::{anyhow, Context};
+
+fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+ Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
+}
+
+fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+ nested_nested_rc(rc).context("nested rc")
+}
+
+fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+ Err(anyhow!(Error::Km(ec))).context("nested nested ec")
+}
+
+fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+ nested_nested_ec(ec).context("nested ec")
+}
+
+fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+ Ok(rc)
+}
+
+fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+ nested_nested_ok(rc).context("nested ok")
+}
+
+fn nested_nested_selinux_perm() -> anyhow::Result<()> {
+ Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
+}
+
+fn nested_selinux_perm() -> anyhow::Result<()> {
+ nested_nested_selinux_perm().context("nested selinux permission denied")
+}
+
+#[derive(Debug, thiserror::Error)]
+enum TestError {
+ #[error("TestError::Fail")]
+ Fail = 0,
+}
+
+fn nested_nested_other_error() -> anyhow::Result<()> {
+ Err(anyhow!(TestError::Fail)).context("nested nested other error")
+}
+
+fn nested_other_error() -> anyhow::Result<()> {
+ nested_nested_other_error().context("nested other error")
+}
+
+fn binder_sse_error(sse: i32) -> BinderResult<()> {
+ Err(BinderStatus::new_service_specific_error(sse, None))
+}
+
+fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
+ Err(BinderStatus::new_exception(ex, None))
+}
+
+#[test]
+fn keystore_error_test() -> anyhow::Result<(), String> {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore_error_tests")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+ // All Error::Rc(x) get mapped on a service specific error
+ // code of x.
+ for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(rc),
+ nested_rc(ResponseCode(rc))
+ .map_err(into_logged_binder)
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // All Keystore Error::Km(x) get mapped on a service
+ // specific error of x.
+ for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ nested_ec(ErrorCode(ec))
+ .map_err(into_logged_binder)
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // All Keymint errors x received through a Binder Result get mapped on
+ // a service specific error of x.
+ for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ map_km_error(binder_sse_error(ec))
+ .with_context(|| format!("Km error code: {}.", ec))
+ .map_err(into_logged_binder)
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // map_km_error creates an Error::Binder variant storing
+ // ExceptionCode::SERVICE_SPECIFIC and the given
+ // service specific error.
+ let sse = map_km_error(binder_sse_error(1));
+ assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
+ // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ sse.context("Non negative service specific error.")
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // map_km_error creates a Error::Binder variant storing the given exception code.
+ let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
+ assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
+ // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ binder_exception
+ .context("Binder Exception.")
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
+ nested_selinux_perm()
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // All other errors get mapped on System Error.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ nested_other_error()
+ .map_err(into_logged_binder)
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // Result::Ok variants get passed to the ok handler.
+ assert_eq!(
+ Ok(ResponseCode::LOCKED),
+ nested_ok(ResponseCode::LOCKED).map_err(into_logged_binder)
+ );
+ assert_eq!(
+ Ok(ResponseCode::SYSTEM_ERROR),
+ nested_ok(ResponseCode::SYSTEM_ERROR).map_err(into_logged_binder)
+ );
+
+ Ok(())
+}
+
+//Helper function to test whether error cases are handled as expected.
+pub fn check_result_contains_error_string<T>(
+ result: anyhow::Result<T>,
+ expected_error_string: &str,
+) {
+ let error_str = format!(
+ "{:#?}",
+ result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
+ );
+ assert!(
+ error_str.contains(expected_error_string),
+ "The string \"{}\" should contain \"{}\"",
+ error_str,
+ expected_error_string
+ );
+}
+
+#[test]
+fn rkpd_error_is_in_sync_with_response_code() {
+ let error_mapping = [
+ (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
+ ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
+ ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+ ),
+ (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
+ (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
+ ];
+ for (rkpd_error, expected_response_code) in error_mapping {
+ let e: Error = rkpd_error.into();
+ assert_eq!(e, Error::Rc(expected_response_code));
+ }
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index bd45207..466fb50 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -111,6 +111,18 @@
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
+#[cfg(test)]
+mod generated_key_parameter_tests;
+
+#[cfg(test)]
+mod basic_tests;
+
+#[cfg(test)]
+mod storage_tests;
+
+#[cfg(test)]
+mod wire_tests;
+
/// This trait is used to associate a primitive to any type that can be stored inside a
/// KeyParameterValue, especially the AIDL enum types, e.g., keymint::{Algorithm, Digest, ...}.
/// This allows for simplifying the macro rules, e.g., for reading from the SQL database.
@@ -1091,490 +1103,3 @@
Authorization { securityLevel: self.security_level, keyParameter: self.value.into() }
}
}
-
-#[cfg(test)]
-mod generated_key_parameter_tests {
- use super::*;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
-
- fn get_field_by_tag_type(tag: Tag) -> KmKeyParameterValue {
- let tag_type = TagType((tag.0 as u32 & 0xF0000000) as i32);
- match tag {
- Tag::ALGORITHM => return KmKeyParameterValue::Algorithm(Default::default()),
- Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
- Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
- Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
- Tag::RSA_OAEP_MGF_DIGEST => return KmKeyParameterValue::Digest(Default::default()),
- Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
- Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
- Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
- Tag::USER_AUTH_TYPE => {
- return KmKeyParameterValue::HardwareAuthenticatorType(Default::default())
- }
- Tag::HARDWARE_TYPE => return KmKeyParameterValue::SecurityLevel(Default::default()),
- _ => {}
- }
- match tag_type {
- TagType::INVALID => return KmKeyParameterValue::Invalid(Default::default()),
- TagType::ENUM | TagType::ENUM_REP => {}
- TagType::UINT | TagType::UINT_REP => {
- return KmKeyParameterValue::Integer(Default::default())
- }
- TagType::ULONG | TagType::ULONG_REP => {
- return KmKeyParameterValue::LongInteger(Default::default())
- }
- TagType::DATE => return KmKeyParameterValue::DateTime(Default::default()),
- TagType::BOOL => return KmKeyParameterValue::BoolValue(Default::default()),
- TagType::BIGNUM | TagType::BYTES => {
- return KmKeyParameterValue::Blob(Default::default())
- }
- _ => {}
- }
- panic!("Unknown tag/tag_type: {:?} {:?}", tag, tag_type);
- }
-
- fn check_field_matches_tag_type(list_o_parameters: &[KmKeyParameter]) {
- for kp in list_o_parameters.iter() {
- match (&kp.value, get_field_by_tag_type(kp.tag)) {
- (&KmKeyParameterValue::Algorithm(_), KmKeyParameterValue::Algorithm(_))
- | (&KmKeyParameterValue::BlockMode(_), KmKeyParameterValue::BlockMode(_))
- | (&KmKeyParameterValue::PaddingMode(_), KmKeyParameterValue::PaddingMode(_))
- | (&KmKeyParameterValue::Digest(_), KmKeyParameterValue::Digest(_))
- | (&KmKeyParameterValue::EcCurve(_), KmKeyParameterValue::EcCurve(_))
- | (&KmKeyParameterValue::Origin(_), KmKeyParameterValue::Origin(_))
- | (&KmKeyParameterValue::KeyPurpose(_), KmKeyParameterValue::KeyPurpose(_))
- | (
- &KmKeyParameterValue::HardwareAuthenticatorType(_),
- KmKeyParameterValue::HardwareAuthenticatorType(_),
- )
- | (&KmKeyParameterValue::SecurityLevel(_), KmKeyParameterValue::SecurityLevel(_))
- | (&KmKeyParameterValue::Invalid(_), KmKeyParameterValue::Invalid(_))
- | (&KmKeyParameterValue::Integer(_), KmKeyParameterValue::Integer(_))
- | (&KmKeyParameterValue::LongInteger(_), KmKeyParameterValue::LongInteger(_))
- | (&KmKeyParameterValue::DateTime(_), KmKeyParameterValue::DateTime(_))
- | (&KmKeyParameterValue::BoolValue(_), KmKeyParameterValue::BoolValue(_))
- | (&KmKeyParameterValue::Blob(_), KmKeyParameterValue::Blob(_)) => {}
- (actual, expected) => panic!(
- "Tag {:?} associated with variant {:?} expected {:?}",
- kp.tag, actual, expected
- ),
- }
- }
- }
-
- #[test]
- fn key_parameter_value_field_matches_tag_type() {
- check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
- }
-
- #[test]
- fn key_parameter_serialization_test() {
- let params = KeyParameterValue::make_key_parameter_defaults_vector();
- let mut out_buffer: Vec<u8> = Default::default();
- serde_cbor::to_writer(&mut out_buffer, ¶ms)
- .expect("Failed to serialize key parameters.");
- let deserialized_params: Vec<KeyParameter> =
- serde_cbor::from_reader(&mut out_buffer.as_slice())
- .expect("Failed to deserialize key parameters.");
- assert_eq!(params, deserialized_params);
- }
-}
-
-#[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};
-
- /// 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)?;
- let key_param = query_from_keyparameter(&db)?;
- assert_eq!(Tag::INVALID, key_param.get_tag());
- 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);",
- [],
- )
- .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([])?;
- let row = rows.next()?.unwrap();
- KeyParameter::new_from_sql(
- Tag(row.get(0)?),
- &SqlField::new(1, row),
- SecurityLevel(row.get(2)?),
- )
- }
-}
-
-/// The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
-/// KeyParameter, for the four different types used in KmKeyParameter, in addition to Invalid
-/// key parameter.
-/// i) bool
-/// ii) integer
-/// iii) longInteger
-/// iv) 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);
- assert_eq!(
- KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(0) },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_bool() {
- let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
- assert_eq!(
- KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_integer() {
- let kp = KeyParameter::new(
- KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
- SecurityLevel::STRONGBOX,
- );
- assert_eq!(
- KmKeyParameter {
- tag: Tag::PURPOSE,
- value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT)
- },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_long_integer() {
- let kp =
- KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
- assert_eq!(
- KmKeyParameter {
- tag: Tag::USER_SECURE_ID,
- value: KmKeyParameterValue::LongInteger(i64::MAX)
- },
- kp.value.into()
- );
- }
- #[test]
- fn test_convert_to_wire_blob() {
- let kp = KeyParameter::new(
- KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
- SecurityLevel::STRONGBOX,
- );
- assert_eq!(
- KmKeyParameter {
- tag: Tag::CONFIRMATION_TOKEN,
- value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
- },
- kp.value.into()
- );
- }
-
- /// unit tests for from conversion
- #[test]
- fn test_convert_from_wire_invalid() {
- let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
- assert_eq!(KeyParameterValue::Invalid, aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_bool() {
- let aidl_kp =
- KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) };
- assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_integer() {
- let aidl_kp = KmKeyParameter {
- tag: Tag::PURPOSE,
- value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
- };
- assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_long_integer() {
- let aidl_kp = KmKeyParameter {
- tag: Tag::USER_SECURE_ID,
- value: KmKeyParameterValue::LongInteger(i64::MAX),
- };
- assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
- }
- #[test]
- fn test_convert_from_wire_blob() {
- let aidl_kp = KmKeyParameter {
- tag: Tag::CONFIRMATION_TOKEN,
- value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
- };
- assert_eq!(
- KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
- aidl_kp.into()
- );
- }
-}
diff --git a/keystore2/src/key_parameter/basic_tests.rs b/keystore2/src/key_parameter/basic_tests.rs
new file mode 100644
index 0000000..2bb3724
--- /dev/null
+++ b/keystore2/src/key_parameter/basic_tests.rs
@@ -0,0 +1,28 @@
+// 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.
+
+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);
+}
diff --git a/keystore2/src/key_parameter/generated_key_parameter_tests.rs b/keystore2/src/key_parameter/generated_key_parameter_tests.rs
new file mode 100644
index 0000000..a5c0a8b
--- /dev/null
+++ b/keystore2/src/key_parameter/generated_key_parameter_tests.rs
@@ -0,0 +1,95 @@
+// 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.
+
+use super::*;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
+
+fn get_field_by_tag_type(tag: Tag) -> KmKeyParameterValue {
+ let tag_type = TagType((tag.0 as u32 & 0xF0000000) as i32);
+ match tag {
+ Tag::ALGORITHM => return KmKeyParameterValue::Algorithm(Default::default()),
+ Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
+ Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
+ Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+ Tag::RSA_OAEP_MGF_DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+ Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
+ Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
+ Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
+ Tag::USER_AUTH_TYPE => {
+ return KmKeyParameterValue::HardwareAuthenticatorType(Default::default())
+ }
+ Tag::HARDWARE_TYPE => return KmKeyParameterValue::SecurityLevel(Default::default()),
+ _ => {}
+ }
+ match tag_type {
+ TagType::INVALID => return KmKeyParameterValue::Invalid(Default::default()),
+ TagType::ENUM | TagType::ENUM_REP => {}
+ TagType::UINT | TagType::UINT_REP => {
+ return KmKeyParameterValue::Integer(Default::default())
+ }
+ TagType::ULONG | TagType::ULONG_REP => {
+ return KmKeyParameterValue::LongInteger(Default::default())
+ }
+ TagType::DATE => return KmKeyParameterValue::DateTime(Default::default()),
+ TagType::BOOL => return KmKeyParameterValue::BoolValue(Default::default()),
+ TagType::BIGNUM | TagType::BYTES => return KmKeyParameterValue::Blob(Default::default()),
+ _ => {}
+ }
+ panic!("Unknown tag/tag_type: {:?} {:?}", tag, tag_type);
+}
+
+fn check_field_matches_tag_type(list_o_parameters: &[KmKeyParameter]) {
+ for kp in list_o_parameters.iter() {
+ match (&kp.value, get_field_by_tag_type(kp.tag)) {
+ (&KmKeyParameterValue::Algorithm(_), KmKeyParameterValue::Algorithm(_))
+ | (&KmKeyParameterValue::BlockMode(_), KmKeyParameterValue::BlockMode(_))
+ | (&KmKeyParameterValue::PaddingMode(_), KmKeyParameterValue::PaddingMode(_))
+ | (&KmKeyParameterValue::Digest(_), KmKeyParameterValue::Digest(_))
+ | (&KmKeyParameterValue::EcCurve(_), KmKeyParameterValue::EcCurve(_))
+ | (&KmKeyParameterValue::Origin(_), KmKeyParameterValue::Origin(_))
+ | (&KmKeyParameterValue::KeyPurpose(_), KmKeyParameterValue::KeyPurpose(_))
+ | (
+ &KmKeyParameterValue::HardwareAuthenticatorType(_),
+ KmKeyParameterValue::HardwareAuthenticatorType(_),
+ )
+ | (&KmKeyParameterValue::SecurityLevel(_), KmKeyParameterValue::SecurityLevel(_))
+ | (&KmKeyParameterValue::Invalid(_), KmKeyParameterValue::Invalid(_))
+ | (&KmKeyParameterValue::Integer(_), KmKeyParameterValue::Integer(_))
+ | (&KmKeyParameterValue::LongInteger(_), KmKeyParameterValue::LongInteger(_))
+ | (&KmKeyParameterValue::DateTime(_), KmKeyParameterValue::DateTime(_))
+ | (&KmKeyParameterValue::BoolValue(_), KmKeyParameterValue::BoolValue(_))
+ | (&KmKeyParameterValue::Blob(_), KmKeyParameterValue::Blob(_)) => {}
+ (actual, expected) => panic!(
+ "Tag {:?} associated with variant {:?} expected {:?}",
+ kp.tag, actual, expected
+ ),
+ }
+ }
+}
+
+#[test]
+fn key_parameter_value_field_matches_tag_type() {
+ check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
+}
+
+#[test]
+fn key_parameter_serialization_test() {
+ let params = KeyParameterValue::make_key_parameter_defaults_vector();
+ let mut out_buffer: Vec<u8> = Default::default();
+ serde_cbor::to_writer(&mut out_buffer, ¶ms).expect("Failed to serialize key parameters.");
+ let deserialized_params: Vec<KeyParameter> =
+ serde_cbor::from_reader(&mut out_buffer.as_slice())
+ .expect("Failed to deserialize key parameters.");
+ assert_eq!(params, deserialized_params);
+}
diff --git a/keystore2/src/key_parameter/storage_tests.rs b/keystore2/src/key_parameter/storage_tests.rs
new file mode 100644
index 0000000..38a57e4
--- /dev/null
+++ b/keystore2/src/key_parameter/storage_tests.rs
@@ -0,0 +1,263 @@
+// 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.
+
+//! 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
+
+use crate::error::*;
+use crate::key_parameter::*;
+use anyhow::Result;
+use rusqlite::types::ToSql;
+use rusqlite::{params, Connection};
+
+/// 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)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::INVALID, key_param.get_tag());
+ 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);",
+ [],
+ )
+ .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([])?;
+ let row = rows.next()?.unwrap();
+ KeyParameter::new_from_sql(Tag(row.get(0)?), &SqlField::new(1, row), SecurityLevel(row.get(2)?))
+}
diff --git a/keystore2/src/key_parameter/wire_tests.rs b/keystore2/src/key_parameter/wire_tests.rs
new file mode 100644
index 0000000..278b766
--- /dev/null
+++ b/keystore2/src/key_parameter/wire_tests.rs
@@ -0,0 +1,119 @@
+// 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.
+
+//! The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
+//! KeyParameter, for the four different types used in KmKeyParameter, in addition to Invalid
+//! key parameter.
+//! i) bool
+//! ii) integer
+//! iii) longInteger
+//! iv) blob
+
+use crate::key_parameter::*;
+/// unit tests for to conversions
+#[test]
+fn test_convert_to_wire_invalid() {
+ let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(0) },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_bool() {
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_integer() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ SecurityLevel::STRONGBOX,
+ );
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::PURPOSE,
+ value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT)
+ },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_long_integer() {
+ let kp = KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ value: KmKeyParameterValue::LongInteger(i64::MAX)
+ },
+ kp.value.into()
+ );
+}
+#[test]
+fn test_convert_to_wire_blob() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
+ },
+ kp.value.into()
+ );
+}
+
+/// unit tests for from conversion
+#[test]
+fn test_convert_from_wire_invalid() {
+ let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
+ assert_eq!(KeyParameterValue::Invalid, aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_bool() {
+ let aidl_kp =
+ KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) };
+ assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::PURPOSE,
+ value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ };
+ assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_long_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ value: KmKeyParameterValue::LongInteger(i64::MAX),
+ };
+ assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
+}
+#[test]
+fn test_convert_from_wire_blob() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
+ };
+ assert_eq!(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ aidl_kp.into()
+ );
+}
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 982bc82..d79445b 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -38,6 +38,9 @@
#[cfg(test)]
use tests::test_getcon as getcon;
+#[cfg(test)]
+mod tests;
+
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.
@@ -397,433 +400,3 @@
selinux::check_permission(caller_ctx, &target_context, perm)
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use anyhow::anyhow;
- use anyhow::Result;
- use keystore2_selinux::*;
-
- const ALL_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- KeyPerm::ConvertStorageKeyToEphemeral,
- ];
-
- const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- // No KeyPerm::Grant
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
-
- const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- // No KeyPerm::Grant
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- KeyPerm::ConvertStorageKeyToEphemeral,
- ];
-
- const UNPRIV_PERMS: KeyPermSet = key_perm_set![
- KeyPerm::Delete,
- KeyPerm::GetInfo,
- 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::AddAuth).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).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());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
- assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
- let shell_ctx = Context::new("u:r:shell:s0")?;
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
- 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));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
- assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
- 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 };
- check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
- .expect("Grant permission check failed.");
-
- // 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(
- 0,
- &selinux::Context::new("ignored").unwrap(),
- KeyPerm::Grant,
- &key,
- &Some(UNPRIV_PERMS)
- ));
-
- check_key_permission(
- 0,
- &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(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
- assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
- assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
-
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
- assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
-
- // Also make sure that the permission fails if the caller is not the owner.
- assert_perm_failed!(check_key_permission(
- 1, // the owner is 0
- &system_server_ctx,
- KeyPerm::Use,
- &key,
- &None
- ));
- // Unless there was a grant.
- assert!(check_key_permission(
- 1,
- &system_server_ctx,
- KeyPerm::Use,
- &key,
- &Some(key_perm_set![KeyPerm::Use])
- )
- .is_ok());
- // But fail if the grant did not cover the requested permission.
- assert_perm_failed!(check_key_permission(
- 1,
- &system_server_ctx,
- KeyPerm::Use,
- &key,
- &Some(key_perm_set![KeyPerm::GetInfo])
- ));
-
- 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,
- };
-
- assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
-
- if is_su {
- assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
- assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
- } else {
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
- assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &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(0, &sctx, KeyPerm::Use, &key, &None)
- } else {
- assert_perm_failed!(check_key_permission(0, &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(
- 0,
- &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::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- 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().name(), "delete");
- assert_eq!(i.next().unwrap().name(), "gen_unique_id");
- assert_eq!(i.next().unwrap().name(), "get_info");
- assert_eq!(i.next().unwrap().name(), "grant");
- assert_eq!(i.next().unwrap().name(), "manage_blob");
- assert_eq!(i.next().unwrap().name(), "rebind");
- assert_eq!(i.next().unwrap().name(), "req_forced_op");
- assert_eq!(i.next().unwrap().name(), "update");
- assert_eq!(i.next().unwrap().name(), "use");
- assert_eq!(i.next().unwrap().name(), "use_dev_id");
- assert_eq!(None, i.next());
- }
- #[test]
- fn key_perm_set_sparse_test() {
- let v = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- 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().name(), "gen_unique_id");
- assert_eq!(i.next().unwrap().name(), "manage_blob");
- assert_eq!(i.next().unwrap().name(), "req_forced_op");
- assert_eq!(i.next().unwrap().name(), "update");
- assert_eq!(i.next().unwrap().name(), "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::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::UseDevId,
- KeyPerm::ReqForcedOp,
- KeyPerm::GenUniqueId,
- KeyPerm::Grant,
- KeyPerm::GetInfo,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- 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::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- 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::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::Grant, // only in v1
- KeyPerm::Rebind,
- KeyPerm::Update,
- KeyPerm::Use,
- ];
- let v2 = key_perm_set![
- KeyPerm::ManageBlob,
- KeyPerm::Delete,
- KeyPerm::ReqForcedOp, // 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::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
- let v2 =
- key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
- assert!(!v1.includes(v2));
- assert!(!v2.includes(v1));
- }
-}
diff --git a/keystore2/src/permission/tests.rs b/keystore2/src/permission/tests.rs
new file mode 100644
index 0000000..f555c12
--- /dev/null
+++ b/keystore2/src/permission/tests.rs
@@ -0,0 +1,434 @@
+// 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.
+
+//! Access control tests.
+
+use super::*;
+use crate::key_perm_set;
+use anyhow::anyhow;
+use anyhow::Result;
+use keystore2_selinux::*;
+
+const ALL_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Grant,
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ KeyPerm::ConvertStorageKeyToEphemeral,
+];
+
+const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ // No KeyPerm::Grant
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+];
+
+const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ // No KeyPerm::Grant
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ KeyPerm::ConvertStorageKeyToEphemeral,
+];
+
+const UNPRIV_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::Delete,
+ KeyPerm::GetInfo,
+ 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::AddAuth).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).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());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
+ 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));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
+ 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 };
+ check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
+ .expect("Grant permission check failed.");
+
+ // 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(
+ 0,
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::Grant,
+ &key,
+ &Some(UNPRIV_PERMS)
+ ));
+
+ check_key_permission(
+ 0,
+ &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(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
+ assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
+ assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
+
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
+ assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
+
+ // Also make sure that the permission fails if the caller is not the owner.
+ assert_perm_failed!(check_key_permission(
+ 1, // the owner is 0
+ &system_server_ctx,
+ KeyPerm::Use,
+ &key,
+ &None
+ ));
+ // Unless there was a grant.
+ assert!(check_key_permission(
+ 1,
+ &system_server_ctx,
+ KeyPerm::Use,
+ &key,
+ &Some(key_perm_set![KeyPerm::Use])
+ )
+ .is_ok());
+ // But fail if the grant did not cover the requested permission.
+ assert_perm_failed!(check_key_permission(
+ 1,
+ &system_server_ctx,
+ KeyPerm::Use,
+ &key,
+ &Some(key_perm_set![KeyPerm::GetInfo])
+ ));
+
+ 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,
+ };
+
+ assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
+
+ if is_su {
+ assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
+ assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
+ } else {
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
+ assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &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(0, &sctx, KeyPerm::Use, &key, &None)
+ } else {
+ assert_perm_failed!(check_key_permission(0, &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(
+ 0,
+ &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::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Grant,
+ KeyPerm::GetInfo,
+ 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().name(), "delete");
+ assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().name(), "get_info");
+ assert_eq!(i.next().unwrap().name(), "grant");
+ assert_eq!(i.next().unwrap().name(), "manage_blob");
+ assert_eq!(i.next().unwrap().name(), "rebind");
+ assert_eq!(i.next().unwrap().name(), "req_forced_op");
+ assert_eq!(i.next().unwrap().name(), "update");
+ assert_eq!(i.next().unwrap().name(), "use");
+ assert_eq!(i.next().unwrap().name(), "use_dev_id");
+ assert_eq!(None, i.next());
+}
+#[test]
+fn key_perm_set_sparse_test() {
+ let v = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ 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().name(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().name(), "manage_blob");
+ assert_eq!(i.next().unwrap().name(), "req_forced_op");
+ assert_eq!(i.next().unwrap().name(), "update");
+ assert_eq!(i.next().unwrap().name(), "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::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::UseDevId,
+ KeyPerm::ReqForcedOp,
+ KeyPerm::GenUniqueId,
+ KeyPerm::Grant,
+ KeyPerm::GetInfo,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::ManageBlob,
+ 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::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::ManageBlob,
+ 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::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::Grant, // only in v1
+ KeyPerm::Rebind,
+ KeyPerm::Update,
+ KeyPerm::Use,
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::ManageBlob,
+ KeyPerm::Delete,
+ KeyPerm::ReqForcedOp, // 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::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
+ let v2 = key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 706a255..fa1de98 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -52,6 +52,9 @@
};
use std::{convert::TryFrom, ops::Deref};
+#[cfg(test)]
+mod tests;
+
const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
/// Allow up to 15 seconds between the user unlocking using a biometric, and the auth
/// token being used to unlock in [`SuperKeyManager::try_unlock_user_with_biometric`].
@@ -1237,288 +1240,3 @@
}
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::database::tests::make_bootlevel_key_entry;
- use crate::database::tests::make_test_key_entry;
- use crate::database::tests::new_test_db;
- use rand::prelude::*;
- const USER_ID: u32 = 0;
- const TEST_KEY_ALIAS: &str = "TEST_KEY";
- const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY";
-
- pub fn generate_password_blob() -> Password<'static> {
- let mut rng = rand::thread_rng();
- let mut password = vec![0u8; 64];
- rng.fill_bytes(&mut password);
-
- let mut zvec = ZVec::new(64).expect("Failed to create ZVec");
- zvec[..].copy_from_slice(&password[..]);
-
- Password::Owned(zvec)
- }
-
- fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) {
- let mut keystore_db = new_test_db().unwrap();
- let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default()));
- legacy_importer.set_empty();
- let skm: Arc<RwLock<SuperKeyManager>> = Default::default();
- assert!(skm
- .write()
- .unwrap()
- .initialize_user(&mut keystore_db, &legacy_importer, USER_ID, pw, false)
- .is_ok());
- (skm, keystore_db, legacy_importer)
- }
-
- fn assert_unlocked(
- skm: &Arc<RwLock<SuperKeyManager>>,
- keystore_db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: u32,
- err_msg: &str,
- ) {
- let user_state =
- skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
- match user_state {
- UserState::AfterFirstUnlock(_) => {}
- _ => panic!("{}", err_msg),
- }
- }
-
- fn assert_locked(
- skm: &Arc<RwLock<SuperKeyManager>>,
- keystore_db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: u32,
- err_msg: &str,
- ) {
- let user_state =
- skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
- match user_state {
- UserState::BeforeFirstUnlock => {}
- _ => panic!("{}", err_msg),
- }
- }
-
- fn assert_uninitialized(
- skm: &Arc<RwLock<SuperKeyManager>>,
- keystore_db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: u32,
- err_msg: &str,
- ) {
- let user_state =
- skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
- match user_state {
- UserState::Uninitialized => {}
- _ => panic!("{}", err_msg),
- }
- }
-
- #[test]
- fn test_initialize_user() {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
- }
-
- #[test]
- fn test_unlock_user() {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
-
- assert!(skm
- .write()
- .unwrap()
- .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
- .is_ok());
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user did not unlock!",
- );
- }
-
- #[test]
- fn test_unlock_wrong_password() {
- let pw: Password = generate_password_blob();
- let wrong_pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
-
- assert!(skm
- .write()
- .unwrap()
- .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw)
- .is_err());
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was unlocked with an incorrect password!",
- );
- }
-
- #[test]
- fn test_unlock_user_idempotent() {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
-
- for _ in 0..5 {
- assert!(skm
- .write()
- .unwrap()
- .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
- .is_ok());
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user did not unlock!",
- );
- }
- }
-
- fn test_user_removal(locked: bool) {
- let pw: Password = generate_password_blob();
- let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
- assert_unlocked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not unlocked after initialization!",
- );
-
- assert!(make_test_key_entry(
- &mut keystore_db,
- Domain::APP,
- USER_ID.into(),
- TEST_KEY_ALIAS,
- None
- )
- .is_ok());
- assert!(make_bootlevel_key_entry(
- &mut keystore_db,
- Domain::APP,
- USER_ID.into(),
- TEST_BOOT_KEY_ALIAS,
- false
- )
- .is_ok());
-
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
-
- if locked {
- skm.write().unwrap().data.user_keys.clear();
- assert_locked(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "Clearing the cache did not lock the user!",
- );
- }
-
- assert!(skm
- .write()
- .unwrap()
- .remove_user(&mut keystore_db, &legacy_importer, USER_ID)
- .is_ok());
- assert_uninitialized(
- &skm,
- &mut keystore_db,
- &legacy_importer,
- USER_ID,
- "The user was not removed!",
- );
-
- assert!(!skm
- .write()
- .unwrap()
- .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
- .unwrap());
-
- assert!(!keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
- .unwrap());
- assert!(!keystore_db
- .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
- .unwrap());
- }
-
- #[test]
- fn test_remove_unlocked_user() {
- test_user_removal(false);
- }
-
- #[test]
- fn test_remove_locked_user() {
- test_user_removal(true);
- }
-}
diff --git a/keystore2/src/super_key/tests.rs b/keystore2/src/super_key/tests.rs
new file mode 100644
index 0000000..76a96a7
--- /dev/null
+++ b/keystore2/src/super_key/tests.rs
@@ -0,0 +1,287 @@
+// 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.
+
+//! Super-key tests.
+
+use super::*;
+use crate::database::tests::make_bootlevel_key_entry;
+use crate::database::tests::make_test_key_entry;
+use crate::database::tests::new_test_db;
+use rand::prelude::*;
+const USER_ID: u32 = 0;
+const TEST_KEY_ALIAS: &str = "TEST_KEY";
+const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY";
+
+pub fn generate_password_blob() -> Password<'static> {
+ let mut rng = rand::thread_rng();
+ let mut password = vec![0u8; 64];
+ rng.fill_bytes(&mut password);
+
+ let mut zvec = ZVec::new(64).expect("Failed to create ZVec");
+ zvec[..].copy_from_slice(&password[..]);
+
+ Password::Owned(zvec)
+}
+
+fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) {
+ let mut keystore_db = new_test_db().unwrap();
+ let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default()));
+ legacy_importer.set_empty();
+ let skm: Arc<RwLock<SuperKeyManager>> = Default::default();
+ assert!(skm
+ .write()
+ .unwrap()
+ .initialize_user(&mut keystore_db, &legacy_importer, USER_ID, pw, false)
+ .is_ok());
+ (skm, keystore_db, legacy_importer)
+}
+
+fn assert_unlocked(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::AfterFirstUnlock(_) => {}
+ _ => panic!("{}", err_msg),
+ }
+}
+
+fn assert_locked(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::BeforeFirstUnlock => {}
+ _ => panic!("{}", err_msg),
+ }
+}
+
+fn assert_uninitialized(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::Uninitialized => {}
+ _ => panic!("{}", err_msg),
+ }
+}
+
+#[test]
+fn test_initialize_user() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+}
+
+#[test]
+fn test_unlock_user() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+ .is_ok());
+ assert_unlocked(&skm, &mut keystore_db, &legacy_importer, USER_ID, "The user did not unlock!");
+}
+
+#[test]
+fn test_unlock_wrong_password() {
+ let pw: Password = generate_password_blob();
+ let wrong_pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw)
+ .is_err());
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was unlocked with an incorrect password!",
+ );
+}
+
+#[test]
+fn test_unlock_user_idempotent() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ for _ in 0..5 {
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+ .is_ok());
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user did not unlock!",
+ );
+ }
+}
+
+fn test_user_removal(locked: bool) {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ assert!(make_test_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_KEY_ALIAS,
+ None
+ )
+ .is_ok());
+ assert!(make_bootlevel_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_BOOT_KEY_ALIAS,
+ false
+ )
+ .is_ok());
+
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+
+ if locked {
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+ }
+
+ assert!(skm.write().unwrap().remove_user(&mut keystore_db, &legacy_importer, USER_ID).is_ok());
+ assert_uninitialized(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not removed!",
+ );
+
+ assert!(!skm
+ .write()
+ .unwrap()
+ .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .unwrap());
+
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+}
+
+#[test]
+fn test_remove_unlocked_user() {
+ test_user_removal(false);
+}
+
+#[test]
+fn test_remove_locked_user() {
+ test_user_removal(true);
+}
diff --git a/keystore2/src/sw_keyblob.rs b/keystore2/src/sw_keyblob.rs
index 47ab49f..c0173b5 100644
--- a/keystore2/src/sw_keyblob.rs
+++ b/keystore2/src/sw_keyblob.rs
@@ -28,6 +28,9 @@
use keystore2_crypto::hmac_sha256;
use std::mem::size_of;
+#[cfg(test)]
+mod tests;
+
/// Root of trust value.
const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW";
@@ -556,481 +559,3 @@
.clone_from_slice(&serialized_size.to_ne_bytes());
Ok(result)
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
- KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue as KPV, KeyPurpose::KeyPurpose,
- PaddingMode::PaddingMode, Tag::Tag,
- };
-
- macro_rules! expect_err {
- ($result:expr, $err_msg:expr) => {
- assert!(
- $result.is_err(),
- "Expected error containing '{}', got success {:?}",
- $err_msg,
- $result
- );
- let err = $result.err();
- assert!(
- format!("{:?}", err).contains($err_msg),
- "Unexpected error {:?}, doesn't contain '{}'",
- err,
- $err_msg
- );
- };
- }
-
- #[test]
- fn test_consume_u8() {
- let buffer = [1, 2];
- let mut data = &buffer[..];
- assert_eq!(1u8, consume_u8(&mut data).unwrap());
- assert_eq!(2u8, consume_u8(&mut data).unwrap());
- let result = consume_u8(&mut data);
- expect_err!(result, "failed to find 1 byte");
- }
-
- #[test]
- fn test_consume_u32() {
- // All supported platforms are little-endian.
- let buffer = [
- 0x01, 0x02, 0x03, 0x04, // little-endian u32
- 0x04, 0x03, 0x02, 0x01, // little-endian u32
- 0x11, 0x12, 0x13,
- ];
- let mut data = &buffer[..];
- assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap());
- assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap());
- let result = consume_u32(&mut data);
- expect_err!(result, "failed to find 4 bytes");
- }
-
- #[test]
- fn test_consume_i64() {
- // All supported platforms are little-endian.
- let buffer = [
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64
- 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64
- 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- ];
- let mut data = &buffer[..];
- assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap());
- assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap());
- let result = consume_i64(&mut data);
- expect_err!(result, "failed to find 8 bytes");
- }
-
- #[test]
- fn test_consume_vec() {
- let buffer = [
- 0x01, 0x00, 0x00, 0x00, 0xaa, //
- 0x00, 0x00, 0x00, 0x00, //
- 0x01, 0x00, 0x00, 0x00, 0xbb, //
- 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data
- ];
- let mut data = &buffer[..];
- assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap());
- assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap());
- assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap());
- let result = consume_vec(&mut data);
- expect_err!(result, "failed to find 7 bytes");
-
- let buffer = [
- 0x01, 0x00, 0x00, //
- ];
- let mut data = &buffer[..];
- let result = consume_vec(&mut data);
- expect_err!(result, "failed to find 4 bytes");
- }
-
- #[test]
- fn test_key_new_from_serialized() {
- let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]);
- // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS
- // tests.
- let tests = [
- (
- concat!(
- "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000",
- "00000000000c0000006400000002000010200000000300003080000000010000",
- "2000000000010000200100000004000020020000000600002001000000be0200",
- "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100",
- "00ce02003011643401cf020030000000003b06b13ae6ae6671",
- ),
- KeyBlob {
- key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::ENCRYPT),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::DECRYPT),
- },
- KeyParameter {
- tag: Tag::BLOCK_MODE,
- value: KPV::BlockMode(BlockMode::CBC),
- },
- KeyParameter {
- tag: Tag::PADDING,
- value: KPV::PaddingMode(PaddingMode::NONE),
- },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871769000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- Some(KeyFormat::RAW),
- ),
- (
- concat!(
- "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b",
- "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654",
- "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004",
- "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14",
- "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7",
- "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909",
- "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3",
- "b877984d000000000000000000000000000000000c0000006400000002000010",
- "030000000a000010030000000100002002000000010000200300000005000020",
- "000000000300003009020000be02001000000000c1020030b0ad0100c2020030",
- "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000",
- "2f69002e55e9b0a3"
- ),
- KeyBlob {
- key_material: hex::decode(concat!(
- "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c",
- "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd",
- "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d",
- "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028",
- "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9",
- "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952",
- "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d",
- ))
- .unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) },
- KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::VERIFY),
- },
- KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871775000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- Some(KeyFormat::PKCS8),
- ),
- (
- concat!(
- "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c",
- "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000",
- "0000000000000000000000000c00000064000000020000108000000003000030",
- "b801000001000020020000000100002003000000050000200400000008000030",
- "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060",
- "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0",
- ),
- KeyBlob {
- key_material: hex::decode(concat!(
- "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05",
- "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc"
- ))
- .unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter {
- tag: Tag::ALGORITHM,
- value: KPV::Algorithm(Algorithm::HMAC),
- },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::VERIFY),
- },
- KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) },
- KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871776000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- Some(KeyFormat::RAW),
- ),
- (
- concat!(
- "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87",
- "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082",
- "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31",
- "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973",
- "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d",
- "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de",
- "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2",
- "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8",
- "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af",
- "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703",
- "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178",
- "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e",
- "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a",
- "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a",
- "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b",
- "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817",
- "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23",
- "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63",
- "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2",
- "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490",
- "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281",
- "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458",
- "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb",
- "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84",
- "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57",
- "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd",
- "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55",
- "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2",
- "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332",
- "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6",
- "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46",
- "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b",
- "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc",
- "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1",
- "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4",
- "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526",
- "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2",
- "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000",
- "007000000002000010010000000300003000080000c800005001000100000000",
- "0001000020020000000100002003000000050000200000000006000020010000",
- "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52",
- "407b010000ce02003011643401cf02003000000000544862e9c961e857",
- ),
- KeyBlob {
- key_material: hex::decode(concat!(
- "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839",
- "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48",
- "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878",
- "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642",
- "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92",
- "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db",
- "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c",
- "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954",
- "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a",
- "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae",
- "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55",
- "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40",
- "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262",
- "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91",
- "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50",
- "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b",
- "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f",
- "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101",
- "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c",
- "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9",
- "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2",
- "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff",
- "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5",
- "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1",
- "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181",
- "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994",
- "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382",
- "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a",
- "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76",
- "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7",
- "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9",
- "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e",
- "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576",
- "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882",
- "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64",
- "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545",
- "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e",
- "3b9845b4f2b61356",
- ))
- .unwrap(),
- hw_enforced: vec![],
- sw_enforced: vec![
- KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) },
- KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) },
- KeyParameter {
- tag: Tag::RSA_PUBLIC_EXPONENT,
- value: KPV::LongInteger(65537),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::SIGN),
- },
- KeyParameter {
- tag: Tag::PURPOSE,
- value: KPV::KeyPurpose(KeyPurpose::VERIFY),
- },
- KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
- KeyParameter {
- tag: Tag::PADDING,
- value: KPV::PaddingMode(PaddingMode::NONE),
- },
- KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
- KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
- KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
- KeyParameter {
- tag: Tag::CREATION_DATETIME,
- value: KPV::DateTime(1628871769000),
- },
- KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
- KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
- ],
- },
- // No support for RSA keys in export_key().
- None,
- ),
- ];
-
- for (input, want, want_format) in tests {
- let input = hex::decode(input).unwrap();
- let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!");
- assert!(got == want);
-
- if let Some(want_format) = want_format {
- let (got_format, _key_material, params) =
- export_key(&input, &[]).expect("invalid keyblob!");
- assert_eq!(got_format, want_format);
- // All the test cases are software-only keys.
- assert_eq!(params, got.sw_enforced);
- }
- }
- }
-
- #[test]
- fn test_add_der_len() {
- let tests = [
- (0, "00"),
- (1, "01"),
- (126, "7e"),
- (127, "7f"),
- (128, "8180"),
- (129, "8181"),
- (255, "81ff"),
- (256, "820100"),
- (257, "820101"),
- (65535, "82ffff"),
- ];
- for (input, want) in tests {
- let mut got = Vec::new();
- add_der_len(&mut got, input).unwrap();
- assert_eq!(hex::encode(got), want, " for input length {input}");
- }
- }
-
- #[test]
- fn test_pkcs8_wrap_key_p256() {
- // Key material taken from `ec_256_key` in
- // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
- let input = hex::decode(concat!(
- "3025", // SEQUENCE (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0420", // OCTET STRING (privateKey)
- "737c2ecd7b8d1940bf2930aa9b4ed3ff",
- "941eed09366bc03299986481f3a4d859",
- ))
- .unwrap();
- let want = hex::decode(concat!(
- // RFC 5208 s5
- "3041", // SEQUENCE (PrivateKeyInfo) {
- "020100", // INTEGER length 1 value 0 (version)
- "3013", // SEQUENCE length 0x13 (AlgorithmIdentifier) {
- "0607", // OBJECT IDENTIFIER length 7 (algorithm)
- "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
- "0608", // OBJECT IDENTIFIER length 8 (param)
- "2a8648ce3d030107", // 1.2.840.10045.3.1.7 (secp256r1)
- // } end SEQUENCE (AlgorithmIdentifier)
- "0427", // OCTET STRING (privateKey) holding...
- "3025", // SEQUENCE (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0420", // OCTET STRING length 0x20 (privateKey)
- "737c2ecd7b8d1940bf2930aa9b4ed3ff",
- "941eed09366bc03299986481f3a4d859",
- // } end SEQUENCE (ECPrivateKey)
- // } end SEQUENCE (PrivateKeyInfo)
- ))
- .unwrap();
- let got = pkcs8_wrap_nist_key(&input, EcCurve::P_256).unwrap();
- assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
- }
-
- #[test]
- fn test_pkcs8_wrap_key_p521() {
- // Key material taken from `ec_521_key` in
- // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
- let input = hex::decode(concat!(
- "3047", // SEQUENCE length 0xd3 (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0442", // OCTET STRING length 0x42 (privateKey)
- "0011458c586db5daa92afab03f4fe46a",
- "a9d9c3ce9a9b7a006a8384bec4c78e8e",
- "9d18d7d08b5bcfa0e53c75b064ad51c4",
- "49bae0258d54b94b1e885ded08ed4fb2",
- "5ce9",
- // } end SEQUENCE (ECPrivateKey)
- ))
- .unwrap();
- let want = hex::decode(concat!(
- // RFC 5208 s5
- "3060", // SEQUENCE (PrivateKeyInfo) {
- "020100", // INTEGER length 1 value 0 (version)
- "3010", // SEQUENCE length 0x10 (AlgorithmIdentifier) {
- "0607", // OBJECT IDENTIFIER length 7 (algorithm)
- "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
- "0605", // OBJECT IDENTIFIER length 5 (param)
- "2b81040023", // 1.3.132.0.35 (secp521r1)
- // } end SEQUENCE (AlgorithmIdentifier)
- "0449", // OCTET STRING (privateKey) holding...
- "3047", // SEQUENCE (ECPrivateKey)
- "020101", // INTEGER length 1 value 1 (version)
- "0442", // OCTET STRING length 0x42 (privateKey)
- "0011458c586db5daa92afab03f4fe46a",
- "a9d9c3ce9a9b7a006a8384bec4c78e8e",
- "9d18d7d08b5bcfa0e53c75b064ad51c4",
- "49bae0258d54b94b1e885ded08ed4fb2",
- "5ce9",
- // } end SEQUENCE (ECPrivateKey)
- // } end SEQUENCE (PrivateKeyInfo)
- ))
- .unwrap();
- let got = pkcs8_wrap_nist_key(&input, EcCurve::P_521).unwrap();
- assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
- }
-}
diff --git a/keystore2/src/sw_keyblob/tests.rs b/keystore2/src/sw_keyblob/tests.rs
new file mode 100644
index 0000000..fe01112
--- /dev/null
+++ b/keystore2/src/sw_keyblob/tests.rs
@@ -0,0 +1,449 @@
+// Copyright 2023, 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.
+
+//! Tests for software-backed keyblobs.
+use super::*;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue as KPV,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, Tag::Tag,
+};
+
+macro_rules! expect_err {
+ ($result:expr, $err_msg:expr) => {
+ assert!(
+ $result.is_err(),
+ "Expected error containing '{}', got success {:?}",
+ $err_msg,
+ $result
+ );
+ let err = $result.err();
+ assert!(
+ format!("{:?}", err).contains($err_msg),
+ "Unexpected error {:?}, doesn't contain '{}'",
+ err,
+ $err_msg
+ );
+ };
+}
+
+#[test]
+fn test_consume_u8() {
+ let buffer = [1, 2];
+ let mut data = &buffer[..];
+ assert_eq!(1u8, consume_u8(&mut data).unwrap());
+ assert_eq!(2u8, consume_u8(&mut data).unwrap());
+ let result = consume_u8(&mut data);
+ expect_err!(result, "failed to find 1 byte");
+}
+
+#[test]
+fn test_consume_u32() {
+ // All supported platforms are little-endian.
+ let buffer = [
+ 0x01, 0x02, 0x03, 0x04, // little-endian u32
+ 0x04, 0x03, 0x02, 0x01, // little-endian u32
+ 0x11, 0x12, 0x13,
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap());
+ assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap());
+ let result = consume_u32(&mut data);
+ expect_err!(result, "failed to find 4 bytes");
+}
+
+#[test]
+fn test_consume_i64() {
+ // All supported platforms are little-endian.
+ let buffer = [
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap());
+ assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap());
+ let result = consume_i64(&mut data);
+ expect_err!(result, "failed to find 8 bytes");
+}
+
+#[test]
+fn test_consume_vec() {
+ let buffer = [
+ 0x01, 0x00, 0x00, 0x00, 0xaa, //
+ 0x00, 0x00, 0x00, 0x00, //
+ 0x01, 0x00, 0x00, 0x00, 0xbb, //
+ 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap());
+ assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap());
+ assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap());
+ let result = consume_vec(&mut data);
+ expect_err!(result, "failed to find 7 bytes");
+
+ let buffer = [
+ 0x01, 0x00, 0x00, //
+ ];
+ let mut data = &buffer[..];
+ let result = consume_vec(&mut data);
+ expect_err!(result, "failed to find 4 bytes");
+}
+
+#[test]
+fn test_key_new_from_serialized() {
+ let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]);
+ // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS
+ // tests.
+ let tests = [
+ (
+ concat!(
+ "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000",
+ "00000000000c0000006400000002000010200000000300003080000000010000",
+ "2000000000010000200100000004000020020000000600002001000000be0200",
+ "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100",
+ "00ce02003011643401cf020030000000003b06b13ae6ae6671",
+ ),
+ KeyBlob {
+ key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::ENCRYPT) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::DECRYPT) },
+ KeyParameter { tag: Tag::BLOCK_MODE, value: KPV::BlockMode(BlockMode::CBC) },
+ KeyParameter { tag: Tag::PADDING, value: KPV::PaddingMode(PaddingMode::NONE) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871769000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::RAW),
+ ),
+ (
+ concat!(
+ "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b",
+ "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654",
+ "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004",
+ "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14",
+ "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7",
+ "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909",
+ "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3",
+ "b877984d000000000000000000000000000000000c0000006400000002000010",
+ "030000000a000010030000000100002002000000010000200300000005000020",
+ "000000000300003009020000be02001000000000c1020030b0ad0100c2020030",
+ "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000",
+ "2f69002e55e9b0a3"
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c",
+ "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd",
+ "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d",
+ "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028",
+ "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9",
+ "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952",
+ "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d",
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) },
+ KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::SIGN) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::VERIFY) },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871775000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::PKCS8),
+ ),
+ (
+ concat!(
+ "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c",
+ "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000",
+ "0000000000000000000000000c00000064000000020000108000000003000030",
+ "b801000001000020020000000100002003000000050000200400000008000030",
+ "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060",
+ "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0",
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05",
+ "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc"
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::HMAC) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::SIGN) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::VERIFY) },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) },
+ KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871776000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::RAW),
+ ),
+ (
+ concat!(
+ "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87",
+ "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082",
+ "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31",
+ "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973",
+ "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d",
+ "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de",
+ "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2",
+ "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8",
+ "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af",
+ "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703",
+ "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178",
+ "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e",
+ "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a",
+ "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a",
+ "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b",
+ "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817",
+ "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23",
+ "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63",
+ "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2",
+ "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490",
+ "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281",
+ "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458",
+ "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb",
+ "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84",
+ "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57",
+ "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd",
+ "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55",
+ "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2",
+ "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332",
+ "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6",
+ "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46",
+ "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b",
+ "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc",
+ "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1",
+ "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4",
+ "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526",
+ "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2",
+ "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000",
+ "007000000002000010010000000300003000080000c800005001000100000000",
+ "0001000020020000000100002003000000050000200000000006000020010000",
+ "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52",
+ "407b010000ce02003011643401cf02003000000000544862e9c961e857",
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839",
+ "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48",
+ "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878",
+ "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642",
+ "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92",
+ "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db",
+ "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c",
+ "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954",
+ "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a",
+ "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae",
+ "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55",
+ "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40",
+ "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262",
+ "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91",
+ "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50",
+ "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b",
+ "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f",
+ "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101",
+ "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c",
+ "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9",
+ "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2",
+ "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff",
+ "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5",
+ "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1",
+ "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181",
+ "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994",
+ "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382",
+ "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a",
+ "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76",
+ "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7",
+ "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9",
+ "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e",
+ "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576",
+ "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882",
+ "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64",
+ "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545",
+ "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e",
+ "3b9845b4f2b61356",
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) },
+ KeyParameter { tag: Tag::RSA_PUBLIC_EXPONENT, value: KPV::LongInteger(65537) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::SIGN) },
+ KeyParameter { tag: Tag::PURPOSE, value: KPV::KeyPurpose(KeyPurpose::VERIFY) },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
+ KeyParameter { tag: Tag::PADDING, value: KPV::PaddingMode(PaddingMode::NONE) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871769000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ // No support for RSA keys in export_key().
+ None,
+ ),
+ ];
+
+ for (input, want, want_format) in tests {
+ let input = hex::decode(input).unwrap();
+ let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!");
+ assert!(got == want);
+
+ if let Some(want_format) = want_format {
+ let (got_format, _key_material, params) =
+ export_key(&input, &[]).expect("invalid keyblob!");
+ assert_eq!(got_format, want_format);
+ // All the test cases are software-only keys.
+ assert_eq!(params, got.sw_enforced);
+ }
+ }
+}
+
+#[test]
+fn test_add_der_len() {
+ let tests = [
+ (0, "00"),
+ (1, "01"),
+ (126, "7e"),
+ (127, "7f"),
+ (128, "8180"),
+ (129, "8181"),
+ (255, "81ff"),
+ (256, "820100"),
+ (257, "820101"),
+ (65535, "82ffff"),
+ ];
+ for (input, want) in tests {
+ let mut got = Vec::new();
+ add_der_len(&mut got, input).unwrap();
+ assert_eq!(hex::encode(got), want, " for input length {input}");
+ }
+}
+
+#[test]
+fn test_pkcs8_wrap_key_p256() {
+ // Key material taken from `ec_256_key` in
+ // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+ let input = hex::decode(concat!(
+ "3025", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0420", // OCTET STRING (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+ "941eed09366bc03299986481f3a4d859",
+ ))
+ .unwrap();
+ let want = hex::decode(concat!(
+ // RFC 5208 s5
+ "3041", // SEQUENCE (PrivateKeyInfo) {
+ "020100", // INTEGER length 1 value 0 (version)
+ "3013", // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+ "0607", // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+ "0608", // OBJECT IDENTIFIER length 8 (param)
+ "2a8648ce3d030107", // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0427", // OCTET STRING (privateKey) holding...
+ "3025", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0420", // OCTET STRING length 0x20 (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+ "941eed09366bc03299986481f3a4d859",
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+ ))
+ .unwrap();
+ let got = pkcs8_wrap_nist_key(&input, EcCurve::P_256).unwrap();
+ assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+}
+
+#[test]
+fn test_pkcs8_wrap_key_p521() {
+ // Key material taken from `ec_521_key` in
+ // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+ let input = hex::decode(concat!(
+ "3047", // SEQUENCE length 0xd3 (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0442", // OCTET STRING length 0x42 (privateKey)
+ "0011458c586db5daa92afab03f4fe46a",
+ "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+ "9d18d7d08b5bcfa0e53c75b064ad51c4",
+ "49bae0258d54b94b1e885ded08ed4fb2",
+ "5ce9",
+ // } end SEQUENCE (ECPrivateKey)
+ ))
+ .unwrap();
+ let want = hex::decode(concat!(
+ // RFC 5208 s5
+ "3060", // SEQUENCE (PrivateKeyInfo) {
+ "020100", // INTEGER length 1 value 0 (version)
+ "3010", // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+ "0607", // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+ "0605", // OBJECT IDENTIFIER length 5 (param)
+ "2b81040023", // 1.3.132.0.35 (secp521r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0449", // OCTET STRING (privateKey) holding...
+ "3047", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0442", // OCTET STRING length 0x42 (privateKey)
+ "0011458c586db5daa92afab03f4fe46a",
+ "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+ "9d18d7d08b5bcfa0e53c75b064ad51c4",
+ "49bae0258d54b94b1e885ded08ed4fb2",
+ "5ce9",
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+ ))
+ .unwrap();
+ let got = pkcs8_wrap_nist_key(&input, EcCurve::P_521).unwrap();
+ assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 54f382d..22c0522 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -49,6 +49,9 @@
use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
use std::iter::IntoIterator;
+#[cfg(test)]
+mod tests;
+
/// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
/// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
pub const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
@@ -628,130 +631,3 @@
aes_gcm_encrypt(plaintext, self.key()).context(ks_err!("Encryption failed."))
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use anyhow::Result;
-
- #[test]
- fn check_device_attestation_permissions_test() -> Result<()> {
- check_device_attestation_permissions().or_else(|error| {
- match error.root_cause().downcast_ref::<Error>() {
- // Expected: the context for this test might not be allowed to attest device IDs.
- Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
- // Other errors are unexpected
- _ => Err(error),
- }
- })
- }
-
- fn create_key_descriptors_from_aliases(key_aliases: &[&str]) -> Vec<KeyDescriptor> {
- key_aliases
- .iter()
- .map(|key_alias| KeyDescriptor {
- domain: Domain::APP,
- nspace: 0,
- alias: Some(key_alias.to_string()),
- blob: None,
- })
- .collect::<Vec<KeyDescriptor>>()
- }
-
- fn aliases_from_key_descriptors(key_descriptors: &[KeyDescriptor]) -> Vec<String> {
- key_descriptors
- .iter()
- .map(
- |kd| {
- if let Some(alias) = &kd.alias {
- String::from(alias)
- } else {
- String::from("")
- }
- },
- )
- .collect::<Vec<String>>()
- }
-
- #[test]
- fn test_safe_amount_to_return() -> Result<()> {
- let key_aliases = vec!["key1", "key2", "key3"];
- let key_descriptors = create_key_descriptors_from_aliases(&key_aliases);
-
- assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 20), 1);
- assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 50), 2);
- assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 100), 3);
- Ok(())
- }
-
- #[test]
- fn test_merge_and_sort_lists_without_filtering() -> Result<()> {
- let legacy_key_aliases = vec!["key_c", "key_a", "key_b"];
- let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
- let db_key_aliases = vec!["key_a", "key_d"];
- let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
- let result =
- merge_and_filter_key_entry_lists(&legacy_key_descriptors, &db_key_descriptors, None);
- assert_eq!(aliases_from_key_descriptors(&result), vec!["key_a", "key_b", "key_c", "key_d"]);
- Ok(())
- }
-
- #[test]
- fn test_merge_and_sort_lists_with_filtering() -> Result<()> {
- let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
- let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
- let db_key_aliases = vec!["key_c", "key_g"];
- let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
- let result = merge_and_filter_key_entry_lists(
- &legacy_key_descriptors,
- &db_key_descriptors,
- Some("key_b"),
- );
- assert_eq!(aliases_from_key_descriptors(&result), vec!["key_c", "key_e", "key_f", "key_g"]);
- Ok(())
- }
-
- #[test]
- fn test_merge_and_sort_lists_with_filtering_and_dups() -> Result<()> {
- let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
- let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
- let db_key_aliases = vec!["key_d", "key_e", "key_g"];
- let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
- let result = merge_and_filter_key_entry_lists(
- &legacy_key_descriptors,
- &db_key_descriptors,
- Some("key_c"),
- );
- assert_eq!(aliases_from_key_descriptors(&result), vec!["key_d", "key_e", "key_f", "key_g"]);
- Ok(())
- }
-
- #[test]
- fn test_list_key_parameters_with_filter_on_security_sensitive_info() -> Result<()> {
- let params = vec![
- KmKeyParameter { tag: Tag::APPLICATION_ID, value: KeyParameterValue::Integer(0) },
- KmKeyParameter { tag: Tag::APPLICATION_DATA, value: KeyParameterValue::Integer(0) },
- KmKeyParameter {
- tag: Tag::CERTIFICATE_NOT_AFTER,
- value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
- },
- KmKeyParameter {
- tag: Tag::CERTIFICATE_NOT_BEFORE,
- value: KeyParameterValue::DateTime(0),
- },
- ];
- let wanted = vec![
- KmKeyParameter {
- tag: Tag::CERTIFICATE_NOT_AFTER,
- value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
- },
- KmKeyParameter {
- tag: Tag::CERTIFICATE_NOT_BEFORE,
- value: KeyParameterValue::DateTime(0),
- },
- ];
-
- assert_eq!(log_security_safe_params(¶ms), wanted);
- Ok(())
- }
-}
diff --git a/keystore2/src/utils/tests.rs b/keystore2/src/utils/tests.rs
new file mode 100644
index 0000000..618ea47
--- /dev/null
+++ b/keystore2/src/utils/tests.rs
@@ -0,0 +1,125 @@
+// 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.
+
+//! Utility functions tests.
+
+use super::*;
+use anyhow::Result;
+
+#[test]
+fn check_device_attestation_permissions_test() -> Result<()> {
+ check_device_attestation_permissions().or_else(|error| {
+ match error.root_cause().downcast_ref::<Error>() {
+ // Expected: the context for this test might not be allowed to attest device IDs.
+ Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
+ // Other errors are unexpected
+ _ => Err(error),
+ }
+ })
+}
+
+fn create_key_descriptors_from_aliases(key_aliases: &[&str]) -> Vec<KeyDescriptor> {
+ key_aliases
+ .iter()
+ .map(|key_alias| KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(key_alias.to_string()),
+ blob: None,
+ })
+ .collect::<Vec<KeyDescriptor>>()
+}
+
+fn aliases_from_key_descriptors(key_descriptors: &[KeyDescriptor]) -> Vec<String> {
+ key_descriptors
+ .iter()
+ .map(|kd| if let Some(alias) = &kd.alias { String::from(alias) } else { String::from("") })
+ .collect::<Vec<String>>()
+}
+
+#[test]
+fn test_safe_amount_to_return() -> Result<()> {
+ let key_aliases = vec!["key1", "key2", "key3"];
+ let key_descriptors = create_key_descriptors_from_aliases(&key_aliases);
+
+ assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 20), 1);
+ assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 50), 2);
+ assert_eq!(estimate_safe_amount_to_return(Domain::APP, 1017, &key_descriptors, 100), 3);
+ Ok(())
+}
+
+#[test]
+fn test_merge_and_sort_lists_without_filtering() -> Result<()> {
+ let legacy_key_aliases = vec!["key_c", "key_a", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_a", "key_d"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result =
+ merge_and_filter_key_entry_lists(&legacy_key_descriptors, &db_key_descriptors, None);
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_a", "key_b", "key_c", "key_d"]);
+ Ok(())
+}
+
+#[test]
+fn test_merge_and_sort_lists_with_filtering() -> Result<()> {
+ let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_c", "key_g"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ Some("key_b"),
+ );
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_c", "key_e", "key_f", "key_g"]);
+ Ok(())
+}
+
+#[test]
+fn test_merge_and_sort_lists_with_filtering_and_dups() -> Result<()> {
+ let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_d", "key_e", "key_g"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ Some("key_c"),
+ );
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_d", "key_e", "key_f", "key_g"]);
+ Ok(())
+}
+
+#[test]
+fn test_list_key_parameters_with_filter_on_security_sensitive_info() -> Result<()> {
+ let params = vec![
+ KmKeyParameter { tag: Tag::APPLICATION_ID, value: KeyParameterValue::Integer(0) },
+ KmKeyParameter { tag: Tag::APPLICATION_DATA, value: KeyParameterValue::Integer(0) },
+ KmKeyParameter {
+ tag: Tag::CERTIFICATE_NOT_AFTER,
+ value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+ },
+ KmKeyParameter { tag: Tag::CERTIFICATE_NOT_BEFORE, value: KeyParameterValue::DateTime(0) },
+ ];
+ let wanted = vec![
+ KmKeyParameter {
+ tag: Tag::CERTIFICATE_NOT_AFTER,
+ value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+ },
+ KmKeyParameter { tag: Tag::CERTIFICATE_NOT_BEFORE, value: KeyParameterValue::DateTime(0) },
+ ];
+
+ assert_eq!(log_security_safe_params(¶ms), wanted);
+ Ok(())
+}