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(¶ms, &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(¶ms, &KeyImportData::None);
+ assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
+ }
+ }
+}