Cope with Keymaster->KeyMint device upgrade
When handling keyblob upgrade required, also watch out for an invalid
keyblob error that might indicate that a key used to be a
km_compat-wrapped Keymaster key.
In this situation, try stripping off the km_compat prefix and
attempt upgrade of the inner keyblob data instead.
Bug: 251426862
Bug: 283077822
Bug: 296403357
Test: tested with ARC upgrade, see b/296403357
Change-Id: I8539455e33ab2e1c97f26174476ee9d616269e74
diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs
index 035edd9..8eba02d 100644
--- a/keystore2/src/km_compat.rs
+++ b/keystore2/src/km_compat.rs
@@ -32,6 +32,11 @@
use anyhow::Context;
use keystore2_crypto::{hmac_sha256, HMAC_SHA256_LEN};
+/// Magic prefix used by the km_compat C++ code to mark a key that is owned by an
+/// underlying Keymaster hardware device that has been wrapped by km_compat. (The
+/// final zero byte indicates that the blob is not software emulated.)
+pub const KEYMASTER_BLOB_HW_PREFIX: &[u8] = b"pKMblob\x00";
+
/// Key data associated with key generation/import.
#[derive(Debug, PartialEq, Eq)]
pub enum KeyImportData<'a> {
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 325c213..159e936 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -914,11 +914,12 @@
uuid: &Uuid,
blob: &[u8],
) -> Result<(Vec<KeyParameter>, Option<Vec<u8>>)> {
- let (km_dev, _) = crate::globals::get_keymint_dev_by_uuid(uuid)
+ let (km_dev, info) = crate::globals::get_keymint_dev_by_uuid(uuid)
.with_context(|| ks_err!("Trying to get km device for id {:?}", uuid))?;
let (characteristics, upgraded_blob) = upgrade_keyblob_if_required_with(
&*km_dev,
+ info.versionNumber,
blob,
&[],
|blob| {
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index 860a1bc..44d805c 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -265,6 +265,7 @@
{
let (f_result, upgraded_blob) = crate::utils::upgrade_keyblob_if_required_with(
&*self.km_dev,
+ self.version(),
&key_blob,
&[],
f,
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/src/rkpd_client.rs
index 938d389..7b4131d 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/src/rkpd_client.rs
@@ -666,7 +666,7 @@
fn test_rkpd_attestation_key_upgrade() {
binder::ProcessState::start_thread_pool();
let security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
- let (keymint, _, _) = get_keymint_device(&security_level).unwrap();
+ let (keymint, info, _) = get_keymint_device(&security_level).unwrap();
let key_id = get_next_key_id();
let mut key_upgraded = false;
@@ -676,6 +676,7 @@
upgrade_keyblob_if_required_with(
&*keymint,
+ info.versionNumber,
&key.keyBlob,
/*upgrade_params=*/ &[],
/*km_op=*/
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index db44d4b..44ca4c8 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -317,7 +317,6 @@
let (begin_result, upgraded_blob) = self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
key_id_guard,
&km_blob,
blob_metadata.km_uuid().copied(),
@@ -561,7 +560,6 @@
issuer_subject,
}) => self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
Some(key_id_guard),
&KeyBlob::Ref(&blob),
blob_metadata.km_uuid().copied(),
@@ -786,7 +784,6 @@
let (creation_result, _) = self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
Some(wrapping_key_id_guard),
&wrapping_key_blob,
wrapping_blob_metadata.km_uuid().copied(),
@@ -842,7 +839,6 @@
fn upgrade_keyblob_if_required_with<T, F>(
&self,
- km_dev: &dyn IKeyMintDevice,
mut key_id_guard: Option<KeyIdGuard>,
key_blob: &KeyBlob,
km_uuid: Option<Uuid>,
@@ -853,7 +849,8 @@
F: Fn(&[u8]) -> Result<T, Error>,
{
let (v, upgraded_blob) = crate::utils::upgrade_keyblob_if_required_with(
- km_dev,
+ &*self.keymint,
+ self.hw_info.versionNumber,
key_blob,
params,
f,
@@ -893,6 +890,7 @@
{
crate::utils::upgrade_keyblob_if_required_with(
&*self.keymint,
+ self.hw_info.versionNumber,
key_blob,
params,
f,
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 584a51c..80aa7c3 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -23,6 +23,8 @@
use crate::{
database::{KeyType, KeystoreDB},
globals::LEGACY_IMPORTER,
+ km_compat,
+ raw_device::KeyMintDevice,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
@@ -163,13 +165,9 @@
.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>(
+/// Upgrade a keyblob then invoke both the `new_blob_handler` and the `km_op` closures. On success
+/// a tuple of the `km_op`s result and the optional upgraded blob is returned.
+fn upgrade_keyblob_and_perform_op<T, KmOp, NewBlobHandler>(
km_dev: &dyn IKeyMintDevice,
key_blob: &[u8],
upgrade_params: &[KmKeyParameter],
@@ -180,22 +178,75 @@
KmOp: Fn(&[u8]) -> Result<T, Error>,
NewBlobHandler: FnOnce(&[u8]) -> Result<()>,
{
+ let upgraded_blob = {
+ let _wp = watchdog::watch_millis(
+ "In utils::upgrade_keyblob_and_perform_op: calling upgradeKey.",
+ 500,
+ );
+ map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
+ }
+ .context(ks_err!("Upgrade failed."))?;
+
+ new_blob_handler(&upgraded_blob).context(ks_err!("calling new_blob_handler."))?;
+
+ km_op(&upgraded_blob)
+ .map(|v| (v, Some(upgraded_blob)))
+ .context(ks_err!("Calling km_op after upgrade."))
+}
+
+/// 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,
+ km_dev_version: i32,
+ 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(ks_err!("Upgrade failed."))?;
-
- new_blob_handler(&upgraded_blob).context(ks_err!("calling new_blob_handler."))?;
-
- km_op(&upgraded_blob)
- .map(|v| (v, Some(upgraded_blob)))
- .context(ks_err!("Calling km_op after upgrade."))
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => upgrade_keyblob_and_perform_op(
+ km_dev,
+ key_blob,
+ upgrade_params,
+ km_op,
+ new_blob_handler,
+ ),
+ // Some devices have been known to upgrade their Keymaster device to be a KeyMint
+ // device with a new release of Android. If this is the case, then any pre-upgrade
+ // keyblobs will have the km_compat prefix attached to them.
+ //
+ // This prefix gets stripped by the km_compat layer when used pre-upgrade, but after
+ // the upgrade the keyblob will be passed as-is to the KeyMint device, which probably
+ // won't expect to see the km_compat prefix.
+ //
+ // So if a keyblob:
+ // a) gets rejected with INVALID_KEY_BLOB
+ // b) when sent to a KeyMint (not km_compat) device
+ // c) and has the km_compat magic prefix
+ // d) and was not a software-emulated key pre-upgrade
+ // then strip the prefix and attempt a key upgrade.
+ Err(Error::Km(ErrorCode::INVALID_KEY_BLOB))
+ if km_dev_version >= KeyMintDevice::KEY_MINT_V1
+ && key_blob.starts_with(km_compat::KEYMASTER_BLOB_HW_PREFIX) =>
+ {
+ log::info!("found apparent km_compat(Keymaster) blob, attempt strip-and-upgrade");
+ let inner_keyblob = &key_blob[km_compat::KEYMASTER_BLOB_HW_PREFIX.len()..];
+ upgrade_keyblob_and_perform_op(
+ km_dev,
+ inner_keyblob,
+ upgrade_params,
+ km_op,
+ new_blob_handler,
+ )
}
r => r.map(|v| (v, None)).context(ks_err!("Calling km_op.")),
}