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/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.")),
}