Implement a back-level KeyMint compatibility wrapper

 - Implement a general back-level KeyMint wrapper, which forwards
   requests to either a back-level real device, or an up-level
   software device. Keyblobs from the latter are given a marker
   prefix and an authentication suffix.
 - Add an FFI wrapper function to allow calculation of HMAC-SHA256,
   so this can be used to give an authenticated suffix to wrapped
   keyblobs.
 - Abstract out the decision process for whether emulation is required
   to a EmulationDetector trait, and provide implementations for
   KeyMint V1 and for a km_compat-wrapped Keymaster.
 - Impose the KeyMint V1 wrapper whenever the real device is detected to
   be a V1 implementation.
 - Add support to the IKeystoreCompatService for returning a device for
   SecurityLevel::SOFTWARE. This device will always be the most recent
   KeyMint version.
 - Clarify what level of IKeyMint implementation gets returned from
   the IKeystoreCompatService for the other security levels.
 - Add an inner function to the km_compat code to allow unit tests
   to still work.

Co-authored-by: Janis Danisevskis <jdanis@google.com>
Bug: 194358913
Test: CtsKeystoreTestCases on oriole/bramble/cuttlefish
Change-Id: I297e8ad1cf00fd15cd5358b2760cd2ca88f53abb
diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs
new file mode 100644
index 0000000..84855df
--- /dev/null
+++ b/keystore2/src/km_compat.rs
@@ -0,0 +1,579 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provide a wrapper around a KeyMint device that allows up-level features to
+//! be emulated on back-level devices.
+
+use crate::error::{map_binder_status, map_binder_status_code, map_or_log_err, Error, ErrorCode};
+use android_hardware_security_keymint::binder::{BinderFeatures, StatusCode, Strong};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::TimeStampToken::TimeStampToken;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    AttestationKey::AttestationKey, BeginResult::BeginResult, EcCurve::EcCurve,
+    HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::BnKeyMintDevice,
+    IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
+    KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
+    KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
+    KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+    Tag::Tag,
+};
+use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+use anyhow::Context;
+use keystore2_crypto::{hmac_sha256, HMAC_SHA256_LEN};
+
+/// Key data associated with key generation/import.
+#[derive(Debug, PartialEq, Eq)]
+pub enum KeyImportData<'a> {
+    None,
+    Pkcs8(&'a [u8]),
+    Raw(&'a [u8]),
+}
+
+impl<'a> KeyImportData<'a> {
+    /// Translate import parameters into a `KeyImportData` instance.
+    fn new(key_format: KeyFormat, key_data: &'a [u8]) -> binder::Result<Self> {
+        match key_format {
+            KeyFormat::PKCS8 => Ok(KeyImportData::Pkcs8(key_data)),
+            KeyFormat::RAW => Ok(KeyImportData::Raw(key_data)),
+            _ => Err(binder::Status::new_service_specific_error(
+                ErrorCode::UNSUPPORTED_KEY_FORMAT.0,
+                None,
+            )),
+        }
+    }
+}
+
+/// A key blob that may be software-emulated or may be directly produced by an
+/// underlying device.  In either variant the inner data is the keyblob itself,
+/// as seen by the relevant device.
+#[derive(Debug, PartialEq, Eq)]
+pub enum KeyBlob<'a> {
+    Raw(&'a [u8]),
+    Wrapped(&'a [u8]),
+}
+
+/// Trait for detecting that software emulation of a current-version KeyMint
+/// feature is required for a back-level KeyMint implementation.
+pub trait EmulationDetector: Send + Sync {
+    /// Indicate whether software emulation is required for key
+    /// generation/import using the provided parameters.
+    fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool;
+}
+
+const KEYBLOB_PREFIX: &[u8] = b"SoftKeyMintForV1Blob";
+const KEYBLOB_HMAC_KEY: &[u8] = b"SoftKeyMintForV1HMACKey";
+
+/// Wrap the provided keyblob:
+/// - prefix it with an identifier specific to this wrapper
+/// - suffix it with an HMAC tag, using the [`KEYBLOB_HMAC_KEY`] and `keyblob`.
+fn wrap_keyblob(keyblob: &[u8]) -> anyhow::Result<Vec<u8>> {
+    let mut result = Vec::with_capacity(KEYBLOB_PREFIX.len() + keyblob.len() + HMAC_SHA256_LEN);
+    result.extend_from_slice(KEYBLOB_PREFIX);
+    result.extend_from_slice(keyblob);
+    let tag = hmac_sha256(KEYBLOB_HMAC_KEY, keyblob)
+        .context("In wrap_keyblob, failed to calculate HMAC-SHA256")?;
+    result.extend_from_slice(&tag);
+    Ok(result)
+}
+
+/// 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 {
+    if !keyblob.starts_with(KEYBLOB_PREFIX) {
+        return KeyBlob::Raw(keyblob);
+    }
+    let without_prefix = &keyblob[KEYBLOB_PREFIX.len()..];
+    if without_prefix.len() < HMAC_SHA256_LEN {
+        return KeyBlob::Raw(keyblob);
+    }
+    let (inner_keyblob, want_tag) = without_prefix.split_at(without_prefix.len() - HMAC_SHA256_LEN);
+    let got_tag = match hmac_sha256(KEYBLOB_HMAC_KEY, inner_keyblob) {
+        Ok(tag) => tag,
+        Err(e) => {
+            log::error!("Error calculating HMAC-SHA256 for keyblob unwrap: {:?}", e);
+            return KeyBlob::Raw(keyblob);
+        }
+    };
+    // Comparison does not need to be constant-time here.
+    if want_tag == got_tag {
+        KeyBlob::Wrapped(inner_keyblob)
+    } else {
+        KeyBlob::Raw(keyblob)
+    }
+}
+
+/// Wrapper around a real device that implements a back-level version of
+/// `IKeyMintDevice`
+pub struct BacklevelKeyMintWrapper<T: EmulationDetector> {
+    /// The `real` device implements some earlier version of `IKeyMintDevice`
+    real: Strong<dyn IKeyMintDevice>,
+    /// The `soft`ware device implements the current version of `IKeyMintDevice`
+    soft: Strong<dyn IKeyMintDevice>,
+    /// Detector for operations that are not supported by the earlier version of
+    /// `IKeyMintDevice`. Or possibly a large flightless bird, who can tell.
+    emu: T,
+}
+
+impl<T> BacklevelKeyMintWrapper<T>
+where
+    T: EmulationDetector + 'static,
+{
+    /// Create a wrapper around the provided back-level KeyMint device, so that
+    /// software emulation can be performed for any current-version features not
+    /// provided by the real device.
+    pub fn wrap(
+        emu: T,
+        real: Strong<dyn IKeyMintDevice>,
+    ) -> anyhow::Result<Strong<dyn IKeyMintDevice>> {
+        // This is a no-op if it was called before.
+        keystore2_km_compat::add_keymint_device_service();
+
+        let keystore_compat_service: Strong<dyn IKeystoreCompatService> = map_binder_status_code(
+            binder::get_interface("android.security.compat"),
+        )
+        .context("In BacklevelKeyMintWrapper::wrap: Trying to connect to compat service.")?;
+        let soft =
+            map_binder_status(keystore_compat_service.getKeyMintDevice(SecurityLevel::SOFTWARE))
+                .map_err(|e| match e {
+                    Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+                        Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
+                    }
+                    e => e,
+                })
+                .context("In BacklevelKeyMintWrapper::wrap: Trying to get software device.")?;
+
+        Ok(BnKeyMintDevice::new_binder(
+            Self { real, soft, emu },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+}
+
+impl<T> binder::Interface for BacklevelKeyMintWrapper<T> where T: EmulationDetector {}
+
+impl<T> IKeyMintDevice for BacklevelKeyMintWrapper<T>
+where
+    T: EmulationDetector + 'static,
+{
+    // For methods that don't involve keyblobs, forward to either the real
+    // device, or to both real & emulated devices.
+    fn getHardwareInfo(&self) -> binder::Result<KeyMintHardwareInfo> {
+        self.real.getHardwareInfo()
+    }
+    fn addRngEntropy(&self, data: &[u8]) -> binder::Result<()> {
+        self.real.addRngEntropy(data)
+    }
+    fn deleteAllKeys(&self) -> binder::Result<()> {
+        self.real.deleteAllKeys()
+    }
+    fn destroyAttestationIds(&self) -> binder::Result<()> {
+        self.real.destroyAttestationIds()
+    }
+    fn deviceLocked(
+        &self,
+        password_only: bool,
+        timestamp_token: Option<&TimeStampToken>,
+    ) -> binder::Result<()> {
+        // Propagate to both real and software devices, but only pay attention
+        // to the result from the real device.
+        let _ = self.soft.deviceLocked(password_only, timestamp_token);
+        self.real.deviceLocked(password_only, timestamp_token)
+    }
+    fn earlyBootEnded(&self) -> binder::Result<()> {
+        // Propagate to both real and software devices, but only pay attention
+        // to the result from the real device.
+        let _ = self.soft.earlyBootEnded();
+        self.real.earlyBootEnded()
+    }
+
+    // For methods that emit keyblobs, check whether the underlying real device
+    // supports the relevant parameters, and forward to the appropriate device.
+    // If the emulated device is used, ensure that the created keyblob gets
+    // prefixed so we can recognize it in future.
+    fn generateKey(
+        &self,
+        key_params: &[KeyParameter],
+        attestation_key: Option<&AttestationKey>,
+    ) -> binder::Result<KeyCreationResult> {
+        if self.emu.emulation_required(key_params, &KeyImportData::None) {
+            let mut result = self.soft.generateKey(key_params, attestation_key)?;
+            result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
+            Ok(result)
+        } else {
+            self.real.generateKey(key_params, attestation_key)
+        }
+    }
+    fn importKey(
+        &self,
+        key_params: &[KeyParameter],
+        key_format: KeyFormat,
+        key_data: &[u8],
+        attestation_key: Option<&AttestationKey>,
+    ) -> binder::Result<KeyCreationResult> {
+        if self.emu.emulation_required(key_params, &KeyImportData::new(key_format, key_data)?) {
+            let mut result =
+                self.soft.importKey(key_params, key_format, key_data, attestation_key)?;
+            result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
+            Ok(result)
+        } else {
+            self.real.importKey(key_params, key_format, key_data, attestation_key)
+        }
+    }
+    fn importWrappedKey(
+        &self,
+        wrapped_key_data: &[u8],
+        wrapping_key_blob: &[u8],
+        masking_key: &[u8],
+        unwrapping_params: &[KeyParameter],
+        password_sid: i64,
+        biometric_sid: i64,
+    ) -> binder::Result<KeyCreationResult> {
+        // A wrapped key cannot be software-emulated, as the wrapping key is
+        // likely hardware-bound.
+        self.real.importWrappedKey(
+            wrapped_key_data,
+            wrapping_key_blob,
+            masking_key,
+            unwrapping_params,
+            password_sid,
+            biometric_sid,
+        )
+    }
+
+    // For methods that use keyblobs, determine which device to forward the
+    // operation to based on whether the keyblob is appropriately prefixed.
+    fn upgradeKey(
+        &self,
+        keyblob_to_upgrade: &[u8],
+        upgrade_params: &[KeyParameter],
+    ) -> binder::Result<Vec<u8>> {
+        match unwrap_keyblob(keyblob_to_upgrade) {
+            KeyBlob::Raw(keyblob) => self.real.upgradeKey(keyblob, upgrade_params),
+            KeyBlob::Wrapped(keyblob) => {
+                // Re-wrap the upgraded keyblob.
+                let upgraded_keyblob = self.soft.upgradeKey(keyblob, upgrade_params)?;
+                map_or_log_err(wrap_keyblob(&upgraded_keyblob), Ok)
+            }
+        }
+    }
+    fn deleteKey(&self, keyblob: &[u8]) -> binder::Result<()> {
+        match unwrap_keyblob(keyblob) {
+            KeyBlob::Raw(keyblob) => self.real.deleteKey(keyblob),
+            KeyBlob::Wrapped(keyblob) => {
+                // Forward to the software implementation for completeness, but
+                // this should always be a no-op.
+                self.soft.deleteKey(keyblob)
+            }
+        }
+    }
+    fn begin(
+        &self,
+        purpose: KeyPurpose,
+        keyblob: &[u8],
+        params: &[KeyParameter],
+        auth_token: Option<&HardwareAuthToken>,
+    ) -> binder::Result<BeginResult> {
+        match unwrap_keyblob(keyblob) {
+            KeyBlob::Raw(keyblob) => self.real.begin(purpose, keyblob, params, auth_token),
+            KeyBlob::Wrapped(keyblob) => self.soft.begin(purpose, keyblob, params, auth_token),
+        }
+    }
+    fn getKeyCharacteristics(
+        &self,
+        keyblob: &[u8],
+        app_id: &[u8],
+        app_data: &[u8],
+    ) -> binder::Result<Vec<KeyCharacteristics>> {
+        match unwrap_keyblob(keyblob) {
+            KeyBlob::Raw(keyblob) => self.real.getKeyCharacteristics(keyblob, app_id, app_data),
+            KeyBlob::Wrapped(keyblob) => self.soft.getKeyCharacteristics(keyblob, app_id, app_data),
+        }
+    }
+    fn convertStorageKeyToEphemeral(&self, storage_keyblob: &[u8]) -> binder::Result<Vec<u8>> {
+        // Storage keys should never be associated with a software emulated device.
+        self.real.convertStorageKeyToEphemeral(storage_keyblob)
+    }
+}
+
+/// Detector for current features that are not implemented by KeyMint V1.
+#[derive(Debug)]
+pub struct KeyMintV1 {
+    sec_level: SecurityLevel,
+}
+
+impl KeyMintV1 {
+    pub fn new(sec_level: SecurityLevel) -> Self {
+        Self { sec_level }
+    }
+}
+
+impl EmulationDetector for KeyMintV1 {
+    fn emulation_required(&self, params: &[KeyParameter], _import_data: &KeyImportData) -> bool {
+        // No current difference from KeyMint v1 for STRONGBOX (it doesn't
+        // support curve 25519).
+        if self.sec_level == SecurityLevel::STRONGBOX {
+            return false;
+        }
+
+        // KeyMint V1 does not support the use of curve 25519, so hunt for that
+        // in the parameters.
+        if params.iter().any(|p| {
+            p.tag == Tag::EC_CURVE && p.value == KeyParameterValue::EcCurve(EcCurve::CURVE_25519)
+        }) {
+            return true;
+        }
+        // In theory, if the `import_data` is `KeyImportData::Pkcs8` we could
+        // check the imported keymaterial for the Ed25519 / X25519 OIDs in the
+        // PKCS8 keydata, and use that to decide to route to software. However,
+        // the KeyMint spec doesn't require that so don't attempt to parse the
+        // key material here.
+        false
+    }
+}
+
+/// Detector for current features that are not implemented by KeyMaster, via the
+/// km_compat wrapper.
+#[derive(Debug)]
+pub struct Keymaster {
+    v1: KeyMintV1,
+}
+
+/// TODO(b/216434270): This could be used this to replace the emulation routing
+/// in the km_compat C++ code, and allow support for imported ECDH keys along
+/// the way. Would need to figure out what would happen to existing emulated
+/// keys though.
+#[allow(dead_code)]
+impl Keymaster {
+    pub fn new(sec_level: SecurityLevel) -> Self {
+        Self { v1: KeyMintV1::new(sec_level) }
+    }
+}
+
+impl EmulationDetector for Keymaster {
+    fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool {
+        // The km_compat wrapper on top of Keymaster emulates the KeyMint V1
+        // interface, so any feature from > v1 needs to be emulated.
+        if self.v1.emulation_required(params, import_data) {
+            return true;
+        }
+
+        // Keymaster does not support ECDH (KeyPurpose::AGREE_KEY), so hunt for
+        // that in the parameters.
+        if params.iter().any(|p| {
+            p.tag == Tag::PURPOSE && p.value == KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY)
+        }) {
+            return true;
+        }
+        false
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_key_import_data() {
+        let data = vec![1, 2, 3];
+        assert_eq!(KeyImportData::new(KeyFormat::PKCS8, &data), Ok(KeyImportData::Pkcs8(&data)));
+        assert_eq!(KeyImportData::new(KeyFormat::RAW, &data), Ok(KeyImportData::Raw(&data)));
+        assert!(KeyImportData::new(KeyFormat::X509, &data).is_err());
+    }
+
+    #[test]
+    fn test_wrap_keyblob() {
+        let keyblob = vec![1, 2, 3];
+        let wrapped = wrap_keyblob(&keyblob).unwrap();
+        assert_eq!(&wrapped[..KEYBLOB_PREFIX.len()], KEYBLOB_PREFIX);
+        assert_eq!(&wrapped[KEYBLOB_PREFIX.len()..KEYBLOB_PREFIX.len() + keyblob.len()], &keyblob);
+        assert_eq!(unwrap_keyblob(&keyblob), KeyBlob::Raw(&keyblob));
+        assert_eq!(unwrap_keyblob(&wrapped), KeyBlob::Wrapped(&keyblob));
+
+        let mut corrupt_prefix = wrapped.clone();
+        corrupt_prefix[0] ^= 0x01;
+        assert_eq!(unwrap_keyblob(&corrupt_prefix), KeyBlob::Raw(&corrupt_prefix));
+
+        let mut corrupt_suffix = wrapped.clone();
+        corrupt_suffix[wrapped.len() - 1] ^= 0x01;
+        assert_eq!(unwrap_keyblob(&corrupt_suffix), KeyBlob::Raw(&corrupt_suffix));
+
+        let too_short = &wrapped[..wrapped.len() - 4];
+        assert_eq!(unwrap_keyblob(too_short), KeyBlob::Raw(too_short));
+    }
+
+    #[test]
+    fn test_keymintv1_emulation_required() {
+        let tests = vec![
+            (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                    },
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
+                    },
+                ],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![KeyParameter {
+                    tag: Tag::PURPOSE,
+                    value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                }],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::P_256),
+                    },
+                ],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::STRONGBOX,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                false,
+            ),
+        ];
+        for (sec_level, params, want) in tests {
+            let v1 = KeyMintV1::new(sec_level);
+            let got = v1.emulation_required(&params, &KeyImportData::None);
+            assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
+        }
+    }
+
+    #[test]
+    fn test_keymaster_emulation_required() {
+        let tests = vec![
+            (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                    },
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
+                    },
+                ],
+                false,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![KeyParameter {
+                    tag: Tag::PURPOSE,
+                    value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                }],
+                true,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::P_256),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::STRONGBOX,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                true,
+            ),
+            (
+                SecurityLevel::STRONGBOX,
+                vec![
+                    KeyParameter {
+                        tag: Tag::PURPOSE,
+                        value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                    },
+                    KeyParameter {
+                        tag: Tag::EC_CURVE,
+                        value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
+                    },
+                ],
+                false,
+            ),
+        ];
+        for (sec_level, params, want) in tests {
+            let v0 = Keymaster::new(sec_level);
+            let got = v0.emulation_required(&params, &KeyImportData::None);
+            assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
+        }
+    }
+}