Merge changes I28f673b6,I146f7cfd into main am: 39b7af2fcd

Original change: https://android-review.googlesource.com/c/platform/system/security/+/2821841

Change-Id: Ie8a1edf47feacc1a8a2be827e5f3cc82160d5742
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/keystore2/aconfig/flags.aconfig b/keystore2/aconfig/flags.aconfig
index 6a65f11..41e1a92 100644
--- a/keystore2/aconfig/flags.aconfig
+++ b/keystore2/aconfig/flags.aconfig
@@ -15,3 +15,11 @@
   bug: "307460850"
   is_fixed_read_only: true
 }
+
+flag {
+  name: "import_previously_emulated_keys"
+  namespace: "hardware_backed_security"
+  description: "Include support for importing keys that were previously software-emulated into KeyMint"
+  bug: "283077822"
+  is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs
index cd58fe4..03c9d02 100644
--- a/keystore2/src/km_compat.rs
+++ b/keystore2/src/km_compat.rs
@@ -37,6 +37,11 @@
 /// final zero byte indicates that the blob is not software emulated.)
 pub const KEYMASTER_BLOB_HW_PREFIX: &[u8] = b"pKMblob\x00";
 
+/// Magic prefix used by the km_compat C++ code to mark a key that is owned by an
+/// software emulation device that has been wrapped by km_compat. (The final one
+/// byte indicates that the blob is software emulated.)
+pub const KEYMASTER_BLOB_SW_PREFIX: &[u8] = b"pKMblob\x01";
+
 /// Key data associated with key generation/import.
 #[derive(Debug, PartialEq, Eq)]
 pub enum KeyImportData<'a> {
@@ -94,7 +99,7 @@
 
 /// Return an unwrapped version of the provided `keyblob`, which may or may
 /// not be associated with the software emulation.
-fn unwrap_keyblob(keyblob: &[u8]) -> KeyBlob {
+pub fn unwrap_keyblob(keyblob: &[u8]) -> KeyBlob {
     if !keyblob.starts_with(KEYBLOB_PREFIX) {
         return KeyBlob::Raw(keyblob);
     }
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 7a27452..6fb0eb2 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -34,7 +34,7 @@
 use crate::utils::{
     check_device_attestation_permissions, check_key_permission,
     check_unique_id_attestation_permissions, is_device_id_attestation_tag,
-    key_characteristics_to_internal, uid_to_android_user, watchdog as wd,
+    key_characteristics_to_internal, uid_to_android_user, watchdog as wd, UNDEFINED_NOT_AFTER,
 };
 use crate::{
     database::{
@@ -81,10 +81,6 @@
 // Blob of 32 zeroes used as empty masking key.
 static ZERO_BLOB_32: &[u8] = &[0; 32];
 
-// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
-// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
-const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
-
 impl KeystoreSecurityLevel {
     /// Creates a new security level instance wrapped in a
     /// BnKeystoreSecurityLevel proxy object. It also enables
diff --git a/keystore2/src/sw_keyblob.rs b/keystore2/src/sw_keyblob.rs
index 11a9b41..dc828a0 100644
--- a/keystore2/src/sw_keyblob.rs
+++ b/keystore2/src/sw_keyblob.rs
@@ -15,8 +15,6 @@
 //! Code for parsing software-backed keyblobs, as emitted by the C++ reference implementation of
 //! KeyMint.
 
-#![allow(dead_code)]
-
 use crate::error::Error;
 use crate::ks_err;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -73,9 +71,135 @@
         | KeyParameterValue::Algorithm(Algorithm::EC) => KeyFormat::PKCS8,
         _ => return Err(bloberr!("Unexpected algorithm {:?}", algo_val)),
     };
+
+    let key_material = match (format, algo_val) {
+        (KeyFormat::PKCS8, KeyParameterValue::Algorithm(Algorithm::EC)) => {
+            // Key material format depends on the curve.
+            let curve = get_tag_value(&combined, Tag::EC_CURVE)
+                .ok_or_else(|| bloberr!("Failed to determine curve for EC key!"))?;
+            match curve {
+                KeyParameterValue::EcCurve(EcCurve::CURVE_25519) => key_material,
+                KeyParameterValue::EcCurve(EcCurve::P_224) => {
+                    pkcs8_wrap_nist_key(&key_material, EcCurve::P_224)?
+                }
+                KeyParameterValue::EcCurve(EcCurve::P_256) => {
+                    pkcs8_wrap_nist_key(&key_material, EcCurve::P_256)?
+                }
+                KeyParameterValue::EcCurve(EcCurve::P_384) => {
+                    pkcs8_wrap_nist_key(&key_material, EcCurve::P_384)?
+                }
+                KeyParameterValue::EcCurve(EcCurve::P_521) => {
+                    pkcs8_wrap_nist_key(&key_material, EcCurve::P_521)?
+                }
+                _ => {
+                    return Err(bloberr!("Unexpected EC curve {curve:?}"));
+                }
+            }
+        }
+        (KeyFormat::RAW, _) => key_material,
+        (format, algo) => {
+            return Err(bloberr!(
+                "Unsupported combination of {format:?} format for {algo:?} algorithm"
+            ));
+        }
+    };
     Ok((format, key_material, combined))
 }
 
+/// DER-encoded `AlgorithmIdentifier` for a P-224 key.
+const DER_ALGORITHM_ID_P224: &[u8] = &[
+    0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
+    0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+    0x06, 0x05, // OBJECT IDENTIFIER (param)
+    0x2b, 0x81, 0x04, 0x00, 0x21, //  1.3.132.0.33 (secp224r1) }
+];
+
+/// DER-encoded `AlgorithmIdentifier` for a P-256 key.
+const DER_ALGORITHM_ID_P256: &[u8] = &[
+    0x30, 0x13, // SEQUENCE (AlgorithmIdentifier) {
+    0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+    0x06, 0x08, // OBJECT IDENTIFIER (param)
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, //  1.2.840.10045.3.1.7 (secp256r1) }
+];
+
+/// DER-encoded `AlgorithmIdentifier` for a P-384 key.
+const DER_ALGORITHM_ID_P384: &[u8] = &[
+    0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
+    0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+    0x06, 0x05, // OBJECT IDENTIFIER (param)
+    0x2b, 0x81, 0x04, 0x00, 0x22, //  1.3.132.0.34 (secp384r1) }
+];
+
+/// DER-encoded `AlgorithmIdentifier` for a P-384 key.
+const DER_ALGORITHM_ID_P521: &[u8] = &[
+    0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
+    0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+    0x06, 0x05, // OBJECT IDENTIFIER (param)
+    0x2b, 0x81, 0x04, 0x00, 0x23, //  1.3.132.0.35 (secp521r1) }
+];
+
+/// DER-encoded integer value zero.
+const DER_VERSION_0: &[u8] = &[
+    0x02, // INTEGER
+    0x01, // len
+    0x00, // value 0
+];
+
+/// Given a NIST curve EC key in the form of a DER-encoded `ECPrivateKey`
+/// (RFC 5915 s3), wrap it in a DER-encoded PKCS#8 format (RFC 5208 s5).
+fn pkcs8_wrap_nist_key(nist_key: &[u8], curve: EcCurve) -> Result<Vec<u8>> {
+    let der_alg_id = match curve {
+        EcCurve::P_224 => DER_ALGORITHM_ID_P224,
+        EcCurve::P_256 => DER_ALGORITHM_ID_P256,
+        EcCurve::P_384 => DER_ALGORITHM_ID_P384,
+        EcCurve::P_521 => DER_ALGORITHM_ID_P521,
+        _ => return Err(bloberr!("unknown curve {curve:?}")),
+    };
+
+    // Output format is:
+    //
+    //    PrivateKeyInfo ::= SEQUENCE {
+    //        version                   INTEGER,
+    //        privateKeyAlgorithm       AlgorithmIdentifier,
+    //        privateKey                OCTET STRING,
+    //    }
+    //
+    // Start by building the OCTET STRING so we know its length.
+    let mut nist_key_octet_string = Vec::new();
+    nist_key_octet_string.push(0x04); // OCTET STRING
+    add_der_len(&mut nist_key_octet_string, nist_key.len())?;
+    nist_key_octet_string.extend_from_slice(nist_key);
+
+    let mut buf = Vec::new();
+    buf.push(0x30); // SEQUENCE
+    add_der_len(&mut buf, DER_VERSION_0.len() + der_alg_id.len() + nist_key_octet_string.len())?;
+    buf.extend_from_slice(DER_VERSION_0);
+    buf.extend_from_slice(der_alg_id);
+    buf.extend_from_slice(&nist_key_octet_string);
+    Ok(buf)
+}
+
+/// Append a DER-encoded length value to the given buffer.
+fn add_der_len(buf: &mut Vec<u8>, len: usize) -> Result<()> {
+    if len <= 0x7f {
+        buf.push(len as u8)
+    } else if len <= 0xff {
+        buf.push(0x81); // One length octet to come
+        buf.push(len as u8);
+    } else if len <= 0xffff {
+        buf.push(0x82); // Two length octets to come
+        buf.push((len >> 8) as u8);
+        buf.push((len & 0xff) as u8);
+    } else {
+        return Err(bloberr!("Unsupported DER length {len}"));
+    }
+    Ok(())
+}
+
 /// Plaintext key blob, with key characteristics.
 #[derive(PartialEq, Eq)]
 struct KeyBlob {
@@ -809,4 +933,104 @@
             }
         }
     }
+
+    #[test]
+    fn test_add_der_len() {
+        let tests = [
+            (0, "00"),
+            (1, "01"),
+            (126, "7e"),
+            (127, "7f"),
+            (128, "8180"),
+            (129, "8181"),
+            (255, "81ff"),
+            (256, "820100"),
+            (257, "820101"),
+            (65535, "82ffff"),
+        ];
+        for (input, want) in tests {
+            let mut got = Vec::new();
+            add_der_len(&mut got, input).unwrap();
+            assert_eq!(hex::encode(got), want, " for input length {input}");
+        }
+    }
+
+    #[test]
+    fn test_pkcs8_wrap_key_p256() {
+        // Key material taken from `ec_256_key` in
+        // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+        let input = hex::decode(concat!(
+            "3025",   // SEQUENCE (ECPrivateKey)
+            "020101", // INTEGER length 1 value 1 (version)
+            "0420",   // OCTET STRING (privateKey)
+            "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+            "941eed09366bc03299986481f3a4d859",
+        ))
+        .unwrap();
+        let want = hex::decode(concat!(
+            // RFC 5208 s5
+            "3041",             // SEQUENCE (PrivateKeyInfo) {
+            "020100",           // INTEGER length 1 value 0 (version)
+            "3013",             // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+            "0607",             // OBJECT IDENTIFIER length 7 (algorithm)
+            "2a8648ce3d0201",   // 1.2.840.10045.2.1 (ecPublicKey)
+            "0608",             // OBJECT IDENTIFIER length 8 (param)
+            "2a8648ce3d030107", //  1.2.840.10045.3.1.7 (secp256r1)
+            // } end SEQUENCE (AlgorithmIdentifier)
+            "0427",   // OCTET STRING (privateKey) holding...
+            "3025",   // SEQUENCE (ECPrivateKey)
+            "020101", // INTEGER length 1 value 1 (version)
+            "0420",   // OCTET STRING length 0x20 (privateKey)
+            "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+            "941eed09366bc03299986481f3a4d859",
+            // } end SEQUENCE (ECPrivateKey)
+            // } end SEQUENCE (PrivateKeyInfo)
+        ))
+        .unwrap();
+        let got = pkcs8_wrap_nist_key(&input, EcCurve::P_256).unwrap();
+        assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+    }
+
+    #[test]
+    fn test_pkcs8_wrap_key_p521() {
+        // Key material taken from `ec_521_key` in
+        // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+        let input = hex::decode(concat!(
+            "3047",   // SEQUENCE length 0xd3 (ECPrivateKey)
+            "020101", // INTEGER length 1 value 1 (version)
+            "0442",   // OCTET STRING length 0x42 (privateKey)
+            "0011458c586db5daa92afab03f4fe46a",
+            "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+            "9d18d7d08b5bcfa0e53c75b064ad51c4",
+            "49bae0258d54b94b1e885ded08ed4fb2",
+            "5ce9",
+            // } end SEQUENCE (ECPrivateKey)
+        ))
+        .unwrap();
+        let want = hex::decode(concat!(
+            // RFC 5208 s5
+            "3060",           // SEQUENCE (PrivateKeyInfo) {
+            "020100",         // INTEGER length 1 value 0 (version)
+            "3010",           // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+            "0607",           // OBJECT IDENTIFIER length 7 (algorithm)
+            "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+            "0605",           // OBJECT IDENTIFIER length 5 (param)
+            "2b81040023",     //  1.3.132.0.35 (secp521r1)
+            // } end SEQUENCE (AlgorithmIdentifier)
+            "0449",   // OCTET STRING (privateKey) holding...
+            "3047",   // SEQUENCE (ECPrivateKey)
+            "020101", // INTEGER length 1 value 1 (version)
+            "0442",   // OCTET STRING length 0x42 (privateKey)
+            "0011458c586db5daa92afab03f4fe46a",
+            "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+            "9d18d7d08b5bcfa0e53c75b064ad51c4",
+            "49bae0258d54b94b1e885ded08ed4fb2",
+            "5ce9",
+            // } end SEQUENCE (ECPrivateKey)
+            // } end SEQUENCE (PrivateKeyInfo)
+        ))
+        .unwrap();
+        let got = pkcs8_wrap_nist_key(&input, EcCurve::P_521).unwrap();
+        assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+    }
 }
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 4fd9c8d..174a22b 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -28,8 +28,8 @@
     raw_device::KeyMintDevice,
 };
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
-    KeyParameter::KeyParameter as KmKeyParameter, Tag::Tag,
+    Algorithm::Algorithm, IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
+    KeyParameter::KeyParameter as KmKeyParameter, KeyParameterValue::KeyParameterValue, Tag::Tag,
 };
 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
 use android_security_apc::aidl::android::security::apc::{
@@ -49,6 +49,10 @@
 use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
 use std::iter::IntoIterator;
 
+/// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
+/// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
+pub const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
+
 /// This function uses its namesake in the permission module and in
 /// combination with with_calling_sid from the binder crate to check
 /// if the caller has the given keystore permission.
@@ -166,6 +170,119 @@
         .collect()
 }
 
+/// Import a keyblob that is of the format used by the software C++ KeyMint implementation.  After
+/// successful import, invoke both the `new_blob_handler` and `km_op` closures. On success a tuple
+/// of the `km_op`s result and the optional upgraded blob is returned.
+fn import_keyblob_and_perform_op<T, KmOp, NewBlobHandler>(
+    km_dev: &dyn IKeyMintDevice,
+    inner_keyblob: &[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<()>,
+{
+    let (format, key_material, mut chars) =
+        crate::sw_keyblob::export_key(inner_keyblob, upgrade_params)?;
+    log::debug!(
+        "importing {:?} key material (len={}) with original chars={:?}",
+        format,
+        key_material.len(),
+        chars
+    );
+    let asymmetric = chars.iter().any(|kp| {
+        kp.tag == Tag::ALGORITHM
+            && (kp.value == KeyParameterValue::Algorithm(Algorithm::RSA)
+                || (kp.value == KeyParameterValue::Algorithm(Algorithm::EC)))
+    });
+
+    // Combine the characteristics of the previous keyblob with the upgrade parameters (which might
+    // include special things like APPLICATION_ID / APPLICATION_DATA).
+    chars.extend_from_slice(upgrade_params);
+
+    // Now filter out values from the existing keyblob that shouldn't be set on import, either
+    // because they are per-operation parameter or because they are auto-added by KeyMint itself.
+    let mut import_params: Vec<KmKeyParameter> = chars
+        .into_iter()
+        .filter(|kp| {
+            !matches!(
+                kp.tag,
+                Tag::ORIGIN
+                    | Tag::ROOT_OF_TRUST
+                    | Tag::OS_VERSION
+                    | Tag::OS_PATCHLEVEL
+                    | Tag::UNIQUE_ID
+                    | Tag::ATTESTATION_CHALLENGE
+                    | Tag::ATTESTATION_APPLICATION_ID
+                    | Tag::ATTESTATION_ID_BRAND
+                    | Tag::ATTESTATION_ID_DEVICE
+                    | Tag::ATTESTATION_ID_PRODUCT
+                    | Tag::ATTESTATION_ID_SERIAL
+                    | Tag::ATTESTATION_ID_IMEI
+                    | Tag::ATTESTATION_ID_MEID
+                    | Tag::ATTESTATION_ID_MANUFACTURER
+                    | Tag::ATTESTATION_ID_MODEL
+                    | Tag::VENDOR_PATCHLEVEL
+                    | Tag::BOOT_PATCHLEVEL
+                    | Tag::DEVICE_UNIQUE_ATTESTATION
+                    | Tag::ATTESTATION_ID_SECOND_IMEI
+                    | Tag::NONCE
+                    | Tag::MAC_LENGTH
+                    | Tag::CERTIFICATE_SERIAL
+                    | Tag::CERTIFICATE_SUBJECT
+                    | Tag::CERTIFICATE_NOT_BEFORE
+                    | Tag::CERTIFICATE_NOT_AFTER
+            )
+        })
+        .collect();
+
+    // Now that any previous values have been removed, add any additional parameters that needed for
+    // import. In particular, if we are generating/importing an asymmetric key, we need to make sure
+    // that NOT_BEFORE and NOT_AFTER are present.
+    if asymmetric {
+        import_params.push(KmKeyParameter {
+            tag: Tag::CERTIFICATE_NOT_BEFORE,
+            value: KeyParameterValue::DateTime(0),
+        });
+        import_params.push(KmKeyParameter {
+            tag: Tag::CERTIFICATE_NOT_AFTER,
+            value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+        });
+    }
+    log::debug!("import parameters={import_params:?}");
+
+    let creation_result = {
+        let _wp = watchdog::watch_millis(
+            "In utils::import_keyblob_and_perform_op: calling importKey.",
+            500,
+        );
+        map_km_error(km_dev.importKey(&import_params, format, &key_material, None))
+    }
+    .context(ks_err!("Upgrade failed."))?;
+
+    // Note that the importKey operation will produce key characteristics that may be different
+    // than are already stored in Keystore's SQL database.  In particular, the KeyMint
+    // implementation will now mark the key as `Origin::IMPORTED` not `Origin::GENERATED`, and
+    // the security level for characteristics will now be `TRUSTED_ENVIRONMENT` not `SOFTWARE`.
+    //
+    // However, the DB metadata still accurately reflects the original origin of the key, and
+    // so we leave the values as-is (and so any `KeyInfo` retrieved in the Java layer will get the
+    // same results before and after import).
+    //
+    // Note that this also applies to the `USAGE_COUNT_LIMIT` parameter -- if the key has already
+    // been used, then the DB version of the parameter will be (and will continue to be) lower
+    // than the original count bound to the keyblob. This means that Keystore's policing of
+    // usage counts will continue where it left off.
+
+    new_blob_handler(&creation_result.keyBlob).context(ks_err!("calling new_blob_handler."))?;
+
+    km_op(&creation_result.keyBlob)
+        .map(|v| (v, Some(creation_result.keyBlob)))
+        .context(ks_err!("Calling km_op after upgrade."))
+}
+
 /// 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>(
@@ -221,33 +338,84 @@
             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) =>
+            if km_dev_version >= KeyMintDevice::KEY_MINT_V1 =>
         {
-            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,
-            )
+            // A KeyMint (not Keymaster via km_compat) device says that this is an invalid keyblob.
+            //
+            // This may be because the keyblob was created before an Android upgrade, and as part of
+            // the device upgrade the underlying Keymaster/KeyMint implementation has been upgraded.
+            //
+            // If that's the case, there are three possible scenarios:
+            if key_blob.starts_with(km_compat::KEYMASTER_BLOB_HW_PREFIX) {
+                // 1) The keyblob was created in hardware by the km_compat C++ code, using a prior
+                //    Keymaster implementation, and wrapped.
+                //
+                //    In this case, the keyblob will have the km_compat magic prefix, including the
+                //    marker that indicates that this was a hardware-backed key.
+                //
+                //    The inner keyblob should still be recognized by the hardware implementation, so
+                //    strip the prefix and attempt a key upgrade.
+                log::info!(
+                    "found apparent km_compat(Keymaster) HW 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,
+                )
+            } else if keystore2_flags::import_previously_emulated_keys()
+                && key_blob.starts_with(km_compat::KEYMASTER_BLOB_SW_PREFIX)
+            {
+                // 2) The keyblob was created in software by the km_compat C++ code because a prior
+                //    Keymaster implementation did not support ECDH (which was only added in KeyMint).
+                //
+                //    In this case, the keyblob with have the km_compat magic prefix, but with the
+                //    marker that indicates that this was a software-emulated key.
+                //
+                //    The inner keyblob should be in the format produced by the C++ reference
+                //    implementation of KeyMint.  Extract the key material and import it into the
+                //    current KeyMint device.
+                log::info!("found apparent km_compat(Keymaster) SW blob, attempt strip-and-import");
+                let inner_keyblob = &key_blob[km_compat::KEYMASTER_BLOB_SW_PREFIX.len()..];
+                import_keyblob_and_perform_op(
+                    km_dev,
+                    inner_keyblob,
+                    upgrade_params,
+                    km_op,
+                    new_blob_handler,
+                )
+            } else if let (true, km_compat::KeyBlob::Wrapped(inner_keyblob)) = (
+                keystore2_flags::import_previously_emulated_keys(),
+                km_compat::unwrap_keyblob(key_blob),
+            ) {
+                // 3) The keyblob was created in software by km_compat.rs because a prior KeyMint
+                //    implementation did not support a feature present in the current KeyMint spec.
+                //    (For example, a curve 25519 key created when the device only supported KeyMint
+                //    v1).
+                //
+                //    In this case, the keyblob with have the km_compat.rs wrapper around it to
+                //    indicate that this was a software-emulated key.
+                //
+                //    The inner keyblob should be in the format produced by the C++ reference
+                //    implementation of KeyMint.  Extract the key material and import it into the
+                //    current KeyMint device.
+                log::info!(
+                    "found apparent km_compat.rs(KeyMint) SW blob, attempt strip-and-import"
+                );
+                import_keyblob_and_perform_op(
+                    km_dev,
+                    inner_keyblob,
+                    upgrade_params,
+                    km_op,
+                    new_blob_handler,
+                )
+            } else {
+                Err(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!("Calling km_op"))
+            }
         }
         r => r.map(|v| (v, None)).context(ks_err!("Calling km_op.")),
     }