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