Symmetric keyguard-bound superencryption
Bug: 163866361
Test: keystore2_test
Test: atest android.keystore.cts.CipherTest#testEmptyPlaintextEncryptsAndDecryptsWhenUnlockedRequired
Change-Id: I8b6136dce9ae93ffbeea04f41eaf468f82c67a91
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 553746a..06b5598 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -138,6 +138,16 @@
check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock with password.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
+
+ DB.with(|db| {
+ SUPER_KEY.unlock_screen_lock_bound_key(
+ &mut db.borrow_mut(),
+ user_id as u32,
+ &password,
+ )
+ })
+ .context("In on_lock_screen_event: unlock_screen_lock_bound_key failed")?;
+
// Unlock super key.
if let UserState::Uninitialized = DB
.with(|db| {
@@ -168,6 +178,8 @@
check_keystore_permission(KeystorePerm::lock())
.context("In on_lock_screen_event: Lock")?;
ENFORCEMENTS.set_device_locked(user_id, true);
+ SUPER_KEY.lock_screen_lock_bound_key(user_id as u32);
+
Ok(())
}
_ => {
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 5f35444..3d9cffe 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -41,7 +41,6 @@
//! from the database module these functions take permission check
//! callbacks.
-use crate::error::{Error as KsError, ErrorCode, ResponseCode};
use crate::impl_metadata; // This is in db_utils.rs
use crate::key_parameter::{KeyParameter, Tag};
use crate::permission::KeyPermSet;
@@ -49,6 +48,11 @@
use crate::{
db_utils::{self, SqlField},
gc::Gc,
+ super_key::USER_SUPER_KEY,
+};
+use crate::{
+ error::{Error as KsError, ErrorCode, ResponseCode},
+ super_key::SuperKeyType,
};
use anyhow::{anyhow, Context, Result};
use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
@@ -816,9 +820,6 @@
const UNASSIGNED_KEY_ID: i64 = -1i64;
const PERBOOT_DB_FILE_NAME: &'static str = &"file:perboot.sqlite?mode=memory&cache=shared";
- /// The alias of the user super key.
- pub const USER_SUPER_KEY_ALIAS: &'static str = &"USER_SUPER_KEY";
-
/// This creates a PerBootDbKeepAlive object to keep the per boot database alive.
pub fn keep_perboot_db_alive() -> Result<PerBootDbKeepAlive> {
let conn = Connection::open_in_memory()
@@ -1149,7 +1150,9 @@
pub fn store_super_key(
&mut self,
user_id: u32,
- blob_info: &(&[u8], &BlobMetaData),
+ key_type: &SuperKeyType,
+ blob: &[u8],
+ blob_metadata: &BlobMetaData,
) -> Result<KeyEntry> {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_id = Self::insert_with_retry(|id| {
@@ -1162,7 +1165,7 @@
KeyType::Super,
Domain::APP.0,
user_id as i64,
- Self::USER_SUPER_KEY_ALIAS,
+ key_type.alias,
KeyLifeCycle::Live,
&KEYSTORE_UUID,
],
@@ -1170,7 +1173,6 @@
})
.context("Failed to insert into keyentry table.")?;
- let (blob, blob_metadata) = *blob_info;
Self::set_blob_internal(
&tx,
key_id,
@@ -1188,12 +1190,16 @@
}
/// Loads super key of a given user, if exists
- pub fn load_super_key(&mut self, user_id: u32) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+ pub fn load_super_key(
+ &mut self,
+ key_type: &SuperKeyType,
+ user_id: u32,
+ ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_descriptor = KeyDescriptor {
domain: Domain::APP,
nspace: user_id as i64,
- alias: Some(String::from("USER_SUPER_KEY")),
+ alias: Some(key_type.alias.into()),
blob: None,
};
let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
@@ -1289,7 +1295,7 @@
Some(&blob),
Some(&metadata),
)
- .context("In get_of_create_key_with.")?;
+ .context("In get_or_create_key_with.")?;
(
id,
KeyEntry {
@@ -2649,7 +2655,7 @@
// OR super key:
KeyType::Super,
user_id,
- Self::USER_SUPER_KEY_ALIAS,
+ USER_SUPER_KEY.alias,
KeyLifeCycle::Live
])
.context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
@@ -4887,29 +4893,23 @@
let mut db = new_test_db()?;
let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
let super_key = keystore2_crypto::generate_aes256_key()?;
- let secret = String::from("keystore2 is great.");
- let secret_bytes = secret.into_bytes();
+ let secret_bytes = b"keystore2 is great.";
let (encrypted_secret, iv, tag) =
- keystore2_crypto::aes_gcm_encrypt(&secret_bytes, &super_key)?;
+ keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
let (encrypted_super_key, metadata) =
SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
- db.store_super_key(1, &(&encrypted_super_key, &metadata))?;
+ db.store_super_key(1, &USER_SUPER_KEY, &encrypted_super_key, &metadata)?;
//check if super key exists
- assert!(db.key_exists(Domain::APP, 1, "USER_SUPER_KEY", KeyType::Super)?);
+ assert!(db.key_exists(Domain::APP, 1, &USER_SUPER_KEY.alias, KeyType::Super)?);
- let (_, key_entry) = db.load_super_key(1)?.unwrap();
+ let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(key_entry, &pw)?;
- let decrypted_secret_bytes = keystore2_crypto::aes_gcm_decrypt(
- &encrypted_secret,
- &iv,
- &tag,
- &loaded_super_key.get_key(),
- )?;
- let decrypted_secret = String::from_utf8((&decrypted_secret_bytes).to_vec())?;
- assert_eq!(String::from("keystore2 is great."), decrypted_secret);
+ let decrypted_secret_bytes =
+ loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
+ assert_eq!(secret_bytes, &*decrypted_secret_bytes);
Ok(())
}
}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 2cc704b..3f003be 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,11 +14,11 @@
//! This is the Keystore 2.0 Enforcements module.
// TODO: more description to follow.
-use crate::authorization::Error as AuthzError;
use crate::database::{AuthTokenEntry, MonotonicRawTime};
use crate::error::{map_binder_status, Error, ErrorCode};
use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
HardwareAuthenticatorType::HardwareAuthenticatorType,
@@ -29,7 +29,7 @@
};
use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
use android_system_keystore2::aidl::android::system::keystore2::{
- IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
+ Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
OperationChallenge::OperationChallenge,
};
use android_system_keystore2::binder::Strong;
@@ -757,16 +757,32 @@
}
/// Given the set of key parameters and flags, check if super encryption is required.
- pub fn super_encryption_required(key_parameters: &[KeyParameter], flags: Option<i32>) -> bool {
- let auth_bound = key_parameters.iter().any(|kp| kp.get_tag() == Tag::USER_SECURE_ID);
-
- let skip_lskf_binding = if let Some(flags) = flags {
- (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0
- } else {
- false
- };
-
- auth_bound && !skip_lskf_binding
+ pub fn super_encryption_required(
+ domain: &Domain,
+ key_parameters: &[KeyParameter],
+ flags: Option<i32>,
+ ) -> SuperEncryptionType {
+ if *domain != Domain::APP {
+ return SuperEncryptionType::None;
+ }
+ if let Some(flags) = flags {
+ if (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0 {
+ return SuperEncryptionType::None;
+ }
+ }
+ if key_parameters
+ .iter()
+ .any(|kp| matches!(kp.key_parameter_value(), KeyParameterValue::UnlockedDeviceRequired))
+ {
+ return SuperEncryptionType::ScreenLockBound;
+ }
+ if key_parameters
+ .iter()
+ .any(|kp| matches!(kp.key_parameter_value(), KeyParameterValue::UserSecureID(_)))
+ {
+ return SuperEncryptionType::LskfBound;
+ }
+ SuperEncryptionType::None
}
/// Finds a matching auth token along with a timestamp token.
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 5f40ece..b15abfa 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -965,8 +965,7 @@
let decrypted = match key_manager
.get_per_boot_key_by_user_id(uid_to_android_user(uid))
{
- Some(key) => aes_gcm_decrypt(data, iv, tag, &(key.get_key()))
- .context(
+ Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
"In load_by_uid_alias: while trying to decrypt legacy blob.",
)?,
None => {
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 7567070..fba33f1 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -14,15 +14,18 @@
//! This module acts as a bridge between the legacy key database and the keystore2 database.
-use crate::database::{
- BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData, KeyMetaEntry,
- KeystoreDB, Uuid, KEYSTORE_UUID,
-};
use crate::error::Error;
use crate::key_parameter::KeyParameterValue;
use crate::legacy_blob::BlobValue;
use crate::utils::uid_to_android_user;
use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{
+ database::{
+ BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+ KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
+ },
+ super_key::USER_SUPER_KEY,
+};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
@@ -450,7 +453,7 @@
let super_key_id = match self
.db
- .load_super_key(user_id)
+ .load_super_key(&USER_SUPER_KEY, user_id)
.context("In check_and_migrate: Failed to load super key")?
{
Some((_, entry)) => entry.id(),
@@ -560,10 +563,12 @@
crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
.context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
- self.db.store_super_key(user_id, &(&blob, &blob_metadata)).context(concat!(
- "In check_and_migrate_super_key: ",
- "Trying to insert legacy super_key into the database."
- ))?;
+ self.db.store_super_key(user_id, &USER_SUPER_KEY, &blob, &blob_metadata).context(
+ concat!(
+ "In check_and_migrate_super_key: ",
+ "Trying to insert legacy super_key into the database."
+ ),
+ )?;
self.legacy_loader.remove_super_key(user_id);
self.recently_migrated_super_key.insert(user_id);
Ok(())
@@ -602,7 +607,7 @@
let super_key_id = self
.db
- .load_super_key(user_id)
+ .load_super_key(&USER_SUPER_KEY, user_id)
.context("In bulk_delete: Failed to load super key")?
.map(|(_, entry)| entry.id());
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index b6df8e8..97b0bed 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -35,6 +35,7 @@
pub mod security_level;
pub mod service;
pub mod shared_secret_negotiation;
+pub mod try_insert;
pub mod utils;
mod attestation_key_utils;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 47bd946..f5f9fd5 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -53,6 +53,13 @@
check_keystore_permission(KeystorePerm::change_password())
.context("In on_user_password_changed.")?;
+ if let Some(pw) = password.as_ref() {
+ DB.with(|db| {
+ SUPER_KEY.ensure_super_key_created(&mut db.borrow_mut(), user_id as u32, pw)
+ })
+ .context("In on_user_password_changed: ensure_super_key_created failed")?;
+ }
+
match DB
.with(|db| {
UserState::get_with_password_changed(
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index e50155b..4583161 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -300,7 +300,8 @@
.upgrade_keyblob_if_required_with(
&*km_dev,
key_id_guard,
- &(&km_blob, &blob_metadata),
+ &km_blob,
+ &blob_metadata,
&operation_parameters,
|blob| loop {
match map_km_error(km_dev.begin(
@@ -462,7 +463,8 @@
.upgrade_keyblob_if_required_with(
&*km_dev,
Some(key_id_guard),
- &(&KeyBlob::Ref(&blob), &blob_metadata),
+ &KeyBlob::Ref(&blob),
+ &blob_metadata,
¶ms,
|blob| {
let attest_key = Some(AttestationKey {
@@ -651,7 +653,8 @@
.upgrade_keyblob_if_required_with(
&*km_dev,
Some(wrapping_key_id_guard),
- &(&wrapping_key_blob, &wrapping_blob_metadata),
+ &wrapping_key_blob,
+ &wrapping_blob_metadata,
&[],
|wrapping_blob| {
let creation_result = map_km_error(km_dev.importWrappedKey(
@@ -671,48 +674,62 @@
.context("In import_wrapped_key: Trying to store the new key.")
}
+ fn store_upgraded_keyblob(
+ key_id_guard: KeyIdGuard,
+ km_uuid: Option<&Uuid>,
+ key_blob: &KeyBlob,
+ upgraded_blob: &[u8],
+ ) -> Result<()> {
+ let (upgraded_blob_to_be_stored, new_blob_metadata) =
+ SuperKeyManager::reencrypt_if_required(key_blob, &upgraded_blob)
+ .context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
+
+ let mut new_blob_metadata = new_blob_metadata.unwrap_or_else(BlobMetaData::new);
+ if let Some(uuid) = km_uuid {
+ new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
+ }
+
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ db.set_blob(
+ &key_id_guard,
+ SubComponentType::KEY_BLOB,
+ Some(&upgraded_blob_to_be_stored),
+ Some(&new_blob_metadata),
+ )
+ })
+ .context("In store_upgraded_keyblob: Failed to insert upgraded blob into the database.")
+ }
+
fn upgrade_keyblob_if_required_with<T, F>(
&self,
km_dev: &dyn IKeyMintDevice,
key_id_guard: Option<KeyIdGuard>,
- blob_info: &(&KeyBlob, &BlobMetaData),
+ key_blob: &KeyBlob,
+ blob_metadata: &BlobMetaData,
params: &[KeyParameter],
f: F,
) -> Result<(T, Option<Vec<u8>>)>
where
F: Fn(&[u8]) -> Result<T, Error>,
{
- match f(blob_info.0) {
+ match f(key_blob) {
Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
- let upgraded_blob = map_km_error(km_dev.upgradeKey(blob_info.0, params))
+ let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, params))
.context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
- let (upgraded_blob_to_be_stored, blob_metadata) =
- SuperKeyManager::reencrypt_on_upgrade_if_required(blob_info.0, &upgraded_blob)
- .context(
- "In upgrade_keyblob_if_required_with: Failed to handle super encryption.",
+ if let Some(kid) = key_id_guard {
+ Self::store_upgraded_keyblob(
+ kid,
+ blob_metadata.km_uuid(),
+ key_blob,
+ &upgraded_blob,
+ )
+ .context(
+ "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
)?;
-
- let mut blob_metadata = blob_metadata.unwrap_or_else(BlobMetaData::new);
- if let Some(uuid) = blob_info.1.km_uuid() {
- blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
}
- key_id_guard.map_or(Ok(()), |key_id_guard| {
- DB.with(|db| {
- let mut db = db.borrow_mut();
- db.set_blob(
- &key_id_guard,
- SubComponentType::KEY_BLOB,
- Some(&upgraded_blob_to_be_stored),
- Some(&blob_metadata),
- )
- })
- .context(concat!(
- "In upgrade_keyblob_if_required_with: ",
- "Failed to insert upgraded blob into the database.",
- ))
- })?;
match f(&upgraded_blob) {
Ok(v) => Ok((v, Some(upgraded_blob))),
Err(e) => Err(e).context(concat!(
@@ -721,10 +738,9 @@
)),
}
}
- Err(e) => {
- Err(e).context("In upgrade_keyblob_if_required_with: Failed perform operation.")
- }
- Ok(v) => Ok((v, None)),
+ result => result
+ .map(|v| (v, None))
+ .context("In upgrade_keyblob_if_required_with: Called closure failed."),
}
}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index aa434d6..ffd9d26 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -18,7 +18,7 @@
database::BlobMetaData, database::BlobMetaEntry, database::EncryptedBy, database::KeyEntry,
database::KeyType, database::KeystoreDB, enforcements::Enforcements, error::Error,
error::ResponseCode, key_parameter::KeyParameter, legacy_blob::LegacyBlobLoader,
- legacy_migrator::LegacyMigrator,
+ legacy_migrator::LegacyMigrator, try_insert::TryInsert,
};
use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
use anyhow::{Context, Result};
@@ -35,6 +35,33 @@
type UserId = u32;
+/// A particular user may have several superencryption keys in the database, each for a
+/// different purpose, distinguished by alias. Each is associated with a static
+/// constant of this type.
+pub struct SuperKeyType {
+ /// Alias used to look the key up in the `persistent.keyentry` table.
+ pub alias: &'static str,
+}
+
+/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory
+/// when the user first unlocks, and remains in memory until the device reboots.
+pub const USER_SUPER_KEY: SuperKeyType = SuperKeyType { alias: "USER_SUPER_KEY" };
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType =
+ SuperKeyType { alias: "USER_SCREEN_LOCK_BOUND_KEY" };
+
+/// Superencryption to apply to a new key.
+#[derive(Debug, Clone, Copy)]
+pub enum SuperEncryptionType {
+ /// Do not superencrypt this key.
+ None,
+ /// Superencrypt with a key that remains in memory from first unlock to reboot.
+ LskfBound,
+ /// Superencrypt with a key cleared from memory when the device is locked.
+ ScreenLockBound,
+}
+
#[derive(Default)]
struct UserSuperKeys {
/// The per boot key is used for LSKF binding of authentication bound keys. There is one
@@ -42,23 +69,23 @@
/// secret, that is itself derived from the user's lock screen knowledge factor (LSKF).
/// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted,
/// and stays memory resident until the device reboots.
- per_boot: Option<SuperKey>,
+ per_boot: Option<Arc<SuperKey>>,
/// The screen lock key works like the per boot key with the distinction that it is cleared
/// from memory when the screen lock is engaged.
- /// TODO the life cycle is not fully implemented at this time.
- screen_lock: Option<Arc<ZVec>>,
+ screen_lock_bound: Option<Arc<SuperKey>>,
}
-#[derive(Default, Clone)]
pub struct SuperKey {
- key: Arc<ZVec>,
+ key: ZVec,
// id of the super key in the database.
id: i64,
}
impl SuperKey {
- pub fn get_key(&self) -> &Arc<ZVec> {
- &self.key
+ /// For most purposes `unwrap_key` handles decryption,
+ /// but legacy handling and some tests need to assume AES and decrypt directly.
+ pub fn aes_gcm_decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+ aes_gcm_decrypt(data, iv, tag, &self.key).context("In aes_gcm_decrypt: decryption failed")
}
pub fn get_id(&self) -> i64 {
@@ -69,7 +96,13 @@
#[derive(Default)]
struct SkmState {
user_keys: HashMap<UserId, UserSuperKeys>,
- key_index: HashMap<i64, Weak<ZVec>>,
+ key_index: HashMap<i64, Weak<SuperKey>>,
+}
+
+impl SkmState {
+ fn add_key_to_key_index(&mut self, super_key: &Arc<SuperKey>) {
+ self.key_index.insert(super_key.id, Arc::downgrade(super_key));
+ }
}
#[derive(Default)]
@@ -79,21 +112,7 @@
impl SuperKeyManager {
pub fn new() -> Self {
- Self { data: Mutex::new(Default::default()) }
- }
-
- pub fn forget_screen_lock_key_for_user(&self, user: UserId) {
- let mut data = self.data.lock().unwrap();
- if let Some(usk) = data.user_keys.get_mut(&user) {
- usk.screen_lock = None;
- }
- }
-
- pub fn forget_screen_lock_keys(&self) {
- let mut data = self.data.lock().unwrap();
- for (_, usk) in data.user_keys.iter_mut() {
- usk.screen_lock = None;
- }
+ Default::default()
}
pub fn forget_all_keys_for_user(&self, user: UserId) {
@@ -101,25 +120,19 @@
data.user_keys.remove(&user);
}
- pub fn forget_all_keys(&self) {
+ fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) {
let mut data = self.data.lock().unwrap();
- data.user_keys.clear();
- data.key_index.clear();
- }
-
- fn install_per_boot_key_for_user(&self, user: UserId, super_key: SuperKey) {
- let mut data = self.data.lock().unwrap();
- data.key_index.insert(super_key.id, Arc::downgrade(&(super_key.key)));
+ data.add_key_to_key_index(&super_key);
data.user_keys.entry(user).or_default().per_boot = Some(super_key);
}
- fn get_key(&self, key_id: &i64) -> Option<Arc<ZVec>> {
+ fn get_key(&self, key_id: &i64) -> Option<Arc<SuperKey>> {
self.data.lock().unwrap().key_index.get(key_id).and_then(|k| k.upgrade())
}
- pub fn get_per_boot_key_by_user_id(&self, user_id: u32) -> Option<SuperKey> {
+ pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
let data = self.data.lock().unwrap();
- data.user_keys.get(&user_id).map(|e| e.per_boot.clone()).flatten()
+ data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
}
/// This function unlocks the super keys for a given user.
@@ -137,7 +150,7 @@
.get_or_create_key_with(
Domain::APP,
user as u64 as i64,
- KeystoreDB::USER_SUPER_KEY_ALIAS,
+ &USER_SUPER_KEY.alias,
crate::database::KEYSTORE_UUID,
|| {
// For backward compatibility we need to check if there is a super key present.
@@ -173,10 +186,11 @@
pub fn unwrap_key<'a>(&self, blob: &'a [u8], metadata: &BlobMetaData) -> Result<KeyBlob<'a>> {
match metadata.encrypted_by() {
Some(EncryptedBy::KeyId(key_id)) => match self.get_key(key_id) {
- Some(key) => Ok(KeyBlob::Sensitive(
- Self::unwrap_key_with_key(blob, metadata, &key).context("In unwrap_key.")?,
- SuperKey { key: key.clone(), id: *key_id },
- )),
+ Some(super_key) => Ok(KeyBlob::Sensitive {
+ key: Self::unwrap_key_with_key(blob, metadata, &super_key)
+ .context("In unwrap_key: unwrap_key_with_key failed")?,
+ reencrypt_with: super_key.clone(),
+ }),
None => Err(Error::Rc(ResponseCode::LOCKED))
.context("In unwrap_key: Key is not usable until the user entered their LSKF."),
},
@@ -186,9 +200,10 @@
}
/// Unwraps an encrypted key blob given an encryption key.
- fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &[u8]) -> Result<ZVec> {
+ fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &SuperKey) -> Result<ZVec> {
match (metadata.iv(), metadata.aead_tag()) {
- (Some(iv), Some(tag)) => aes_gcm_decrypt(blob, iv, tag, key)
+ (Some(iv), Some(tag)) => key
+ .aes_gcm_decrypt(blob, iv, tag)
.context("In unwrap_key_with_key: Failed to decrypt the key blob."),
(iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
concat!(
@@ -205,15 +220,10 @@
pub fn super_key_exists_in_db_for_user(
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
) -> Result<bool> {
let key_in_db = db
- .key_exists(
- Domain::APP,
- user_id as u64 as i64,
- KeystoreDB::USER_SUPER_KEY_ALIAS,
- KeyType::Super,
- )
+ .key_exists(Domain::APP, user_id as u64 as i64, &USER_SUPER_KEY.alias, KeyType::Super)
.context("In super_key_exists_in_db_for_user.")?;
if key_in_db {
@@ -232,11 +242,11 @@
&self,
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
pw: &Password,
) -> Result<UserState> {
let result = legacy_migrator
- .with_try_migrate_super_key(user_id, pw, || db.load_super_key(user_id))
+ .with_try_migrate_super_key(user_id, pw, || db.load_super_key(&USER_SUPER_KEY, user_id))
.context("In check_and_unlock_super_key. Failed to load super key")?;
match result {
@@ -260,7 +270,7 @@
&self,
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
pw: Option<&Password>,
) -> Result<UserState> {
let super_key_exists_in_db =
@@ -279,7 +289,7 @@
.context("In check_and_initialize_super_key.")?;
let key_entry = db
- .store_super_key(user_id, &(&encrypted_super_key, &blob_metadata))
+ .store_super_key(user_id, &USER_SUPER_KEY, &encrypted_super_key, &blob_metadata)
.context("In check_and_initialize_super_key. Failed to store super key.")?;
let super_key = self
@@ -294,10 +304,10 @@
//helper function to populate super key cache from the super key blob loaded from the database
fn populate_cache_from_super_key_blob(
&self,
- user_id: u32,
+ user_id: UserId,
entry: KeyEntry,
pw: &Password,
- ) -> Result<SuperKey> {
+ ) -> Result<Arc<SuperKey>> {
let super_key = Self::extract_super_key_from_key_entry(entry, pw).context(
"In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
)?;
@@ -306,7 +316,10 @@
}
/// Extracts super key from the entry loaded from the database
- pub fn extract_super_key_from_key_entry(entry: KeyEntry, pw: &Password) -> Result<SuperKey> {
+ pub fn extract_super_key_from_key_entry(
+ entry: KeyEntry,
+ pw: &Password,
+ ) -> Result<Arc<SuperKey>> {
if let Some((blob, metadata)) = entry.key_blob_info() {
let key = match (
metadata.encrypted_by(),
@@ -336,7 +349,7 @@
));
}
};
- Ok(SuperKey { key: Arc::new(key), id: entry.id() })
+ Ok(Arc::new(SuperKey { key, id: entry.id() }))
} else {
Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
.context("In extract_super_key_from_key_entry: No key blob info.")
@@ -370,7 +383,7 @@
&self,
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
key_blob: &[u8],
) -> Result<(Vec<u8>, BlobMetaData)> {
match UserState::get(db, legacy_migrator, self, user_id)
@@ -414,17 +427,31 @@
domain: &Domain,
key_parameters: &[KeyParameter],
flags: Option<i32>,
- user_id: u32,
+ user_id: UserId,
key_blob: &[u8],
) -> Result<(Vec<u8>, BlobMetaData)> {
- match (*domain, Enforcements::super_encryption_required(key_parameters, flags)) {
- (Domain::APP, true) => {
+ match Enforcements::super_encryption_required(domain, key_parameters, flags) {
+ SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
+ SuperEncryptionType::LskfBound => {
self.super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob).context(
"In handle_super_encryption_on_key_init.
Failed to super encrypt the key.",
)
}
- _ => Ok((key_blob.to_vec(), BlobMetaData::new())),
+ SuperEncryptionType::ScreenLockBound => {
+ let mut data = self.data.lock().unwrap();
+ let entry = data.user_keys.entry(user_id).or_default();
+ if let Some(super_key) = entry.screen_lock_bound.as_ref() {
+ Self::encrypt_with_super_key(key_blob, &super_key).context(concat!(
+ "In handle_super_encryption_on_key_init. ",
+ "Failed to encrypt the key with screen_lock_bound key."
+ ))
+ } else {
+ // FIXME: until ec encryption lands, we only superencrypt if the key
+ // happens to be present ie the device is unlocked.
+ Ok((key_blob.to_vec(), BlobMetaData::new()))
+ }
+ }
}
}
@@ -448,17 +475,14 @@
/// Check if a given key needs re-super-encryption, from its KeyBlob type.
/// If so, re-super-encrypt the key and return a new set of metadata,
/// containing the new super encryption information.
- pub fn reencrypt_on_upgrade_if_required<'a>(
+ pub fn reencrypt_if_required<'a>(
key_blob_before_upgrade: &KeyBlob,
key_after_upgrade: &'a [u8],
) -> Result<(KeyBlob<'a>, Option<BlobMetaData>)> {
match key_blob_before_upgrade {
- KeyBlob::Sensitive(_, super_key) => {
+ KeyBlob::Sensitive { reencrypt_with: super_key, .. } => {
let (key, metadata) = Self::encrypt_with_super_key(key_after_upgrade, super_key)
- .context(concat!(
- "In reencrypt_on_upgrade_if_required. ",
- "Failed to re-super-encrypt key on key upgrade."
- ))?;
+ .context("In reencrypt_if_required: Failed to re-super-encrypt key.")?;
Ok((KeyBlob::NonSensitive(key), Some(metadata)))
}
_ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
@@ -472,6 +496,72 @@
}
false
}
+
+ /// Fetch a superencryption key from the database, or create it if it doesn't already exist.
+ /// When this is called, the caller must hold the lock on the SuperKeyManager.
+ /// So it's OK that the check and creation are different DB transactions.
+ fn get_or_create_super_key(
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ key_type: &SuperKeyType,
+ password: &Password,
+ ) -> Result<Arc<SuperKey>> {
+ let loaded_key = db.load_super_key(key_type, user_id)?;
+ if let Some((_, key_entry)) = loaded_key {
+ Ok(Self::extract_super_key_from_key_entry(key_entry, password)?)
+ } else {
+ let super_key = generate_aes256_key()
+ .context("In get_or_create_super_key: Failed to generate AES 256 key.")?;
+ //derive an AES256 key from the password and re-encrypt the super key
+ //before we insert it in the database.
+ let (encrypted_super_key, blob_metadata) =
+ Self::encrypt_with_password(&super_key, password)
+ .context("In get_or_create_super_key.")?;
+ let key_entry = db
+ .store_super_key(user_id, key_type, &encrypted_super_key, &blob_metadata)
+ .context("In get_or_create_super_key. Failed to store super key.")?;
+ Ok(Arc::new(SuperKey { key: super_key, id: key_entry.id() }))
+ }
+ }
+
+ /// Create the screen-lock bound key for this user if it doesn't already exist
+ pub fn ensure_super_key_created(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ password: &Password,
+ ) -> Result<()> {
+ // Lock data to ensure no concurrent attempts to create the key.
+ let _data = self.data.lock().unwrap();
+ Self::get_or_create_super_key(db, user_id, &USER_SCREEN_LOCK_BOUND_KEY, password)?;
+ Ok(())
+ }
+
+ /// Decrypt the screen-lock bound key for this user using the password and store in memory.
+ pub fn unlock_screen_lock_bound_key(
+ &self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ password: &Password,
+ ) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ let entry = data.user_keys.entry(user_id).or_default();
+ let aes = entry
+ .screen_lock_bound
+ .get_or_try_to_insert_with(|| {
+ Self::get_or_create_super_key(db, user_id, &USER_SCREEN_LOCK_BOUND_KEY, password)
+ })?
+ .clone();
+ data.add_key_to_key_index(&aes);
+ Ok(())
+ }
+
+ /// Wipe the screen-lock bound key for this user from memory.
+ pub fn lock_screen_lock_bound_key(&self, user_id: UserId) {
+ let mut data = self.data.lock().unwrap();
+ let mut entry = data.user_keys.entry(user_id).or_default();
+ entry.screen_lock_bound = None;
+ }
}
/// This enum represents different states of the user's life cycle in the device.
@@ -479,7 +569,7 @@
pub enum UserState {
// The user has registered LSKF and has unlocked the device by entering PIN/Password,
// and hence the per-boot super key is available in the cache.
- LskfUnlocked(SuperKey),
+ LskfUnlocked(Arc<SuperKey>),
// The user has registered LSKF, but has not unlocked the device using password, after reboot.
// Hence the per-boot super-key(s) is not available in the cache.
// However, the encrypted super key is available in the database.
@@ -494,7 +584,7 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
- user_id: u32,
+ user_id: UserId,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
@@ -517,7 +607,7 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
- user_id: u32,
+ user_id: UserId,
password: Option<&Password>,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
@@ -551,7 +641,7 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
- user_id: u32,
+ user_id: UserId,
password: &Password,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
@@ -577,18 +667,18 @@
db: &mut KeystoreDB,
skm: &SuperKeyManager,
legacy_migrator: &LegacyMigrator,
- user_id: u32,
+ user_id: UserId,
keep_non_super_encrypted_keys: bool,
) -> Result<()> {
// mark keys created on behalf of the user as unreferenced.
legacy_migrator
.bulk_delete_user(user_id, keep_non_super_encrypted_keys)
.context("In reset_user: Trying to delete legacy keys.")?;
- db.unbind_keys_for_user(user_id as u32, keep_non_super_encrypted_keys)
+ db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
.context("In reset user. Error in unbinding keys.")?;
//delete super key in cache, if exists
- skm.forget_all_keys_for_user(user_id as u32);
+ skm.forget_all_keys_for_user(user_id);
Ok(())
}
}
@@ -599,7 +689,7 @@
/// `Ref` holds a reference to a key blob when it does not need to be modified if its
/// life time allows it.
pub enum KeyBlob<'a> {
- Sensitive(ZVec, SuperKey),
+ Sensitive { key: ZVec, reencrypt_with: Arc<SuperKey> },
NonSensitive(Vec<u8>),
Ref(&'a [u8]),
}
@@ -610,7 +700,7 @@
fn deref(&self) -> &Self::Target {
match self {
- Self::Sensitive(key, _) => &key,
+ Self::Sensitive { key, .. } => &key,
Self::NonSensitive(key) => &key,
Self::Ref(key) => key,
}
diff --git a/keystore2/src/try_insert.rs b/keystore2/src/try_insert.rs
new file mode 100644
index 0000000..6dd3962
--- /dev/null
+++ b/keystore2/src/try_insert.rs
@@ -0,0 +1,100 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! The TryInsert trait adds to Option<T> the method
+//! get_or_try_to_insert_with, which is analogous to
+//! get_or_insert_with, but allows the called function to fail and propagates the failure.
+
+/// The TryInsert trait adds to Option<T> the method
+/// get_or_try_to_insert_with, which is analogous to
+/// get_or_insert_with, but allows the called function to fail and propagates the failure.
+pub trait TryInsert {
+ /// Type of the Ok branch of the Result
+ type Item;
+ /// Inserts a value computed from `f` into the option if it is [`None`],
+ /// then returns a mutable reference to the contained value. If `f`
+ /// returns Err, the Option is unchanged.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut x = None;
+ /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
+ /// {
+ /// let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
+ /// assert_eq!(y, &5);
+ ///
+ /// *y = 7;
+ /// }
+ ///
+ /// assert_eq!(x, Some(7));
+ /// ```
+ fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+ &mut self,
+ f: F,
+ ) -> Result<&mut Self::Item, E>;
+}
+
+impl<T> TryInsert for Option<T> {
+ type Item = T;
+ fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+ &mut self,
+ f: F,
+ ) -> Result<&mut Self::Item, E> {
+ if self.is_none() {
+ *self = Some(f()?);
+ }
+
+ match self {
+ Some(v) => Ok(v),
+ // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+ // variant in the code above.
+ None => unsafe { std::hint::unreachable_unchecked() },
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn fails() -> Result<i32, String> {
+ Err("fail".to_string())
+ }
+
+ fn succeeds() -> Result<i32, String> {
+ Ok(99)
+ }
+
+ #[test]
+ fn test() {
+ let mut x = None;
+ assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
+ assert_eq!(x, None);
+ assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
+ assert_eq!(x, Some(99));
+ x = Some(42);
+ assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
+ assert_eq!(x, Some(42));
+ assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
+ assert_eq!(x, Some(42));
+ *x.get_or_try_to_insert_with(fails).unwrap() = 2;
+ assert_eq!(x, Some(2));
+ *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
+ assert_eq!(x, Some(3));
+ x = None;
+ *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
+ assert_eq!(x, Some(5));
+ }
+}