summaryrefslogtreecommitdiff
path: root/keystore2/src/super_key.rs
diff options
context:
space:
mode:
Diffstat (limited to 'keystore2/src/super_key.rs')
-rw-r--r--keystore2/src/super_key.rs241
1 files changed, 182 insertions, 59 deletions
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 3fa4cf02..b78560f7 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -13,6 +13,7 @@
// limitations under the License.
use crate::{
+ boot_level_keys::{get_level_zero_key, BootLevelKeyCache},
database::BlobMetaData,
database::BlobMetaEntry,
database::EncryptedBy,
@@ -34,6 +35,7 @@ use keystore2_crypto::{
aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
AES_256_KEY_LENGTH,
};
+use keystore2_system_property::PropertyWatcher;
use std::ops::Deref;
use std::{
collections::HashMap,
@@ -41,6 +43,8 @@ use std::{
sync::{Mutex, Weak},
};
+const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
+
type UserId = u32;
/// Encryption algorithm used by a particular type of superencryption key
@@ -90,13 +94,47 @@ pub enum SuperEncryptionType {
LskfBound,
/// Superencrypt with a key cleared from memory when the device is locked.
ScreenLockBound,
+ /// Superencrypt with a key based on the desired boot level
+ BootLevel(i32),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum SuperKeyIdentifier {
+ /// id of the super key in the database.
+ DatabaseId(i64),
+ /// Boot level of the encrypting boot level key
+ BootLevel(i32),
+}
+
+impl SuperKeyIdentifier {
+ fn from_metadata(metadata: &BlobMetaData) -> Option<Self> {
+ if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
+ Some(SuperKeyIdentifier::DatabaseId(*key_id))
+ } else if let Some(boot_level) = metadata.max_boot_level() {
+ Some(SuperKeyIdentifier::BootLevel(*boot_level))
+ } else {
+ None
+ }
+ }
+
+ fn add_to_metadata(&self, metadata: &mut BlobMetaData) {
+ match self {
+ SuperKeyIdentifier::DatabaseId(id) => {
+ metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(*id)));
+ }
+ SuperKeyIdentifier::BootLevel(level) => {
+ metadata.add(BlobMetaEntry::MaxBootLevel(*level));
+ }
+ }
+ }
}
pub struct SuperKey {
algorithm: SuperEncryptionAlgorithm,
key: ZVec,
- // id of the super key in the database.
- id: i64,
+ /// Identifier of the encrypting key, used to write an encrypted blob
+ /// back to the database after re-encryption eg on a key update.
+ id: SuperKeyIdentifier,
/// ECDH is more expensive than AES. So on ECDH private keys we set the
/// reencrypt_with field to point at the corresponding AES key, and the
/// keys will be re-encrypted with AES on first use.
@@ -136,11 +174,20 @@ struct UserSuperKeys {
struct SkmState {
user_keys: HashMap<UserId, UserSuperKeys>,
key_index: HashMap<i64, Weak<SuperKey>>,
+ boot_level_key_cache: Option<BootLevelKeyCache>,
}
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));
+ fn add_key_to_key_index(&mut self, super_key: &Arc<SuperKey>) -> Result<()> {
+ if let SuperKeyIdentifier::DatabaseId(id) = super_key.id {
+ self.key_index.insert(id, Arc::downgrade(super_key));
+ Ok(())
+ } else {
+ Err(Error::sys()).context(format!(
+ "In add_key_to_key_index: cannot add key with ID {:?}",
+ super_key.id
+ ))
+ }
}
}
@@ -150,19 +197,101 @@ pub struct SuperKeyManager {
}
impl SuperKeyManager {
+ pub fn set_up_boot_level_cache(self: &Arc<Self>, db: &mut KeystoreDB) -> Result<()> {
+ let mut data = self.data.lock().unwrap();
+ if data.boot_level_key_cache.is_some() {
+ log::info!("In set_up_boot_level_cache: called for a second time");
+ return Ok(());
+ }
+ let level_zero_key = get_level_zero_key(db)
+ .context("In set_up_boot_level_cache: get_level_zero_key failed")?;
+ data.boot_level_key_cache = Some(BootLevelKeyCache::new(level_zero_key));
+ log::info!("Starting boot level watcher.");
+ let clone = self.clone();
+ std::thread::spawn(move || {
+ clone
+ .watch_boot_level()
+ .unwrap_or_else(|e| log::error!("watch_boot_level failed:\n{:?}", e));
+ });
+ Ok(())
+ }
+
+ /// Watch the `keystore.boot_level` system property, and keep boot level up to date.
+ /// Blocks waiting for system property changes, so must be run in its own thread.
+ fn watch_boot_level(&self) -> Result<()> {
+ let mut w = PropertyWatcher::new("keystore.boot_level")
+ .context("In watch_boot_level: PropertyWatcher::new failed")?;
+ loop {
+ let level = w
+ .read(|_n, v| v.parse::<usize>().map_err(std::convert::Into::into))
+ .context("In watch_boot_level: read of property failed")?;
+ // watch_boot_level should only be called once data.boot_level_key_cache is Some,
+ // so it's safe to unwrap in the branches below.
+ if level < MAX_MAX_BOOT_LEVEL {
+ log::info!("Read keystore.boot_level value {}", level);
+ let mut data = self.data.lock().unwrap();
+ data.boot_level_key_cache
+ .as_mut()
+ .unwrap()
+ .advance_boot_level(level)
+ .context("In watch_boot_level: advance_boot_level failed")?;
+ } else {
+ log::info!(
+ "keystore.boot_level {} hits maximum {}, finishing.",
+ level,
+ MAX_MAX_BOOT_LEVEL
+ );
+ let mut data = self.data.lock().unwrap();
+ data.boot_level_key_cache.as_mut().unwrap().finish();
+ break;
+ }
+ w.wait().context("In watch_boot_level: property wait failed")?;
+ }
+ Ok(())
+ }
+
+ pub fn level_accessible(&self, boot_level: i32) -> bool {
+ self.data
+ .lock()
+ .unwrap()
+ .boot_level_key_cache
+ .as_ref()
+ .map_or(false, |c| c.level_accessible(boot_level as usize))
+ }
+
pub fn forget_all_keys_for_user(&self, user: UserId) {
let mut data = self.data.lock().unwrap();
data.user_keys.remove(&user);
}
- fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) {
+ fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) -> Result<()> {
let mut data = self.data.lock().unwrap();
- data.add_key_to_key_index(&super_key);
+ data.add_key_to_key_index(&super_key)
+ .context("In install_per_boot_key_for_user: add_key_to_key_index failed")?;
data.user_keys.entry(user).or_default().per_boot = Some(super_key);
+ Ok(())
}
- fn lookup_key(&self, key_id: &i64) -> Option<Arc<SuperKey>> {
- self.data.lock().unwrap().key_index.get(key_id).and_then(|k| k.upgrade())
+ fn lookup_key(&self, key_id: &SuperKeyIdentifier) -> Result<Option<Arc<SuperKey>>> {
+ let mut data = self.data.lock().unwrap();
+ Ok(match key_id {
+ SuperKeyIdentifier::DatabaseId(id) => data.key_index.get(id).and_then(|k| k.upgrade()),
+ SuperKeyIdentifier::BootLevel(level) => data
+ .boot_level_key_cache
+ .as_mut()
+ .map(|b| b.aes_key(*level as usize))
+ .transpose()
+ .context("In lookup_key: aes_key failed")?
+ .flatten()
+ .map(|key| {
+ Arc::new(SuperKey {
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ key,
+ id: *key_id,
+ reencrypt_with: None,
+ })
+ }),
+ })
}
pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
@@ -215,26 +344,27 @@ impl SuperKeyManager {
Ok(())
}
- /// Unwraps an encrypted key blob given metadata identifying the encryption key.
- /// The function queries `metadata.encrypted_by()` to determine the encryption key.
- /// It then checks if the required key is memory resident, and if so decrypts the
- /// blob.
- pub fn unwrap_key<'a>(&self, blob: &'a [u8], metadata: &BlobMetaData) -> Result<KeyBlob<'a>> {
- let key_id = if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
- key_id
+ /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
+ /// the relevant super key.
+ pub fn unwrap_key_if_required<'a>(
+ &self,
+ metadata: &BlobMetaData,
+ blob: &'a [u8],
+ ) -> Result<KeyBlob<'a>> {
+ Ok(if let Some(key_id) = SuperKeyIdentifier::from_metadata(metadata) {
+ let super_key = self
+ .lookup_key(&key_id)
+ .context("In unwrap_key: lookup_key failed")?
+ .ok_or(Error::Rc(ResponseCode::LOCKED))
+ .context("In unwrap_key: Required super decryption key is not in memory.")?;
+ 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.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
+ force_reencrypt: super_key.reencrypt_with.is_some(),
+ }
} else {
- return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
- .context("In unwrap_key: Cannot determine wrapping key.");
- };
- let super_key = self
- .lookup_key(&key_id)
- .ok_or(Error::Rc(ResponseCode::LOCKED))
- .context("In unwrap_key: Required super decryption key is not in memory.")?;
- 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.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
- force_reencrypt: super_key.reencrypt_with.is_some(),
+ KeyBlob::Ref(blob)
})
}
@@ -389,7 +519,7 @@ impl SuperKeyManager {
.context(
"In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
)?;
- self.install_per_boot_key_for_user(user_id, super_key.clone());
+ self.install_per_boot_key_for_user(user_id, super_key.clone())?;
Ok(super_key)
}
@@ -430,7 +560,12 @@ impl SuperKeyManager {
));
}
};
- Ok(Arc::new(SuperKey { algorithm, key, id: entry.id(), reencrypt_with }))
+ Ok(Arc::new(SuperKey {
+ algorithm,
+ key,
+ id: SuperKeyIdentifier::DatabaseId(entry.id()),
+ reencrypt_with,
+ }))
} else {
Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
.context("In extract_super_key_from_key_entry: No key blob info.")
@@ -498,7 +633,7 @@ impl SuperKeyManager {
.context("In encrypt_with_aes_super_key: Failed to encrypt new super key.")?;
metadata.add(BlobMetaEntry::Iv(iv));
metadata.add(BlobMetaEntry::AeadTag(tag));
- metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key.id)));
+ super_key.id.add_to_metadata(&mut metadata);
Ok((encrypted_key, metadata))
}
@@ -554,27 +689,23 @@ impl SuperKeyManager {
metadata.add(BlobMetaEntry::Salt(salt));
metadata.add(BlobMetaEntry::Iv(iv));
metadata.add(BlobMetaEntry::AeadTag(aead_tag));
- metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(key_id_guard.id())));
+ SuperKeyIdentifier::DatabaseId(key_id_guard.id())
+ .add_to_metadata(&mut metadata);
Ok((encrypted_key, metadata))
}
}
- }
- }
-
- /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
- /// the relevant super key.
- pub fn unwrap_key_if_required<'a>(
- &self,
- metadata: &BlobMetaData,
- key_blob: &'a [u8],
- ) -> Result<KeyBlob<'a>> {
- if Self::key_super_encrypted(&metadata) {
- let unwrapped_key = self
- .unwrap_key(key_blob, metadata)
- .context("In unwrap_key_if_required. Error in unwrapping the key.")?;
- Ok(unwrapped_key)
- } else {
- Ok(KeyBlob::Ref(key_blob))
+ SuperEncryptionType::BootLevel(level) => {
+ let key_id = SuperKeyIdentifier::BootLevel(level);
+ let super_key = self
+ .lookup_key(&key_id)
+ .context("In handle_super_encryption_on_key_init: lookup_key failed")?
+ .ok_or(Error::Rc(ResponseCode::LOCKED))
+ .context("In handle_super_encryption_on_key_init: Boot stage key absent")?;
+ Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
+ "In handle_super_encryption_on_key_init: ",
+ "Failed to encrypt with BootLevel key."
+ ))
+ }
}
}
@@ -596,14 +727,6 @@ impl SuperKeyManager {
}
}
- // Helper function to decide if a key is super encrypted, given metadata.
- fn key_super_encrypted(metadata: &BlobMetaData) -> bool {
- if let Some(&EncryptedBy::KeyId(_)) = metadata.encrypted_by() {
- return true;
- }
- 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.
@@ -663,7 +786,7 @@ impl SuperKeyManager {
Ok(Arc::new(SuperKey {
algorithm: key_type.algorithm,
key: super_key,
- id: key_entry.id(),
+ id: SuperKeyIdentifier::DatabaseId(key_entry.id()),
reencrypt_with,
}))
}
@@ -702,8 +825,8 @@ impl SuperKeyManager {
)
})?
.clone();
- data.add_key_to_key_index(&aes);
- data.add_key_to_key_index(&ecdh);
+ data.add_key_to_key_index(&aes)?;
+ data.add_key_to_key_index(&ecdh)?;
Ok(())
}