blob: 263ad5655fa939387a1a0e1c503735bc50e05137 [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> {
129 // Now we're in a new process, wait to be notified before starting.
130 reader.recv();
131
132 // Action A: create a new unlocked-device-required key (which thus requires
133 // super-encryption), while the device is unlocked.
134 let ks2 = get_keystore_service();
135 let sec_level = ks2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
136 let params = AuthSetBuilder::new()
137 .unlocked_device_required()
138 .algorithm(Algorithm::EC)
139 .purpose(KeyPurpose::SIGN)
140 .purpose(KeyPurpose::VERIFY)
141 .digest(Digest::SHA_2_256)
142 .ec_curve(EcCurve::P_256);
143
144 let KeyMetadata { key, .. } = sec_level
145 .generateKey(
146 &KeyDescriptor {
147 domain: Domain::APP,
148 nspace: -1,
149 alias: Some("unlocked-device-required".to_string()),
150 blob: None,
151 },
152 None,
153 &params,
154 0,
155 b"entropy",
156 )
157 .expect("key generation failed");
158 info!("A: created unlocked-device-required key while unlocked {key:?}");
159 writer.send(&BarrierReached {}); // A done.
160
161 // Action B: fail to use the unlocked-device-required key while locked.
162 reader.recv();
163 let params =
164 AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256);
165 let result = sec_level.createOperation(&key, &params, UNFORCED);
166 info!("B: use unlocked-device-required key while locked => {result:?}");
167 assert!(result.is_err());
168 writer.send(&BarrierReached {}); // B done.
169
170 // Action C: try to use the unlocked-device-required key while unlocked with a
171 // password.
172 reader.recv();
173 let result = sec_level.createOperation(&key, &params, UNFORCED);
174 info!("C: use unlocked-device-required key while lskf-unlocked => {result:?}");
175 assert!(result.is_ok(), "failed with {result:?}");
176 abort_op(result);
177 writer.send(&BarrierReached {}); // C done.
178
179 // Action D: try to use the unlocked-device-required key while unlocked with a weak
180 // biometric.
181 reader.recv();
182 let result = sec_level.createOperation(&key, &params, UNFORCED);
183 info!("D: use unlocked-device-required key while weak-locked => {result:?}");
184 assert!(result.is_ok(), "createOperation failed: {result:?}");
185 abort_op(result);
186 writer.send(&BarrierReached {}); // D done.
187
188 let _ = sec_level.deleteKey(&key);
189 Ok(())
190 },
191 )
192 }
193 .unwrap();
194
195 // Now that the separate process has been forked off, it's safe to use binder.
196 let user = TestUser::new();
197 let user_id = user.id;
198 let auth_service = get_authorization();
199
200 // Lock and unlock to ensure super keys are already created.
201 auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_DISABLED).unwrap();
202 auth_service.onDeviceUnlocked(user_id, Some(PASSWORD)).unwrap();
203 auth_service.addAuthToken(&fake_lskf_token(GK_SID)).unwrap();
204
205 info!("trigger child process action A while unlocked and wait for completion");
206 child_handle.send(&BarrierReached {});
207 child_handle.recv();
208
209 // Move to locked and don't allow weak unlock, so super keys are wiped.
210 auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_DISABLED).unwrap();
211
212 info!("trigger child process action B while locked and wait for completion");
213 child_handle.send(&BarrierReached {});
214 child_handle.recv();
215
216 // Unlock with password => loads super key from database.
217 auth_service.onDeviceUnlocked(user_id, Some(PASSWORD)).unwrap();
218 auth_service.addAuthToken(&fake_lskf_token(GK_SID)).unwrap();
219
220 info!("trigger child process action C while lskf-unlocked and wait for completion");
221 child_handle.send(&BarrierReached {});
222 child_handle.recv();
223
224 // Move to locked and allow weak unlock, then do a weak unlock.
225 auth_service.onDeviceLocked(user_id, &[BIO_SID1, BIO_SID2], WEAK_UNLOCK_ENABLED).unwrap();
226 auth_service.onDeviceUnlocked(user_id, None).unwrap();
227
228 info!("trigger child process action D while weak-unlocked and wait for completion");
229 child_handle.send(&BarrierReached {});
230 child_handle.recv();
231
232 assert_eq!(child_handle.get_result(), Ok(()), "child process failed");
233}
234
235/// Generate a fake [`HardwareAuthToken`] for the given sid.
236fn fake_lskf_token(gk_sid: i64) -> HardwareAuthToken {
237 HardwareAuthToken {
238 challenge: 0,
239 userId: gk_sid,
240 authenticatorId: 0,
241 authenticatorType: HardwareAuthenticatorType::PASSWORD,
242 timestamp: Timestamp { milliSeconds: 123 },
243 mac: vec![1, 2, 3],
244 }
245}