| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1 | // Copyright 2020, The Android Open Source Project | 
|  | 2 | // | 
|  | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 4 | // you may not use this file except in compliance with the License. | 
|  | 5 | // You may obtain a copy of the License at | 
|  | 6 | // | 
|  | 7 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 8 | // | 
|  | 9 | // Unless required by applicable law or agreed to in writing, software | 
|  | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 12 | // See the License for the specific language governing permissions and | 
|  | 13 | // limitations under the License. | 
|  | 14 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 15 | //! This is the Keystore 2.0 database module. | 
|  | 16 | //! The database module provides a connection to the backing SQLite store. | 
|  | 17 | //! We have two databases one for persistent key blob storage and one for | 
|  | 18 | //! items that have a per boot life cycle. | 
|  | 19 | //! | 
|  | 20 | //! ## Persistent database | 
|  | 21 | //! The persistent database has tables for key blobs. They are organized | 
|  | 22 | //! as follows: | 
|  | 23 | //! The `keyentry` table is the primary table for key entries. It is | 
|  | 24 | //! accompanied by two tables for blobs and parameters. | 
|  | 25 | //! Each key entry occupies exactly one row in the `keyentry` table and | 
|  | 26 | //! zero or more rows in the tables `blobentry` and `keyparameter`. | 
|  | 27 | //! | 
|  | 28 | //! ## Per boot database | 
|  | 29 | //! The per boot database stores items with a per boot lifecycle. | 
|  | 30 | //! Currently, there is only the `grant` table in this database. | 
|  | 31 | //! Grants are references to a key that can be used to access a key by | 
|  | 32 | //! clients that don't own that key. Grants can only be created by the | 
|  | 33 | //! owner of a key. And only certain components can create grants. | 
|  | 34 | //! This is governed by SEPolicy. | 
|  | 35 | //! | 
|  | 36 | //! ## Access control | 
|  | 37 | //! Some database functions that load keys or create grants perform | 
|  | 38 | //! access control. This is because in some cases access control | 
|  | 39 | //! can only be performed after some information about the designated | 
|  | 40 | //! key was loaded from the database. To decouple the permission checks | 
|  | 41 | //! from the database module these functions take permission check | 
|  | 42 | //! callbacks. | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 43 |  | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 44 | use crate::db_utils::{self, SqlField}; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 45 | use crate::error::{Error as KsError, ErrorCode, ResponseCode}; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 46 | use crate::impl_metadata; // This is in db_utils.rs | 
| Janis Danisevskis | 4522c2b | 2020-11-27 18:04:58 -0800 | [diff] [blame] | 47 | use crate::key_parameter::{KeyParameter, Tag}; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 48 | use crate::permission::KeyPermSet; | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 49 | use crate::utils::get_current_time_in_seconds; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 50 | use anyhow::{anyhow, Context, Result}; | 
|  | 51 | use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError}; | 
| Janis Danisevskis | 60400fe | 2020-08-26 15:24:42 -0700 | [diff] [blame] | 52 |  | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 53 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 54 | HardwareAuthToken::HardwareAuthToken, | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 55 | HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel, | 
| Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 56 | }; | 
|  | 57 | use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{ | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 58 | Timestamp::Timestamp, | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 59 | }; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 60 | use android_system_keystore2::aidl::android::system::keystore2::{ | 
| Janis Danisevskis | 04b0283 | 2020-10-26 09:21:40 -0700 | [diff] [blame] | 61 | Domain::Domain, KeyDescriptor::KeyDescriptor, | 
| Janis Danisevskis | 60400fe | 2020-08-26 15:24:42 -0700 | [diff] [blame] | 62 | }; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 63 | use lazy_static::lazy_static; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 64 | use log::error; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 65 | #[cfg(not(test))] | 
|  | 66 | use rand::prelude::random; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 67 | use rusqlite::{ | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 68 | params, | 
|  | 69 | types::FromSql, | 
|  | 70 | types::FromSqlResult, | 
|  | 71 | types::ToSqlOutput, | 
|  | 72 | types::{FromSqlError, Value, ValueRef}, | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 73 | Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 74 | }; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 75 | use std::{ | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 76 | collections::{HashMap, HashSet}, | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 77 | path::Path, | 
|  | 78 | sync::{Condvar, Mutex}, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 79 | time::{Duration, SystemTime}, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 80 | }; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 81 | #[cfg(test)] | 
|  | 82 | use tests::random; | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 83 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 84 | impl_metadata!( | 
|  | 85 | /// A set of metadata for key entries. | 
|  | 86 | #[derive(Debug, Default, Eq, PartialEq)] | 
|  | 87 | pub struct KeyMetaData; | 
|  | 88 | /// A metadata entry for key entries. | 
|  | 89 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 90 | pub enum KeyMetaEntry { | 
|  | 91 | /// If present, indicates that the sensitive part of key | 
|  | 92 | /// is encrypted with another key or a key derived from a password. | 
|  | 93 | EncryptedBy(EncryptedBy) with accessor encrypted_by, | 
|  | 94 | /// If the blob is password encrypted this field is set to the | 
|  | 95 | /// salt used for the key derivation. | 
|  | 96 | Salt(Vec<u8>) with accessor salt, | 
|  | 97 | /// If the blob is encrypted, this field is set to the initialization vector. | 
|  | 98 | Iv(Vec<u8>) with accessor iv, | 
|  | 99 | /// If the blob is encrypted, this field holds the AEAD TAG. | 
|  | 100 | AeadTag(Vec<u8>) with accessor aead_tag, | 
|  | 101 | /// Creation date of a the key entry. | 
|  | 102 | CreationDate(DateTime) with accessor creation_date, | 
|  | 103 | /// Expiration date for attestation keys. | 
|  | 104 | AttestationExpirationDate(DateTime) with accessor attestation_expiration_date, | 
|  | 105 | //  --- ADD NEW META DATA FIELDS HERE --- | 
|  | 106 | // For backwards compatibility add new entries only to | 
|  | 107 | // end of this list and above this comment. | 
|  | 108 | }; | 
|  | 109 | ); | 
|  | 110 |  | 
|  | 111 | impl KeyMetaData { | 
|  | 112 | fn load_from_db(key_id: i64, tx: &Transaction) -> Result<Self> { | 
|  | 113 | let mut stmt = tx | 
|  | 114 | .prepare( | 
|  | 115 | "SELECT tag, data from persistent.keymetadata | 
|  | 116 | WHERE keyentryid = ?;", | 
|  | 117 | ) | 
|  | 118 | .context("In KeyMetaData::load_from_db: prepare statement failed.")?; | 
|  | 119 |  | 
|  | 120 | let mut metadata: HashMap<i64, KeyMetaEntry> = Default::default(); | 
|  | 121 |  | 
|  | 122 | let mut rows = | 
|  | 123 | stmt.query(params![key_id]).context("In KeyMetaData::load_from_db: query failed.")?; | 
|  | 124 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
|  | 125 | let db_tag: i64 = row.get(0).context("Failed to read tag.")?; | 
|  | 126 | metadata.insert( | 
|  | 127 | db_tag, | 
|  | 128 | KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row)) | 
|  | 129 | .context("Failed to read KeyMetaEntry.")?, | 
|  | 130 | ); | 
|  | 131 | Ok(()) | 
|  | 132 | }) | 
|  | 133 | .context("In KeyMetaData::load_from_db.")?; | 
|  | 134 |  | 
|  | 135 | Ok(Self { data: metadata }) | 
|  | 136 | } | 
|  | 137 |  | 
|  | 138 | fn store_in_db(&self, key_id: i64, tx: &Transaction) -> Result<()> { | 
|  | 139 | let mut stmt = tx | 
|  | 140 | .prepare( | 
|  | 141 | "INSERT into persistent.keymetadata (keyentryid, tag, data) | 
|  | 142 | VALUES (?, ?, ?);", | 
|  | 143 | ) | 
|  | 144 | .context("In KeyMetaData::store_in_db: Failed to prepare statement.")?; | 
|  | 145 |  | 
|  | 146 | let iter = self.data.iter(); | 
|  | 147 | for (tag, entry) in iter { | 
|  | 148 | stmt.insert(params![key_id, tag, entry,]).with_context(|| { | 
|  | 149 | format!("In KeyMetaData::store_in_db: Failed to insert {:?}", entry) | 
|  | 150 | })?; | 
|  | 151 | } | 
|  | 152 | Ok(()) | 
|  | 153 | } | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | /// Indicates the type of the keyentry. | 
|  | 157 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 158 | pub enum KeyType { | 
|  | 159 | /// This is a client key type. These keys are created or imported through the Keystore 2.0 | 
|  | 160 | /// AIDL interface android.system.keystore2. | 
|  | 161 | Client, | 
|  | 162 | /// This is a super key type. These keys are created by keystore itself and used to encrypt | 
|  | 163 | /// other key blobs to provide LSKF binding. | 
|  | 164 | Super, | 
|  | 165 | /// This is an attestation key. These keys are created by the remote provisioning mechanism. | 
|  | 166 | Attestation, | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | impl ToSql for KeyType { | 
|  | 170 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 171 | Ok(ToSqlOutput::Owned(Value::Integer(match self { | 
|  | 172 | KeyType::Client => 0, | 
|  | 173 | KeyType::Super => 1, | 
|  | 174 | KeyType::Attestation => 2, | 
|  | 175 | }))) | 
|  | 176 | } | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 | impl FromSql for KeyType { | 
|  | 180 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 181 | match i64::column_result(value)? { | 
|  | 182 | 0 => Ok(KeyType::Client), | 
|  | 183 | 1 => Ok(KeyType::Super), | 
|  | 184 | 2 => Ok(KeyType::Attestation), | 
|  | 185 | v => Err(FromSqlError::OutOfRange(v)), | 
|  | 186 | } | 
|  | 187 | } | 
|  | 188 | } | 
|  | 189 |  | 
|  | 190 | /// Indicates how the sensitive part of this key blob is encrypted. | 
|  | 191 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 192 | pub enum EncryptedBy { | 
|  | 193 | /// The keyblob is encrypted by a user password. | 
|  | 194 | /// In the database this variant is represented as NULL. | 
|  | 195 | Password, | 
|  | 196 | /// The keyblob is encrypted by another key with wrapped key id. | 
|  | 197 | /// In the database this variant is represented as non NULL value | 
|  | 198 | /// that is convertible to i64, typically NUMERIC. | 
|  | 199 | KeyId(i64), | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | impl ToSql for EncryptedBy { | 
|  | 203 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 204 | match self { | 
|  | 205 | Self::Password => Ok(ToSqlOutput::Owned(Value::Null)), | 
|  | 206 | Self::KeyId(id) => id.to_sql(), | 
|  | 207 | } | 
|  | 208 | } | 
|  | 209 | } | 
|  | 210 |  | 
|  | 211 | impl FromSql for EncryptedBy { | 
|  | 212 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 213 | match value { | 
|  | 214 | ValueRef::Null => Ok(Self::Password), | 
|  | 215 | _ => Ok(Self::KeyId(i64::column_result(value)?)), | 
|  | 216 | } | 
|  | 217 | } | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | /// A database representation of wall clock time. DateTime stores unix epoch time as | 
|  | 221 | /// i64 in milliseconds. | 
|  | 222 | #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 223 | pub struct DateTime(i64); | 
|  | 224 |  | 
|  | 225 | /// Error type returned when creating DateTime or converting it from and to | 
|  | 226 | /// SystemTime. | 
|  | 227 | #[derive(thiserror::Error, Debug)] | 
|  | 228 | pub enum DateTimeError { | 
|  | 229 | /// This is returned when SystemTime and Duration computations fail. | 
|  | 230 | #[error(transparent)] | 
|  | 231 | SystemTimeError(#[from] SystemTimeError), | 
|  | 232 |  | 
|  | 233 | /// This is returned when type conversions fail. | 
|  | 234 | #[error(transparent)] | 
|  | 235 | TypeConversion(#[from] std::num::TryFromIntError), | 
|  | 236 |  | 
|  | 237 | /// This is returned when checked time arithmetic failed. | 
|  | 238 | #[error("Time arithmetic failed.")] | 
|  | 239 | TimeArithmetic, | 
|  | 240 | } | 
|  | 241 |  | 
|  | 242 | impl DateTime { | 
|  | 243 | /// Constructs a new DateTime object denoting the current time. This may fail during | 
|  | 244 | /// conversion to unix epoch time and during conversion to the internal i64 representation. | 
|  | 245 | pub fn now() -> Result<Self, DateTimeError> { | 
|  | 246 | Ok(Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?)) | 
|  | 247 | } | 
|  | 248 |  | 
|  | 249 | /// Constructs a new DateTime object from milliseconds. | 
|  | 250 | pub fn from_millis_epoch(millis: i64) -> Self { | 
|  | 251 | Self(millis) | 
|  | 252 | } | 
|  | 253 |  | 
|  | 254 | /// Returns unix epoch time in milliseconds. | 
|  | 255 | pub fn to_millis_epoch(&self) -> i64 { | 
|  | 256 | self.0 | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | /// Returns unix epoch time in seconds. | 
|  | 260 | pub fn to_secs_epoch(&self) -> i64 { | 
|  | 261 | self.0 / 1000 | 
|  | 262 | } | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | impl ToSql for DateTime { | 
|  | 266 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 267 | Ok(ToSqlOutput::Owned(Value::Integer(self.0))) | 
|  | 268 | } | 
|  | 269 | } | 
|  | 270 |  | 
|  | 271 | impl FromSql for DateTime { | 
|  | 272 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 273 | Ok(Self(i64::column_result(value)?)) | 
|  | 274 | } | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | impl TryInto<SystemTime> for DateTime { | 
|  | 278 | type Error = DateTimeError; | 
|  | 279 |  | 
|  | 280 | fn try_into(self) -> Result<SystemTime, Self::Error> { | 
|  | 281 | // We want to construct a SystemTime representation equivalent to self, denoting | 
|  | 282 | // a point in time THEN, but we cannot set the time directly. We can only construct | 
|  | 283 | // a SystemTime denoting NOW, and we can get the duration between EPOCH and NOW, | 
|  | 284 | // and between EPOCH and THEN. With this common reference we can construct the | 
|  | 285 | // duration between NOW and THEN which we can add to our SystemTime representation | 
|  | 286 | // of NOW to get a SystemTime representation of THEN. | 
|  | 287 | // Durations can only be positive, thus the if statement below. | 
|  | 288 | let now = SystemTime::now(); | 
|  | 289 | let now_epoch = now.duration_since(SystemTime::UNIX_EPOCH)?; | 
|  | 290 | let then_epoch = Duration::from_millis(self.0.try_into()?); | 
|  | 291 | Ok(if now_epoch > then_epoch { | 
|  | 292 | // then = now - (now_epoch - then_epoch) | 
|  | 293 | now_epoch | 
|  | 294 | .checked_sub(then_epoch) | 
|  | 295 | .and_then(|d| now.checked_sub(d)) | 
|  | 296 | .ok_or(DateTimeError::TimeArithmetic)? | 
|  | 297 | } else { | 
|  | 298 | // then = now + (then_epoch - now_epoch) | 
|  | 299 | then_epoch | 
|  | 300 | .checked_sub(now_epoch) | 
|  | 301 | .and_then(|d| now.checked_add(d)) | 
|  | 302 | .ok_or(DateTimeError::TimeArithmetic)? | 
|  | 303 | }) | 
|  | 304 | } | 
|  | 305 | } | 
|  | 306 |  | 
|  | 307 | impl TryFrom<SystemTime> for DateTime { | 
|  | 308 | type Error = DateTimeError; | 
|  | 309 |  | 
|  | 310 | fn try_from(t: SystemTime) -> Result<Self, Self::Error> { | 
|  | 311 | Ok(Self(t.duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?)) | 
|  | 312 | } | 
|  | 313 | } | 
|  | 314 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 315 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] | 
|  | 316 | enum KeyLifeCycle { | 
|  | 317 | /// Existing keys have a key ID but are not fully populated yet. | 
|  | 318 | /// This is a transient state. If Keystore finds any such keys when it starts up, it must move | 
|  | 319 | /// them to Unreferenced for garbage collection. | 
|  | 320 | Existing, | 
|  | 321 | /// A live key is fully populated and usable by clients. | 
|  | 322 | Live, | 
|  | 323 | /// An unreferenced key is scheduled for garbage collection. | 
|  | 324 | Unreferenced, | 
|  | 325 | } | 
|  | 326 |  | 
|  | 327 | impl ToSql for KeyLifeCycle { | 
|  | 328 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 329 | match self { | 
|  | 330 | Self::Existing => Ok(ToSqlOutput::Owned(Value::Integer(0))), | 
|  | 331 | Self::Live => Ok(ToSqlOutput::Owned(Value::Integer(1))), | 
|  | 332 | Self::Unreferenced => Ok(ToSqlOutput::Owned(Value::Integer(2))), | 
|  | 333 | } | 
|  | 334 | } | 
|  | 335 | } | 
|  | 336 |  | 
|  | 337 | impl FromSql for KeyLifeCycle { | 
|  | 338 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 339 | match i64::column_result(value)? { | 
|  | 340 | 0 => Ok(KeyLifeCycle::Existing), | 
|  | 341 | 1 => Ok(KeyLifeCycle::Live), | 
|  | 342 | 2 => Ok(KeyLifeCycle::Unreferenced), | 
|  | 343 | v => Err(FromSqlError::OutOfRange(v)), | 
|  | 344 | } | 
|  | 345 | } | 
|  | 346 | } | 
|  | 347 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 348 | /// Keys have a KeyMint blob component and optional public certificate and | 
|  | 349 | /// certificate chain components. | 
|  | 350 | /// KeyEntryLoadBits is a bitmap that indicates to `KeystoreDB::load_key_entry` | 
|  | 351 | /// which components shall be loaded from the database if present. | 
|  | 352 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 353 | pub struct KeyEntryLoadBits(u32); | 
|  | 354 |  | 
|  | 355 | impl KeyEntryLoadBits { | 
|  | 356 | /// Indicate to `KeystoreDB::load_key_entry` that no component shall be loaded. | 
|  | 357 | pub const NONE: KeyEntryLoadBits = Self(0); | 
|  | 358 | /// Indicate to `KeystoreDB::load_key_entry` that the KeyMint component shall be loaded. | 
|  | 359 | pub const KM: KeyEntryLoadBits = Self(1); | 
|  | 360 | /// Indicate to `KeystoreDB::load_key_entry` that the Public components shall be loaded. | 
|  | 361 | pub const PUBLIC: KeyEntryLoadBits = Self(2); | 
|  | 362 | /// Indicate to `KeystoreDB::load_key_entry` that both components shall be loaded. | 
|  | 363 | pub const BOTH: KeyEntryLoadBits = Self(3); | 
|  | 364 |  | 
|  | 365 | /// Returns true if this object indicates that the public components shall be loaded. | 
|  | 366 | pub const fn load_public(&self) -> bool { | 
|  | 367 | self.0 & Self::PUBLIC.0 != 0 | 
|  | 368 | } | 
|  | 369 |  | 
|  | 370 | /// Returns true if the object indicates that the KeyMint component shall be loaded. | 
|  | 371 | pub const fn load_km(&self) -> bool { | 
|  | 372 | self.0 & Self::KM.0 != 0 | 
|  | 373 | } | 
|  | 374 | } | 
|  | 375 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 376 | lazy_static! { | 
|  | 377 | static ref KEY_ID_LOCK: KeyIdLockDb = KeyIdLockDb::new(); | 
|  | 378 | } | 
|  | 379 |  | 
|  | 380 | struct KeyIdLockDb { | 
|  | 381 | locked_keys: Mutex<HashSet<i64>>, | 
|  | 382 | cond_var: Condvar, | 
|  | 383 | } | 
|  | 384 |  | 
|  | 385 | /// A locked key. While a guard exists for a given key id, the same key cannot be loaded | 
|  | 386 | /// from the database a second time. Most functions manipulating the key blob database | 
|  | 387 | /// require a KeyIdGuard. | 
|  | 388 | #[derive(Debug)] | 
|  | 389 | pub struct KeyIdGuard(i64); | 
|  | 390 |  | 
|  | 391 | impl KeyIdLockDb { | 
|  | 392 | fn new() -> Self { | 
|  | 393 | Self { locked_keys: Mutex::new(HashSet::new()), cond_var: Condvar::new() } | 
|  | 394 | } | 
|  | 395 |  | 
|  | 396 | /// This function blocks until an exclusive lock for the given key entry id can | 
|  | 397 | /// be acquired. It returns a guard object, that represents the lifecycle of the | 
|  | 398 | /// acquired lock. | 
|  | 399 | pub fn get(&self, key_id: i64) -> KeyIdGuard { | 
|  | 400 | let mut locked_keys = self.locked_keys.lock().unwrap(); | 
|  | 401 | while locked_keys.contains(&key_id) { | 
|  | 402 | locked_keys = self.cond_var.wait(locked_keys).unwrap(); | 
|  | 403 | } | 
|  | 404 | locked_keys.insert(key_id); | 
|  | 405 | KeyIdGuard(key_id) | 
|  | 406 | } | 
|  | 407 |  | 
|  | 408 | /// This function attempts to acquire an exclusive lock on a given key id. If the | 
|  | 409 | /// given key id is already taken the function returns None immediately. If a lock | 
|  | 410 | /// can be acquired this function returns a guard object, that represents the | 
|  | 411 | /// lifecycle of the acquired lock. | 
|  | 412 | pub fn try_get(&self, key_id: i64) -> Option<KeyIdGuard> { | 
|  | 413 | let mut locked_keys = self.locked_keys.lock().unwrap(); | 
|  | 414 | if locked_keys.insert(key_id) { | 
|  | 415 | Some(KeyIdGuard(key_id)) | 
|  | 416 | } else { | 
|  | 417 | None | 
|  | 418 | } | 
|  | 419 | } | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | impl KeyIdGuard { | 
|  | 423 | /// Get the numeric key id of the locked key. | 
|  | 424 | pub fn id(&self) -> i64 { | 
|  | 425 | self.0 | 
|  | 426 | } | 
|  | 427 | } | 
|  | 428 |  | 
|  | 429 | impl Drop for KeyIdGuard { | 
|  | 430 | fn drop(&mut self) { | 
|  | 431 | let mut locked_keys = KEY_ID_LOCK.locked_keys.lock().unwrap(); | 
|  | 432 | locked_keys.remove(&self.0); | 
| Janis Danisevskis | 7fd5358 | 2020-11-23 13:40:34 -0800 | [diff] [blame] | 433 | drop(locked_keys); | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 434 | KEY_ID_LOCK.cond_var.notify_all(); | 
|  | 435 | } | 
|  | 436 | } | 
|  | 437 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 438 | /// This type represents a Keystore 2.0 key entry. | 
|  | 439 | /// An entry has a unique `id` by which it can be found in the database. | 
|  | 440 | /// It has a security level field, key parameters, and three optional fields | 
|  | 441 | /// for the KeyMint blob, public certificate and a public certificate chain. | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 442 | #[derive(Debug, Default, Eq, PartialEq)] | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 443 | pub struct KeyEntry { | 
|  | 444 | id: i64, | 
|  | 445 | km_blob: Option<Vec<u8>>, | 
|  | 446 | cert: Option<Vec<u8>>, | 
|  | 447 | cert_chain: Option<Vec<u8>>, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 448 | sec_level: SecurityLevel, | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 449 | parameters: Vec<KeyParameter>, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 450 | metadata: KeyMetaData, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 451 | } | 
|  | 452 |  | 
|  | 453 | impl KeyEntry { | 
|  | 454 | /// Returns the unique id of the Key entry. | 
|  | 455 | pub fn id(&self) -> i64 { | 
|  | 456 | self.id | 
|  | 457 | } | 
|  | 458 | /// Exposes the optional KeyMint blob. | 
|  | 459 | pub fn km_blob(&self) -> &Option<Vec<u8>> { | 
|  | 460 | &self.km_blob | 
|  | 461 | } | 
|  | 462 | /// Extracts the Optional KeyMint blob. | 
|  | 463 | pub fn take_km_blob(&mut self) -> Option<Vec<u8>> { | 
|  | 464 | self.km_blob.take() | 
|  | 465 | } | 
|  | 466 | /// Exposes the optional public certificate. | 
|  | 467 | pub fn cert(&self) -> &Option<Vec<u8>> { | 
|  | 468 | &self.cert | 
|  | 469 | } | 
|  | 470 | /// Extracts the optional public certificate. | 
|  | 471 | pub fn take_cert(&mut self) -> Option<Vec<u8>> { | 
|  | 472 | self.cert.take() | 
|  | 473 | } | 
|  | 474 | /// Exposes the optional public certificate chain. | 
|  | 475 | pub fn cert_chain(&self) -> &Option<Vec<u8>> { | 
|  | 476 | &self.cert_chain | 
|  | 477 | } | 
|  | 478 | /// Extracts the optional public certificate_chain. | 
|  | 479 | pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> { | 
|  | 480 | self.cert_chain.take() | 
|  | 481 | } | 
|  | 482 | /// Returns the security level of the key entry. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 483 | pub fn sec_level(&self) -> SecurityLevel { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 484 | self.sec_level | 
|  | 485 | } | 
| Janis Danisevskis | 04b0283 | 2020-10-26 09:21:40 -0700 | [diff] [blame] | 486 | /// Exposes the key parameters of this key entry. | 
|  | 487 | pub fn key_parameters(&self) -> &Vec<KeyParameter> { | 
|  | 488 | &self.parameters | 
|  | 489 | } | 
|  | 490 | /// Consumes this key entry and extracts the keyparameters from it. | 
|  | 491 | pub fn into_key_parameters(self) -> Vec<KeyParameter> { | 
|  | 492 | self.parameters | 
|  | 493 | } | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 494 | /// Exposes the key metadata of this key entry. | 
|  | 495 | pub fn metadata(&self) -> &KeyMetaData { | 
|  | 496 | &self.metadata | 
|  | 497 | } | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 498 | } | 
|  | 499 |  | 
|  | 500 | /// Indicates the sub component of a key entry for persistent storage. | 
|  | 501 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 502 | pub struct SubComponentType(u32); | 
|  | 503 | impl SubComponentType { | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 504 | /// Persistent identifier for a key blob. | 
|  | 505 | pub const KEY_BLOB: SubComponentType = Self(0); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 506 | /// Persistent identifier for a certificate blob. | 
|  | 507 | pub const CERT: SubComponentType = Self(1); | 
|  | 508 | /// Persistent identifier for a certificate chain blob. | 
|  | 509 | pub const CERT_CHAIN: SubComponentType = Self(2); | 
|  | 510 | } | 
|  | 511 |  | 
|  | 512 | impl ToSql for SubComponentType { | 
|  | 513 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 514 | self.0.to_sql() | 
|  | 515 | } | 
|  | 516 | } | 
|  | 517 |  | 
|  | 518 | impl FromSql for SubComponentType { | 
|  | 519 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 520 | Ok(Self(u32::column_result(value)?)) | 
|  | 521 | } | 
|  | 522 | } | 
|  | 523 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 524 | /// KeystoreDB wraps a connection to an SQLite database and tracks its | 
|  | 525 | /// ownership. It also implements all of Keystore 2.0's database functionality. | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 526 | pub struct KeystoreDB { | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 527 | conn: Connection, | 
|  | 528 | } | 
|  | 529 |  | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 530 | /// Database representation of the monotonic time retrieved from the system call clock_gettime with | 
|  | 531 | /// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in seconds. | 
|  | 532 | #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 533 | pub struct MonotonicRawTime(i64); | 
|  | 534 |  | 
|  | 535 | impl MonotonicRawTime { | 
|  | 536 | /// Constructs a new MonotonicRawTime | 
|  | 537 | pub fn now() -> Self { | 
|  | 538 | Self(get_current_time_in_seconds()) | 
|  | 539 | } | 
|  | 540 |  | 
|  | 541 | /// Returns the integer value of MonotonicRawTime as i64 | 
|  | 542 | pub fn seconds(&self) -> i64 { | 
|  | 543 | self.0 | 
|  | 544 | } | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 545 |  | 
|  | 546 | /// Like i64::checked_sub. | 
|  | 547 | pub fn checked_sub(&self, other: &Self) -> Option<Self> { | 
|  | 548 | self.0.checked_sub(other.0).map(Self) | 
|  | 549 | } | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 550 | } | 
|  | 551 |  | 
|  | 552 | impl ToSql for MonotonicRawTime { | 
|  | 553 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 554 | Ok(ToSqlOutput::Owned(Value::Integer(self.0))) | 
|  | 555 | } | 
|  | 556 | } | 
|  | 557 |  | 
|  | 558 | impl FromSql for MonotonicRawTime { | 
|  | 559 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 560 | Ok(Self(i64::column_result(value)?)) | 
|  | 561 | } | 
|  | 562 | } | 
|  | 563 |  | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 564 | /// This struct encapsulates the information to be stored in the database about the auth tokens | 
|  | 565 | /// received by keystore. | 
|  | 566 | pub struct AuthTokenEntry { | 
|  | 567 | auth_token: HardwareAuthToken, | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 568 | time_received: MonotonicRawTime, | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 569 | } | 
|  | 570 |  | 
|  | 571 | impl AuthTokenEntry { | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 572 | fn new(auth_token: HardwareAuthToken, time_received: MonotonicRawTime) -> Self { | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 573 | AuthTokenEntry { auth_token, time_received } | 
|  | 574 | } | 
|  | 575 |  | 
|  | 576 | /// Checks if this auth token satisfies the given authentication information. | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 577 | pub fn satisfies(&self, user_secure_ids: &[i64], auth_type: HardwareAuthenticatorType) -> bool { | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 578 | user_secure_ids.iter().any(|&sid| { | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 579 | (sid == self.auth_token.userId || sid == self.auth_token.authenticatorId) | 
|  | 580 | && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0) | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 581 | }) | 
|  | 582 | } | 
|  | 583 |  | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 584 | /// Returns the auth token wrapped by the AuthTokenEntry | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 585 | pub fn auth_token(&self) -> &HardwareAuthToken { | 
|  | 586 | &self.auth_token | 
|  | 587 | } | 
|  | 588 |  | 
|  | 589 | /// Returns the auth token wrapped by the AuthTokenEntry | 
|  | 590 | pub fn take_auth_token(self) -> HardwareAuthToken { | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 591 | self.auth_token | 
|  | 592 | } | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 593 |  | 
|  | 594 | /// Returns the time that this auth token was received. | 
|  | 595 | pub fn time_received(&self) -> MonotonicRawTime { | 
|  | 596 | self.time_received | 
|  | 597 | } | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 598 | } | 
|  | 599 |  | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 600 | impl KeystoreDB { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 601 | /// This will create a new database connection connecting the two | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 602 | /// files persistent.sqlite and perboot.sqlite in the given directory. | 
|  | 603 | /// It also attempts to initialize all of the tables. | 
|  | 604 | /// KeystoreDB cannot be used by multiple threads. | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 605 | /// Each thread should open their own connection using `thread_local!`. | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 606 | pub fn new(db_root: &Path) -> Result<Self> { | 
|  | 607 | // Build the path to the sqlite files. | 
|  | 608 | let mut persistent_path = db_root.to_path_buf(); | 
|  | 609 | persistent_path.push("persistent.sqlite"); | 
|  | 610 | let mut perboot_path = db_root.to_path_buf(); | 
|  | 611 | perboot_path.push("perboot.sqlite"); | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 612 |  | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 613 | // Now convert them to strings prefixed with "file:" | 
|  | 614 | let mut persistent_path_str = "file:".to_owned(); | 
|  | 615 | persistent_path_str.push_str(&persistent_path.to_string_lossy()); | 
|  | 616 | let mut perboot_path_str = "file:".to_owned(); | 
|  | 617 | perboot_path_str.push_str(&perboot_path.to_string_lossy()); | 
|  | 618 |  | 
|  | 619 | let conn = Self::make_connection(&persistent_path_str, &perboot_path_str)?; | 
| Janis Danisevskis | aea2734 | 2021-01-29 08:38:11 -0800 | [diff] [blame] | 620 | conn.busy_handler(Some(|_| { | 
|  | 621 | std::thread::sleep(std::time::Duration::from_micros(50)); | 
|  | 622 | true | 
|  | 623 | })) | 
|  | 624 | .context("In KeystoreDB::new: Failed to set busy handler.")?; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 625 |  | 
|  | 626 | Self::init_tables(&conn)?; | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 627 | Ok(Self { conn }) | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 628 | } | 
|  | 629 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 630 | fn init_tables(conn: &Connection) -> Result<()> { | 
|  | 631 | conn.execute( | 
|  | 632 | "CREATE TABLE IF NOT EXISTS persistent.keyentry ( | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 633 | id INTEGER UNIQUE, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 634 | key_type INTEGER, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 635 | domain INTEGER, | 
|  | 636 | namespace INTEGER, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 637 | alias BLOB, | 
|  | 638 | state INTEGER);", | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 639 | NO_PARAMS, | 
|  | 640 | ) | 
|  | 641 | .context("Failed to initialize \"keyentry\" table.")?; | 
|  | 642 |  | 
|  | 643 | conn.execute( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 644 | "CREATE TABLE IF NOT EXISTS persistent.blobentry ( | 
|  | 645 | id INTEGER PRIMARY KEY, | 
|  | 646 | subcomponent_type INTEGER, | 
|  | 647 | keyentryid INTEGER, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 648 | blob BLOB);", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 649 | NO_PARAMS, | 
|  | 650 | ) | 
|  | 651 | .context("Failed to initialize \"blobentry\" table.")?; | 
|  | 652 |  | 
|  | 653 | conn.execute( | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 654 | "CREATE TABLE IF NOT EXISTS persistent.keyparameter ( | 
| Hasini Gunasinghe | af99366 | 2020-07-24 18:40:20 +0000 | [diff] [blame] | 655 | keyentryid INTEGER, | 
|  | 656 | tag INTEGER, | 
|  | 657 | data ANY, | 
|  | 658 | security_level INTEGER);", | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 659 | NO_PARAMS, | 
|  | 660 | ) | 
|  | 661 | .context("Failed to initialize \"keyparameter\" table.")?; | 
|  | 662 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 663 | conn.execute( | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 664 | "CREATE TABLE IF NOT EXISTS persistent.keymetadata ( | 
|  | 665 | keyentryid INTEGER, | 
|  | 666 | tag INTEGER, | 
|  | 667 | data ANY);", | 
|  | 668 | NO_PARAMS, | 
|  | 669 | ) | 
|  | 670 | .context("Failed to initialize \"keymetadata\" table.")?; | 
|  | 671 |  | 
|  | 672 | conn.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 673 | "CREATE TABLE IF NOT EXISTS persistent.grant ( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 674 | id INTEGER UNIQUE, | 
|  | 675 | grantee INTEGER, | 
|  | 676 | keyentryid INTEGER, | 
|  | 677 | access_vector INTEGER);", | 
|  | 678 | NO_PARAMS, | 
|  | 679 | ) | 
|  | 680 | .context("Failed to initialize \"grant\" table.")?; | 
|  | 681 |  | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 682 | //TODO: only drop the following two perboot tables if this is the first start up | 
|  | 683 | //during the boot (b/175716626). | 
|  | 684 | // conn.execute("DROP TABLE IF EXISTS perboot.authtoken;", NO_PARAMS) | 
|  | 685 | //     .context("Failed to drop perboot.authtoken table")?; | 
|  | 686 | conn.execute( | 
|  | 687 | "CREATE TABLE IF NOT EXISTS perboot.authtoken ( | 
|  | 688 | id INTEGER PRIMARY KEY, | 
|  | 689 | challenge INTEGER, | 
|  | 690 | user_id INTEGER, | 
|  | 691 | auth_id INTEGER, | 
|  | 692 | authenticator_type INTEGER, | 
|  | 693 | timestamp INTEGER, | 
|  | 694 | mac BLOB, | 
|  | 695 | time_received INTEGER, | 
|  | 696 | UNIQUE(user_id, auth_id, authenticator_type));", | 
|  | 697 | NO_PARAMS, | 
|  | 698 | ) | 
|  | 699 | .context("Failed to initialize \"authtoken\" table.")?; | 
|  | 700 |  | 
|  | 701 | // conn.execute("DROP TABLE IF EXISTS perboot.metadata;", NO_PARAMS) | 
|  | 702 | //     .context("Failed to drop perboot.metadata table")?; | 
|  | 703 | // metadata table stores certain miscellaneous information required for keystore functioning | 
|  | 704 | // during a boot cycle, as key-value pairs. | 
|  | 705 | conn.execute( | 
|  | 706 | "CREATE TABLE IF NOT EXISTS perboot.metadata ( | 
|  | 707 | key TEXT, | 
|  | 708 | value BLOB, | 
|  | 709 | UNIQUE(key));", | 
|  | 710 | NO_PARAMS, | 
|  | 711 | ) | 
|  | 712 | .context("Failed to initialize \"metadata\" table.")?; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 713 | Ok(()) | 
|  | 714 | } | 
|  | 715 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 716 | fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> { | 
|  | 717 | let conn = | 
|  | 718 | Connection::open_in_memory().context("Failed to initialize SQLite connection.")?; | 
|  | 719 |  | 
|  | 720 | conn.execute("ATTACH DATABASE ? as persistent;", params![persistent_file]) | 
|  | 721 | .context("Failed to attach database persistent.")?; | 
|  | 722 | conn.execute("ATTACH DATABASE ? as perboot;", params![perboot_file]) | 
|  | 723 | .context("Failed to attach database perboot.")?; | 
|  | 724 |  | 
|  | 725 | Ok(conn) | 
|  | 726 | } | 
|  | 727 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 728 | /// Get one unreferenced key. There is no particular order in which the keys are returned. | 
|  | 729 | fn get_unreferenced_key_id(tx: &Transaction) -> Result<Option<i64>> { | 
|  | 730 | tx.query_row( | 
|  | 731 | "SELECT id FROM persistent.keyentry WHERE state = ?", | 
|  | 732 | params![KeyLifeCycle::Unreferenced], | 
|  | 733 | |row| row.get(0), | 
|  | 734 | ) | 
|  | 735 | .optional() | 
|  | 736 | .context("In get_unreferenced_key_id: Trying to get unreferenced key id.") | 
|  | 737 | } | 
|  | 738 |  | 
|  | 739 | /// Returns a key id guard and key entry for one unreferenced key entry. Of the optional | 
|  | 740 | /// fields of the key entry only the km_blob field will be populated. This is required | 
|  | 741 | /// to subject the blob to its KeyMint instance for deletion. | 
|  | 742 | pub fn get_unreferenced_key(&mut self) -> Result<Option<(KeyIdGuard, KeyEntry)>> { | 
|  | 743 | self.with_transaction(TransactionBehavior::Deferred, |tx| { | 
|  | 744 | let key_id = match Self::get_unreferenced_key_id(tx) | 
|  | 745 | .context("Trying to get unreferenced key id")? | 
|  | 746 | { | 
|  | 747 | None => return Ok(None), | 
|  | 748 | Some(id) => KEY_ID_LOCK.try_get(id).ok_or_else(KsError::sys).context(concat!( | 
|  | 749 | "A key id lock was held for an unreferenced key. ", | 
|  | 750 | "This should never happen." | 
|  | 751 | ))?, | 
|  | 752 | }; | 
|  | 753 | let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, key_id.id()) | 
|  | 754 | .context("Trying to get key components.")?; | 
|  | 755 | Ok(Some((key_id, key_entry))) | 
|  | 756 | }) | 
|  | 757 | .context("In get_unreferenced_key.") | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | /// This function purges all remnants of a key entry from the database. | 
|  | 761 | /// Important: This does not check if the key was unreferenced, nor does it | 
|  | 762 | /// subject the key to its KeyMint instance for permanent invalidation. | 
|  | 763 | /// This function should only be called by the garbage collector. | 
|  | 764 | /// To delete a key call `mark_unreferenced`, which transitions the key to the unreferenced | 
|  | 765 | /// state, deletes all grants to the key, and notifies the garbage collector. | 
|  | 766 | /// The garbage collector will: | 
|  | 767 | ///  1. Call get_unreferenced_key. | 
|  | 768 | ///  2. Determine the proper way to dispose of sensitive key material, e.g., call | 
|  | 769 | ///     `KeyMintDevice::delete()`. | 
|  | 770 | ///  3. Call `purge_key_entry`. | 
|  | 771 | pub fn purge_key_entry(&mut self, key_id: KeyIdGuard) -> Result<()> { | 
|  | 772 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 773 | tx.execute("DELETE FROM persistent.keyentry WHERE id = ?;", params![key_id.id()]) | 
|  | 774 | .context("Trying to delete keyentry.")?; | 
|  | 775 | tx.execute( | 
|  | 776 | "DELETE FROM persistent.blobentry WHERE keyentryid = ?;", | 
|  | 777 | params![key_id.id()], | 
|  | 778 | ) | 
|  | 779 | .context("Trying to delete blobentries.")?; | 
|  | 780 | tx.execute( | 
|  | 781 | "DELETE FROM persistent.keymetadata WHERE keyentryid = ?;", | 
|  | 782 | params![key_id.id()], | 
|  | 783 | ) | 
|  | 784 | .context("Trying to delete keymetadata.")?; | 
|  | 785 | tx.execute( | 
|  | 786 | "DELETE FROM persistent.keyparameter WHERE keyentryid = ?;", | 
|  | 787 | params![key_id.id()], | 
|  | 788 | ) | 
|  | 789 | .context("Trying to delete keyparameters.")?; | 
|  | 790 | let grants_deleted = tx | 
|  | 791 | .execute("DELETE FROM persistent.grant WHERE keyentryid = ?;", params![key_id.id()]) | 
|  | 792 | .context("Trying to delete grants.")?; | 
|  | 793 | if grants_deleted != 0 { | 
|  | 794 | log::error!("Purged key that still had grants. This should not happen."); | 
|  | 795 | } | 
|  | 796 | Ok(()) | 
|  | 797 | }) | 
|  | 798 | .context("In purge_key_entry.") | 
|  | 799 | } | 
|  | 800 |  | 
|  | 801 | /// This maintenance function should be called only once before the database is used for the | 
|  | 802 | /// first time. It restores the invariant that `KeyLifeCycle::Existing` is a transient state. | 
|  | 803 | /// The function transitions all key entries from Existing to Unreferenced unconditionally and | 
|  | 804 | /// returns the number of rows affected. If this returns a value greater than 0, it means that | 
|  | 805 | /// Keystore crashed at some point during key generation. Callers may want to log such | 
|  | 806 | /// occurrences. | 
|  | 807 | /// Unlike with `mark_unreferenced`, we don't need to purge grants, because only keys that made | 
|  | 808 | /// it to `KeyLifeCycle::Live` may have grants. | 
|  | 809 | pub fn cleanup_leftovers(&mut self) -> Result<usize> { | 
|  | 810 | self.conn | 
|  | 811 | .execute( | 
|  | 812 | "UPDATE persistent.keyentry SET state = ? WHERE state = ?;", | 
|  | 813 | params![KeyLifeCycle::Unreferenced, KeyLifeCycle::Existing], | 
|  | 814 | ) | 
|  | 815 | .context("In cleanup_leftovers.") | 
|  | 816 | } | 
|  | 817 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 818 | /// Atomically loads a key entry and associated metadata or creates it using the | 
|  | 819 | /// callback create_new_key callback. The callback is called during a database | 
|  | 820 | /// transaction. This means that implementers should be mindful about using | 
|  | 821 | /// blocking operations such as IPC or grabbing mutexes. | 
|  | 822 | pub fn get_or_create_key_with<F>( | 
|  | 823 | &mut self, | 
|  | 824 | domain: Domain, | 
|  | 825 | namespace: i64, | 
|  | 826 | alias: &str, | 
|  | 827 | create_new_key: F, | 
|  | 828 | ) -> Result<(KeyIdGuard, KeyEntry)> | 
|  | 829 | where | 
|  | 830 | F: FnOnce() -> Result<(Vec<u8>, KeyMetaData)>, | 
|  | 831 | { | 
|  | 832 | let tx = self | 
|  | 833 | .conn | 
|  | 834 | .transaction_with_behavior(TransactionBehavior::Immediate) | 
|  | 835 | .context("In get_or_create_key_with: Failed to initialize transaction.")?; | 
|  | 836 |  | 
|  | 837 | let id = { | 
|  | 838 | let mut stmt = tx | 
|  | 839 | .prepare( | 
|  | 840 | "SELECT id FROM persistent.keyentry | 
|  | 841 | WHERE | 
|  | 842 | key_type = ? | 
|  | 843 | AND domain = ? | 
|  | 844 | AND namespace = ? | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 845 | AND alias = ? | 
|  | 846 | AND state = ?;", | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 847 | ) | 
|  | 848 | .context("In get_or_create_key_with: Failed to select from keyentry table.")?; | 
|  | 849 | let mut rows = stmt | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 850 | .query(params![KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live]) | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 851 | .context("In get_or_create_key_with: Failed to query from keyentry table.")?; | 
|  | 852 |  | 
|  | 853 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
|  | 854 | Ok(match row { | 
|  | 855 | Some(r) => r.get(0).context("Failed to unpack id.")?, | 
|  | 856 | None => None, | 
|  | 857 | }) | 
|  | 858 | }) | 
|  | 859 | .context("In get_or_create_key_with.")? | 
|  | 860 | }; | 
|  | 861 |  | 
|  | 862 | let (id, entry) = match id { | 
|  | 863 | Some(id) => ( | 
|  | 864 | id, | 
|  | 865 | Self::load_key_components(&tx, KeyEntryLoadBits::KM, id) | 
|  | 866 | .context("In get_or_create_key_with.")?, | 
|  | 867 | ), | 
|  | 868 |  | 
|  | 869 | None => { | 
|  | 870 | let id = Self::insert_with_retry(|id| { | 
|  | 871 | tx.execute( | 
|  | 872 | "INSERT into persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 873 | (id, key_type, domain, namespace, alias, state) | 
|  | 874 | VALUES(?, ?, ?, ?, ?, ?);", | 
|  | 875 | params![id, KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live], | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 876 | ) | 
|  | 877 | }) | 
|  | 878 | .context("In get_or_create_key_with.")?; | 
|  | 879 |  | 
|  | 880 | let (blob, metadata) = create_new_key().context("In get_or_create_key_with.")?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 881 | Self::insert_blob_internal(&tx, id, SubComponentType::KEY_BLOB, &blob) | 
|  | 882 | .context("In get_of_create_key_with.")?; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 883 | metadata.store_in_db(id, &tx).context("In get_or_create_key_with.")?; | 
|  | 884 | (id, KeyEntry { id, km_blob: Some(blob), metadata, ..Default::default() }) | 
|  | 885 | } | 
|  | 886 | }; | 
|  | 887 | tx.commit().context("In get_or_create_key_with: Failed to commit transaction.")?; | 
|  | 888 | Ok((KEY_ID_LOCK.get(id), entry)) | 
|  | 889 | } | 
|  | 890 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 891 | /// Creates a transaction with the given behavior and executes f with the new transaction. | 
|  | 892 | /// The transaction is committed only if f returns Ok. | 
|  | 893 | fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T> | 
|  | 894 | where | 
|  | 895 | F: FnOnce(&Transaction) -> Result<T>, | 
|  | 896 | { | 
|  | 897 | let tx = self | 
|  | 898 | .conn | 
|  | 899 | .transaction_with_behavior(behavior) | 
|  | 900 | .context("In with_transaction: Failed to initialize transaction.")?; | 
|  | 901 | f(&tx).and_then(|result| { | 
|  | 902 | tx.commit().context("In with_transaction: Failed to commit transaction.")?; | 
|  | 903 | Ok(result) | 
|  | 904 | }) | 
|  | 905 | } | 
|  | 906 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 907 | /// Creates a new key entry and allocates a new randomized id for the new key. | 
|  | 908 | /// The key id gets associated with a domain and namespace but not with an alias. | 
|  | 909 | /// To complete key generation `rebind_alias` should be called after all of the | 
|  | 910 | /// key artifacts, i.e., blobs and parameters have been associated with the new | 
|  | 911 | /// key id. Finalizing with `rebind_alias` makes the creation of a new key entry | 
|  | 912 | /// atomic even if key generation is not. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 913 | pub fn create_key_entry(&mut self, domain: Domain, namespace: i64) -> Result<KeyIdGuard> { | 
|  | 914 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 915 | Self::create_key_entry_internal(tx, domain, namespace) | 
|  | 916 | }) | 
|  | 917 | .context("In create_key_entry.") | 
|  | 918 | } | 
|  | 919 |  | 
|  | 920 | fn create_key_entry_internal( | 
|  | 921 | tx: &Transaction, | 
|  | 922 | domain: Domain, | 
|  | 923 | namespace: i64, | 
|  | 924 | ) -> Result<KeyIdGuard> { | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 925 | match domain { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 926 | Domain::APP | Domain::SELINUX => {} | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 927 | _ => { | 
|  | 928 | return Err(KsError::sys()) | 
|  | 929 | .context(format!("Domain {:?} must be either App or SELinux.", domain)); | 
|  | 930 | } | 
|  | 931 | } | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 932 | Ok(KEY_ID_LOCK.get( | 
|  | 933 | Self::insert_with_retry(|id| { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 934 | tx.execute( | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 935 | "INSERT into persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 936 | (id, key_type, domain, namespace, alias, state) | 
|  | 937 | VALUES(?, ?, ?, ?, NULL, ?);", | 
|  | 938 | params![ | 
|  | 939 | id, | 
|  | 940 | KeyType::Client, | 
|  | 941 | domain.0 as u32, | 
|  | 942 | namespace, | 
|  | 943 | KeyLifeCycle::Existing | 
|  | 944 | ], | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 945 | ) | 
|  | 946 | }) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 947 | .context("In create_key_entry_internal")?, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 948 | )) | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 949 | } | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 950 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 951 | /// Inserts a new blob and associates it with the given key id. Each blob | 
|  | 952 | /// has a sub component type and a security level. | 
|  | 953 | /// Each key can have one of each sub component type associated. If more | 
|  | 954 | /// are added only the most recent can be retrieved, and superseded blobs | 
|  | 955 | /// will get garbage collected. The security level field of components | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 956 | /// other than `SubComponentType::KEY_BLOB` are ignored. | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 957 | pub fn insert_blob( | 
|  | 958 | &mut self, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 959 | key_id: &KeyIdGuard, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 960 | sc_type: SubComponentType, | 
|  | 961 | blob: &[u8], | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 962 | ) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 963 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 964 | Self::insert_blob_internal(&tx, key_id.0, sc_type, blob) | 
|  | 965 | }) | 
|  | 966 | .context("In insert_blob.") | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 967 | } | 
|  | 968 |  | 
|  | 969 | fn insert_blob_internal( | 
|  | 970 | tx: &Transaction, | 
|  | 971 | key_id: i64, | 
|  | 972 | sc_type: SubComponentType, | 
|  | 973 | blob: &[u8], | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 974 | ) -> Result<()> { | 
|  | 975 | tx.execute( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 976 | "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob) | 
|  | 977 | VALUES (?, ?, ?);", | 
|  | 978 | params![sc_type, key_id, blob], | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 979 | ) | 
|  | 980 | .context("In insert_blob_internal: Failed to insert blob.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 981 | Ok(()) | 
|  | 982 | } | 
|  | 983 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 984 | /// Inserts a collection of key parameters into the `persistent.keyparameter` table | 
|  | 985 | /// and associates them with the given `key_id`. | 
|  | 986 | pub fn insert_keyparameter<'a>( | 
|  | 987 | &mut self, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 988 | key_id: &KeyIdGuard, | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 989 | params: impl IntoIterator<Item = &'a KeyParameter>, | 
|  | 990 | ) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 991 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 992 | Self::insert_keyparameter_internal(tx, key_id, params) | 
|  | 993 | }) | 
|  | 994 | .context("In insert_keyparameter.") | 
|  | 995 | } | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 996 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 997 | fn insert_keyparameter_internal<'a>( | 
|  | 998 | tx: &Transaction, | 
|  | 999 | key_id: &KeyIdGuard, | 
|  | 1000 | params: impl IntoIterator<Item = &'a KeyParameter>, | 
|  | 1001 | ) -> Result<()> { | 
|  | 1002 | let mut stmt = tx | 
|  | 1003 | .prepare( | 
|  | 1004 | "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level) | 
|  | 1005 | VALUES (?, ?, ?, ?);", | 
|  | 1006 | ) | 
|  | 1007 | .context("In insert_keyparameter_internal: Failed to prepare statement.")?; | 
|  | 1008 |  | 
|  | 1009 | let iter = params.into_iter(); | 
|  | 1010 | for p in iter { | 
|  | 1011 | stmt.insert(params![ | 
|  | 1012 | key_id.0, | 
|  | 1013 | p.get_tag().0, | 
|  | 1014 | p.key_parameter_value(), | 
|  | 1015 | p.security_level().0 | 
|  | 1016 | ]) | 
|  | 1017 | .with_context(|| { | 
|  | 1018 | format!("In insert_keyparameter_internal: Failed to insert {:?}", p) | 
|  | 1019 | })?; | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1020 | } | 
|  | 1021 | Ok(()) | 
|  | 1022 | } | 
|  | 1023 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1024 | /// Insert a set of key entry specific metadata into the database. | 
|  | 1025 | pub fn insert_key_metadata( | 
|  | 1026 | &mut self, | 
|  | 1027 | key_id: &KeyIdGuard, | 
|  | 1028 | metadata: &KeyMetaData, | 
|  | 1029 | ) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1030 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1031 | metadata.store_in_db(key_id.0, &tx) | 
|  | 1032 | }) | 
|  | 1033 | .context("In insert_key_metadata.") | 
|  | 1034 | } | 
|  | 1035 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1036 | /// Updates the alias column of the given key id `newid` with the given alias, | 
|  | 1037 | /// and atomically, removes the alias, domain, and namespace from another row | 
|  | 1038 | /// with the same alias-domain-namespace tuple if such row exits. | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1039 | /// Returns Ok(true) if an old key was marked unreferenced as a hint to the garbage | 
|  | 1040 | /// collector. | 
|  | 1041 | fn rebind_alias( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1042 | tx: &Transaction, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1043 | newid: &KeyIdGuard, | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1044 | alias: &str, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1045 | domain: Domain, | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1046 | namespace: i64, | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1047 | ) -> Result<bool> { | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1048 | match domain { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1049 | Domain::APP | Domain::SELINUX => {} | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1050 | _ => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1051 | return Err(KsError::sys()).context(format!( | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1052 | "In rebind_alias: Domain {:?} must be either App or SELinux.", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1053 | domain | 
|  | 1054 | )); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1055 | } | 
|  | 1056 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1057 | let updated = tx | 
|  | 1058 | .execute( | 
|  | 1059 | "UPDATE persistent.keyentry | 
|  | 1060 | SET alias = NULL, domain = NULL, namespace = NULL, state = ? | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1061 | WHERE alias = ? AND domain = ? AND namespace = ?;", | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1062 | params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace], | 
|  | 1063 | ) | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1064 | .context("In rebind_alias: Failed to rebind existing entry.")?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1065 | let result = tx | 
|  | 1066 | .execute( | 
|  | 1067 | "UPDATE persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1068 | SET alias = ?, state = ? | 
|  | 1069 | WHERE id = ? AND domain = ? AND namespace = ? AND state = ?;", | 
|  | 1070 | params![ | 
|  | 1071 | alias, | 
|  | 1072 | KeyLifeCycle::Live, | 
|  | 1073 | newid.0, | 
|  | 1074 | domain.0 as u32, | 
|  | 1075 | namespace, | 
|  | 1076 | KeyLifeCycle::Existing | 
|  | 1077 | ], | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1078 | ) | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1079 | .context("In rebind_alias: Failed to set alias.")?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1080 | if result != 1 { | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1081 | return Err(KsError::sys()).context(format!( | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1082 | "In rebind_alias: Expected to update a single entry but instead updated {}.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1083 | result | 
|  | 1084 | )); | 
|  | 1085 | } | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1086 | Ok(updated != 0) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1087 | } | 
|  | 1088 |  | 
|  | 1089 | /// Store a new key in a single transaction. | 
|  | 1090 | /// The function creates a new key entry, populates the blob, key parameter, and metadata | 
|  | 1091 | /// fields, and rebinds the given alias to the new key. | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1092 | /// The boolean returned is a hint for the garbage collector. If true, a key was replaced, | 
|  | 1093 | /// is now unreferenced and needs to be collected. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1094 | pub fn store_new_key<'a>( | 
|  | 1095 | &mut self, | 
|  | 1096 | key: KeyDescriptor, | 
|  | 1097 | params: impl IntoIterator<Item = &'a KeyParameter>, | 
|  | 1098 | blob: &[u8], | 
|  | 1099 | cert: Option<&[u8]>, | 
|  | 1100 | cert_chain: Option<&[u8]>, | 
|  | 1101 | metadata: &KeyMetaData, | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1102 | ) -> Result<(bool, KeyIdGuard)> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1103 | let (alias, domain, namespace) = match key { | 
|  | 1104 | KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None } | 
|  | 1105 | | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => { | 
|  | 1106 | (alias, key.domain, nspace) | 
|  | 1107 | } | 
|  | 1108 | _ => { | 
|  | 1109 | return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)) | 
|  | 1110 | .context("In store_new_key: Need alias and domain must be APP or SELINUX.") | 
|  | 1111 | } | 
|  | 1112 | }; | 
|  | 1113 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1114 | let key_id = Self::create_key_entry_internal(tx, domain, namespace) | 
|  | 1115 | .context("Trying to create new key entry.")?; | 
|  | 1116 | Self::insert_blob_internal(tx, key_id.id(), SubComponentType::KEY_BLOB, blob) | 
|  | 1117 | .context("Trying to insert the key blob.")?; | 
|  | 1118 | if let Some(cert) = cert { | 
|  | 1119 | Self::insert_blob_internal(tx, key_id.id(), SubComponentType::CERT, cert) | 
|  | 1120 | .context("Trying to insert the certificate.")?; | 
|  | 1121 | } | 
|  | 1122 | if let Some(cert_chain) = cert_chain { | 
|  | 1123 | Self::insert_blob_internal( | 
|  | 1124 | tx, | 
|  | 1125 | key_id.id(), | 
|  | 1126 | SubComponentType::CERT_CHAIN, | 
|  | 1127 | cert_chain, | 
|  | 1128 | ) | 
|  | 1129 | .context("Trying to insert the certificate chain.")?; | 
|  | 1130 | } | 
|  | 1131 | Self::insert_keyparameter_internal(tx, &key_id, params) | 
|  | 1132 | .context("Trying to insert key parameters.")?; | 
|  | 1133 | metadata.store_in_db(key_id.id(), tx).context("Tryin to insert key metadata.")?; | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1134 | let need_gc = Self::rebind_alias(tx, &key_id, &alias, domain, namespace) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1135 | .context("Trying to rebind alias.")?; | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1136 | Ok((need_gc, key_id)) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1137 | }) | 
|  | 1138 | .context("In store_new_key.") | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1139 | } | 
|  | 1140 |  | 
|  | 1141 | // Helper function loading the key_id given the key descriptor | 
|  | 1142 | // tuple comprising domain, namespace, and alias. | 
|  | 1143 | // Requires a valid transaction. | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1144 | fn load_key_entry_id(tx: &Transaction, key: &KeyDescriptor, key_type: KeyType) -> Result<i64> { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1145 | let alias = key | 
|  | 1146 | .alias | 
|  | 1147 | .as_ref() | 
|  | 1148 | .map_or_else(|| Err(KsError::sys()), Ok) | 
|  | 1149 | .context("In load_key_entry_id: Alias must be specified.")?; | 
|  | 1150 | let mut stmt = tx | 
|  | 1151 | .prepare( | 
|  | 1152 | "SELECT id FROM persistent.keyentry | 
|  | 1153 | WHERE | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1154 | key_type =  ? | 
|  | 1155 | AND domain = ? | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1156 | AND namespace = ? | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1157 | AND alias = ? | 
|  | 1158 | AND state = ?;", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1159 | ) | 
|  | 1160 | .context("In load_key_entry_id: Failed to select from keyentry table.")?; | 
|  | 1161 | let mut rows = stmt | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1162 | .query(params![key_type, key.domain.0 as u32, key.nspace, alias, KeyLifeCycle::Live]) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1163 | .context("In load_key_entry_id: Failed to read from keyentry table.")?; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1164 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1165 | row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)? | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1166 | .get(0) | 
|  | 1167 | .context("Failed to unpack id.") | 
|  | 1168 | }) | 
|  | 1169 | .context("In load_key_entry_id.") | 
|  | 1170 | } | 
|  | 1171 |  | 
|  | 1172 | /// This helper function completes the access tuple of a key, which is required | 
|  | 1173 | /// to perform access control. The strategy depends on the `domain` field in the | 
|  | 1174 | /// key descriptor. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1175 | /// * Domain::SELINUX: The access tuple is complete and this function only loads | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1176 | ///       the key_id for further processing. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1177 | /// * Domain::APP: Like Domain::SELINUX, but the tuple is completed by `caller_uid` | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1178 | ///       which serves as the namespace. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1179 | /// * Domain::GRANT: The grant table is queried for the `key_id` and the | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1180 | ///       `access_vector`. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1181 | /// * Domain::KEY_ID: The keyentry table is queried for the owning `domain` and | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1182 | ///       `namespace`. | 
|  | 1183 | /// In each case the information returned is sufficient to perform the access | 
|  | 1184 | /// check and the key id can be used to load further key artifacts. | 
|  | 1185 | fn load_access_tuple( | 
|  | 1186 | tx: &Transaction, | 
|  | 1187 | key: KeyDescriptor, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1188 | key_type: KeyType, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1189 | caller_uid: u32, | 
|  | 1190 | ) -> Result<(i64, KeyDescriptor, Option<KeyPermSet>)> { | 
|  | 1191 | match key.domain { | 
|  | 1192 | // Domain App or SELinux. In this case we load the key_id from | 
|  | 1193 | // the keyentry database for further loading of key components. | 
|  | 1194 | // We already have the full access tuple to perform access control. | 
|  | 1195 | // The only distinction is that we use the caller_uid instead | 
|  | 1196 | // of the caller supplied namespace if the domain field is | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1197 | // Domain::APP. | 
|  | 1198 | Domain::APP | Domain::SELINUX => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1199 | let mut access_key = key; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1200 | if access_key.domain == Domain::APP { | 
|  | 1201 | access_key.nspace = caller_uid as i64; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1202 | } | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1203 | let key_id = Self::load_key_entry_id(&tx, &access_key, key_type) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1204 | .with_context(|| format!("With key.domain = {:?}.", access_key.domain))?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1205 |  | 
|  | 1206 | Ok((key_id, access_key, None)) | 
|  | 1207 | } | 
|  | 1208 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1209 | // Domain::GRANT. In this case we load the key_id and the access_vector | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1210 | // from the grant table. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1211 | Domain::GRANT => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1212 | let mut stmt = tx | 
|  | 1213 | .prepare( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1214 | "SELECT keyentryid, access_vector FROM persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1215 | WHERE grantee = ? AND id = ?;", | 
|  | 1216 | ) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1217 | .context("Domain::GRANT prepare statement failed")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1218 | let mut rows = stmt | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1219 | .query(params![caller_uid as i64, key.nspace]) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1220 | .context("Domain:Grant: query failed.")?; | 
|  | 1221 | let (key_id, access_vector): (i64, i32) = | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1222 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1223 | let r = | 
|  | 1224 | row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1225 | Ok(( | 
|  | 1226 | r.get(0).context("Failed to unpack key_id.")?, | 
|  | 1227 | r.get(1).context("Failed to unpack access_vector.")?, | 
|  | 1228 | )) | 
|  | 1229 | }) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1230 | .context("Domain::GRANT.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1231 | Ok((key_id, key, Some(access_vector.into()))) | 
|  | 1232 | } | 
|  | 1233 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1234 | // Domain::KEY_ID. In this case we load the domain and namespace from the | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1235 | // keyentry database because we need them for access control. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1236 | Domain::KEY_ID => { | 
| Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 1237 | let (domain, namespace): (Domain, i64) = { | 
|  | 1238 | let mut stmt = tx | 
|  | 1239 | .prepare( | 
|  | 1240 | "SELECT domain, namespace FROM persistent.keyentry | 
|  | 1241 | WHERE | 
|  | 1242 | id = ? | 
|  | 1243 | AND state = ?;", | 
|  | 1244 | ) | 
|  | 1245 | .context("Domain::KEY_ID: prepare statement failed")?; | 
|  | 1246 | let mut rows = stmt | 
|  | 1247 | .query(params![key.nspace, KeyLifeCycle::Live]) | 
|  | 1248 | .context("Domain::KEY_ID: query failed.")?; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1249 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1250 | let r = | 
|  | 1251 | row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1252 | Ok(( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1253 | Domain(r.get(0).context("Failed to unpack domain.")?), | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1254 | r.get(1).context("Failed to unpack namespace.")?, | 
|  | 1255 | )) | 
|  | 1256 | }) | 
| Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 1257 | .context("Domain::KEY_ID.")? | 
|  | 1258 | }; | 
|  | 1259 |  | 
|  | 1260 | // We may use a key by id after loading it by grant. | 
|  | 1261 | // In this case we have to check if the caller has a grant for this particular | 
|  | 1262 | // key. We can skip this if we already know that the caller is the owner. | 
|  | 1263 | // But we cannot know this if domain is anything but App. E.g. in the case | 
|  | 1264 | // of Domain::SELINUX we have to speculatively check for grants because we have to | 
|  | 1265 | // consult the SEPolicy before we know if the caller is the owner. | 
|  | 1266 | let access_vector: Option<KeyPermSet> = | 
|  | 1267 | if domain != Domain::APP || namespace != caller_uid as i64 { | 
|  | 1268 | let access_vector: Option<i32> = tx | 
|  | 1269 | .query_row( | 
|  | 1270 | "SELECT access_vector FROM persistent.grant | 
|  | 1271 | WHERE grantee = ? AND keyentryid = ?;", | 
|  | 1272 | params![caller_uid as i64, key.nspace], | 
|  | 1273 | |row| row.get(0), | 
|  | 1274 | ) | 
|  | 1275 | .optional() | 
|  | 1276 | .context("Domain::KEY_ID: query grant failed.")?; | 
|  | 1277 | access_vector.map(|p| p.into()) | 
|  | 1278 | } else { | 
|  | 1279 | None | 
|  | 1280 | }; | 
|  | 1281 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1282 | let key_id = key.nspace; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1283 | let mut access_key = key; | 
|  | 1284 | access_key.domain = domain; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1285 | access_key.nspace = namespace; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1286 |  | 
| Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 1287 | Ok((key_id, access_key, access_vector)) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1288 | } | 
|  | 1289 | _ => Err(anyhow!(KsError::sys())), | 
|  | 1290 | } | 
|  | 1291 | } | 
|  | 1292 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1293 | fn load_blob_components( | 
|  | 1294 | key_id: i64, | 
|  | 1295 | load_bits: KeyEntryLoadBits, | 
|  | 1296 | tx: &Transaction, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1297 | ) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1298 | let mut stmt = tx | 
|  | 1299 | .prepare( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1300 | "SELECT MAX(id), subcomponent_type, blob FROM persistent.blobentry | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1301 | WHERE keyentryid = ? GROUP BY subcomponent_type;", | 
|  | 1302 | ) | 
|  | 1303 | .context("In load_blob_components: prepare statement failed.")?; | 
|  | 1304 |  | 
|  | 1305 | let mut rows = | 
|  | 1306 | stmt.query(params![key_id]).context("In load_blob_components: query failed.")?; | 
|  | 1307 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1308 | let mut km_blob: Option<Vec<u8>> = None; | 
|  | 1309 | let mut cert_blob: Option<Vec<u8>> = None; | 
|  | 1310 | let mut cert_chain_blob: Option<Vec<u8>> = None; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1311 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1312 | let sub_type: SubComponentType = | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1313 | row.get(1).context("Failed to extract subcomponent_type.")?; | 
|  | 1314 | match (sub_type, load_bits.load_public(), load_bits.load_km()) { | 
|  | 1315 | (SubComponentType::KEY_BLOB, _, true) => { | 
|  | 1316 | km_blob = Some(row.get(2).context("Failed to extract KM blob.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1317 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1318 | (SubComponentType::CERT, true, _) => { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1319 | cert_blob = | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1320 | Some(row.get(2).context("Failed to extract public certificate blob.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1321 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1322 | (SubComponentType::CERT_CHAIN, true, _) => { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1323 | cert_chain_blob = | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1324 | Some(row.get(2).context("Failed to extract certificate chain blob.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1325 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1326 | (SubComponentType::CERT, _, _) | 
|  | 1327 | | (SubComponentType::CERT_CHAIN, _, _) | 
|  | 1328 | | (SubComponentType::KEY_BLOB, _, _) => {} | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1329 | _ => Err(KsError::sys()).context("Unknown subcomponent type.")?, | 
|  | 1330 | } | 
|  | 1331 | Ok(()) | 
|  | 1332 | }) | 
|  | 1333 | .context("In load_blob_components.")?; | 
|  | 1334 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1335 | Ok((km_blob, cert_blob, cert_chain_blob)) | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1336 | } | 
|  | 1337 |  | 
|  | 1338 | fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> { | 
|  | 1339 | let mut stmt = tx | 
|  | 1340 | .prepare( | 
|  | 1341 | "SELECT tag, data, security_level from persistent.keyparameter | 
|  | 1342 | WHERE keyentryid = ?;", | 
|  | 1343 | ) | 
|  | 1344 | .context("In load_key_parameters: prepare statement failed.")?; | 
|  | 1345 |  | 
|  | 1346 | let mut parameters: Vec<KeyParameter> = Vec::new(); | 
|  | 1347 |  | 
|  | 1348 | let mut rows = | 
|  | 1349 | stmt.query(params![key_id]).context("In load_key_parameters: query failed.")?; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1350 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1351 | let tag = Tag(row.get(0).context("Failed to read tag.")?); | 
|  | 1352 | let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1353 | parameters.push( | 
|  | 1354 | KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level) | 
|  | 1355 | .context("Failed to read KeyParameter.")?, | 
|  | 1356 | ); | 
|  | 1357 | Ok(()) | 
|  | 1358 | }) | 
|  | 1359 | .context("In load_key_parameters.")?; | 
|  | 1360 |  | 
|  | 1361 | Ok(parameters) | 
|  | 1362 | } | 
|  | 1363 |  | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 1364 | /// Decrements the usage count of a limited use key. This function first checks whether the | 
|  | 1365 | /// usage has been exhausted, if not, decreases the usage count. If the usage count reaches | 
|  | 1366 | /// zero, the key also gets marked unreferenced and scheduled for deletion. | 
|  | 1367 | /// Returns Ok(true) if the key was marked unreferenced as a hint to the garbage collector. | 
|  | 1368 | pub fn check_and_update_key_usage_count(&mut self, key_id: i64) -> Result<bool> { | 
|  | 1369 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1370 | let limit: Option<i32> = tx | 
|  | 1371 | .query_row( | 
|  | 1372 | "SELECT data FROM persistent.keyparameter WHERE keyentryid = ? AND tag = ?;", | 
|  | 1373 | params![key_id, Tag::USAGE_COUNT_LIMIT.0], | 
|  | 1374 | |row| row.get(0), | 
|  | 1375 | ) | 
|  | 1376 | .optional() | 
|  | 1377 | .context("Trying to load usage count")?; | 
|  | 1378 |  | 
|  | 1379 | let limit = limit | 
|  | 1380 | .ok_or(KsError::Km(ErrorCode::INVALID_KEY_BLOB)) | 
|  | 1381 | .context("The Key no longer exists. Key is exhausted.")?; | 
|  | 1382 |  | 
|  | 1383 | tx.execute( | 
|  | 1384 | "UPDATE persistent.keyparameter | 
|  | 1385 | SET data = data - 1 | 
|  | 1386 | WHERE keyentryid = ? AND tag = ? AND data > 0;", | 
|  | 1387 | params![key_id, Tag::USAGE_COUNT_LIMIT.0], | 
|  | 1388 | ) | 
|  | 1389 | .context("Failed to update key usage count.")?; | 
|  | 1390 |  | 
|  | 1391 | match limit { | 
|  | 1392 | 1 => Self::mark_unreferenced(tx, key_id) | 
|  | 1393 | .context("Trying to mark limited use key for deletion."), | 
|  | 1394 | 0 => Err(KsError::Km(ErrorCode::INVALID_KEY_BLOB)).context("Key is exhausted."), | 
|  | 1395 | _ => Ok(false), | 
|  | 1396 | } | 
|  | 1397 | }) | 
|  | 1398 | .context("In check_and_update_key_usage_count.") | 
|  | 1399 | } | 
|  | 1400 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1401 | /// Load a key entry by the given key descriptor. | 
|  | 1402 | /// It uses the `check_permission` callback to verify if the access is allowed | 
|  | 1403 | /// given the key access tuple read from the database using `load_access_tuple`. | 
|  | 1404 | /// With `load_bits` the caller may specify which blobs shall be loaded from | 
|  | 1405 | /// the blob database. | 
|  | 1406 | pub fn load_key_entry( | 
|  | 1407 | &mut self, | 
|  | 1408 | key: KeyDescriptor, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1409 | key_type: KeyType, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1410 | load_bits: KeyEntryLoadBits, | 
|  | 1411 | caller_uid: u32, | 
|  | 1412 | check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1413 | ) -> Result<(KeyIdGuard, KeyEntry)> { | 
|  | 1414 | // KEY ID LOCK 1/2 | 
|  | 1415 | // If we got a key descriptor with a key id we can get the lock right away. | 
|  | 1416 | // Otherwise we have to defer it until we know the key id. | 
|  | 1417 | let key_id_guard = match key.domain { | 
|  | 1418 | Domain::KEY_ID => Some(KEY_ID_LOCK.get(key.nspace)), | 
|  | 1419 | _ => None, | 
|  | 1420 | }; | 
|  | 1421 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1422 | let tx = self | 
|  | 1423 | .conn | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1424 | .unchecked_transaction() | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1425 | .context("In load_key_entry: Failed to initialize transaction.")?; | 
|  | 1426 |  | 
|  | 1427 | // Load the key_id and complete the access control tuple. | 
|  | 1428 | let (key_id, access_key_descriptor, access_vector) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1429 | Self::load_access_tuple(&tx, key, key_type, caller_uid) | 
|  | 1430 | .context("In load_key_entry.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1431 |  | 
|  | 1432 | // Perform access control. It is vital that we return here if the permission is denied. | 
|  | 1433 | // So do not touch that '?' at the end. | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1434 | check_permission(&access_key_descriptor, access_vector).context("In load_key_entry.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1435 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1436 | // KEY ID LOCK 2/2 | 
|  | 1437 | // If we did not get a key id lock by now, it was because we got a key descriptor | 
|  | 1438 | // without a key id. At this point we got the key id, so we can try and get a lock. | 
|  | 1439 | // However, we cannot block here, because we are in the middle of the transaction. | 
|  | 1440 | // So first we try to get the lock non blocking. If that fails, we roll back the | 
|  | 1441 | // transaction and block until we get the lock. After we successfully got the lock, | 
|  | 1442 | // we start a new transaction and load the access tuple again. | 
|  | 1443 | // | 
|  | 1444 | // We don't need to perform access control again, because we already established | 
|  | 1445 | // that the caller had access to the given key. But we need to make sure that the | 
|  | 1446 | // key id still exists. So we have to load the key entry by key id this time. | 
|  | 1447 | let (key_id_guard, tx) = match key_id_guard { | 
|  | 1448 | None => match KEY_ID_LOCK.try_get(key_id) { | 
|  | 1449 | None => { | 
|  | 1450 | // Roll back the transaction. | 
|  | 1451 | tx.rollback().context("In load_key_entry: Failed to roll back transaction.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1452 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1453 | // Block until we have a key id lock. | 
|  | 1454 | let key_id_guard = KEY_ID_LOCK.get(key_id); | 
|  | 1455 |  | 
|  | 1456 | // Create a new transaction. | 
|  | 1457 | let tx = self.conn.unchecked_transaction().context( | 
|  | 1458 | "In load_key_entry: Failed to initialize transaction. (deferred key lock)", | 
|  | 1459 | )?; | 
|  | 1460 |  | 
|  | 1461 | Self::load_access_tuple( | 
|  | 1462 | &tx, | 
|  | 1463 | // This time we have to load the key by the retrieved key id, because the | 
|  | 1464 | // alias may have been rebound after we rolled back the transaction. | 
|  | 1465 | KeyDescriptor { | 
|  | 1466 | domain: Domain::KEY_ID, | 
|  | 1467 | nspace: key_id, | 
|  | 1468 | ..Default::default() | 
|  | 1469 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1470 | key_type, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1471 | caller_uid, | 
|  | 1472 | ) | 
|  | 1473 | .context("In load_key_entry. (deferred key lock)")?; | 
|  | 1474 | (key_id_guard, tx) | 
|  | 1475 | } | 
|  | 1476 | Some(l) => (l, tx), | 
|  | 1477 | }, | 
|  | 1478 | Some(key_id_guard) => (key_id_guard, tx), | 
|  | 1479 | }; | 
|  | 1480 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1481 | let key_entry = Self::load_key_components(&tx, load_bits, key_id_guard.id()) | 
|  | 1482 | .context("In load_key_entry.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1483 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1484 | tx.commit().context("In load_key_entry: Failed to commit transaction.")?; | 
|  | 1485 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1486 | Ok((key_id_guard, key_entry)) | 
|  | 1487 | } | 
|  | 1488 |  | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1489 | fn mark_unreferenced(tx: &Transaction, key_id: i64) -> Result<bool> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1490 | let updated = tx | 
|  | 1491 | .execute( | 
|  | 1492 | "UPDATE persistent.keyentry SET state = ? WHERE id = ?;", | 
|  | 1493 | params![KeyLifeCycle::Unreferenced, key_id], | 
|  | 1494 | ) | 
|  | 1495 | .context("In mark_unreferenced: Failed to update state of key entry.")?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1496 | tx.execute("DELETE from persistent.grant WHERE keyentryid = ?;", params![key_id]) | 
|  | 1497 | .context("In mark_unreferenced: Failed to drop grants.")?; | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1498 | Ok(updated != 0) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1499 | } | 
|  | 1500 |  | 
|  | 1501 | /// Marks the given key as unreferenced and removes all of the grants to this key. | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1502 | /// Returns Ok(true) if a key was marked unreferenced as a hint for the garbage collector. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1503 | pub fn unbind_key( | 
|  | 1504 | &mut self, | 
|  | 1505 | key: KeyDescriptor, | 
|  | 1506 | key_type: KeyType, | 
|  | 1507 | caller_uid: u32, | 
|  | 1508 | check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>, | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1509 | ) -> Result<bool> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1510 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1511 | let (key_id, access_key_descriptor, access_vector) = | 
|  | 1512 | Self::load_access_tuple(tx, key, key_type, caller_uid) | 
|  | 1513 | .context("Trying to get access tuple.")?; | 
|  | 1514 |  | 
|  | 1515 | // Perform access control. It is vital that we return here if the permission is denied. | 
|  | 1516 | // So do not touch that '?' at the end. | 
|  | 1517 | check_permission(&access_key_descriptor, access_vector) | 
|  | 1518 | .context("While checking permission.")?; | 
|  | 1519 |  | 
|  | 1520 | Self::mark_unreferenced(tx, key_id).context("Trying to mark the key unreferenced.") | 
|  | 1521 | }) | 
|  | 1522 | .context("In unbind_key.") | 
|  | 1523 | } | 
|  | 1524 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1525 | fn load_key_components( | 
|  | 1526 | tx: &Transaction, | 
|  | 1527 | load_bits: KeyEntryLoadBits, | 
|  | 1528 | key_id: i64, | 
|  | 1529 | ) -> Result<KeyEntry> { | 
|  | 1530 | let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?; | 
|  | 1531 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1532 | let (km_blob, cert_blob, cert_chain_blob) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1533 | Self::load_blob_components(key_id, load_bits, &tx) | 
|  | 1534 | .context("In load_key_components.")?; | 
|  | 1535 |  | 
|  | 1536 | let parameters = | 
|  | 1537 | Self::load_key_parameters(key_id, &tx).context("In load_key_components.")?; | 
|  | 1538 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1539 | // Extract the security level by checking the security level of the origin tag. | 
|  | 1540 | // Super keys don't have key parameters so we use security_level software by default. | 
|  | 1541 | let sec_level = parameters | 
|  | 1542 | .iter() | 
|  | 1543 | .find_map(|k| match k.get_tag() { | 
|  | 1544 | Tag::ORIGIN => Some(*k.security_level()), | 
|  | 1545 | _ => None, | 
|  | 1546 | }) | 
|  | 1547 | .unwrap_or(SecurityLevel::SOFTWARE); | 
|  | 1548 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1549 | Ok(KeyEntry { | 
|  | 1550 | id: key_id, | 
|  | 1551 | km_blob, | 
|  | 1552 | cert: cert_blob, | 
|  | 1553 | cert_chain: cert_chain_blob, | 
|  | 1554 | sec_level, | 
|  | 1555 | parameters, | 
|  | 1556 | metadata, | 
|  | 1557 | }) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1558 | } | 
|  | 1559 |  | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 1560 | /// Returns a list of KeyDescriptors in the selected domain/namespace. | 
|  | 1561 | /// The key descriptors will have the domain, nspace, and alias field set. | 
|  | 1562 | /// Domain must be APP or SELINUX, the caller must make sure of that. | 
|  | 1563 | pub fn list(&mut self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> { | 
|  | 1564 | let mut stmt = self | 
|  | 1565 | .conn | 
|  | 1566 | .prepare( | 
|  | 1567 | "SELECT alias FROM persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1568 | WHERE domain = ? AND namespace = ? AND alias IS NOT NULL AND state = ?;", | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 1569 | ) | 
|  | 1570 | .context("In list: Failed to prepare.")?; | 
|  | 1571 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1572 | let mut rows = stmt | 
|  | 1573 | .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live]) | 
|  | 1574 | .context("In list: Failed to query.")?; | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 1575 |  | 
|  | 1576 | let mut descriptors: Vec<KeyDescriptor> = Vec::new(); | 
|  | 1577 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
|  | 1578 | descriptors.push(KeyDescriptor { | 
|  | 1579 | domain, | 
|  | 1580 | nspace: namespace, | 
|  | 1581 | alias: Some(row.get(0).context("Trying to extract alias.")?), | 
|  | 1582 | blob: None, | 
|  | 1583 | }); | 
|  | 1584 | Ok(()) | 
|  | 1585 | }) | 
|  | 1586 | .context("In list.")?; | 
|  | 1587 | Ok(descriptors) | 
|  | 1588 | } | 
|  | 1589 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1590 | /// Adds a grant to the grant table. | 
|  | 1591 | /// Like `load_key_entry` this function loads the access tuple before | 
|  | 1592 | /// it uses the callback for a permission check. Upon success, | 
|  | 1593 | /// it inserts the `grantee_uid`, `key_id`, and `access_vector` into the | 
|  | 1594 | /// grant table. The new row will have a randomized id, which is used as | 
|  | 1595 | /// grant id in the namespace field of the resulting KeyDescriptor. | 
|  | 1596 | pub fn grant( | 
|  | 1597 | &mut self, | 
|  | 1598 | key: KeyDescriptor, | 
|  | 1599 | caller_uid: u32, | 
|  | 1600 | grantee_uid: u32, | 
|  | 1601 | access_vector: KeyPermSet, | 
|  | 1602 | check_permission: impl FnOnce(&KeyDescriptor, &KeyPermSet) -> Result<()>, | 
|  | 1603 | ) -> Result<KeyDescriptor> { | 
|  | 1604 | let tx = self | 
|  | 1605 | .conn | 
|  | 1606 | .transaction_with_behavior(TransactionBehavior::Immediate) | 
|  | 1607 | .context("In grant: Failed to initialize transaction.")?; | 
|  | 1608 |  | 
|  | 1609 | // Load the key_id and complete the access control tuple. | 
|  | 1610 | // We ignore the access vector here because grants cannot be granted. | 
|  | 1611 | // The access vector returned here expresses the permissions the | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1612 | // grantee has if key.domain == Domain::GRANT. But this vector | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1613 | // cannot include the grant permission by design, so there is no way the | 
|  | 1614 | // subsequent permission check can pass. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1615 | // We could check key.domain == Domain::GRANT and fail early. | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1616 | // But even if we load the access tuple by grant here, the permission | 
|  | 1617 | // check denies the attempt to create a grant by grant descriptor. | 
|  | 1618 | let (key_id, access_key_descriptor, _) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1619 | Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid).context("In grant")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1620 |  | 
|  | 1621 | // Perform access control. It is vital that we return here if the permission | 
|  | 1622 | // was denied. So do not touch that '?' at the end of the line. | 
|  | 1623 | // This permission check checks if the caller has the grant permission | 
|  | 1624 | // for the given key and in addition to all of the permissions | 
|  | 1625 | // expressed in `access_vector`. | 
|  | 1626 | check_permission(&access_key_descriptor, &access_vector) | 
|  | 1627 | .context("In grant: check_permission failed.")?; | 
|  | 1628 |  | 
|  | 1629 | let grant_id = if let Some(grant_id) = tx | 
|  | 1630 | .query_row( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1631 | "SELECT id FROM persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1632 | WHERE keyentryid = ? AND grantee = ?;", | 
|  | 1633 | params![key_id, grantee_uid], | 
|  | 1634 | |row| row.get(0), | 
|  | 1635 | ) | 
|  | 1636 | .optional() | 
|  | 1637 | .context("In grant: Failed get optional existing grant id.")? | 
|  | 1638 | { | 
|  | 1639 | tx.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1640 | "UPDATE persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1641 | SET access_vector = ? | 
|  | 1642 | WHERE id = ?;", | 
|  | 1643 | params![i32::from(access_vector), grant_id], | 
|  | 1644 | ) | 
|  | 1645 | .context("In grant: Failed to update existing grant.")?; | 
|  | 1646 | grant_id | 
|  | 1647 | } else { | 
| Joel Galenson | 845f74b | 2020-09-09 14:11:55 -0700 | [diff] [blame] | 1648 | Self::insert_with_retry(|id| { | 
|  | 1649 | tx.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1650 | "INSERT INTO persistent.grant (id, grantee, keyentryid, access_vector) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1651 | VALUES (?, ?, ?, ?);", | 
| Joel Galenson | 845f74b | 2020-09-09 14:11:55 -0700 | [diff] [blame] | 1652 | params![id, grantee_uid, key_id, i32::from(access_vector)], | 
|  | 1653 | ) | 
|  | 1654 | }) | 
|  | 1655 | .context("In grant")? | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1656 | }; | 
|  | 1657 | tx.commit().context("In grant: failed to commit transaction.")?; | 
|  | 1658 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1659 | Ok(KeyDescriptor { domain: Domain::GRANT, nspace: grant_id, alias: None, blob: None }) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1660 | } | 
|  | 1661 |  | 
|  | 1662 | /// This function checks permissions like `grant` and `load_key_entry` | 
|  | 1663 | /// before removing a grant from the grant table. | 
|  | 1664 | pub fn ungrant( | 
|  | 1665 | &mut self, | 
|  | 1666 | key: KeyDescriptor, | 
|  | 1667 | caller_uid: u32, | 
|  | 1668 | grantee_uid: u32, | 
|  | 1669 | check_permission: impl FnOnce(&KeyDescriptor) -> Result<()>, | 
|  | 1670 | ) -> Result<()> { | 
|  | 1671 | let tx = self | 
|  | 1672 | .conn | 
|  | 1673 | .transaction_with_behavior(TransactionBehavior::Immediate) | 
|  | 1674 | .context("In ungrant: Failed to initialize transaction.")?; | 
|  | 1675 |  | 
|  | 1676 | // Load the key_id and complete the access control tuple. | 
|  | 1677 | // We ignore the access vector here because grants cannot be granted. | 
|  | 1678 | let (key_id, access_key_descriptor, _) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1679 | Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid) | 
|  | 1680 | .context("In ungrant.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1681 |  | 
|  | 1682 | // Perform access control. We must return here if the permission | 
|  | 1683 | // was denied. So do not touch the '?' at the end of this line. | 
|  | 1684 | check_permission(&access_key_descriptor).context("In grant: check_permission failed.")?; | 
|  | 1685 |  | 
|  | 1686 | tx.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1687 | "DELETE FROM persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1688 | WHERE keyentryid = ? AND grantee = ?;", | 
|  | 1689 | params![key_id, grantee_uid], | 
|  | 1690 | ) | 
|  | 1691 | .context("Failed to delete grant.")?; | 
|  | 1692 |  | 
|  | 1693 | tx.commit().context("In ungrant: failed to commit transaction.")?; | 
|  | 1694 |  | 
|  | 1695 | Ok(()) | 
|  | 1696 | } | 
|  | 1697 |  | 
| Joel Galenson | 845f74b | 2020-09-09 14:11:55 -0700 | [diff] [blame] | 1698 | // Generates a random id and passes it to the given function, which will | 
|  | 1699 | // try to insert it into a database.  If that insertion fails, retry; | 
|  | 1700 | // otherwise return the id. | 
|  | 1701 | fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> { | 
|  | 1702 | loop { | 
|  | 1703 | let newid: i64 = random(); | 
|  | 1704 | match inserter(newid) { | 
|  | 1705 | // If the id already existed, try again. | 
|  | 1706 | Err(rusqlite::Error::SqliteFailure( | 
|  | 1707 | libsqlite3_sys::Error { | 
|  | 1708 | code: libsqlite3_sys::ErrorCode::ConstraintViolation, | 
|  | 1709 | extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE, | 
|  | 1710 | }, | 
|  | 1711 | _, | 
|  | 1712 | )) => (), | 
|  | 1713 | Err(e) => { | 
|  | 1714 | return Err(e).context("In insert_with_retry: failed to insert into database.") | 
|  | 1715 | } | 
|  | 1716 | _ => return Ok(newid), | 
|  | 1717 | } | 
|  | 1718 | } | 
|  | 1719 | } | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1720 |  | 
|  | 1721 | /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table | 
|  | 1722 | pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> { | 
|  | 1723 | self.conn | 
|  | 1724 | .execute( | 
|  | 1725 | "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id, | 
|  | 1726 | authenticator_type, timestamp, mac, time_received) VALUES(?, ?, ?, ?, ?, ?, ?);", | 
|  | 1727 | params![ | 
|  | 1728 | auth_token.challenge, | 
|  | 1729 | auth_token.userId, | 
|  | 1730 | auth_token.authenticatorId, | 
|  | 1731 | auth_token.authenticatorType.0 as i32, | 
|  | 1732 | auth_token.timestamp.milliSeconds as i64, | 
|  | 1733 | auth_token.mac, | 
|  | 1734 | MonotonicRawTime::now(), | 
|  | 1735 | ], | 
|  | 1736 | ) | 
|  | 1737 | .context("In insert_auth_token: failed to insert auth token into the database")?; | 
|  | 1738 | Ok(()) | 
|  | 1739 | } | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1740 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1741 | /// Find the newest auth token matching the given predicate. | 
|  | 1742 | pub fn find_auth_token_entry<F>( | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1743 | &mut self, | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1744 | p: F, | 
|  | 1745 | ) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>> | 
|  | 1746 | where | 
|  | 1747 | F: Fn(&AuthTokenEntry) -> bool, | 
|  | 1748 | { | 
|  | 1749 | self.with_transaction(TransactionBehavior::Deferred, |tx| { | 
|  | 1750 | let mut stmt = tx | 
|  | 1751 | .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;") | 
|  | 1752 | .context("Prepare statement failed.")?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1753 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1754 | let mut rows = stmt.query(NO_PARAMS).context("Failed to query.")?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1755 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1756 | while let Some(row) = rows.next().context("Failed to get next row.")? { | 
|  | 1757 | let entry = AuthTokenEntry::new( | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1758 | HardwareAuthToken { | 
|  | 1759 | challenge: row.get(1)?, | 
|  | 1760 | userId: row.get(2)?, | 
|  | 1761 | authenticatorId: row.get(3)?, | 
|  | 1762 | authenticatorType: HardwareAuthenticatorType(row.get(4)?), | 
|  | 1763 | timestamp: Timestamp { milliSeconds: row.get(5)? }, | 
|  | 1764 | mac: row.get(6)?, | 
|  | 1765 | }, | 
|  | 1766 | row.get(7)?, | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1767 | ); | 
|  | 1768 | if p(&entry) { | 
|  | 1769 | return Ok(Some(( | 
|  | 1770 | entry, | 
|  | 1771 | Self::get_last_off_body(tx) | 
|  | 1772 | .context("In find_auth_token_entry: Trying to get last off body")?, | 
|  | 1773 | ))); | 
|  | 1774 | } | 
|  | 1775 | } | 
|  | 1776 | Ok(None) | 
|  | 1777 | }) | 
|  | 1778 | .context("In find_auth_token_entry.") | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1779 | } | 
|  | 1780 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1781 | /// Insert last_off_body into the metadata table at the initialization of auth token table | 
|  | 1782 | pub fn insert_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> { | 
|  | 1783 | self.conn | 
|  | 1784 | .execute( | 
|  | 1785 | "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);", | 
|  | 1786 | params!["last_off_body", last_off_body], | 
|  | 1787 | ) | 
|  | 1788 | .context("In insert_last_off_body: failed to insert.")?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1789 | Ok(()) | 
|  | 1790 | } | 
|  | 1791 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1792 | /// Update last_off_body when on_device_off_body is called | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1793 | pub fn update_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> { | 
|  | 1794 | self.conn | 
|  | 1795 | .execute( | 
|  | 1796 | "UPDATE perboot.metadata SET value = ? WHERE key = ?;", | 
|  | 1797 | params![last_off_body, "last_off_body"], | 
|  | 1798 | ) | 
|  | 1799 | .context("In update_last_off_body: failed to update.")?; | 
|  | 1800 | Ok(()) | 
|  | 1801 | } | 
|  | 1802 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1803 | /// Get last_off_body time when finding auth tokens | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1804 | fn get_last_off_body(tx: &Transaction) -> Result<MonotonicRawTime> { | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 1805 | tx.query_row( | 
|  | 1806 | "SELECT value from perboot.metadata WHERE key = ?;", | 
|  | 1807 | params!["last_off_body"], | 
|  | 1808 | |row| Ok(row.get(0)?), | 
|  | 1809 | ) | 
|  | 1810 | .context("In get_last_off_body: query_row failed.") | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1811 | } | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1812 | } | 
|  | 1813 |  | 
|  | 1814 | #[cfg(test)] | 
|  | 1815 | mod tests { | 
|  | 1816 |  | 
|  | 1817 | use super::*; | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1818 | use crate::key_parameter::{ | 
|  | 1819 | Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter, | 
|  | 1820 | KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel, | 
|  | 1821 | }; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1822 | use crate::key_perm_set; | 
|  | 1823 | use crate::permission::{KeyPerm, KeyPermSet}; | 
| Janis Danisevskis | 2a8330a | 2021-01-20 15:34:26 -0800 | [diff] [blame] | 1824 | use keystore2_test_utils::TempDir; | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1825 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ | 
|  | 1826 | HardwareAuthToken::HardwareAuthToken, | 
|  | 1827 | HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type, | 
| Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 1828 | }; | 
|  | 1829 | use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{ | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1830 | Timestamp::Timestamp, | 
|  | 1831 | }; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1832 | use rusqlite::NO_PARAMS; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1833 | use rusqlite::{Error, TransactionBehavior}; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1834 | use std::cell::RefCell; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1835 | use std::sync::atomic::{AtomicU8, Ordering}; | 
|  | 1836 | use std::sync::Arc; | 
|  | 1837 | use std::thread; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1838 | use std::time::{Duration, SystemTime}; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1839 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 1840 | fn new_test_db() -> Result<KeystoreDB> { | 
|  | 1841 | let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?; | 
|  | 1842 |  | 
|  | 1843 | KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?; | 
|  | 1844 | Ok(KeystoreDB { conn }) | 
|  | 1845 | } | 
|  | 1846 |  | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 1847 | fn rebind_alias( | 
|  | 1848 | db: &mut KeystoreDB, | 
|  | 1849 | newid: &KeyIdGuard, | 
|  | 1850 | alias: &str, | 
|  | 1851 | domain: Domain, | 
|  | 1852 | namespace: i64, | 
|  | 1853 | ) -> Result<bool> { | 
|  | 1854 | db.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1855 | KeystoreDB::rebind_alias(tx, newid, alias, domain, namespace) | 
|  | 1856 | }) | 
|  | 1857 | .context("In rebind_alias.") | 
|  | 1858 | } | 
|  | 1859 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1860 | #[test] | 
|  | 1861 | fn datetime() -> Result<()> { | 
|  | 1862 | let conn = Connection::open_in_memory()?; | 
|  | 1863 | conn.execute("CREATE TABLE test (ts DATETIME);", NO_PARAMS)?; | 
|  | 1864 | let now = SystemTime::now(); | 
|  | 1865 | let duration = Duration::from_secs(1000); | 
|  | 1866 | let then = now.checked_sub(duration).unwrap(); | 
|  | 1867 | let soon = now.checked_add(duration).unwrap(); | 
|  | 1868 | conn.execute( | 
|  | 1869 | "INSERT INTO test (ts) VALUES (?), (?), (?);", | 
|  | 1870 | params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?], | 
|  | 1871 | )?; | 
|  | 1872 | let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?; | 
|  | 1873 | let mut rows = stmt.query(NO_PARAMS)?; | 
|  | 1874 | assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?); | 
|  | 1875 | assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?); | 
|  | 1876 | assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?); | 
|  | 1877 | assert!(rows.next()?.is_none()); | 
|  | 1878 | assert!(DateTime::try_from(then)? < DateTime::try_from(now)?); | 
|  | 1879 | assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?); | 
|  | 1880 | assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?); | 
|  | 1881 | Ok(()) | 
|  | 1882 | } | 
|  | 1883 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1884 | // Ensure that we're using the "injected" random function, not the real one. | 
|  | 1885 | #[test] | 
|  | 1886 | fn test_mocked_random() { | 
|  | 1887 | let rand1 = random(); | 
|  | 1888 | let rand2 = random(); | 
|  | 1889 | let rand3 = random(); | 
|  | 1890 | if rand1 == rand2 { | 
|  | 1891 | assert_eq!(rand2 + 1, rand3); | 
|  | 1892 | } else { | 
|  | 1893 | assert_eq!(rand1 + 1, rand2); | 
|  | 1894 | assert_eq!(rand2, rand3); | 
|  | 1895 | } | 
|  | 1896 | } | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1897 |  | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1898 | // Test that we have the correct tables. | 
|  | 1899 | #[test] | 
|  | 1900 | fn test_tables() -> Result<()> { | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 1901 | let db = new_test_db()?; | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1902 | let tables = db | 
|  | 1903 | .conn | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1904 | .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")? | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1905 | .query_map(params![], |row| row.get(0))? | 
|  | 1906 | .collect::<rusqlite::Result<Vec<String>>>()?; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1907 | assert_eq!(tables.len(), 5); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1908 | assert_eq!(tables[0], "blobentry"); | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1909 | assert_eq!(tables[1], "grant"); | 
|  | 1910 | assert_eq!(tables[2], "keyentry"); | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1911 | assert_eq!(tables[3], "keymetadata"); | 
|  | 1912 | assert_eq!(tables[4], "keyparameter"); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1913 | let tables = db | 
|  | 1914 | .conn | 
|  | 1915 | .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")? | 
|  | 1916 | .query_map(params![], |row| row.get(0))? | 
|  | 1917 | .collect::<rusqlite::Result<Vec<String>>>()?; | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1918 |  | 
|  | 1919 | assert_eq!(tables.len(), 2); | 
|  | 1920 | assert_eq!(tables[0], "authtoken"); | 
|  | 1921 | assert_eq!(tables[1], "metadata"); | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1922 | Ok(()) | 
|  | 1923 | } | 
|  | 1924 |  | 
|  | 1925 | #[test] | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1926 | fn test_auth_token_table_invariant() -> Result<()> { | 
|  | 1927 | let mut db = new_test_db()?; | 
|  | 1928 | let auth_token1 = HardwareAuthToken { | 
|  | 1929 | challenge: i64::MAX, | 
|  | 1930 | userId: 200, | 
|  | 1931 | authenticatorId: 200, | 
|  | 1932 | authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0), | 
|  | 1933 | timestamp: Timestamp { milliSeconds: 500 }, | 
|  | 1934 | mac: String::from("mac").into_bytes(), | 
|  | 1935 | }; | 
|  | 1936 | db.insert_auth_token(&auth_token1)?; | 
|  | 1937 | let auth_tokens_returned = get_auth_tokens(&mut db)?; | 
|  | 1938 | assert_eq!(auth_tokens_returned.len(), 1); | 
|  | 1939 |  | 
|  | 1940 | // insert another auth token with the same values for the columns in the UNIQUE constraint | 
|  | 1941 | // of the auth token table and different value for timestamp | 
|  | 1942 | let auth_token2 = HardwareAuthToken { | 
|  | 1943 | challenge: i64::MAX, | 
|  | 1944 | userId: 200, | 
|  | 1945 | authenticatorId: 200, | 
|  | 1946 | authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0), | 
|  | 1947 | timestamp: Timestamp { milliSeconds: 600 }, | 
|  | 1948 | mac: String::from("mac").into_bytes(), | 
|  | 1949 | }; | 
|  | 1950 |  | 
|  | 1951 | db.insert_auth_token(&auth_token2)?; | 
|  | 1952 | let mut auth_tokens_returned = get_auth_tokens(&mut db)?; | 
|  | 1953 | assert_eq!(auth_tokens_returned.len(), 1); | 
|  | 1954 |  | 
|  | 1955 | if let Some(auth_token) = auth_tokens_returned.pop() { | 
|  | 1956 | assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600); | 
|  | 1957 | } | 
|  | 1958 |  | 
|  | 1959 | // insert another auth token with the different values for the columns in the UNIQUE | 
|  | 1960 | // constraint of the auth token table | 
|  | 1961 | let auth_token3 = HardwareAuthToken { | 
|  | 1962 | challenge: i64::MAX, | 
|  | 1963 | userId: 201, | 
|  | 1964 | authenticatorId: 200, | 
|  | 1965 | authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0), | 
|  | 1966 | timestamp: Timestamp { milliSeconds: 600 }, | 
|  | 1967 | mac: String::from("mac").into_bytes(), | 
|  | 1968 | }; | 
|  | 1969 |  | 
|  | 1970 | db.insert_auth_token(&auth_token3)?; | 
|  | 1971 | let auth_tokens_returned = get_auth_tokens(&mut db)?; | 
|  | 1972 | assert_eq!(auth_tokens_returned.len(), 2); | 
|  | 1973 |  | 
|  | 1974 | Ok(()) | 
|  | 1975 | } | 
|  | 1976 |  | 
|  | 1977 | // utility function for test_auth_token_table_invariant() | 
|  | 1978 | fn get_auth_tokens(db: &mut KeystoreDB) -> Result<Vec<AuthTokenEntry>> { | 
|  | 1979 | let mut stmt = db.conn.prepare("SELECT * from perboot.authtoken;")?; | 
|  | 1980 |  | 
|  | 1981 | let auth_token_entries: Vec<AuthTokenEntry> = stmt | 
|  | 1982 | .query_map(NO_PARAMS, |row| { | 
|  | 1983 | Ok(AuthTokenEntry::new( | 
|  | 1984 | HardwareAuthToken { | 
|  | 1985 | challenge: row.get(1)?, | 
|  | 1986 | userId: row.get(2)?, | 
|  | 1987 | authenticatorId: row.get(3)?, | 
|  | 1988 | authenticatorType: HardwareAuthenticatorType(row.get(4)?), | 
|  | 1989 | timestamp: Timestamp { milliSeconds: row.get(5)? }, | 
|  | 1990 | mac: row.get(6)?, | 
|  | 1991 | }, | 
|  | 1992 | row.get(7)?, | 
|  | 1993 | )) | 
|  | 1994 | })? | 
|  | 1995 | .collect::<Result<Vec<AuthTokenEntry>, Error>>()?; | 
|  | 1996 | Ok(auth_token_entries) | 
|  | 1997 | } | 
|  | 1998 |  | 
|  | 1999 | #[test] | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2000 | fn test_persistence_for_files() -> Result<()> { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2001 | let temp_dir = TempDir::new("persistent_db_test")?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2002 | let mut db = KeystoreDB::new(temp_dir.path())?; | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2003 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2004 | db.create_key_entry(Domain::APP, 100)?; | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2005 | let entries = get_keyentry(&db)?; | 
|  | 2006 | assert_eq!(entries.len(), 1); | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2007 |  | 
|  | 2008 | let db = KeystoreDB::new(temp_dir.path())?; | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2009 |  | 
|  | 2010 | let entries_new = get_keyentry(&db)?; | 
|  | 2011 | assert_eq!(entries, entries_new); | 
|  | 2012 | Ok(()) | 
|  | 2013 | } | 
|  | 2014 |  | 
|  | 2015 | #[test] | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2016 | fn test_create_key_entry() -> Result<()> { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2017 | fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>) { | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2018 | (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref()) | 
|  | 2019 | } | 
|  | 2020 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2021 | let mut db = new_test_db()?; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2022 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2023 | db.create_key_entry(Domain::APP, 100)?; | 
|  | 2024 | db.create_key_entry(Domain::SELINUX, 101)?; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2025 |  | 
|  | 2026 | let entries = get_keyentry(&db)?; | 
|  | 2027 | assert_eq!(entries.len(), 2); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2028 | assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None)); | 
|  | 2029 | assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None)); | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2030 |  | 
|  | 2031 | // Test that we must pass in a valid Domain. | 
|  | 2032 | check_result_is_error_containing_string( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2033 | db.create_key_entry(Domain::GRANT, 102), | 
|  | 2034 | "Domain Domain(1) must be either App or SELinux.", | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2035 | ); | 
|  | 2036 | check_result_is_error_containing_string( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2037 | db.create_key_entry(Domain::BLOB, 103), | 
|  | 2038 | "Domain Domain(3) must be either App or SELinux.", | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2039 | ); | 
|  | 2040 | check_result_is_error_containing_string( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2041 | db.create_key_entry(Domain::KEY_ID, 104), | 
|  | 2042 | "Domain Domain(4) must be either App or SELinux.", | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2043 | ); | 
|  | 2044 |  | 
|  | 2045 | Ok(()) | 
|  | 2046 | } | 
|  | 2047 |  | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2048 | #[test] | 
|  | 2049 | fn test_rebind_alias() -> Result<()> { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2050 | fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>) { | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2051 | (ke.domain, ke.namespace, ke.alias.as_deref()) | 
|  | 2052 | } | 
|  | 2053 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 2054 | let mut db = new_test_db()?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2055 | db.create_key_entry(Domain::APP, 42)?; | 
|  | 2056 | db.create_key_entry(Domain::APP, 42)?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2057 | let entries = get_keyentry(&db)?; | 
|  | 2058 | assert_eq!(entries.len(), 2); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2059 | assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None)); | 
|  | 2060 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None)); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2061 |  | 
|  | 2062 | // Test that the first call to rebind_alias sets the alias. | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 2063 | rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2064 | let entries = get_keyentry(&db)?; | 
|  | 2065 | assert_eq!(entries.len(), 2); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2066 | assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), Some("foo"))); | 
|  | 2067 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None)); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2068 |  | 
|  | 2069 | // Test that the second call to rebind_alias also empties the old one. | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 2070 | rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2071 | let entries = get_keyentry(&db)?; | 
|  | 2072 | assert_eq!(entries.len(), 2); | 
| Joel Galenson | 7fa5c41 | 2020-11-19 10:56:54 -0800 | [diff] [blame] | 2073 | assert_eq!(extractor(&entries[0]), (None, None, None)); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2074 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo"))); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2075 |  | 
|  | 2076 | // Test that we must pass in a valid Domain. | 
|  | 2077 | check_result_is_error_containing_string( | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 2078 | rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42), | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2079 | "Domain Domain(1) must be either App or SELinux.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2080 | ); | 
|  | 2081 | check_result_is_error_containing_string( | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 2082 | rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42), | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2083 | "Domain Domain(3) must be either App or SELinux.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2084 | ); | 
|  | 2085 | check_result_is_error_containing_string( | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 2086 | rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42), | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2087 | "Domain Domain(4) must be either App or SELinux.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2088 | ); | 
|  | 2089 |  | 
|  | 2090 | // Test that we correctly handle setting an alias for something that does not exist. | 
|  | 2091 | check_result_is_error_containing_string( | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 2092 | rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42), | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2093 | "Expected to update a single entry but instead updated 0", | 
|  | 2094 | ); | 
|  | 2095 | // Test that we correctly abort the transaction in this case. | 
|  | 2096 | let entries = get_keyentry(&db)?; | 
|  | 2097 | assert_eq!(entries.len(), 2); | 
| Joel Galenson | 7fa5c41 | 2020-11-19 10:56:54 -0800 | [diff] [blame] | 2098 | assert_eq!(extractor(&entries[0]), (None, None, None)); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2099 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo"))); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2100 |  | 
|  | 2101 | Ok(()) | 
|  | 2102 | } | 
|  | 2103 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2104 | #[test] | 
|  | 2105 | fn test_grant_ungrant() -> Result<()> { | 
|  | 2106 | const CALLER_UID: u32 = 15; | 
|  | 2107 | const GRANTEE_UID: u32 = 12; | 
|  | 2108 | const SELINUX_NAMESPACE: i64 = 7; | 
|  | 2109 |  | 
|  | 2110 | let mut db = new_test_db()?; | 
|  | 2111 | db.conn.execute( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2112 | "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state) | 
|  | 2113 | VALUES (1, 0, 0, 15, 'key', 1), (2, 0, 2, 7, 'yek', 1);", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2114 | NO_PARAMS, | 
|  | 2115 | )?; | 
|  | 2116 | let app_key = KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2117 | domain: super::Domain::APP, | 
|  | 2118 | nspace: 0, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2119 | alias: Some("key".to_string()), | 
|  | 2120 | blob: None, | 
|  | 2121 | }; | 
|  | 2122 | const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()]; | 
|  | 2123 | const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()]; | 
|  | 2124 |  | 
|  | 2125 | // Reset totally predictable random number generator in case we | 
|  | 2126 | // are not the first test running on this thread. | 
|  | 2127 | reset_random(); | 
|  | 2128 | let next_random = 0i64; | 
|  | 2129 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2130 | let app_granted_key = db | 
|  | 2131 | .grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2132 | assert_eq!(*a, PVEC1); | 
|  | 2133 | assert_eq!( | 
|  | 2134 | *k, | 
|  | 2135 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2136 | domain: super::Domain::APP, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2137 | // namespace must be set to the caller_uid. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2138 | nspace: CALLER_UID as i64, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2139 | alias: Some("key".to_string()), | 
|  | 2140 | blob: None, | 
|  | 2141 | } | 
|  | 2142 | ); | 
|  | 2143 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2144 | }) | 
|  | 2145 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2146 |  | 
|  | 2147 | assert_eq!( | 
|  | 2148 | app_granted_key, | 
|  | 2149 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2150 | domain: super::Domain::GRANT, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2151 | // The grantid is next_random due to the mock random number generator. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2152 | nspace: next_random, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2153 | alias: None, | 
|  | 2154 | blob: None, | 
|  | 2155 | } | 
|  | 2156 | ); | 
|  | 2157 |  | 
|  | 2158 | let selinux_key = KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2159 | domain: super::Domain::SELINUX, | 
|  | 2160 | nspace: SELINUX_NAMESPACE, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2161 | alias: Some("yek".to_string()), | 
|  | 2162 | blob: None, | 
|  | 2163 | }; | 
|  | 2164 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2165 | let selinux_granted_key = db | 
|  | 2166 | .grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2167 | assert_eq!(*a, PVEC1); | 
|  | 2168 | assert_eq!( | 
|  | 2169 | *k, | 
|  | 2170 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2171 | domain: super::Domain::SELINUX, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2172 | // namespace must be the supplied SELinux | 
|  | 2173 | // namespace. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2174 | nspace: SELINUX_NAMESPACE, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2175 | alias: Some("yek".to_string()), | 
|  | 2176 | blob: None, | 
|  | 2177 | } | 
|  | 2178 | ); | 
|  | 2179 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2180 | }) | 
|  | 2181 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2182 |  | 
|  | 2183 | assert_eq!( | 
|  | 2184 | selinux_granted_key, | 
|  | 2185 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2186 | domain: super::Domain::GRANT, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2187 | // The grantid is next_random + 1 due to the mock random number generator. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2188 | nspace: next_random + 1, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2189 | alias: None, | 
|  | 2190 | blob: None, | 
|  | 2191 | } | 
|  | 2192 | ); | 
|  | 2193 |  | 
|  | 2194 | // This should update the existing grant with PVEC2. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2195 | let selinux_granted_key = db | 
|  | 2196 | .grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2197 | assert_eq!(*a, PVEC2); | 
|  | 2198 | assert_eq!( | 
|  | 2199 | *k, | 
|  | 2200 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2201 | domain: super::Domain::SELINUX, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2202 | // namespace must be the supplied SELinux | 
|  | 2203 | // namespace. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2204 | nspace: SELINUX_NAMESPACE, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2205 | alias: Some("yek".to_string()), | 
|  | 2206 | blob: None, | 
|  | 2207 | } | 
|  | 2208 | ); | 
|  | 2209 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2210 | }) | 
|  | 2211 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2212 |  | 
|  | 2213 | assert_eq!( | 
|  | 2214 | selinux_granted_key, | 
|  | 2215 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2216 | domain: super::Domain::GRANT, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2217 | // Same grant id as before. The entry was only updated. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2218 | nspace: next_random + 1, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2219 | alias: None, | 
|  | 2220 | blob: None, | 
|  | 2221 | } | 
|  | 2222 | ); | 
|  | 2223 |  | 
|  | 2224 | { | 
|  | 2225 | // Limiting scope of stmt, because it borrows db. | 
|  | 2226 | let mut stmt = db | 
|  | 2227 | .conn | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2228 | .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?; | 
| Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 2229 | let mut rows = | 
|  | 2230 | stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>(NO_PARAMS, |row| { | 
|  | 2231 | Ok(( | 
|  | 2232 | row.get(0)?, | 
|  | 2233 | row.get(1)?, | 
|  | 2234 | row.get(2)?, | 
|  | 2235 | KeyPermSet::from(row.get::<_, i32>(3)?), | 
|  | 2236 | )) | 
|  | 2237 | })?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2238 |  | 
|  | 2239 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 2240 | assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1)); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2241 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 2242 | assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2)); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2243 | assert!(rows.next().is_none()); | 
|  | 2244 | } | 
|  | 2245 |  | 
|  | 2246 | debug_dump_keyentry_table(&mut db)?; | 
|  | 2247 | println!("app_key {:?}", app_key); | 
|  | 2248 | println!("selinux_key {:?}", selinux_key); | 
|  | 2249 |  | 
|  | 2250 | db.ungrant(app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?; | 
|  | 2251 | db.ungrant(selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?; | 
|  | 2252 |  | 
|  | 2253 | Ok(()) | 
|  | 2254 | } | 
|  | 2255 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2256 | static TEST_KEY_BLOB: &[u8] = b"my test blob"; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2257 | static TEST_CERT_BLOB: &[u8] = b"my test cert"; | 
|  | 2258 | static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain"; | 
|  | 2259 |  | 
|  | 2260 | #[test] | 
|  | 2261 | fn test_insert_blob() -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2262 | let key_id = KEY_ID_LOCK.get(3000); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2263 | let mut db = new_test_db()?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2264 | db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?; | 
|  | 2265 | db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?; | 
|  | 2266 | db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?; | 
|  | 2267 | drop(key_id); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2268 |  | 
|  | 2269 | let mut stmt = db.conn.prepare( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2270 | "SELECT subcomponent_type, keyentryid, blob FROM persistent.blobentry | 
|  | 2271 | ORDER BY subcomponent_type ASC;", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2272 | )?; | 
|  | 2273 | let mut rows = stmt | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2274 | .query_map::<(SubComponentType, i64, Vec<u8>), _, _>(NO_PARAMS, |row| { | 
|  | 2275 | Ok((row.get(0)?, row.get(1)?, row.get(2)?)) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2276 | })?; | 
|  | 2277 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2278 | assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec())); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2279 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2280 | assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec())); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2281 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2282 | assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec())); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2283 |  | 
|  | 2284 | Ok(()) | 
|  | 2285 | } | 
|  | 2286 |  | 
|  | 2287 | static TEST_ALIAS: &str = "my super duper key"; | 
|  | 2288 |  | 
|  | 2289 | #[test] | 
|  | 2290 | fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> { | 
|  | 2291 | let mut db = new_test_db()?; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2292 | let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2293 | .context("test_insert_and_load_full_keyentry_domain_app")? | 
|  | 2294 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2295 | let (_key_guard, key_entry) = db | 
|  | 2296 | .load_key_entry( | 
|  | 2297 | KeyDescriptor { | 
|  | 2298 | domain: Domain::APP, | 
|  | 2299 | nspace: 0, | 
|  | 2300 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2301 | blob: None, | 
|  | 2302 | }, | 
|  | 2303 | KeyType::Client, | 
|  | 2304 | KeyEntryLoadBits::BOTH, | 
|  | 2305 | 1, | 
|  | 2306 | |_k, _av| Ok(()), | 
|  | 2307 | ) | 
|  | 2308 | .unwrap(); | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2309 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2310 |  | 
|  | 2311 | db.unbind_key( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2312 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2313 | domain: Domain::APP, | 
|  | 2314 | nspace: 0, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2315 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2316 | blob: None, | 
|  | 2317 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2318 | KeyType::Client, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2319 | 1, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2320 | |_, _| Ok(()), | 
|  | 2321 | ) | 
|  | 2322 | .unwrap(); | 
|  | 2323 |  | 
|  | 2324 | assert_eq!( | 
|  | 2325 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2326 | db.load_key_entry( | 
|  | 2327 | KeyDescriptor { | 
|  | 2328 | domain: Domain::APP, | 
|  | 2329 | nspace: 0, | 
|  | 2330 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2331 | blob: None, | 
|  | 2332 | }, | 
|  | 2333 | KeyType::Client, | 
|  | 2334 | KeyEntryLoadBits::NONE, | 
|  | 2335 | 1, | 
|  | 2336 | |_k, _av| Ok(()), | 
|  | 2337 | ) | 
|  | 2338 | .unwrap_err() | 
|  | 2339 | .root_cause() | 
|  | 2340 | .downcast_ref::<KsError>() | 
|  | 2341 | ); | 
|  | 2342 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2343 | Ok(()) | 
|  | 2344 | } | 
|  | 2345 |  | 
|  | 2346 | #[test] | 
|  | 2347 | fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> { | 
|  | 2348 | let mut db = new_test_db()?; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2349 | let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2350 | .context("test_insert_and_load_full_keyentry_domain_selinux")? | 
|  | 2351 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2352 | let (_key_guard, key_entry) = db | 
|  | 2353 | .load_key_entry( | 
|  | 2354 | KeyDescriptor { | 
|  | 2355 | domain: Domain::SELINUX, | 
|  | 2356 | nspace: 1, | 
|  | 2357 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2358 | blob: None, | 
|  | 2359 | }, | 
|  | 2360 | KeyType::Client, | 
|  | 2361 | KeyEntryLoadBits::BOTH, | 
|  | 2362 | 1, | 
|  | 2363 | |_k, _av| Ok(()), | 
|  | 2364 | ) | 
|  | 2365 | .unwrap(); | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2366 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2367 |  | 
|  | 2368 | db.unbind_key( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2369 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2370 | domain: Domain::SELINUX, | 
|  | 2371 | nspace: 1, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2372 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2373 | blob: None, | 
|  | 2374 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2375 | KeyType::Client, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2376 | 1, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2377 | |_, _| Ok(()), | 
|  | 2378 | ) | 
|  | 2379 | .unwrap(); | 
|  | 2380 |  | 
|  | 2381 | assert_eq!( | 
|  | 2382 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2383 | db.load_key_entry( | 
|  | 2384 | KeyDescriptor { | 
|  | 2385 | domain: Domain::SELINUX, | 
|  | 2386 | nspace: 1, | 
|  | 2387 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2388 | blob: None, | 
|  | 2389 | }, | 
|  | 2390 | KeyType::Client, | 
|  | 2391 | KeyEntryLoadBits::NONE, | 
|  | 2392 | 1, | 
|  | 2393 | |_k, _av| Ok(()), | 
|  | 2394 | ) | 
|  | 2395 | .unwrap_err() | 
|  | 2396 | .root_cause() | 
|  | 2397 | .downcast_ref::<KsError>() | 
|  | 2398 | ); | 
|  | 2399 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2400 | Ok(()) | 
|  | 2401 | } | 
|  | 2402 |  | 
|  | 2403 | #[test] | 
|  | 2404 | fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> { | 
|  | 2405 | let mut db = new_test_db()?; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2406 | let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2407 | .context("test_insert_and_load_full_keyentry_domain_key_id")? | 
|  | 2408 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2409 | let (_, key_entry) = db | 
|  | 2410 | .load_key_entry( | 
|  | 2411 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
|  | 2412 | KeyType::Client, | 
|  | 2413 | KeyEntryLoadBits::BOTH, | 
|  | 2414 | 1, | 
|  | 2415 | |_k, _av| Ok(()), | 
|  | 2416 | ) | 
|  | 2417 | .unwrap(); | 
|  | 2418 |  | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2419 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2420 |  | 
|  | 2421 | db.unbind_key( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2422 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2423 | KeyType::Client, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2424 | 1, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2425 | |_, _| Ok(()), | 
|  | 2426 | ) | 
|  | 2427 | .unwrap(); | 
|  | 2428 |  | 
|  | 2429 | assert_eq!( | 
|  | 2430 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2431 | db.load_key_entry( | 
|  | 2432 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
|  | 2433 | KeyType::Client, | 
|  | 2434 | KeyEntryLoadBits::NONE, | 
|  | 2435 | 1, | 
|  | 2436 | |_k, _av| Ok(()), | 
|  | 2437 | ) | 
|  | 2438 | .unwrap_err() | 
|  | 2439 | .root_cause() | 
|  | 2440 | .downcast_ref::<KsError>() | 
|  | 2441 | ); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2442 |  | 
|  | 2443 | Ok(()) | 
|  | 2444 | } | 
|  | 2445 |  | 
|  | 2446 | #[test] | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2447 | fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> { | 
|  | 2448 | let mut db = new_test_db()?; | 
|  | 2449 | let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123)) | 
|  | 2450 | .context("test_check_and_update_key_usage_count_with_limited_use_key")? | 
|  | 2451 | .0; | 
|  | 2452 | // Update the usage count of the limited use key. | 
|  | 2453 | db.check_and_update_key_usage_count(key_id)?; | 
|  | 2454 |  | 
|  | 2455 | let (_key_guard, key_entry) = db.load_key_entry( | 
|  | 2456 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
|  | 2457 | KeyType::Client, | 
|  | 2458 | KeyEntryLoadBits::BOTH, | 
|  | 2459 | 1, | 
|  | 2460 | |_k, _av| Ok(()), | 
|  | 2461 | )?; | 
|  | 2462 |  | 
|  | 2463 | // The usage count is decremented now. | 
|  | 2464 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122))); | 
|  | 2465 |  | 
|  | 2466 | Ok(()) | 
|  | 2467 | } | 
|  | 2468 |  | 
|  | 2469 | #[test] | 
|  | 2470 | fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> { | 
|  | 2471 | let mut db = new_test_db()?; | 
|  | 2472 | let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1)) | 
|  | 2473 | .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")? | 
|  | 2474 | .0; | 
|  | 2475 | // Update the usage count of the limited use key. | 
|  | 2476 | db.check_and_update_key_usage_count(key_id).expect(concat!( | 
|  | 2477 | "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ", | 
|  | 2478 | "This should succeed." | 
|  | 2479 | )); | 
|  | 2480 |  | 
|  | 2481 | // Try to update the exhausted limited use key. | 
|  | 2482 | let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!( | 
|  | 2483 | "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ", | 
|  | 2484 | "This should fail." | 
|  | 2485 | )); | 
|  | 2486 | assert_eq!( | 
|  | 2487 | &KsError::Km(ErrorCode::INVALID_KEY_BLOB), | 
|  | 2488 | e.root_cause().downcast_ref::<KsError>().unwrap() | 
|  | 2489 | ); | 
|  | 2490 |  | 
|  | 2491 | Ok(()) | 
|  | 2492 | } | 
|  | 2493 |  | 
|  | 2494 | #[test] | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2495 | fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> { | 
|  | 2496 | let mut db = new_test_db()?; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2497 | let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2498 | .context("test_insert_and_load_full_keyentry_from_grant")? | 
|  | 2499 | .0; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2500 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2501 | let granted_key = db | 
|  | 2502 | .grant( | 
|  | 2503 | KeyDescriptor { | 
|  | 2504 | domain: Domain::APP, | 
|  | 2505 | nspace: 0, | 
|  | 2506 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2507 | blob: None, | 
|  | 2508 | }, | 
|  | 2509 | 1, | 
|  | 2510 | 2, | 
|  | 2511 | key_perm_set![KeyPerm::use_()], | 
|  | 2512 | |_k, _av| Ok(()), | 
|  | 2513 | ) | 
|  | 2514 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2515 |  | 
|  | 2516 | debug_dump_grant_table(&mut db)?; | 
|  | 2517 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2518 | let (_key_guard, key_entry) = db | 
|  | 2519 | .load_key_entry( | 
|  | 2520 | granted_key.clone(), | 
|  | 2521 | KeyType::Client, | 
|  | 2522 | KeyEntryLoadBits::BOTH, | 
|  | 2523 | 2, | 
|  | 2524 | |k, av| { | 
|  | 2525 | assert_eq!(Domain::GRANT, k.domain); | 
|  | 2526 | assert!(av.unwrap().includes(KeyPerm::use_())); | 
|  | 2527 | Ok(()) | 
|  | 2528 | }, | 
|  | 2529 | ) | 
|  | 2530 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2531 |  | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2532 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2533 |  | 
|  | 2534 | db.unbind_key(granted_key.clone(), KeyType::Client, 2, |_, _| Ok(())).unwrap(); | 
|  | 2535 |  | 
|  | 2536 | assert_eq!( | 
|  | 2537 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2538 | db.load_key_entry( | 
|  | 2539 | granted_key, | 
|  | 2540 | KeyType::Client, | 
|  | 2541 | KeyEntryLoadBits::NONE, | 
|  | 2542 | 2, | 
|  | 2543 | |_k, _av| Ok(()), | 
|  | 2544 | ) | 
|  | 2545 | .unwrap_err() | 
|  | 2546 | .root_cause() | 
|  | 2547 | .downcast_ref::<KsError>() | 
|  | 2548 | ); | 
|  | 2549 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2550 | Ok(()) | 
|  | 2551 | } | 
|  | 2552 |  | 
| Janis Danisevskis | 4576002 | 2021-01-19 16:34:10 -0800 | [diff] [blame] | 2553 | // This test attempts to load a key by key id while the caller is not the owner | 
|  | 2554 | // but a grant exists for the given key and the caller. | 
|  | 2555 | #[test] | 
|  | 2556 | fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> { | 
|  | 2557 | let mut db = new_test_db()?; | 
|  | 2558 | const OWNER_UID: u32 = 1u32; | 
|  | 2559 | const GRANTEE_UID: u32 = 2u32; | 
|  | 2560 | const SOMEONE_ELSE_UID: u32 = 3u32; | 
|  | 2561 | let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None) | 
|  | 2562 | .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")? | 
|  | 2563 | .0; | 
|  | 2564 |  | 
|  | 2565 | db.grant( | 
|  | 2566 | KeyDescriptor { | 
|  | 2567 | domain: Domain::APP, | 
|  | 2568 | nspace: 0, | 
|  | 2569 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2570 | blob: None, | 
|  | 2571 | }, | 
|  | 2572 | OWNER_UID, | 
|  | 2573 | GRANTEE_UID, | 
|  | 2574 | key_perm_set![KeyPerm::use_()], | 
|  | 2575 | |_k, _av| Ok(()), | 
|  | 2576 | ) | 
|  | 2577 | .unwrap(); | 
|  | 2578 |  | 
|  | 2579 | debug_dump_grant_table(&mut db)?; | 
|  | 2580 |  | 
|  | 2581 | let id_descriptor = | 
|  | 2582 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() }; | 
|  | 2583 |  | 
|  | 2584 | let (_, key_entry) = db | 
|  | 2585 | .load_key_entry( | 
|  | 2586 | id_descriptor.clone(), | 
|  | 2587 | KeyType::Client, | 
|  | 2588 | KeyEntryLoadBits::BOTH, | 
|  | 2589 | GRANTEE_UID, | 
|  | 2590 | |k, av| { | 
|  | 2591 | assert_eq!(Domain::APP, k.domain); | 
|  | 2592 | assert_eq!(OWNER_UID as i64, k.nspace); | 
|  | 2593 | assert!(av.unwrap().includes(KeyPerm::use_())); | 
|  | 2594 | Ok(()) | 
|  | 2595 | }, | 
|  | 2596 | ) | 
|  | 2597 | .unwrap(); | 
|  | 2598 |  | 
|  | 2599 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
|  | 2600 |  | 
|  | 2601 | let (_, key_entry) = db | 
|  | 2602 | .load_key_entry( | 
|  | 2603 | id_descriptor.clone(), | 
|  | 2604 | KeyType::Client, | 
|  | 2605 | KeyEntryLoadBits::BOTH, | 
|  | 2606 | SOMEONE_ELSE_UID, | 
|  | 2607 | |k, av| { | 
|  | 2608 | assert_eq!(Domain::APP, k.domain); | 
|  | 2609 | assert_eq!(OWNER_UID as i64, k.nspace); | 
|  | 2610 | assert!(av.is_none()); | 
|  | 2611 | Ok(()) | 
|  | 2612 | }, | 
|  | 2613 | ) | 
|  | 2614 | .unwrap(); | 
|  | 2615 |  | 
|  | 2616 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
|  | 2617 |  | 
|  | 2618 | db.unbind_key(id_descriptor.clone(), KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap(); | 
|  | 2619 |  | 
|  | 2620 | assert_eq!( | 
|  | 2621 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2622 | db.load_key_entry( | 
|  | 2623 | id_descriptor, | 
|  | 2624 | KeyType::Client, | 
|  | 2625 | KeyEntryLoadBits::NONE, | 
|  | 2626 | GRANTEE_UID, | 
|  | 2627 | |_k, _av| Ok(()), | 
|  | 2628 | ) | 
|  | 2629 | .unwrap_err() | 
|  | 2630 | .root_cause() | 
|  | 2631 | .downcast_ref::<KsError>() | 
|  | 2632 | ); | 
|  | 2633 |  | 
|  | 2634 | Ok(()) | 
|  | 2635 | } | 
|  | 2636 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2637 | static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key"; | 
|  | 2638 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2639 | #[test] | 
|  | 2640 | fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> { | 
|  | 2641 | let handle = { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2642 | let temp_dir = Arc::new(TempDir::new("id_lock_test")?); | 
|  | 2643 | let temp_dir_clone = temp_dir.clone(); | 
|  | 2644 | let mut db = KeystoreDB::new(temp_dir.path())?; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2645 | let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2646 | .context("test_insert_and_load_full_keyentry_domain_app")? | 
|  | 2647 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2648 | let (_key_guard, key_entry) = db | 
|  | 2649 | .load_key_entry( | 
|  | 2650 | KeyDescriptor { | 
|  | 2651 | domain: Domain::APP, | 
|  | 2652 | nspace: 0, | 
|  | 2653 | alias: Some(KEY_LOCK_TEST_ALIAS.to_string()), | 
|  | 2654 | blob: None, | 
|  | 2655 | }, | 
|  | 2656 | KeyType::Client, | 
|  | 2657 | KeyEntryLoadBits::BOTH, | 
|  | 2658 | 33, | 
|  | 2659 | |_k, _av| Ok(()), | 
|  | 2660 | ) | 
|  | 2661 | .unwrap(); | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2662 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None)); | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2663 | let state = Arc::new(AtomicU8::new(1)); | 
|  | 2664 | let state2 = state.clone(); | 
|  | 2665 |  | 
|  | 2666 | // Spawning a second thread that attempts to acquire the key id lock | 
|  | 2667 | // for the same key as the primary thread. The primary thread then | 
|  | 2668 | // waits, thereby forcing the secondary thread into the second stage | 
|  | 2669 | // of acquiring the lock (see KEY ID LOCK 2/2 above). | 
|  | 2670 | // The test succeeds if the secondary thread observes the transition | 
|  | 2671 | // of `state` from 1 to 2, despite having a whole second to overtake | 
|  | 2672 | // the primary thread. | 
|  | 2673 | let handle = thread::spawn(move || { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2674 | let temp_dir = temp_dir_clone; | 
|  | 2675 | let mut db = KeystoreDB::new(temp_dir.path()).unwrap(); | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2676 | assert!(db | 
|  | 2677 | .load_key_entry( | 
|  | 2678 | KeyDescriptor { | 
|  | 2679 | domain: Domain::APP, | 
|  | 2680 | nspace: 0, | 
|  | 2681 | alias: Some(KEY_LOCK_TEST_ALIAS.to_string()), | 
|  | 2682 | blob: None, | 
|  | 2683 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2684 | KeyType::Client, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2685 | KeyEntryLoadBits::BOTH, | 
|  | 2686 | 33, | 
|  | 2687 | |_k, _av| Ok(()), | 
|  | 2688 | ) | 
|  | 2689 | .is_ok()); | 
|  | 2690 | // We should only see a 2 here because we can only return | 
|  | 2691 | // from load_key_entry when the `_key_guard` expires, | 
|  | 2692 | // which happens at the end of the scope. | 
|  | 2693 | assert_eq!(2, state2.load(Ordering::Relaxed)); | 
|  | 2694 | }); | 
|  | 2695 |  | 
|  | 2696 | thread::sleep(std::time::Duration::from_millis(1000)); | 
|  | 2697 |  | 
|  | 2698 | assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed)); | 
|  | 2699 |  | 
|  | 2700 | // Return the handle from this scope so we can join with the | 
|  | 2701 | // secondary thread after the key id lock has expired. | 
|  | 2702 | handle | 
|  | 2703 | // This is where the `_key_guard` goes out of scope, | 
|  | 2704 | // which is the reason for concurrent load_key_entry on the same key | 
|  | 2705 | // to unblock. | 
|  | 2706 | }; | 
|  | 2707 | // Join with the secondary thread and unwrap, to propagate failing asserts to the | 
|  | 2708 | // main test thread. We will not see failing asserts in secondary threads otherwise. | 
|  | 2709 | handle.join().unwrap(); | 
|  | 2710 | Ok(()) | 
|  | 2711 | } | 
|  | 2712 |  | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 2713 | #[test] | 
|  | 2714 | fn list() -> Result<()> { | 
|  | 2715 | let temp_dir = TempDir::new("list_test")?; | 
|  | 2716 | let mut db = KeystoreDB::new(temp_dir.path())?; | 
|  | 2717 | static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[ | 
|  | 2718 | (Domain::APP, 1, "test1"), | 
|  | 2719 | (Domain::APP, 1, "test2"), | 
|  | 2720 | (Domain::APP, 1, "test3"), | 
|  | 2721 | (Domain::APP, 1, "test4"), | 
|  | 2722 | (Domain::APP, 1, "test5"), | 
|  | 2723 | (Domain::APP, 1, "test6"), | 
|  | 2724 | (Domain::APP, 1, "test7"), | 
|  | 2725 | (Domain::APP, 2, "test1"), | 
|  | 2726 | (Domain::APP, 2, "test2"), | 
|  | 2727 | (Domain::APP, 2, "test3"), | 
|  | 2728 | (Domain::APP, 2, "test4"), | 
|  | 2729 | (Domain::APP, 2, "test5"), | 
|  | 2730 | (Domain::APP, 2, "test6"), | 
|  | 2731 | (Domain::APP, 2, "test8"), | 
|  | 2732 | (Domain::SELINUX, 100, "test1"), | 
|  | 2733 | (Domain::SELINUX, 100, "test2"), | 
|  | 2734 | (Domain::SELINUX, 100, "test3"), | 
|  | 2735 | (Domain::SELINUX, 100, "test4"), | 
|  | 2736 | (Domain::SELINUX, 100, "test5"), | 
|  | 2737 | (Domain::SELINUX, 100, "test6"), | 
|  | 2738 | (Domain::SELINUX, 100, "test9"), | 
|  | 2739 | ]; | 
|  | 2740 |  | 
|  | 2741 | let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES | 
|  | 2742 | .iter() | 
|  | 2743 | .map(|(domain, ns, alias)| { | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2744 | let entry = make_test_key_entry(&mut db, *domain, *ns, *alias, None) | 
|  | 2745 | .unwrap_or_else(|e| { | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 2746 | panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e) | 
|  | 2747 | }); | 
|  | 2748 | (entry.id(), *ns) | 
|  | 2749 | }) | 
|  | 2750 | .collect(); | 
|  | 2751 |  | 
|  | 2752 | for (domain, namespace) in | 
|  | 2753 | &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)] | 
|  | 2754 | { | 
|  | 2755 | let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES | 
|  | 2756 | .iter() | 
|  | 2757 | .filter_map(|(domain, ns, alias)| match ns { | 
|  | 2758 | ns if *ns == *namespace => Some(KeyDescriptor { | 
|  | 2759 | domain: *domain, | 
|  | 2760 | nspace: *ns, | 
|  | 2761 | alias: Some(alias.to_string()), | 
|  | 2762 | blob: None, | 
|  | 2763 | }), | 
|  | 2764 | _ => None, | 
|  | 2765 | }) | 
|  | 2766 | .collect(); | 
|  | 2767 | list_o_descriptors.sort(); | 
|  | 2768 | let mut list_result = db.list(*domain, *namespace)?; | 
|  | 2769 | list_result.sort(); | 
|  | 2770 | assert_eq!(list_o_descriptors, list_result); | 
|  | 2771 |  | 
|  | 2772 | let mut list_o_ids: Vec<i64> = list_o_descriptors | 
|  | 2773 | .into_iter() | 
|  | 2774 | .map(|d| { | 
|  | 2775 | let (_, entry) = db | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2776 | .load_key_entry( | 
|  | 2777 | d, | 
|  | 2778 | KeyType::Client, | 
|  | 2779 | KeyEntryLoadBits::NONE, | 
|  | 2780 | *namespace as u32, | 
|  | 2781 | |_, _| Ok(()), | 
|  | 2782 | ) | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 2783 | .unwrap(); | 
|  | 2784 | entry.id() | 
|  | 2785 | }) | 
|  | 2786 | .collect(); | 
|  | 2787 | list_o_ids.sort_unstable(); | 
|  | 2788 | let mut loaded_entries: Vec<i64> = list_o_keys | 
|  | 2789 | .iter() | 
|  | 2790 | .filter_map(|(id, ns)| match ns { | 
|  | 2791 | ns if *ns == *namespace => Some(*id), | 
|  | 2792 | _ => None, | 
|  | 2793 | }) | 
|  | 2794 | .collect(); | 
|  | 2795 | loaded_entries.sort_unstable(); | 
|  | 2796 | assert_eq!(list_o_ids, loaded_entries); | 
|  | 2797 | } | 
|  | 2798 | assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101)?); | 
|  | 2799 |  | 
|  | 2800 | Ok(()) | 
|  | 2801 | } | 
|  | 2802 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2803 | // Helpers | 
|  | 2804 |  | 
|  | 2805 | // Checks that the given result is an error containing the given string. | 
|  | 2806 | fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) { | 
|  | 2807 | let error_str = format!( | 
|  | 2808 | "{:#?}", | 
|  | 2809 | result.err().unwrap_or_else(|| panic!("Expected the error: {}", target)) | 
|  | 2810 | ); | 
|  | 2811 | assert!( | 
|  | 2812 | error_str.contains(target), | 
|  | 2813 | "The string \"{}\" should contain \"{}\"", | 
|  | 2814 | error_str, | 
|  | 2815 | target | 
|  | 2816 | ); | 
|  | 2817 | } | 
|  | 2818 |  | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2819 | #[derive(Debug, PartialEq)] | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2820 | #[allow(dead_code)] | 
|  | 2821 | struct KeyEntryRow { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2822 | id: i64, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2823 | key_type: KeyType, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2824 | domain: Option<Domain>, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2825 | namespace: Option<i64>, | 
|  | 2826 | alias: Option<String>, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2827 | state: KeyLifeCycle, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2828 | } | 
|  | 2829 |  | 
|  | 2830 | fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> { | 
|  | 2831 | db.conn | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2832 | .prepare("SELECT * FROM persistent.keyentry;")? | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2833 | .query_map(NO_PARAMS, |row| { | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2834 | Ok(KeyEntryRow { | 
|  | 2835 | id: row.get(0)?, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2836 | key_type: row.get(1)?, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2837 | domain: match row.get(2)? { | 
|  | 2838 | Some(i) => Some(Domain(i)), | 
|  | 2839 | None => None, | 
|  | 2840 | }, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2841 | namespace: row.get(3)?, | 
|  | 2842 | alias: row.get(4)?, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2843 | state: row.get(5)?, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2844 | }) | 
|  | 2845 | })? | 
|  | 2846 | .map(|r| r.context("Could not read keyentry row.")) | 
|  | 2847 | .collect::<Result<Vec<_>>>() | 
|  | 2848 | } | 
|  | 2849 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 2850 | // Note: The parameters and SecurityLevel associations are nonsensical. This | 
|  | 2851 | // collection is only used to check if the parameters are preserved as expected by the | 
|  | 2852 | // database. | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 2853 | fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> { | 
|  | 2854 | let mut params = vec![ | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 2855 | KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2856 | KeyParameter::new( | 
|  | 2857 | KeyParameterValue::KeyPurpose(KeyPurpose::SIGN), | 
|  | 2858 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2859 | ), | 
|  | 2860 | KeyParameter::new( | 
|  | 2861 | KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT), | 
|  | 2862 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2863 | ), | 
|  | 2864 | KeyParameter::new( | 
|  | 2865 | KeyParameterValue::Algorithm(Algorithm::RSA), | 
|  | 2866 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2867 | ), | 
|  | 2868 | KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2869 | KeyParameter::new( | 
|  | 2870 | KeyParameterValue::BlockMode(BlockMode::ECB), | 
|  | 2871 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2872 | ), | 
|  | 2873 | KeyParameter::new( | 
|  | 2874 | KeyParameterValue::BlockMode(BlockMode::GCM), | 
|  | 2875 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2876 | ), | 
|  | 2877 | KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX), | 
|  | 2878 | KeyParameter::new( | 
|  | 2879 | KeyParameterValue::Digest(Digest::MD5), | 
|  | 2880 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2881 | ), | 
|  | 2882 | KeyParameter::new( | 
|  | 2883 | KeyParameterValue::Digest(Digest::SHA_2_224), | 
|  | 2884 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2885 | ), | 
|  | 2886 | KeyParameter::new( | 
|  | 2887 | KeyParameterValue::Digest(Digest::SHA_2_256), | 
|  | 2888 | SecurityLevel::STRONGBOX, | 
|  | 2889 | ), | 
|  | 2890 | KeyParameter::new( | 
|  | 2891 | KeyParameterValue::PaddingMode(PaddingMode::NONE), | 
|  | 2892 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2893 | ), | 
|  | 2894 | KeyParameter::new( | 
|  | 2895 | KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP), | 
|  | 2896 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2897 | ), | 
|  | 2898 | KeyParameter::new( | 
|  | 2899 | KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS), | 
|  | 2900 | SecurityLevel::STRONGBOX, | 
|  | 2901 | ), | 
|  | 2902 | KeyParameter::new( | 
|  | 2903 | KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN), | 
|  | 2904 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2905 | ), | 
|  | 2906 | KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2907 | KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX), | 
|  | 2908 | KeyParameter::new( | 
|  | 2909 | KeyParameterValue::EcCurve(EcCurve::P_224), | 
|  | 2910 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2911 | ), | 
|  | 2912 | KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX), | 
|  | 2913 | KeyParameter::new( | 
|  | 2914 | KeyParameterValue::EcCurve(EcCurve::P_384), | 
|  | 2915 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2916 | ), | 
|  | 2917 | KeyParameter::new( | 
|  | 2918 | KeyParameterValue::EcCurve(EcCurve::P_521), | 
|  | 2919 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2920 | ), | 
|  | 2921 | KeyParameter::new( | 
|  | 2922 | KeyParameterValue::RSAPublicExponent(3), | 
|  | 2923 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2924 | ), | 
|  | 2925 | KeyParameter::new( | 
|  | 2926 | KeyParameterValue::IncludeUniqueID, | 
|  | 2927 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2928 | ), | 
|  | 2929 | KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX), | 
|  | 2930 | KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX), | 
|  | 2931 | KeyParameter::new( | 
|  | 2932 | KeyParameterValue::ActiveDateTime(1234567890), | 
|  | 2933 | SecurityLevel::STRONGBOX, | 
|  | 2934 | ), | 
|  | 2935 | KeyParameter::new( | 
|  | 2936 | KeyParameterValue::OriginationExpireDateTime(1234567890), | 
|  | 2937 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2938 | ), | 
|  | 2939 | KeyParameter::new( | 
|  | 2940 | KeyParameterValue::UsageExpireDateTime(1234567890), | 
|  | 2941 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2942 | ), | 
|  | 2943 | KeyParameter::new( | 
|  | 2944 | KeyParameterValue::MinSecondsBetweenOps(1234567890), | 
|  | 2945 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2946 | ), | 
|  | 2947 | KeyParameter::new( | 
|  | 2948 | KeyParameterValue::MaxUsesPerBoot(1234567890), | 
|  | 2949 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2950 | ), | 
|  | 2951 | KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX), | 
|  | 2952 | KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX), | 
|  | 2953 | KeyParameter::new( | 
|  | 2954 | KeyParameterValue::NoAuthRequired, | 
|  | 2955 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2956 | ), | 
|  | 2957 | KeyParameter::new( | 
|  | 2958 | KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD), | 
|  | 2959 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2960 | ), | 
|  | 2961 | KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE), | 
|  | 2962 | KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE), | 
|  | 2963 | KeyParameter::new( | 
|  | 2964 | KeyParameterValue::TrustedUserPresenceRequired, | 
|  | 2965 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2966 | ), | 
|  | 2967 | KeyParameter::new( | 
|  | 2968 | KeyParameterValue::TrustedConfirmationRequired, | 
|  | 2969 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2970 | ), | 
|  | 2971 | KeyParameter::new( | 
|  | 2972 | KeyParameterValue::UnlockedDeviceRequired, | 
|  | 2973 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2974 | ), | 
|  | 2975 | KeyParameter::new( | 
|  | 2976 | KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]), | 
|  | 2977 | SecurityLevel::SOFTWARE, | 
|  | 2978 | ), | 
|  | 2979 | KeyParameter::new( | 
|  | 2980 | KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]), | 
|  | 2981 | SecurityLevel::SOFTWARE, | 
|  | 2982 | ), | 
|  | 2983 | KeyParameter::new( | 
|  | 2984 | KeyParameterValue::CreationDateTime(12345677890), | 
|  | 2985 | SecurityLevel::SOFTWARE, | 
|  | 2986 | ), | 
|  | 2987 | KeyParameter::new( | 
|  | 2988 | KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED), | 
|  | 2989 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2990 | ), | 
|  | 2991 | KeyParameter::new( | 
|  | 2992 | KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]), | 
|  | 2993 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2994 | ), | 
|  | 2995 | KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2996 | KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE), | 
|  | 2997 | KeyParameter::new( | 
|  | 2998 | KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2999 | SecurityLevel::SOFTWARE, | 
|  | 3000 | ), | 
|  | 3001 | KeyParameter::new( | 
|  | 3002 | KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3003 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3004 | ), | 
|  | 3005 | KeyParameter::new( | 
|  | 3006 | KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3007 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3008 | ), | 
|  | 3009 | KeyParameter::new( | 
|  | 3010 | KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3011 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3012 | ), | 
|  | 3013 | KeyParameter::new( | 
|  | 3014 | KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3015 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3016 | ), | 
|  | 3017 | KeyParameter::new( | 
|  | 3018 | KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3019 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3020 | ), | 
|  | 3021 | KeyParameter::new( | 
|  | 3022 | KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3023 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3024 | ), | 
|  | 3025 | KeyParameter::new( | 
|  | 3026 | KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3027 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3028 | ), | 
|  | 3029 | KeyParameter::new( | 
|  | 3030 | KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3031 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3032 | ), | 
|  | 3033 | KeyParameter::new( | 
|  | 3034 | KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3035 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3036 | ), | 
|  | 3037 | KeyParameter::new( | 
|  | 3038 | KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3039 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3040 | ), | 
|  | 3041 | KeyParameter::new( | 
|  | 3042 | KeyParameterValue::VendorPatchLevel(3), | 
|  | 3043 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3044 | ), | 
|  | 3045 | KeyParameter::new( | 
|  | 3046 | KeyParameterValue::BootPatchLevel(4), | 
|  | 3047 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3048 | ), | 
|  | 3049 | KeyParameter::new( | 
|  | 3050 | KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3051 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3052 | ), | 
|  | 3053 | KeyParameter::new( | 
|  | 3054 | KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 3055 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3056 | ), | 
|  | 3057 | KeyParameter::new( | 
|  | 3058 | KeyParameterValue::MacLength(256), | 
|  | 3059 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3060 | ), | 
|  | 3061 | KeyParameter::new( | 
|  | 3062 | KeyParameterValue::ResetSinceIdRotation, | 
|  | 3063 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3064 | ), | 
|  | 3065 | KeyParameter::new( | 
|  | 3066 | KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]), | 
|  | 3067 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 3068 | ), | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 3069 | ]; | 
|  | 3070 | if let Some(value) = max_usage_count { | 
|  | 3071 | params.push(KeyParameter::new( | 
|  | 3072 | KeyParameterValue::UsageCountLimit(value), | 
|  | 3073 | SecurityLevel::SOFTWARE, | 
|  | 3074 | )); | 
|  | 3075 | } | 
|  | 3076 | params | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 3077 | } | 
|  | 3078 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3079 | fn make_test_key_entry( | 
|  | 3080 | db: &mut KeystoreDB, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 3081 | domain: Domain, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3082 | namespace: i64, | 
|  | 3083 | alias: &str, | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 3084 | max_usage_count: Option<i32>, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 3085 | ) -> Result<KeyIdGuard> { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3086 | let key_id = db.create_key_entry(domain, namespace)?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 3087 | db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?; | 
|  | 3088 | db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?; | 
|  | 3089 | db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?; | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 3090 |  | 
|  | 3091 | let params = make_test_params(max_usage_count); | 
|  | 3092 | db.insert_keyparameter(&key_id, ¶ms)?; | 
|  | 3093 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 3094 | let mut metadata = KeyMetaData::new(); | 
|  | 3095 | metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password)); | 
|  | 3096 | metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3])); | 
|  | 3097 | metadata.add(KeyMetaEntry::Iv(vec![2, 3, 1])); | 
|  | 3098 | metadata.add(KeyMetaEntry::AeadTag(vec![3, 1, 2])); | 
|  | 3099 | db.insert_key_metadata(&key_id, &metadata)?; | 
| Janis Danisevskis | 4507f3b | 2021-01-13 16:34:39 -0800 | [diff] [blame] | 3100 | rebind_alias(db, &key_id, alias, domain, namespace)?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3101 | Ok(key_id) | 
|  | 3102 | } | 
|  | 3103 |  | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 3104 | fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry { | 
|  | 3105 | let params = make_test_params(max_usage_count); | 
|  | 3106 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 3107 | let mut metadata = KeyMetaData::new(); | 
|  | 3108 | metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password)); | 
|  | 3109 | metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3])); | 
|  | 3110 | metadata.add(KeyMetaEntry::Iv(vec![2, 3, 1])); | 
|  | 3111 | metadata.add(KeyMetaEntry::AeadTag(vec![3, 1, 2])); | 
|  | 3112 |  | 
|  | 3113 | KeyEntry { | 
|  | 3114 | id: key_id, | 
|  | 3115 | km_blob: Some(TEST_KEY_BLOB.to_vec()), | 
|  | 3116 | cert: Some(TEST_CERT_BLOB.to_vec()), | 
|  | 3117 | cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()), | 
|  | 3118 | sec_level: SecurityLevel::TRUSTED_ENVIRONMENT, | 
| Qi Wu | b9433b5 | 2020-12-01 14:52:46 +0800 | [diff] [blame] | 3119 | parameters: params, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 3120 | metadata, | 
|  | 3121 | } | 
|  | 3122 | } | 
|  | 3123 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3124 | fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 3125 | let mut stmt = db.conn.prepare( | 
|  | 3126 | "SELECT id, key_type, domain, namespace, alias, state FROM persistent.keyentry;", | 
|  | 3127 | )?; | 
|  | 3128 | let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle), _, _>( | 
|  | 3129 | NO_PARAMS, | 
|  | 3130 | |row| { | 
|  | 3131 | Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?, row.get(5)?)) | 
|  | 3132 | }, | 
|  | 3133 | )?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3134 |  | 
|  | 3135 | println!("Key entry table rows:"); | 
|  | 3136 | for r in rows { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 3137 | let (id, key_type, domain, namespace, alias, state) = r.unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3138 | println!( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 3139 | "    id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?}", | 
|  | 3140 | id, key_type, domain, namespace, alias, state | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3141 | ); | 
|  | 3142 | } | 
|  | 3143 | Ok(()) | 
|  | 3144 | } | 
|  | 3145 |  | 
|  | 3146 | fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 3147 | let mut stmt = db | 
|  | 3148 | .conn | 
|  | 3149 | .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3150 | let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| { | 
|  | 3151 | Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)) | 
|  | 3152 | })?; | 
|  | 3153 |  | 
|  | 3154 | println!("Grant table rows:"); | 
|  | 3155 | for r in rows { | 
|  | 3156 | let (id, gt, ki, av) = r.unwrap(); | 
|  | 3157 | println!("    id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av); | 
|  | 3158 | } | 
|  | 3159 | Ok(()) | 
|  | 3160 | } | 
|  | 3161 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 3162 | // Use a custom random number generator that repeats each number once. | 
|  | 3163 | // This allows us to test repeated elements. | 
|  | 3164 |  | 
|  | 3165 | thread_local! { | 
|  | 3166 | static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0); | 
|  | 3167 | } | 
|  | 3168 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 3169 | fn reset_random() { | 
|  | 3170 | RANDOM_COUNTER.with(|counter| { | 
|  | 3171 | *counter.borrow_mut() = 0; | 
|  | 3172 | }) | 
|  | 3173 | } | 
|  | 3174 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 3175 | pub fn random() -> i64 { | 
|  | 3176 | RANDOM_COUNTER.with(|counter| { | 
|  | 3177 | let result = *counter.borrow() / 2; | 
|  | 3178 | *counter.borrow_mut() += 1; | 
|  | 3179 | result | 
|  | 3180 | }) | 
|  | 3181 | } | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 3182 |  | 
|  | 3183 | #[test] | 
|  | 3184 | fn test_last_off_body() -> Result<()> { | 
|  | 3185 | let mut db = new_test_db()?; | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame] | 3186 | db.insert_last_off_body(MonotonicRawTime::now())?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 3187 | let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?; | 
|  | 3188 | let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?; | 
|  | 3189 | tx.commit()?; | 
|  | 3190 | let one_second = Duration::from_secs(1); | 
|  | 3191 | thread::sleep(one_second); | 
|  | 3192 | db.update_last_off_body(MonotonicRawTime::now())?; | 
|  | 3193 | let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?; | 
|  | 3194 | let last_off_body_2 = KeystoreDB::get_last_off_body(&tx2)?; | 
|  | 3195 | tx2.commit()?; | 
|  | 3196 | assert!(last_off_body_1.seconds() < last_off_body_2.seconds()); | 
|  | 3197 | Ok(()) | 
|  | 3198 | } | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 3199 | } |