blob: 4e3c6925f3ce1f214bd6e58a387101d2816869df [file] [log] [blame]
David Drysdalef7ed95a2024-05-08 13:51:45 +01001// 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
17use crate::keystore2_client_test_utils::BarrierReached;
18use android_security_authorization::aidl::android::security::authorization::{
19 IKeystoreAuthorization::IKeystoreAuthorization
20};
21use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::{
22 IKeystoreMaintenance,
23};
24use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
25 Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, HardwareAuthToken::HardwareAuthToken,
26 HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
27 KeyPurpose::KeyPurpose
28};
29use android_system_keystore2::aidl::android::system::keystore2::{
30 CreateOperationResponse::CreateOperationResponse, Domain::Domain, KeyDescriptor::KeyDescriptor,
31 KeyMetadata::KeyMetadata,
32};
33use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
34 Timestamp::Timestamp,
35};
36use keystore2_test_utils::{
37 get_keystore_service, run_as, authorizations::AuthSetBuilder,
38};
39use log::{warn, info};
40use nix::unistd::{Gid, Uid};
41use rustutils::users::AID_USER_OFFSET;
42
43/// Test user ID.
44const TEST_USER_ID: i32 = 100;
45/// Fake password blob.
46static PASSWORD: &[u8] = &[
47 0x42, 0x39, 0x30, 0x37, 0x44, 0x37, 0x32, 0x37, 0x39, 0x39, 0x43, 0x42, 0x39, 0x41, 0x42, 0x30,
48 0x34, 0x31, 0x30, 0x38, 0x46, 0x44, 0x33, 0x45, 0x39, 0x42, 0x32, 0x38, 0x36, 0x35, 0x41, 0x36,
49 0x33, 0x44, 0x42, 0x42, 0x43, 0x36, 0x33, 0x42, 0x34, 0x39, 0x37, 0x33, 0x35, 0x45, 0x41, 0x41,
50 0x32, 0x45, 0x31, 0x35, 0x43, 0x43, 0x46, 0x32, 0x39, 0x36, 0x33, 0x34, 0x31, 0x32, 0x41, 0x39,
51];
52/// Fake SID value corresponding to Gatekeeper.
53static GK_SID: i64 = 123456;
54/// Fake SID value corresponding to a biometric authenticator.
55static BIO_SID1: i64 = 345678;
56/// Fake SID value corresponding to a biometric authenticator.
57static BIO_SID2: i64 = 456789;
58
59const WEAK_UNLOCK_ENABLED: bool = true;
60const WEAK_UNLOCK_DISABLED: bool = false;
61const UNFORCED: bool = false;
62
63fn get_authorization() -> binder::Strong<dyn IKeystoreAuthorization> {
64 binder::get_interface("android.security.authorization").unwrap()
65}
66
67fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
68 binder::get_interface("android.security.maintenance").unwrap()
69}
70
71fn abort_op(result: binder::Result<CreateOperationResponse>) {
72 if let Ok(rsp) = result {
73 if let Some(op) = rsp.iOperation {
74 if let Err(e) = op.abort() {
75 warn!("abort op failed: {e:?}");
76 }
77 } else {
78 warn!("can't abort op with missing iOperation");
79 }
80 } else {
81 warn!("can't abort failed op: {result:?}");
82 }
83}
84
85/// RAII structure to ensure that test users are removed at the end of a test.
86struct TestUser {
87 id: i32,
88 maint: binder::Strong<dyn IKeystoreMaintenance>,
89}
90
91impl TestUser {
92 fn new() -> Self {
93 Self::new_user(TEST_USER_ID, PASSWORD)
94 }
95 fn new_user(user_id: i32, password: &[u8]) -> Self {
96 let maint = get_maintenance();
97 maint.onUserAdded(user_id).expect("failed to add test user");
98 maint
99 .initUserSuperKeys(user_id, password, /* allowExisting= */ false)
100 .expect("failed to init test user");
101 Self { id: user_id, maint }
102 }
103}
104
105impl Drop for TestUser {
106 fn drop(&mut self) {
107 let _ = self.maint.onUserRemoved(self.id);
108 }
109}
110
111#[test]
112fn keystore2_test_unlocked_device_required() {
113 android_logger::init_once(
114 android_logger::Config::default()
115 .with_tag("keystore2_client_tests")
116 .with_max_level(log::LevelFilter::Debug),
117 );
118 static CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
119 const UID: u32 = TEST_USER_ID as u32 * AID_USER_OFFSET + 1001;
120
121 // Safety: only one thread at this point, and nothing yet done with binder.
122 let mut child_handle = unsafe {
123 // Perform keystore actions while running as the test user.
124 run_as::run_as_child(
125 CTX,
126 Uid::from_raw(UID),
127 Gid::from_raw(UID),
128 move |reader, writer| -> Result<(), String> {
David Drysdalef7ed95a2024-05-08 13:51:45 +0100129 // Action A: create a new unlocked-device-required key (which thus requires
130 // super-encryption), while the device is unlocked.
131 let ks2 = get_keystore_service();
Rajesh Nyamagoudca4f7af2024-07-29 18:39:01 +0000132 if ks2.getInterfaceVersion().unwrap() < 4 {
133 // Assuming `IKeystoreAuthorization::onDeviceLocked` and
134 // `IKeystoreAuthorization::onDeviceUnlocked` APIs will be supported on devices
135 // with `IKeystoreService` >= 4.
136 return Ok(());
137 }
138
139 // Now we're in a new process, wait to be notified before starting.
140 reader.recv();
141
David Drysdalef7ed95a2024-05-08 13:51:45 +0100142 let sec_level = ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
143 let params = AuthSetBuilder::new()
Eric Biggersa2ca6422024-08-08 19:18:51 +0000144 .no_auth_required()
David Drysdalef7ed95a2024-05-08 13:51:45 +0100145 .unlocked_device_required()
146 .algorithm(Algorithm::EC)
147 .purpose(KeyPurpose::SIGN)
148 .purpose(KeyPurpose::VERIFY)
149 .digest(Digest::SHA_2_256)
150 .ec_curve(EcCurve::P_256);
151
152 let KeyMetadata { key, .. } = sec_level
153 .generateKey(
154 &KeyDescriptor {
155 domain: Domain::APP,
156 nspace: -1,
157 alias: Some("unlocked-device-required".to_string()),
158 blob: None,
159 },
160 None,
161 &params,
162 0,
163 b"entropy",
164 )
165 .expect("key generation failed");
166 info!("A: created unlocked-device-required key while unlocked {key:?}");
167 writer.send(&BarrierReached {}); // A done.
168
169 // Action B: fail to use the unlocked-device-required key while locked.
170 reader.recv();
171 let params =
172 AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
173 let result = sec_level.createOperation(&key, &params, UNFORCED);
174 info!("B: use unlocked-device-required key while locked => {result:?}");
175 assert!(result.is_err());
176 writer.send(&BarrierReached {}); // B done.
177
178 // Action C: try to use the unlocked-device-required key while unlocked with a
179 // password.
180 reader.recv();
181 let result = sec_level.createOperation(&key, &params, UNFORCED);
182 info!("C: use unlocked-device-required key while lskf-unlocked => {result:?}");
183 assert!(result.is_ok(), "failed with {result:?}");
184 abort_op(result);
185 writer.send(&BarrierReached {}); // C done.
186
187 // Action D: try to use the unlocked-device-required key while unlocked with a weak
188 // biometric.
189 reader.recv();
190 let result = sec_level.createOperation(&key, &params, UNFORCED);
191 info!("D: use unlocked-device-required key while weak-locked => {result:?}");
192 assert!(result.is_ok(), "createOperation failed: {result:?}");
193 abort_op(result);
194 writer.send(&BarrierReached {}); // D done.
195
196 let _ = sec_level.deleteKey(&key);
197 Ok(())
198 },
199 )
200 }
201 .unwrap();
202
Rajesh Nyamagoudca4f7af2024-07-29 18:39:01 +0000203 let ks2 = get_keystore_service();
204 if ks2.getInterfaceVersion().unwrap() < 4 {
205 // Assuming `IKeystoreAuthorization::onDeviceLocked` and
206 // `IKeystoreAuthorization::onDeviceUnlocked` APIs will be supported on devices
207 // with `IKeystoreService` >= 4.
208 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
209 return;
210 }
David Drysdalef7ed95a2024-05-08 13:51:45 +0100211 // Now that the separate process has been forked off, it's safe to use binder.
212 let user = TestUser::new();
213 let user_id = user.id;
214 let auth_service = get_authorization();
215
216 // Lock and unlock to ensure super keys are already created.
217 auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_DISABLED).unwrap();
218 auth_service.onDeviceUnlocked(user_id, Some(PASSWORD)).unwrap();
219 auth_service.addAuthToken(&fake_lskf_token(GK_SID)).unwrap();
220
221 info!("trigger child process action A while unlocked and wait for completion");
222 child_handle.send(&BarrierReached {});
223 child_handle.recv();
224
225 // Move to locked and don't allow weak unlock, so super keys are wiped.
226 auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_DISABLED).unwrap();
227
228 info!("trigger child process action B while locked and wait for completion");
229 child_handle.send(&BarrierReached {});
230 child_handle.recv();
231
232 // Unlock with password => loads super key from database.
233 auth_service.onDeviceUnlocked(user_id, Some(PASSWORD)).unwrap();
234 auth_service.addAuthToken(&fake_lskf_token(GK_SID)).unwrap();
235
236 info!("trigger child process action C while lskf-unlocked and wait for completion");
237 child_handle.send(&BarrierReached {});
238 child_handle.recv();
239
240 // Move to locked and allow weak unlock, then do a weak unlock.
241 auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_ENABLED).unwrap();
242 auth_service.onDeviceUnlocked(user_id, None).unwrap();
243
244 info!("trigger child process action D while weak-unlocked and wait for completion");
245 child_handle.send(&BarrierReached {});
246 child_handle.recv();
247
248 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
249}
250
251/// Generate a fake [`HardwareAuthToken`] for the given sid.
252fn fake_lskf_token(gk_sid: i64) -> HardwareAuthToken {
253 HardwareAuthToken {
254 challenge: 0,
255 userId: gk_sid,
256 authenticatorId: 0,
257 authenticatorType: HardwareAuthenticatorType::PASSWORD,
258 timestamp: Timestamp { milliSeconds: 123 },
259 mac: vec![1, 2, 3],
260 }
261}