blob: 035edd90449ab857f69f2de6d43c4e545be94066 [file] [log] [blame]
David Drysdalec97eb9e2022-01-26 13:03:48 -08001// Copyright 2020, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Provide a wrapper around a KeyMint device that allows up-level features to
16//! be emulated on back-level devices.
17
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +000018use crate::ks_err;
David Drysdalec97eb9e2022-01-26 13:03:48 -080019use crate::error::{map_binder_status, map_binder_status_code, map_or_log_err, Error, ErrorCode};
20use android_hardware_security_keymint::binder::{BinderFeatures, StatusCode, Strong};
21use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::TimeStampToken::TimeStampToken;
22use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
23 AttestationKey::AttestationKey, BeginResult::BeginResult, EcCurve::EcCurve,
24 HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::BnKeyMintDevice,
25 IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
26 KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
27 KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
28 KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
29 Tag::Tag,
30};
31use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
32use anyhow::Context;
33use keystore2_crypto::{hmac_sha256, HMAC_SHA256_LEN};
34
35/// Key data associated with key generation/import.
36#[derive(Debug, PartialEq, Eq)]
37pub enum KeyImportData<'a> {
38 None,
39 Pkcs8(&'a [u8]),
40 Raw(&'a [u8]),
41}
42
43impl<'a> KeyImportData<'a> {
44 /// Translate import parameters into a `KeyImportData` instance.
45 fn new(key_format: KeyFormat, key_data: &'a [u8]) -> binder::Result<Self> {
46 match key_format {
47 KeyFormat::PKCS8 => Ok(KeyImportData::Pkcs8(key_data)),
48 KeyFormat::RAW => Ok(KeyImportData::Raw(key_data)),
49 _ => Err(binder::Status::new_service_specific_error(
50 ErrorCode::UNSUPPORTED_KEY_FORMAT.0,
51 None,
52 )),
53 }
54 }
55}
56
57/// A key blob that may be software-emulated or may be directly produced by an
58/// underlying device. In either variant the inner data is the keyblob itself,
59/// as seen by the relevant device.
60#[derive(Debug, PartialEq, Eq)]
61pub enum KeyBlob<'a> {
62 Raw(&'a [u8]),
63 Wrapped(&'a [u8]),
64}
65
66/// Trait for detecting that software emulation of a current-version KeyMint
67/// feature is required for a back-level KeyMint implementation.
68pub trait EmulationDetector: Send + Sync {
69 /// Indicate whether software emulation is required for key
70 /// generation/import using the provided parameters.
71 fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool;
72}
73
74const KEYBLOB_PREFIX: &[u8] = b"SoftKeyMintForV1Blob";
75const KEYBLOB_HMAC_KEY: &[u8] = b"SoftKeyMintForV1HMACKey";
76
77/// Wrap the provided keyblob:
78/// - prefix it with an identifier specific to this wrapper
79/// - suffix it with an HMAC tag, using the [`KEYBLOB_HMAC_KEY`] and `keyblob`.
80fn wrap_keyblob(keyblob: &[u8]) -> anyhow::Result<Vec<u8>> {
81 let mut result = Vec::with_capacity(KEYBLOB_PREFIX.len() + keyblob.len() + HMAC_SHA256_LEN);
82 result.extend_from_slice(KEYBLOB_PREFIX);
83 result.extend_from_slice(keyblob);
84 let tag = hmac_sha256(KEYBLOB_HMAC_KEY, keyblob)
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +000085 .context(ks_err!("failed to calculate HMAC-SHA256"))?;
David Drysdalec97eb9e2022-01-26 13:03:48 -080086 result.extend_from_slice(&tag);
87 Ok(result)
88}
89
90/// Return an unwrapped version of the provided `keyblob`, which may or may
91/// not be associated with the software emulation.
92fn unwrap_keyblob(keyblob: &[u8]) -> KeyBlob {
93 if !keyblob.starts_with(KEYBLOB_PREFIX) {
94 return KeyBlob::Raw(keyblob);
95 }
96 let without_prefix = &keyblob[KEYBLOB_PREFIX.len()..];
97 if without_prefix.len() < HMAC_SHA256_LEN {
98 return KeyBlob::Raw(keyblob);
99 }
100 let (inner_keyblob, want_tag) = without_prefix.split_at(without_prefix.len() - HMAC_SHA256_LEN);
101 let got_tag = match hmac_sha256(KEYBLOB_HMAC_KEY, inner_keyblob) {
102 Ok(tag) => tag,
103 Err(e) => {
104 log::error!("Error calculating HMAC-SHA256 for keyblob unwrap: {:?}", e);
105 return KeyBlob::Raw(keyblob);
106 }
107 };
108 // Comparison does not need to be constant-time here.
109 if want_tag == got_tag {
110 KeyBlob::Wrapped(inner_keyblob)
111 } else {
112 KeyBlob::Raw(keyblob)
113 }
114}
115
116/// Wrapper around a real device that implements a back-level version of
117/// `IKeyMintDevice`
118pub struct BacklevelKeyMintWrapper<T: EmulationDetector> {
119 /// The `real` device implements some earlier version of `IKeyMintDevice`
120 real: Strong<dyn IKeyMintDevice>,
121 /// The `soft`ware device implements the current version of `IKeyMintDevice`
122 soft: Strong<dyn IKeyMintDevice>,
123 /// Detector for operations that are not supported by the earlier version of
124 /// `IKeyMintDevice`. Or possibly a large flightless bird, who can tell.
125 emu: T,
126}
127
128impl<T> BacklevelKeyMintWrapper<T>
129where
130 T: EmulationDetector + 'static,
131{
132 /// Create a wrapper around the provided back-level KeyMint device, so that
133 /// software emulation can be performed for any current-version features not
134 /// provided by the real device.
135 pub fn wrap(
136 emu: T,
137 real: Strong<dyn IKeyMintDevice>,
138 ) -> anyhow::Result<Strong<dyn IKeyMintDevice>> {
139 // This is a no-op if it was called before.
140 keystore2_km_compat::add_keymint_device_service();
141
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000142 let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
143 map_binder_status_code(binder::get_interface("android.security.compat"))
144 .context(ks_err!("Trying to connect to compat service."))?;
David Drysdalec97eb9e2022-01-26 13:03:48 -0800145 let soft =
146 map_binder_status(keystore_compat_service.getKeyMintDevice(SecurityLevel::SOFTWARE))
147 .map_err(|e| match e {
148 Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
149 Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
150 }
151 e => e,
152 })
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000153 .context(ks_err!("Trying to get software device."))?;
David Drysdalec97eb9e2022-01-26 13:03:48 -0800154
155 Ok(BnKeyMintDevice::new_binder(
156 Self { real, soft, emu },
157 BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
158 ))
159 }
160}
161
162impl<T> binder::Interface for BacklevelKeyMintWrapper<T> where T: EmulationDetector {}
163
164impl<T> IKeyMintDevice for BacklevelKeyMintWrapper<T>
165where
166 T: EmulationDetector + 'static,
167{
168 // For methods that don't involve keyblobs, forward to either the real
169 // device, or to both real & emulated devices.
170 fn getHardwareInfo(&self) -> binder::Result<KeyMintHardwareInfo> {
171 self.real.getHardwareInfo()
172 }
173 fn addRngEntropy(&self, data: &[u8]) -> binder::Result<()> {
174 self.real.addRngEntropy(data)
175 }
176 fn deleteAllKeys(&self) -> binder::Result<()> {
177 self.real.deleteAllKeys()
178 }
179 fn destroyAttestationIds(&self) -> binder::Result<()> {
180 self.real.destroyAttestationIds()
181 }
182 fn deviceLocked(
183 &self,
184 password_only: bool,
185 timestamp_token: Option<&TimeStampToken>,
186 ) -> binder::Result<()> {
187 // Propagate to both real and software devices, but only pay attention
188 // to the result from the real device.
189 let _ = self.soft.deviceLocked(password_only, timestamp_token);
190 self.real.deviceLocked(password_only, timestamp_token)
191 }
192 fn earlyBootEnded(&self) -> binder::Result<()> {
193 // Propagate to both real and software devices, but only pay attention
194 // to the result from the real device.
195 let _ = self.soft.earlyBootEnded();
196 self.real.earlyBootEnded()
197 }
198
199 // For methods that emit keyblobs, check whether the underlying real device
200 // supports the relevant parameters, and forward to the appropriate device.
201 // If the emulated device is used, ensure that the created keyblob gets
202 // prefixed so we can recognize it in future.
203 fn generateKey(
204 &self,
205 key_params: &[KeyParameter],
206 attestation_key: Option<&AttestationKey>,
207 ) -> binder::Result<KeyCreationResult> {
208 if self.emu.emulation_required(key_params, &KeyImportData::None) {
209 let mut result = self.soft.generateKey(key_params, attestation_key)?;
210 result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
211 Ok(result)
212 } else {
213 self.real.generateKey(key_params, attestation_key)
214 }
215 }
216 fn importKey(
217 &self,
218 key_params: &[KeyParameter],
219 key_format: KeyFormat,
220 key_data: &[u8],
221 attestation_key: Option<&AttestationKey>,
222 ) -> binder::Result<KeyCreationResult> {
223 if self.emu.emulation_required(key_params, &KeyImportData::new(key_format, key_data)?) {
224 let mut result =
225 self.soft.importKey(key_params, key_format, key_data, attestation_key)?;
226 result.keyBlob = map_or_log_err(wrap_keyblob(&result.keyBlob), Ok)?;
227 Ok(result)
228 } else {
229 self.real.importKey(key_params, key_format, key_data, attestation_key)
230 }
231 }
232 fn importWrappedKey(
233 &self,
234 wrapped_key_data: &[u8],
235 wrapping_key_blob: &[u8],
236 masking_key: &[u8],
237 unwrapping_params: &[KeyParameter],
238 password_sid: i64,
239 biometric_sid: i64,
240 ) -> binder::Result<KeyCreationResult> {
241 // A wrapped key cannot be software-emulated, as the wrapping key is
242 // likely hardware-bound.
243 self.real.importWrappedKey(
244 wrapped_key_data,
245 wrapping_key_blob,
246 masking_key,
247 unwrapping_params,
248 password_sid,
249 biometric_sid,
250 )
251 }
252
253 // For methods that use keyblobs, determine which device to forward the
254 // operation to based on whether the keyblob is appropriately prefixed.
255 fn upgradeKey(
256 &self,
257 keyblob_to_upgrade: &[u8],
258 upgrade_params: &[KeyParameter],
259 ) -> binder::Result<Vec<u8>> {
260 match unwrap_keyblob(keyblob_to_upgrade) {
261 KeyBlob::Raw(keyblob) => self.real.upgradeKey(keyblob, upgrade_params),
262 KeyBlob::Wrapped(keyblob) => {
263 // Re-wrap the upgraded keyblob.
264 let upgraded_keyblob = self.soft.upgradeKey(keyblob, upgrade_params)?;
265 map_or_log_err(wrap_keyblob(&upgraded_keyblob), Ok)
266 }
267 }
268 }
269 fn deleteKey(&self, keyblob: &[u8]) -> binder::Result<()> {
270 match unwrap_keyblob(keyblob) {
271 KeyBlob::Raw(keyblob) => self.real.deleteKey(keyblob),
272 KeyBlob::Wrapped(keyblob) => {
273 // Forward to the software implementation for completeness, but
274 // this should always be a no-op.
275 self.soft.deleteKey(keyblob)
276 }
277 }
278 }
279 fn begin(
280 &self,
281 purpose: KeyPurpose,
282 keyblob: &[u8],
283 params: &[KeyParameter],
284 auth_token: Option<&HardwareAuthToken>,
285 ) -> binder::Result<BeginResult> {
286 match unwrap_keyblob(keyblob) {
287 KeyBlob::Raw(keyblob) => self.real.begin(purpose, keyblob, params, auth_token),
288 KeyBlob::Wrapped(keyblob) => self.soft.begin(purpose, keyblob, params, auth_token),
289 }
290 }
291 fn getKeyCharacteristics(
292 &self,
293 keyblob: &[u8],
294 app_id: &[u8],
295 app_data: &[u8],
296 ) -> binder::Result<Vec<KeyCharacteristics>> {
297 match unwrap_keyblob(keyblob) {
298 KeyBlob::Raw(keyblob) => self.real.getKeyCharacteristics(keyblob, app_id, app_data),
299 KeyBlob::Wrapped(keyblob) => self.soft.getKeyCharacteristics(keyblob, app_id, app_data),
300 }
301 }
Shawn Willdene2fac0c2022-02-17 15:46:14 -0700302 fn getRootOfTrustChallenge(&self) -> binder::Result<[u8; 16]> {
303 self.real.getRootOfTrustChallenge()
304 }
305 fn getRootOfTrust(&self, challenge: &[u8; 16]) -> binder::Result<Vec<u8>> {
306 self.real.getRootOfTrust(challenge)
307 }
308 fn sendRootOfTrust(&self, root_of_trust: &[u8]) -> binder::Result<()> {
309 self.real.sendRootOfTrust(root_of_trust)
310 }
David Drysdalec97eb9e2022-01-26 13:03:48 -0800311 fn convertStorageKeyToEphemeral(&self, storage_keyblob: &[u8]) -> binder::Result<Vec<u8>> {
312 // Storage keys should never be associated with a software emulated device.
313 self.real.convertStorageKeyToEphemeral(storage_keyblob)
314 }
315}
316
317/// Detector for current features that are not implemented by KeyMint V1.
318#[derive(Debug)]
319pub struct KeyMintV1 {
320 sec_level: SecurityLevel,
321}
322
323impl KeyMintV1 {
324 pub fn new(sec_level: SecurityLevel) -> Self {
325 Self { sec_level }
326 }
327}
328
329impl EmulationDetector for KeyMintV1 {
330 fn emulation_required(&self, params: &[KeyParameter], _import_data: &KeyImportData) -> bool {
331 // No current difference from KeyMint v1 for STRONGBOX (it doesn't
332 // support curve 25519).
333 if self.sec_level == SecurityLevel::STRONGBOX {
334 return false;
335 }
336
337 // KeyMint V1 does not support the use of curve 25519, so hunt for that
338 // in the parameters.
339 if params.iter().any(|p| {
340 p.tag == Tag::EC_CURVE && p.value == KeyParameterValue::EcCurve(EcCurve::CURVE_25519)
341 }) {
342 return true;
343 }
344 // In theory, if the `import_data` is `KeyImportData::Pkcs8` we could
345 // check the imported keymaterial for the Ed25519 / X25519 OIDs in the
346 // PKCS8 keydata, and use that to decide to route to software. However,
347 // the KeyMint spec doesn't require that so don't attempt to parse the
348 // key material here.
349 false
350 }
351}
352
353/// Detector for current features that are not implemented by KeyMaster, via the
354/// km_compat wrapper.
355#[derive(Debug)]
356pub struct Keymaster {
357 v1: KeyMintV1,
358}
359
360/// TODO(b/216434270): This could be used this to replace the emulation routing
361/// in the km_compat C++ code, and allow support for imported ECDH keys along
362/// the way. Would need to figure out what would happen to existing emulated
363/// keys though.
364#[allow(dead_code)]
365impl Keymaster {
366 pub fn new(sec_level: SecurityLevel) -> Self {
367 Self { v1: KeyMintV1::new(sec_level) }
368 }
369}
370
371impl EmulationDetector for Keymaster {
372 fn emulation_required(&self, params: &[KeyParameter], import_data: &KeyImportData) -> bool {
373 // The km_compat wrapper on top of Keymaster emulates the KeyMint V1
374 // interface, so any feature from > v1 needs to be emulated.
375 if self.v1.emulation_required(params, import_data) {
376 return true;
377 }
378
379 // Keymaster does not support ECDH (KeyPurpose::AGREE_KEY), so hunt for
380 // that in the parameters.
381 if params.iter().any(|p| {
382 p.tag == Tag::PURPOSE && p.value == KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY)
383 }) {
384 return true;
385 }
386 false
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_key_import_data() {
396 let data = vec![1, 2, 3];
397 assert_eq!(KeyImportData::new(KeyFormat::PKCS8, &data), Ok(KeyImportData::Pkcs8(&data)));
398 assert_eq!(KeyImportData::new(KeyFormat::RAW, &data), Ok(KeyImportData::Raw(&data)));
399 assert!(KeyImportData::new(KeyFormat::X509, &data).is_err());
400 }
401
402 #[test]
403 fn test_wrap_keyblob() {
404 let keyblob = vec![1, 2, 3];
405 let wrapped = wrap_keyblob(&keyblob).unwrap();
406 assert_eq!(&wrapped[..KEYBLOB_PREFIX.len()], KEYBLOB_PREFIX);
407 assert_eq!(&wrapped[KEYBLOB_PREFIX.len()..KEYBLOB_PREFIX.len() + keyblob.len()], &keyblob);
408 assert_eq!(unwrap_keyblob(&keyblob), KeyBlob::Raw(&keyblob));
409 assert_eq!(unwrap_keyblob(&wrapped), KeyBlob::Wrapped(&keyblob));
410
411 let mut corrupt_prefix = wrapped.clone();
412 corrupt_prefix[0] ^= 0x01;
413 assert_eq!(unwrap_keyblob(&corrupt_prefix), KeyBlob::Raw(&corrupt_prefix));
414
415 let mut corrupt_suffix = wrapped.clone();
416 corrupt_suffix[wrapped.len() - 1] ^= 0x01;
417 assert_eq!(unwrap_keyblob(&corrupt_suffix), KeyBlob::Raw(&corrupt_suffix));
418
419 let too_short = &wrapped[..wrapped.len() - 4];
420 assert_eq!(unwrap_keyblob(too_short), KeyBlob::Raw(too_short));
421 }
422
423 #[test]
424 fn test_keymintv1_emulation_required() {
425 let tests = vec![
426 (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
427 (
428 SecurityLevel::TRUSTED_ENVIRONMENT,
429 vec![
430 KeyParameter {
431 tag: Tag::PURPOSE,
432 value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
433 },
434 KeyParameter {
435 tag: Tag::PURPOSE,
436 value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
437 },
438 ],
439 false,
440 ),
441 (
442 SecurityLevel::TRUSTED_ENVIRONMENT,
443 vec![KeyParameter {
444 tag: Tag::PURPOSE,
445 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
446 }],
447 false,
448 ),
449 (
450 SecurityLevel::TRUSTED_ENVIRONMENT,
451 vec![
452 KeyParameter {
453 tag: Tag::PURPOSE,
454 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
455 },
456 KeyParameter {
457 tag: Tag::EC_CURVE,
458 value: KeyParameterValue::EcCurve(EcCurve::P_256),
459 },
460 ],
461 false,
462 ),
463 (
464 SecurityLevel::TRUSTED_ENVIRONMENT,
465 vec![
466 KeyParameter {
467 tag: Tag::PURPOSE,
468 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
469 },
470 KeyParameter {
471 tag: Tag::EC_CURVE,
472 value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
473 },
474 ],
475 true,
476 ),
477 (
478 SecurityLevel::STRONGBOX,
479 vec![
480 KeyParameter {
481 tag: Tag::PURPOSE,
482 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
483 },
484 KeyParameter {
485 tag: Tag::EC_CURVE,
486 value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
487 },
488 ],
489 false,
490 ),
491 ];
492 for (sec_level, params, want) in tests {
493 let v1 = KeyMintV1::new(sec_level);
494 let got = v1.emulation_required(&params, &KeyImportData::None);
495 assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
496 }
497 }
498
499 #[test]
500 fn test_keymaster_emulation_required() {
501 let tests = vec![
502 (SecurityLevel::TRUSTED_ENVIRONMENT, vec![], false),
503 (
504 SecurityLevel::TRUSTED_ENVIRONMENT,
505 vec![
506 KeyParameter {
507 tag: Tag::PURPOSE,
508 value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
509 },
510 KeyParameter {
511 tag: Tag::PURPOSE,
512 value: KeyParameterValue::KeyPurpose(KeyPurpose::VERIFY),
513 },
514 ],
515 false,
516 ),
517 (
518 SecurityLevel::TRUSTED_ENVIRONMENT,
519 vec![KeyParameter {
520 tag: Tag::PURPOSE,
521 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
522 }],
523 true,
524 ),
525 (
526 SecurityLevel::TRUSTED_ENVIRONMENT,
527 vec![
528 KeyParameter {
529 tag: Tag::PURPOSE,
530 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
531 },
532 KeyParameter {
533 tag: Tag::EC_CURVE,
534 value: KeyParameterValue::EcCurve(EcCurve::P_256),
535 },
536 ],
537 true,
538 ),
539 (
540 SecurityLevel::TRUSTED_ENVIRONMENT,
541 vec![
542 KeyParameter {
543 tag: Tag::PURPOSE,
544 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
545 },
546 KeyParameter {
547 tag: Tag::EC_CURVE,
548 value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
549 },
550 ],
551 true,
552 ),
553 (
554 SecurityLevel::STRONGBOX,
555 vec![
556 KeyParameter {
557 tag: Tag::PURPOSE,
558 value: KeyParameterValue::KeyPurpose(KeyPurpose::AGREE_KEY),
559 },
560 KeyParameter {
561 tag: Tag::EC_CURVE,
562 value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
563 },
564 ],
565 true,
566 ),
567 (
568 SecurityLevel::STRONGBOX,
569 vec![
570 KeyParameter {
571 tag: Tag::PURPOSE,
572 value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
573 },
574 KeyParameter {
575 tag: Tag::EC_CURVE,
576 value: KeyParameterValue::EcCurve(EcCurve::CURVE_25519),
577 },
578 ],
579 false,
580 ),
581 ];
582 for (sec_level, params, want) in tests {
583 let v0 = Keymaster::new(sec_level);
584 let got = v0.emulation_required(&params, &KeyImportData::None);
585 assert_eq!(got, want, "emulation_required({:?})={}, want {}", params, got, want);
586 }
587 }
588}