Keystore 2.0: Teach keystore to decrypt generic blobs.
This CL addresses various gaps in legacy support.
* Encrypted legacy blobs.
* Encrypted key characteristics files (pre Android Q).
* Encrypted certificate and certificate chain entries
(pre Android R).
To support key migration even when the corresponding user is locked,
keys can now be migrated in the legacy database by renaming files.
In order to construct a complete a key characteristics cache from old
characteristics files the information must be augmented with the
characteristics that can be extracted from the key blob by calling
KeyMintDevice::getKeyCharacteristics. For this to work, the blob
may need to be decrypted, upgraded, and reencrypted. The crypto steps
may fail with ResponseCode::LOCKED though if the user is locked.
If the key was upgraded in the process both the old and the new key
blob must be inserted into the database in order for the garbage
collector to reap and invalidate the superseded blob correctly.
At the time APPLICATION_ID and APPLICATION_DATA are usually not
available. This would cause such bound keys to fail with
ErrorCode::INVALID_KEY_BLOB. However, APPLICATION_ID/DATA were
never exposed to applications though, so this should be acceptable
for now.
Bug: 213173772
Bug: 213172664
Bug: 203101472
Test: keystore2_test
Change-Id: Id8561d3f98d53182709d9f4feeeecda3b1535077
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index c924bef..a312c4b 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -15,7 +15,8 @@
//! This module implements utility functions used by the Keystore 2.0 service
//! implementation.
-use crate::error::{map_binder_status, Error, ErrorCode};
+use crate::error::{map_binder_status, map_km_error, Error, ErrorCode};
+use crate::key_parameter::KeyParameter;
use crate::permission;
use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
use crate::{
@@ -23,7 +24,8 @@
globals::LEGACY_IMPORTER,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- KeyCharacteristics::KeyCharacteristics, Tag::Tag,
+ IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
+ KeyParameter::KeyParameter as KmKeyParameter, Tag::Tag,
};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_security_apc::aidl::android::security::apc::{
@@ -40,6 +42,8 @@
APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
APC_COMPAT_ERROR_SYSTEM_ERROR,
};
+use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
+use std::iter::IntoIterator;
/// This function uses its namesake in the permission module and in
/// combination with with_calling_sid from the binder crate to check
@@ -135,18 +139,60 @@
/// representation of the keystore service.
pub fn key_characteristics_to_internal(
key_characteristics: Vec<KeyCharacteristics>,
-) -> Vec<crate::key_parameter::KeyParameter> {
+) -> Vec<KeyParameter> {
key_characteristics
.into_iter()
.flat_map(|aidl_key_char| {
let sec_level = aidl_key_char.securityLevel;
- aidl_key_char.authorizations.into_iter().map(move |aidl_kp| {
- crate::key_parameter::KeyParameter::new(aidl_kp.into(), sec_level)
- })
+ aidl_key_char
+ .authorizations
+ .into_iter()
+ .map(move |aidl_kp| KeyParameter::new(aidl_kp.into(), sec_level))
})
.collect()
}
+/// This function can be used to upgrade key blobs on demand. The return value of
+/// `km_op` is inspected and if ErrorCode::KEY_REQUIRES_UPGRADE is encountered,
+/// an attempt is made to upgrade the key blob. On success `new_blob_handler` is called
+/// with the upgraded blob as argument. Then `km_op` is called a second time with the
+/// upgraded blob as argument. On success a tuple of the `km_op`s result and the
+/// optional upgraded blob is returned.
+pub fn upgrade_keyblob_if_required_with<T, KmOp, NewBlobHandler>(
+ km_dev: &dyn IKeyMintDevice,
+ key_blob: &[u8],
+ upgrade_params: &[KmKeyParameter],
+ km_op: KmOp,
+ new_blob_handler: NewBlobHandler,
+) -> Result<(T, Option<Vec<u8>>)>
+where
+ KmOp: Fn(&[u8]) -> Result<T, Error>,
+ NewBlobHandler: FnOnce(&[u8]) -> Result<()>,
+{
+ match km_op(key_blob) {
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = {
+ let _wp = watchdog::watch_millis(
+ "In utils::upgrade_keyblob_if_required_with: calling upgradeKey.",
+ 500,
+ );
+ map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
+ }
+ .context("In utils::upgrade_keyblob_if_required_with: Upgrade failed.")?;
+
+ new_blob_handler(&upgraded_blob)
+ .context("In utils::upgrade_keyblob_if_required_with: calling new_blob_handler.")?;
+
+ km_op(&upgraded_blob)
+ .map(|v| (v, Some(upgraded_blob)))
+ .context("In utils::upgrade_keyblob_if_required_with: Calling km_op after upgrade.")
+ }
+ r => r
+ .map(|v| (v, None))
+ .context("In utils::upgrade_keyblob_if_required_with: Calling km_op."),
+ }
+}
+
/// Converts a set of key characteristics from the internal representation into a set of
/// Authorizations as they are used to convey key characteristics to the clients of keystore.
pub fn key_parameters_to_authorizations(
@@ -255,6 +301,36 @@
}
}
+/// Trait implemented by objects that can be used to decrypt cipher text using AES-GCM.
+pub trait AesGcm {
+ /// Deciphers `data` using the initialization vector `iv` and AEAD tag `tag`
+ /// and AES-GCM. The implementation provides the key material and selects
+ /// the implementation variant, e.g., AES128 or AES265.
+ fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec>;
+
+ /// Encrypts `data` and returns the ciphertext, the initialization vector `iv`
+ /// and AEAD tag `tag`. The implementation provides the key material and selects
+ /// the implementation variant, e.g., AES128 or AES265.
+ fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)>;
+}
+
+/// Marks an object as AES-GCM key.
+pub trait AesGcmKey {
+ /// Provides access to the raw key material.
+ fn key(&self) -> &[u8];
+}
+
+impl<T: AesGcmKey> AesGcm for T {
+ fn decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+ aes_gcm_decrypt(data, iv, tag, self.key())
+ .context("In AesGcm<T>::decrypt: Decryption failed")
+ }
+
+ fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>)> {
+ aes_gcm_encrypt(plaintext, self.key()).context("In AesGcm<T>::encrypt: Encryption failed.")
+ }
+}
+
/// This module provides empty/noop implementations of the watch dog utility functions.
#[cfg(not(feature = "watchdog"))]
pub mod watchdog {