1 // Copyright 2024, 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 //! Tests for user authentication interactions (via `IKeystoreAuthorization`).
16
17 use crate::keystore2_client_test_utils::{BarrierReached, BarrierReachedWithData};
18 use android_security_authorization::aidl::android::security::authorization::{
19 IKeystoreAuthorization::IKeystoreAuthorization
20 };
21 use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::{
22 IKeystoreMaintenance,
23 };
24 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
25 Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
26 HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
27 KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
28 };
29 use android_hardware_gatekeeper::aidl::android::hardware::gatekeeper::{
30 IGatekeeper::IGatekeeper, IGatekeeper::ERROR_RETRY_TIMEOUT,
31 };
32 use android_system_keystore2::aidl::android::system::keystore2::{
33 CreateOperationResponse::CreateOperationResponse, Domain::Domain, KeyDescriptor::KeyDescriptor,
34 KeyMetadata::KeyMetadata,
35 };
36 use android_system_keystore2::binder::{ExceptionCode, Result as BinderResult};
37 use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
38 Timestamp::Timestamp,
39 };
40 use anyhow::Context;
41 use keystore2_test_utils::{
42 authorizations::AuthSetBuilder, expect, get_keystore_service, run_as,
43 run_as::{ChannelReader, ChannelWriter}, expect_km_error,
44 };
45 use log::{warn, info};
46 use rustutils::users::AID_USER_OFFSET;
47 use std::{time::Duration, thread::sleep};
48
49 /// Test user ID.
50 const TEST_USER_ID: i32 = 100;
51 /// Corresponding uid value.
52 const UID: u32 = TEST_USER_ID as u32 * AID_USER_OFFSET + 1001;
53 /// Fake synthetic password blob.
54 static SYNTHETIC_PASSWORD: &[u8] = &[
55 0x42, 0x39, 0x30, 0x37, 0x44, 0x37, 0x32, 0x37, 0x39, 0x39, 0x43, 0x42, 0x39, 0x41, 0x42, 0x30,
56 0x34, 0x31, 0x30, 0x38, 0x46, 0x44, 0x33, 0x45, 0x39, 0x42, 0x32, 0x38, 0x36, 0x35, 0x41, 0x36,
57 0x33, 0x44, 0x42, 0x42, 0x43, 0x36, 0x33, 0x42, 0x34, 0x39, 0x37, 0x33, 0x35, 0x45, 0x41, 0x41,
58 0x32, 0x45, 0x31, 0x35, 0x43, 0x43, 0x46, 0x32, 0x39, 0x36, 0x33, 0x34, 0x31, 0x32, 0x41, 0x39,
59 ];
60 /// Gatekeeper password.
61 static GK_PASSWORD: &[u8] = b"correcthorsebatterystaple";
62 /// Fake SID value corresponding to Gatekeeper.
63 static GK_FAKE_SID: i64 = 123456;
64 /// Fake SID value corresponding to a biometric authenticator.
65 static BIO_FAKE_SID1: i64 = 345678;
66 /// Fake SID value corresponding to a biometric authenticator.
67 static BIO_FAKE_SID2: i64 = 456789;
68
69 const WEAK_UNLOCK_ENABLED: bool = true;
70 const WEAK_UNLOCK_DISABLED: bool = false;
71 const UNFORCED: bool = false;
72
get_authorization() -> binder::Strong<dyn IKeystoreAuthorization>73 fn get_authorization() -> binder::Strong<dyn IKeystoreAuthorization> {
74 binder::get_interface("android.security.authorization").unwrap()
75 }
76
get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance>77 fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
78 binder::get_interface("android.security.maintenance").unwrap()
79 }
80
81 /// Get the default Gatekeeper instance. This may fail on older devices where Gatekeeper is still a
82 /// HIDL interface rather than AIDL.
get_gatekeeper() -> Option<binder::Strong<dyn IGatekeeper>>83 fn get_gatekeeper() -> Option<binder::Strong<dyn IGatekeeper>> {
84 binder::get_interface("android.hardware.gatekeeper.IGatekeeper/default").ok()
85 }
86
87 /// Indicate whether a Gatekeeper result indicates a delayed-retry is needed.
is_gk_retry<T: std::fmt::Debug>(result: &BinderResult<T>) -> bool88 fn is_gk_retry<T: std::fmt::Debug>(result: &BinderResult<T>) -> bool {
89 matches!(result, Err(s) if s.exception_code() == ExceptionCode::SERVICE_SPECIFIC
90 && s.service_specific_error() == ERROR_RETRY_TIMEOUT)
91 }
92
abort_op(result: binder::Result<CreateOperationResponse>)93 fn abort_op(result: binder::Result<CreateOperationResponse>) {
94 if let Ok(rsp) = result {
95 if let Some(op) = rsp.iOperation {
96 if let Err(e) = op.abort() {
97 warn!("abort op failed: {e:?}");
98 }
99 } else {
100 warn!("can't abort op with missing iOperation");
101 }
102 } else {
103 warn!("can't abort failed op: {result:?}");
104 }
105 }
106
107 /// RAII structure to ensure that test users are removed at the end of a test.
108 struct TestUser {
109 id: i32,
110 maint: binder::Strong<dyn IKeystoreMaintenance>,
111 gk: Option<binder::Strong<dyn IGatekeeper>>,
112 gk_sid: Option<i64>,
113 gk_handle: Vec<u8>,
114 }
115
116 impl TestUser {
new() -> Self117 fn new() -> Self {
118 Self::new_user(TEST_USER_ID, SYNTHETIC_PASSWORD)
119 }
new_user(user_id: i32, sp: &[u8]) -> Self120 fn new_user(user_id: i32, sp: &[u8]) -> Self {
121 let maint = get_maintenance();
122 maint.onUserAdded(user_id).expect("failed to add test user");
123 maint
124 .initUserSuperKeys(user_id, sp, /* allowExisting= */ false)
125 .expect("failed to init test user");
126 let gk = get_gatekeeper();
127 let (gk_sid, gk_handle) = if let Some(gk) = &gk {
128 // AIDL Gatekeeper is available, so enroll a password.
129 loop {
130 let result = gk.enroll(user_id, &[], &[], GK_PASSWORD);
131 if is_gk_retry(&result) {
132 sleep(Duration::from_secs(1));
133 continue;
134 }
135 let rsp = result.expect("gk.enroll() failed");
136 info!("registered test user {user_id} as sid {} with GK", rsp.secureUserId);
137 break (Some(rsp.secureUserId), rsp.data);
138 }
139 } else {
140 (None, vec![])
141 };
142 Self { id: user_id, maint, gk, gk_sid, gk_handle }
143 }
144
145 /// Perform Gatekeeper verification, which will return a HAT on success.
gk_verify(&self, challenge: i64) -> Option<HardwareAuthToken>146 fn gk_verify(&self, challenge: i64) -> Option<HardwareAuthToken> {
147 let Some(gk) = &self.gk else { return None };
148 loop {
149 let result = gk.verify(self.id, challenge, &self.gk_handle, GK_PASSWORD);
150 if is_gk_retry(&result) {
151 sleep(Duration::from_secs(1));
152 continue;
153 }
154 let rsp = result.expect("gk.verify failed");
155 break Some(rsp.hardwareAuthToken);
156 }
157 }
158 }
159
160 impl Drop for TestUser {
drop(&mut self)161 fn drop(&mut self) {
162 let _ = self.maint.onUserRemoved(self.id);
163 if let Some(gk) = &self.gk {
164 info!("deregister test user {} with GK", self.id);
165 if let Err(e) = gk.deleteUser(self.id) {
166 warn!("failed to deregister test user {}: {e:?}", self.id);
167 }
168 }
169 }
170 }
171
172 #[test]
test_auth_bound_timeout_with_gk()173 fn test_auth_bound_timeout_with_gk() {
174 type Barrier = BarrierReachedWithData<Option<i64>>;
175 android_logger::init_once(
176 android_logger::Config::default()
177 .with_tag("keystore2_client_tests")
178 .with_max_level(log::LevelFilter::Debug),
179 );
180
181 let child_fn = move |reader: &mut ChannelReader<Barrier>,
182 writer: &mut ChannelWriter<Barrier>|
183 -> Result<(), run_as::Error> {
184 // Now we're in a new process, wait to be notified before starting.
185 let gk_sid: i64 = match reader.recv().0 {
186 Some(sid) => sid,
187 None => {
188 // There is no AIDL Gatekeeper available, so abandon the test. It would be nice to
189 // know this before starting the child process, but finding it out requires Binder,
190 // which can't be used until after the child has forked.
191 return Ok(());
192 }
193 };
194
195 // Action A: create a new auth-bound key which requires auth in the last 3 seconds,
196 // and fail to start an operation using it.
197 let ks2 = get_keystore_service();
198 let sec_level =
199 ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
200 let params = AuthSetBuilder::new()
201 .user_secure_id(gk_sid)
202 .user_secure_id(BIO_FAKE_SID1)
203 .user_secure_id(BIO_FAKE_SID2)
204 .user_auth_type(HardwareAuthenticatorType::ANY)
205 .auth_timeout(3)
206 .algorithm(Algorithm::EC)
207 .purpose(KeyPurpose::SIGN)
208 .purpose(KeyPurpose::VERIFY)
209 .digest(Digest::SHA_2_256)
210 .ec_curve(EcCurve::P_256);
211
212 let KeyMetadata { key, .. } = sec_level
213 .generateKey(
214 &KeyDescriptor {
215 domain: Domain::APP,
216 nspace: -1,
217 alias: Some("auth-bound-timeout".to_string()),
218 blob: None,
219 },
220 None,
221 ¶ms,
222 0,
223 b"entropy",
224 )
225 .context("key generation failed")?;
226 info!("A: created auth-timeout key {key:?}");
227
228 // No HATs so cannot create an operation using the key.
229 let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
230 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
231 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
232 info!("A: failed auth-bound operation (no HAT) as expected {result:?}");
233
234 writer.send(&Barrier::new(None)); // A done.
235
236 // Action B: succeed when a valid HAT is available.
237 reader.recv();
238
239 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
240 expect!(result.is_ok());
241 let op = result.unwrap().iOperation.context("no operation in result")?;
242 let result = op.finish(Some(b"data"), None);
243 expect!(result.is_ok());
244 info!("B: performed auth-bound operation (with valid GK HAT) as expected");
245
246 writer.send(&Barrier::new(None)); // B done.
247
248 // Action C: fail again when the HAT is old enough to not even be checked.
249 reader.recv();
250 info!("C: wait so that any HAT times out");
251 sleep(Duration::from_secs(4));
252 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
253 info!("C: failed auth-bound operation (HAT is too old) as expected {result:?}");
254 writer.send(&Barrier::new(None)); // C done.
255
256 Ok(())
257 };
258
259 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
260 // `--test-threads=1`), and nothing yet done with binder.
261 let mut child_handle = unsafe {
262 // Perform keystore actions while running as the test user.
263 run_as::run_as_child_app(UID, UID, child_fn)
264 }
265 .unwrap();
266
267 // Now that the separate process has been forked off, it's safe to use binder to setup a test
268 // user.
269 let _ks2 = get_keystore_service();
270 let user = TestUser::new();
271 if user.gk.is_none() {
272 // Can't run this test if there's no AIDL Gatekeeper.
273 child_handle.send(&Barrier::new(None));
274 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
275 return;
276 }
277 let user_id = user.id;
278 let auth_service = get_authorization();
279
280 // Lock and unlock to ensure super keys are already created.
281 auth_service
282 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
283 .unwrap();
284 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
285
286 info!("trigger child process action A and wait for completion");
287 child_handle.send(&Barrier::new(Some(user.gk_sid.unwrap())));
288 child_handle.recv_or_die();
289
290 // Unlock with GK password to get a genuine auth token.
291 let real_hat = user.gk_verify(0).expect("failed to perform GK verify");
292 auth_service.addAuthToken(&real_hat).unwrap();
293
294 info!("trigger child process action B and wait for completion");
295 child_handle.send(&Barrier::new(None));
296 child_handle.recv_or_die();
297
298 info!("trigger child process action C and wait for completion");
299 child_handle.send(&Barrier::new(None));
300 child_handle.recv_or_die();
301
302 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
303 }
304
305 #[test]
test_auth_bound_timeout_failure()306 fn test_auth_bound_timeout_failure() {
307 android_logger::init_once(
308 android_logger::Config::default()
309 .with_tag("keystore2_client_tests")
310 .with_max_level(log::LevelFilter::Debug),
311 );
312
313 let child_fn = move |reader: &mut ChannelReader<BarrierReached>,
314 writer: &mut ChannelWriter<BarrierReached>|
315 -> Result<(), run_as::Error> {
316 // Now we're in a new process, wait to be notified before starting.
317 reader.recv();
318
319 // Action A: create a new auth-bound key which requires auth in the last 3 seconds,
320 // and fail to start an operation using it.
321 let ks2 = get_keystore_service();
322
323 let sec_level =
324 ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
325 let params = AuthSetBuilder::new()
326 .user_secure_id(BIO_FAKE_SID1)
327 .user_secure_id(BIO_FAKE_SID2)
328 .user_auth_type(HardwareAuthenticatorType::ANY)
329 .auth_timeout(3)
330 .algorithm(Algorithm::EC)
331 .purpose(KeyPurpose::SIGN)
332 .purpose(KeyPurpose::VERIFY)
333 .digest(Digest::SHA_2_256)
334 .ec_curve(EcCurve::P_256);
335
336 let KeyMetadata { key, .. } = sec_level
337 .generateKey(
338 &KeyDescriptor {
339 domain: Domain::APP,
340 nspace: -1,
341 alias: Some("auth-bound-timeout".to_string()),
342 blob: None,
343 },
344 None,
345 ¶ms,
346 0,
347 b"entropy",
348 )
349 .context("key generation failed")?;
350 info!("A: created auth-timeout key {key:?}");
351
352 // No HATs so cannot create an operation using the key.
353 let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
354 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
355 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
356 info!("A: failed auth-bound operation (no HAT) as expected {result:?}");
357
358 writer.send(&BarrierReached {}); // A done.
359
360 // Action B: fail again when an invalid HAT is available.
361 reader.recv();
362
363 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
364 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
365 info!("B: failed auth-bound operation (HAT is invalid) as expected {result:?}");
366
367 writer.send(&BarrierReached {}); // B done.
368
369 // Action C: fail again when the HAT is old enough to not even be checked.
370 reader.recv();
371 info!("C: wait so that any HAT times out");
372 sleep(Duration::from_secs(4));
373 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
374 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
375 info!("C: failed auth-bound operation (HAT is too old) as expected {result:?}");
376 writer.send(&BarrierReached {}); // C done.
377
378 Ok(())
379 };
380
381 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
382 // `--test-threads=1`), and nothing yet done with binder.
383 let mut child_handle = unsafe {
384 // Perform keystore actions while running as the test user.
385 run_as::run_as_child_app(UID, UID, child_fn)
386 }
387 .unwrap();
388
389 // Now that the separate process has been forked off, it's safe to use binder to setup a test
390 // user.
391 let _ks2 = get_keystore_service();
392 let user = TestUser::new();
393 let user_id = user.id;
394 let auth_service = get_authorization();
395
396 // Lock and unlock to ensure super keys are already created.
397 auth_service
398 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
399 .unwrap();
400 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
401 auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
402
403 info!("trigger child process action A and wait for completion");
404 child_handle.send(&BarrierReached {});
405 child_handle.recv_or_die();
406
407 // Unlock with password and a fake auth token that matches the key
408 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
409 auth_service.addAuthToken(&fake_bio_lskf_token(GK_FAKE_SID, BIO_FAKE_SID1)).unwrap();
410
411 info!("trigger child process action B and wait for completion");
412 child_handle.send(&BarrierReached {});
413 child_handle.recv_or_die();
414
415 info!("trigger child process action C and wait for completion");
416 child_handle.send(&BarrierReached {});
417 child_handle.recv_or_die();
418
419 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
420 }
421
422 #[test]
test_auth_bound_per_op_with_gk()423 fn test_auth_bound_per_op_with_gk() {
424 type Barrier = BarrierReachedWithData<Option<i64>>;
425 android_logger::init_once(
426 android_logger::Config::default()
427 .with_tag("keystore2_client_tests")
428 .with_max_level(log::LevelFilter::Debug),
429 );
430
431 let child_fn = move |reader: &mut ChannelReader<Barrier>,
432 writer: &mut ChannelWriter<Barrier>|
433 -> Result<(), run_as::Error> {
434 // Now we're in a new process, wait to be notified before starting.
435 let gk_sid: i64 = match reader.recv().0 {
436 Some(sid) => sid,
437 None => {
438 // There is no AIDL Gatekeeper available, so abandon the test. It would be nice to
439 // know this before starting the child process, but finding it out requires Binder,
440 // which can't be used until after the child has forked.
441 return Ok(());
442 }
443 };
444
445 // Action A: create a new auth-bound key which requires auth-per-operation (because
446 // AUTH_TIMEOUT is not specified), and fail to finish an operation using it.
447 let ks2 = get_keystore_service();
448 let sec_level =
449 ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
450 let params = AuthSetBuilder::new()
451 .user_secure_id(gk_sid)
452 .user_secure_id(BIO_FAKE_SID1)
453 .user_auth_type(HardwareAuthenticatorType::ANY)
454 .algorithm(Algorithm::EC)
455 .purpose(KeyPurpose::SIGN)
456 .purpose(KeyPurpose::VERIFY)
457 .digest(Digest::SHA_2_256)
458 .ec_curve(EcCurve::P_256);
459
460 let KeyMetadata { key, .. } = sec_level
461 .generateKey(
462 &KeyDescriptor {
463 domain: Domain::APP,
464 nspace: -1,
465 alias: Some("auth-per-op".to_string()),
466 blob: None,
467 },
468 None,
469 ¶ms,
470 0,
471 b"entropy",
472 )
473 .context("key generation failed")?;
474 info!("A: created auth-per-op key {key:?}");
475
476 // We can create an operation using the key...
477 let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
478 let result = sec_level
479 .createOperation(&key, ¶ms, UNFORCED)
480 .expect("failed to create auth-per-op operation");
481 let op = result.iOperation.context("no operation in result")?;
482 info!("A: created auth-per-op operation, got challenge {:?}", result.operationChallenge);
483
484 // .. but attempting to finish the operation fails because Keystore can't find a HAT.
485 let result = op.finish(Some(b"data"), None);
486 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
487 info!("A: failed auth-per-op op (no HAT) as expected {result:?}");
488
489 writer.send(&Barrier::new(None)); // A done.
490
491 // Action B: start an operation and pass out the challenge
492 reader.recv();
493 let result = sec_level
494 .createOperation(&key, ¶ms, UNFORCED)
495 .expect("failed to create auth-per-op operation");
496 let op = result.iOperation.context("no operation in result")?;
497 info!("B: created auth-per-op operation, got challenge {:?}", result.operationChallenge);
498 writer.send(&Barrier::new(Some(result.operationChallenge.unwrap().challenge))); // B done.
499
500 // Action C: finishing the operation succeeds now there's a per-op HAT.
501 reader.recv();
502 let result = op.finish(Some(b"data"), None);
503 expect!(result.is_ok());
504 info!("C: performed auth-per-op op expected");
505 writer.send(&Barrier::new(None)); // D done.
506
507 Ok(())
508 };
509
510 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
511 // `--test-threads=1`), and nothing yet done with binder.
512 let mut child_handle = unsafe {
513 // Perform keystore actions while running as the test user.
514 run_as::run_as_child_app(UID, UID, child_fn)
515 }
516 .unwrap();
517
518 // Now that the separate process has been forked off, it's safe to use binder to setup a test
519 // user.
520 let _ks2 = get_keystore_service();
521 let user = TestUser::new();
522 if user.gk.is_none() {
523 // Can't run this test if there's no AIDL Gatekeeper.
524 child_handle.send(&Barrier::new(None));
525 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
526 return;
527 }
528 let user_id = user.id;
529 let auth_service = get_authorization();
530
531 // Lock and unlock to ensure super keys are already created.
532 auth_service
533 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
534 .unwrap();
535 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
536
537 info!("trigger child process action A and wait for completion");
538 child_handle.send(&Barrier::new(Some(user.gk_sid.unwrap())));
539 child_handle.recv_or_die();
540
541 info!("trigger child process action B and wait for completion");
542 child_handle.send(&Barrier::new(None));
543 let challenge = child_handle.recv_or_die().0.expect("no challenge");
544
545 // Unlock with GK and the challenge to get a genuine per-op auth token
546 let real_hat = user.gk_verify(challenge).expect("failed to perform GK verify");
547 auth_service.addAuthToken(&real_hat).unwrap();
548
549 info!("trigger child process action C and wait for completion");
550 child_handle.send(&Barrier::new(None));
551 child_handle.recv_or_die();
552
553 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
554 }
555
556 #[test]
test_auth_bound_per_op_failure()557 fn test_auth_bound_per_op_failure() {
558 type Barrier = BarrierReachedWithData<i64>;
559 android_logger::init_once(
560 android_logger::Config::default()
561 .with_tag("keystore2_client_tests")
562 .with_max_level(log::LevelFilter::Debug),
563 );
564
565 let child_fn = move |reader: &mut ChannelReader<Barrier>,
566 writer: &mut ChannelWriter<Barrier>|
567 -> Result<(), run_as::Error> {
568 // Now we're in a new process, wait to be notified before starting.
569 reader.recv();
570
571 // Action A: create a new auth-bound key which requires auth-per-operation (because
572 // AUTH_TIMEOUT is not specified), and fail to finish an operation using it.
573 let ks2 = get_keystore_service();
574
575 let sec_level =
576 ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
577 let params = AuthSetBuilder::new()
578 .user_secure_id(GK_FAKE_SID)
579 .user_secure_id(BIO_FAKE_SID1)
580 .user_auth_type(HardwareAuthenticatorType::ANY)
581 .algorithm(Algorithm::EC)
582 .purpose(KeyPurpose::SIGN)
583 .purpose(KeyPurpose::VERIFY)
584 .digest(Digest::SHA_2_256)
585 .ec_curve(EcCurve::P_256);
586
587 let KeyMetadata { key, .. } = sec_level
588 .generateKey(
589 &KeyDescriptor {
590 domain: Domain::APP,
591 nspace: -1,
592 alias: Some("auth-per-op".to_string()),
593 blob: None,
594 },
595 None,
596 ¶ms,
597 0,
598 b"entropy",
599 )
600 .context("key generation failed")?;
601 info!("A: created auth-per-op key {key:?}");
602
603 // We can create an operation using the key...
604 let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
605 let result = sec_level
606 .createOperation(&key, ¶ms, UNFORCED)
607 .expect("failed to create auth-per-op operation");
608 let op = result.iOperation.context("no operation in result")?;
609 info!("A: created auth-per-op operation, got challenge {:?}", result.operationChallenge);
610
611 // .. but attempting to finish the operation fails because Keystore can't find a HAT.
612 let result = op.finish(Some(b"data"), None);
613 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
614 info!("A: failed auth-per-op op (no HAT) as expected {result:?}");
615
616 writer.send(&Barrier::new(0)); // A done.
617
618 // Action B: fail again when an irrelevant HAT is available.
619 reader.recv();
620
621 let result = sec_level
622 .createOperation(&key, ¶ms, UNFORCED)
623 .expect("failed to create auth-per-op operation");
624 let op = result.iOperation.context("no operation in result")?;
625 info!("B: created auth-per-op operation, got challenge {:?}", result.operationChallenge);
626 // The operation fails because the HAT that Keystore received is not related to the
627 // challenge.
628 let result = op.finish(Some(b"data"), None);
629 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
630 info!("B: failed auth-per-op op (HAT is not per-op) as expected {result:?}");
631
632 writer.send(&Barrier::new(0)); // B done.
633
634 // Action C: start an operation and pass out the challenge
635 reader.recv();
636 let result = sec_level
637 .createOperation(&key, ¶ms, UNFORCED)
638 .expect("failed to create auth-per-op operation");
639 let op = result.iOperation.context("no operation in result")?;
640 info!("C: created auth-per-op operation, got challenge {:?}", result.operationChallenge);
641 writer.send(&Barrier::new(result.operationChallenge.unwrap().challenge)); // C done.
642
643 // Action D: finishing the operation still fails because the per-op HAT
644 // is invalid (the HMAC signature is faked and so the secure world
645 // rejects the HAT).
646 reader.recv();
647 let result = op.finish(Some(b"data"), None);
648 expect_km_error!(&result, ErrorCode::KEY_USER_NOT_AUTHENTICATED);
649 info!("D: failed auth-per-op op (HAT is per-op but invalid) as expected {result:?}");
650 writer.send(&Barrier::new(0)); // D done.
651
652 Ok(())
653 };
654
655 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
656 // `--test-threads=1`), and nothing yet done with binder.
657 let mut child_handle = unsafe {
658 // Perform keystore actions while running as the test user.
659 run_as::run_as_child_app(UID, UID, child_fn)
660 }
661 .unwrap();
662
663 // Now that the separate process has been forked off, it's safe to use binder to setup a test
664 // user.
665 let _ks2 = get_keystore_service();
666 let user = TestUser::new();
667 let user_id = user.id;
668 let auth_service = get_authorization();
669
670 // Lock and unlock to ensure super keys are already created.
671 auth_service
672 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
673 .unwrap();
674 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
675 auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
676
677 info!("trigger child process action A and wait for completion");
678 child_handle.send(&Barrier::new(0));
679 child_handle.recv_or_die();
680
681 // Unlock with password and a fake auth token.
682 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
683 auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
684
685 info!("trigger child process action B and wait for completion");
686 child_handle.send(&Barrier::new(0));
687 child_handle.recv_or_die();
688
689 info!("trigger child process action C and wait for completion");
690 child_handle.send(&Barrier::new(0));
691 let challenge = child_handle.recv_or_die().0;
692
693 // Add a fake auth token with the challenge value.
694 auth_service.addAuthToken(&fake_lskf_token_with_challenge(GK_FAKE_SID, challenge)).unwrap();
695
696 info!("trigger child process action D and wait for completion");
697 child_handle.send(&Barrier::new(0));
698 child_handle.recv_or_die();
699
700 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
701 }
702
703 #[test]
test_unlocked_device_required()704 fn test_unlocked_device_required() {
705 android_logger::init_once(
706 android_logger::Config::default()
707 .with_tag("keystore2_client_tests")
708 .with_max_level(log::LevelFilter::Debug),
709 );
710
711 let child_fn = move |reader: &mut ChannelReader<BarrierReached>,
712 writer: &mut ChannelWriter<BarrierReached>|
713 -> Result<(), run_as::Error> {
714 let ks2 = get_keystore_service();
715 if ks2.getInterfaceVersion().unwrap() < 4 {
716 // Assuming `IKeystoreAuthorization::onDeviceLocked` and
717 // `IKeystoreAuthorization::onDeviceUnlocked` APIs will be supported on devices
718 // with `IKeystoreService` >= 4.
719 return Ok(());
720 }
721
722 // Now we're in a new process, wait to be notified before starting.
723 reader.recv();
724
725 // Action A: create a new unlocked-device-required key (which thus requires
726 // super-encryption), while the device is unlocked.
727 let sec_level =
728 ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).context("no TEE")?;
729 let params = AuthSetBuilder::new()
730 .no_auth_required()
731 .unlocked_device_required()
732 .algorithm(Algorithm::EC)
733 .purpose(KeyPurpose::SIGN)
734 .purpose(KeyPurpose::VERIFY)
735 .digest(Digest::SHA_2_256)
736 .ec_curve(EcCurve::P_256);
737
738 let KeyMetadata { key, .. } = sec_level
739 .generateKey(
740 &KeyDescriptor {
741 domain: Domain::APP,
742 nspace: -1,
743 alias: Some("unlocked-device-required".to_string()),
744 blob: None,
745 },
746 None,
747 ¶ms,
748 0,
749 b"entropy",
750 )
751 .context("key generation failed")?;
752 info!("A: created unlocked-device-required key while unlocked {key:?}");
753 writer.send(&BarrierReached {}); // A done.
754
755 // Action B: fail to use the unlocked-device-required key while locked.
756 reader.recv();
757 let params = AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
758 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
759 info!("B: use unlocked-device-required key while locked => {result:?}");
760 expect_km_error!(&result, ErrorCode::DEVICE_LOCKED);
761 writer.send(&BarrierReached {}); // B done.
762
763 // Action C: try to use the unlocked-device-required key while unlocked with a
764 // password.
765 reader.recv();
766 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
767 info!("C: use unlocked-device-required key while lskf-unlocked => {result:?}");
768 expect!(result.is_ok(), "failed with {result:?}");
769 abort_op(result);
770 writer.send(&BarrierReached {}); // C done.
771
772 // Action D: try to use the unlocked-device-required key while unlocked with a weak
773 // biometric.
774 reader.recv();
775 let result = sec_level.createOperation(&key, ¶ms, UNFORCED);
776 info!("D: use unlocked-device-required key while weak-locked => {result:?}");
777 expect!(result.is_ok(), "createOperation failed: {result:?}");
778 abort_op(result);
779 writer.send(&BarrierReached {}); // D done.
780
781 Ok(())
782 };
783
784 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
785 // `--test-threads=1`), and nothing yet done with binder.
786 let mut child_handle = unsafe {
787 // Perform keystore actions while running as the test user.
788 run_as::run_as_child_app(UID, UID, child_fn)
789 }
790 .unwrap();
791
792 let ks2 = get_keystore_service();
793 if ks2.getInterfaceVersion().unwrap() < 4 {
794 // Assuming `IKeystoreAuthorization::onDeviceLocked` and
795 // `IKeystoreAuthorization::onDeviceUnlocked` APIs will be supported on devices
796 // with `IKeystoreService` >= 4.
797 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
798 return;
799 }
800 // Now that the separate process has been forked off, it's safe to use binder.
801 let user = TestUser::new();
802 let user_id = user.id;
803 let auth_service = get_authorization();
804
805 // Lock and unlock to ensure super keys are already created.
806 auth_service
807 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
808 .unwrap();
809 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
810 auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
811
812 info!("trigger child process action A while unlocked and wait for completion");
813 child_handle.send(&BarrierReached {});
814 child_handle.recv_or_die();
815
816 // Move to locked and don't allow weak unlock, so super keys are wiped.
817 auth_service
818 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_DISABLED)
819 .unwrap();
820
821 info!("trigger child process action B while locked and wait for completion");
822 child_handle.send(&BarrierReached {});
823 child_handle.recv_or_die();
824
825 // Unlock with password => loads super key from database.
826 auth_service.onDeviceUnlocked(user_id, Some(SYNTHETIC_PASSWORD)).unwrap();
827 auth_service.addAuthToken(&fake_lskf_token(GK_FAKE_SID)).unwrap();
828
829 info!("trigger child process action C while lskf-unlocked and wait for completion");
830 child_handle.send(&BarrierReached {});
831 child_handle.recv_or_die();
832
833 // Move to locked and allow weak unlock, then do a weak unlock.
834 auth_service
835 .onDeviceLocked(user_id, &[BIO_FAKE_SID1, BIO_FAKE_SID2], WEAK_UNLOCK_ENABLED)
836 .unwrap();
837 auth_service.onDeviceUnlocked(user_id, None).unwrap();
838
839 info!("trigger child process action D while weak-unlocked and wait for completion");
840 child_handle.send(&BarrierReached {});
841 child_handle.recv_or_die();
842
843 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
844 }
845
846 /// Generate a fake [`HardwareAuthToken`] for the given sid.
fake_lskf_token(gk_sid: i64) -> HardwareAuthToken847 fn fake_lskf_token(gk_sid: i64) -> HardwareAuthToken {
848 fake_lskf_token_with_challenge(gk_sid, 0)
849 }
850
851 /// Generate a fake [`HardwareAuthToken`] for the given sid and challenge.
fake_lskf_token_with_challenge(gk_sid: i64, challenge: i64) -> HardwareAuthToken852 fn fake_lskf_token_with_challenge(gk_sid: i64, challenge: i64) -> HardwareAuthToken {
853 HardwareAuthToken {
854 challenge,
855 userId: gk_sid,
856 authenticatorId: 0,
857 authenticatorType: HardwareAuthenticatorType::PASSWORD,
858 timestamp: Timestamp { milliSeconds: 123 },
859 mac: vec![1, 2, 3],
860 }
861 }
862
863 /// Generate a fake [`HardwareAuthToken`] for the given sids
fake_bio_lskf_token(gk_sid: i64, bio_sid: i64) -> HardwareAuthToken864 fn fake_bio_lskf_token(gk_sid: i64, bio_sid: i64) -> HardwareAuthToken {
865 HardwareAuthToken {
866 challenge: 0,
867 userId: gk_sid,
868 authenticatorId: bio_sid,
869 authenticatorType: HardwareAuthenticatorType::PASSWORD,
870 timestamp: Timestamp { milliSeconds: 123 },
871 mac: vec![1, 2, 3],
872 }
873 }
874