1 // Copyright 2022, 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 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel;
16 use android_security_authorization::aidl::android::security::authorization::{
17 IKeystoreAuthorization::IKeystoreAuthorization,
18 };
19 use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::IKeystoreMaintenance;
20 use android_system_keystore2::aidl::android::system::keystore2::{
21 Domain::Domain, KeyDescriptor::KeyDescriptor,
22 };
23 use keystore2::key_parameter::KeyParameter as KsKeyparameter;
24 use keystore2::legacy_blob::test_utils::legacy_blob_test_vectors::*;
25 use keystore2::legacy_blob::test_utils::*;
26 use keystore2::legacy_blob::LegacyKeyCharacteristics;
27 use keystore2::utils::AesGcm;
28 use keystore2_crypto::{Password, ZVec};
29 use keystore2_test_utils::{get_keystore_service, key_generations, run_as, SecLevel};
30 use nix::unistd::getuid;
31 use rustutils::users::AID_USER_OFFSET;
32 use serde::{Deserialize, Serialize};
33 use std::ops::Deref;
34 use std::path::PathBuf;
35
36 static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
37 static AUTH_SERVICE_NAME: &str = "android.security.authorization";
38 const SELINUX_SHELL_NAMESPACE: i64 = 1;
39
rkp_only() -> bool40 fn rkp_only() -> bool {
41 matches!(rustutils::system_properties::read("remote_provisioning.tee.rkp_only"), Ok(Some(v)) if v == "1")
42 }
43
get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance>44 fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
45 binder::get_interface(USER_MANAGER_SERVICE_NAME).unwrap()
46 }
47
get_authorization() -> binder::Strong<dyn IKeystoreAuthorization>48 fn get_authorization() -> binder::Strong<dyn IKeystoreAuthorization> {
49 binder::get_interface(AUTH_SERVICE_NAME).unwrap()
50 }
51
52 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53 struct KeygenResult {
54 cert: Vec<u8>,
55 cert_chain: Vec<u8>,
56 key_parameters: Vec<KsKeyparameter>,
57 }
58
59 struct TestKey(ZVec);
60
61 impl keystore2::utils::AesGcmKey for TestKey {
key(&self) -> &[u8]62 fn key(&self) -> &[u8] {
63 &self.0
64 }
65 }
66
67 impl Deref for TestKey {
68 type Target = [u8];
deref(&self) -> &Self::Target69 fn deref(&self) -> &Self::Target {
70 &self.0
71 }
72 }
73
keystore2_restart_service()74 fn keystore2_restart_service() {
75 let output = std::process::Command::new("pidof")
76 .arg("keystore2")
77 .output()
78 .expect("failed to execute pidof keystore2");
79
80 let id = String::from_utf8(output.stdout).unwrap();
81 let id: String = id.chars().filter(|c| c.is_ascii_digit()).collect();
82
83 let _status = std::process::Command::new("kill").arg("-9").arg(id).status().unwrap();
84
85 // Loop till we find keystore2 service up and running.
86 loop {
87 let output = std::process::Command::new("pidof")
88 .arg("keystore2")
89 .output()
90 .expect("failed to execute pidof keystore2");
91
92 if output.status.code() == Some(0) {
93 break;
94 }
95 }
96 }
97
98 /// Create legacy blobs file layout for a user with user-id 99 and app-id 10001 with
99 /// user-cert, ca-certs and encrypted key-characteristics files and tries to import
100 /// these legacy blobs under user context.
101 ///
102 /// Expected File layout for user with user-id "98" and app-id "10001" and key-alias
103 /// "authbound":
104 /// /data/misc/keystore/user_99/.masterkey
105 /// /data/misc/keystore/user_99/9910001_USRPKEY_authbound
106 /// /data/misc/keystore/user_99/.9910001_chr_USRPKEY_authbound
107 /// /data/misc/keystore/user_99/9910001_USRCERT_authbound
108 /// /data/misc/keystore/user_99/9910001_CACERT_authbound
109 ///
110 /// Test performs below tasks -
111 /// With su context it performs following tasks -
112 /// 1. Remove this user if already exist.
113 /// 2. Generate a key-blob, user cert-blob and ca-cert-blob to store it in legacy blobs file
114 /// layout.
115 /// 3. Prepare file layout using generated key-blob, user cert and ca certs.
116 /// 4. Restart the keystore2 service to make it detect the populated legacy blobs.
117 /// 5. Inform the keystore2 service about the user and unlock the user.
118 /// With user-99 context it performs following tasks -
119 /// 6. To load and import the legacy key using its alias.
120 /// 7. After successful key import validate the user cert and cert-chain with initially
121 /// generated blobs.
122 /// 8. Validate imported key perameters. Imported key parameters list should be the combination
123 /// of the key-parameters in characteristics file and the characteristics according to
124 /// the augmentation rules. There might be duplicate entries with different values for the
125 /// parameters like OS_VERSION, OS_VERSION, BOOT_PATCHLEVEL, VENDOR_PATCHLEVEL etc.
126 /// 9. Confirm keystore2 service cleanup the legacy blobs after successful import.
127 #[test]
keystore2_encrypted_characteristics() -> anyhow::Result<()>128 fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
129 let auid = 99 * AID_USER_OFFSET + 10001;
130 let agid = 99 * AID_USER_OFFSET + 10001;
131
132 // Cleanup user directory if it exists
133 let path_buf = PathBuf::from("/data/misc/keystore/user_99");
134 if path_buf.as_path().is_dir() {
135 std::fs::remove_dir_all(path_buf.as_path()).unwrap();
136 }
137
138 let gen_key_fn = || {
139 // Remove user if already exist.
140 let maint_service = get_maintenance();
141 match maint_service.onUserRemoved(99) {
142 Ok(_) => {
143 println!("User did exist, deleted successfully");
144 }
145 Err(e) => {
146 println!("onUserRemoved error: {:#?}", e);
147 }
148 }
149 let sl = SecLevel::tee();
150
151 // Generate Key BLOB and prepare legacy keystore blob files.
152 let att_challenge: Option<&[u8]> = if rkp_only() { None } else { Some(b"foo") };
153 let key_metadata = key_generations::generate_ec_p256_signing_key(
154 &sl,
155 Domain::BLOB,
156 SELINUX_SHELL_NAMESPACE,
157 None,
158 att_challenge,
159 )
160 .expect("Failed to generate key blob");
161
162 // Create keystore file layout for user_99.
163 let pw: Password = PASSWORD.into();
164 let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
165 let super_key =
166 TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
167
168 let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
169 if !path_buf.as_path().is_dir() {
170 std::fs::create_dir(path_buf.as_path()).unwrap();
171 }
172 path_buf.push(".masterkey");
173 if !path_buf.as_path().is_file() {
174 std::fs::write(path_buf.as_path(), SUPERKEY).unwrap();
175 }
176
177 let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
178 path_buf.push("9910001_USRPKEY_authbound");
179 if !path_buf.as_path().is_file() {
180 make_encrypted_key_file(
181 path_buf.as_path(),
182 &super_key,
183 &key_metadata.key.blob.unwrap(),
184 )
185 .unwrap();
186 }
187
188 let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
189 path_buf.push(".9910001_chr_USRPKEY_authbound");
190 if !path_buf.as_path().is_file() {
191 make_encrypted_characteristics_file(path_buf.as_path(), &super_key, KEY_PARAMETERS)
192 .unwrap();
193 }
194
195 let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
196 path_buf.push("9910001_USRCERT_authbound");
197 if !path_buf.as_path().is_file() {
198 make_cert_blob_file(path_buf.as_path(), key_metadata.certificate.as_ref().unwrap())
199 .unwrap();
200 }
201
202 if let Some(chain) = key_metadata.certificateChain.as_ref() {
203 let mut path_buf = PathBuf::from("/data/misc/keystore/user_99");
204 path_buf.push("9910001_CACERT_authbound");
205 if !path_buf.as_path().is_file() {
206 make_cert_blob_file(path_buf.as_path(), chain).unwrap();
207 }
208 }
209
210 // Keystore2 disables the legacy importer when it finds the legacy database empty.
211 // However, if the device boots with an empty legacy database, the optimization kicks in
212 // and keystore2 never checks the legacy file system layout.
213 // So, restart keystore2 service to detect populated legacy database.
214 keystore2_restart_service();
215
216 let auth_service = get_authorization();
217 match auth_service.onDeviceUnlocked(99, Some(PASSWORD)) {
218 Ok(result) => {
219 println!("Unlock Result: {:?}", result);
220 }
221 Err(e) => {
222 panic!("Unlock should have succeeded: {:?}", e);
223 }
224 }
225
226 let mut key_params: Vec<KsKeyparameter> = Vec::new();
227 for param in key_metadata.authorizations {
228 let key_param = KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
229 key_params.push(key_param);
230 }
231
232 KeygenResult {
233 cert: key_metadata.certificate.unwrap(),
234 cert_chain: key_metadata.certificateChain.unwrap_or_default(),
235 key_parameters: key_params,
236 }
237 };
238
239 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
240 // `--test-threads=1`), and nothing yet done with binder.
241 let mut gen_key_result = unsafe { run_as::run_as_root(gen_key_fn) };
242
243 let use_key_fn = move || {
244 println!("UID: {}", getuid());
245 println!("Android User ID: {}", rustutils::users::multiuser_get_user_id(9910001));
246 println!("Android app ID: {}", rustutils::users::multiuser_get_app_id(9910001));
247
248 let test_alias = "authbound";
249 let keystore2 = get_keystore_service();
250
251 match keystore2.getKeyEntry(&KeyDescriptor {
252 domain: Domain::APP,
253 nspace: SELINUX_SHELL_NAMESPACE,
254 alias: Some(test_alias.to_string()),
255 blob: None,
256 }) {
257 Ok(key_entry_response) => {
258 assert_eq!(key_entry_response.metadata.certificate.unwrap(), gen_key_result.cert);
259 assert_eq!(
260 key_entry_response.metadata.certificateChain.unwrap_or_default(),
261 gen_key_result.cert_chain
262 );
263 assert_eq!(key_entry_response.metadata.key.domain, Domain::KEY_ID);
264 assert_ne!(key_entry_response.metadata.key.nspace, 0);
265 assert_eq!(
266 key_entry_response.metadata.keySecurityLevel,
267 SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT
268 );
269
270 // Preapare KsKeyParameter list from getKeEntry response Authorizations.
271 let mut key_params: Vec<KsKeyparameter> = Vec::new();
272 for param in key_entry_response.metadata.authorizations {
273 let key_param =
274 KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
275 key_params.push(key_param);
276 }
277
278 // Combine keyparameters from gen_key_result and keyparameters
279 // from legacy key-char file.
280 let mut legacy_file_key_params: Vec<KsKeyparameter> = Vec::new();
281 match structured_test_params() {
282 LegacyKeyCharacteristics::File(legacy_key_params) => {
283 for param in &legacy_key_params {
284 let mut present_in_gen_params = false;
285 for gen_param in &gen_key_result.key_parameters {
286 if param.get_tag() == gen_param.get_tag() {
287 present_in_gen_params = true;
288 }
289 }
290 if !present_in_gen_params {
291 legacy_file_key_params.push(param.clone());
292 }
293 }
294 }
295 _ => {
296 panic!("Expecting file characteristics");
297 }
298 }
299
300 // Remove Key-Params which have security levels other than TRUSTED_ENVIRONMENT
301 gen_key_result.key_parameters.retain(|in_element| {
302 *in_element.security_level()
303 == SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT
304 });
305
306 println!("GetKeyEntry response key params: {:#?}", key_params);
307 println!("Generated key params: {:#?}", gen_key_result.key_parameters);
308
309 gen_key_result.key_parameters.append(&mut legacy_file_key_params);
310
311 println!("Combined key params: {:#?}", gen_key_result.key_parameters);
312
313 // Validate all keyparameters present in getKeyEntry response.
314 for param in &key_params {
315 gen_key_result.key_parameters.retain(|in_element| *in_element != *param);
316 }
317
318 println!(
319 "GetKeyEntry response unmatched key params: {:#?}",
320 gen_key_result.key_parameters
321 );
322 assert_eq!(gen_key_result.key_parameters.len(), 0);
323 }
324 Err(s) => {
325 panic!("getKeyEntry should have succeeded. {:?}", s);
326 }
327 };
328 };
329
330 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
331 // `--test-threads=1`), and nothing yet done with binder.
332 unsafe { run_as::run_as_app(auid, agid, use_key_fn) };
333
334 // Make sure keystore2 clean up imported legacy db.
335 let path_buf = PathBuf::from("/data/misc/keystore/user_99");
336 if path_buf.as_path().is_dir() {
337 panic!("Keystore service should have deleted this dir {:?}", path_buf);
338 }
339 Ok(())
340 }
341
342 /// Create legacy blobs file layout for a user with user-id 98 and app-id 10001 with encrypted
343 /// user-cert and ca-certs files and tries to import these legacy blobs under user context.
344 ///
345 /// Expected File layout for user with user-id "98" and app-id "10001" and key-alias
346 /// "authboundcertenc":
347 /// /data/misc/keystore/user_98/.masterkey
348 /// /data/misc/keystore/user_98/9810001_USRPKEY_authboundcertenc
349 /// /data/misc/keystore/user_98/.9810001_chr_USRPKEY_authboundcertenc
350 /// /data/misc/keystore/user_98/9810001_USRCERT_authboundcertenc
351 /// /data/misc/keystore/user_98/9810001_CACERT_authboundcertenc
352 ///
353 /// Test performs below tasks -
354 /// With su context it performs following tasks -
355 /// 1. Remove this user if already exist.
356 /// 2. Generate a key-blob, user cert-blob and ca-cert-blob to store it in legacy blobs file
357 /// layout.
358 /// 3. Prepare file layout using generated key-blob, user cert and ca certs.
359 /// 4. Restart the keystore2 service to make it detect the populated legacy blobs.
360 /// 5. Inform the keystore2 service about the user and unlock the user.
361 /// With user-98 context it performs following tasks -
362 /// 6. To load and import the legacy key using its alias.
363 /// 7. After successful key import validate the user cert and cert-chain with initially
364 /// generated blobs.
365 /// 8. Validate imported key parameters. Imported key parameters list should be the combination
366 /// of the key-parameters in characteristics file and the characteristics according to
367 /// the augmentation rules. There might be duplicate entries with different values for the
368 /// parameters like OS_VERSION, OS_VERSION, BOOT_PATCHLEVEL, VENDOR_PATCHLEVEL etc.
369 /// 9. Confirm keystore2 service cleanup the legacy blobs after successful import.
370 #[test]
keystore2_encrypted_certificates() -> anyhow::Result<()>371 fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
372 let auid = 98 * AID_USER_OFFSET + 10001;
373 let agid = 98 * AID_USER_OFFSET + 10001;
374
375 // Cleanup user directory if it exists
376 let path_buf = PathBuf::from("/data/misc/keystore/user_98");
377 if path_buf.as_path().is_dir() {
378 std::fs::remove_dir_all(path_buf.as_path()).unwrap();
379 }
380
381 let gen_key_fn = || {
382 // Remove user if already exist.
383 let maint_service = get_maintenance();
384 match maint_service.onUserRemoved(98) {
385 Ok(_) => {
386 println!("User did exist, deleted successfully");
387 }
388 Err(e) => {
389 println!("onUserRemoved error: {:#?}", e);
390 }
391 }
392
393 let sl = SecLevel::tee();
394 // Generate Key BLOB and prepare legacy keystore blob files.
395 let att_challenge: Option<&[u8]> = if rkp_only() { None } else { Some(b"foo") };
396 let key_metadata = key_generations::generate_ec_p256_signing_key(
397 &sl,
398 Domain::BLOB,
399 SELINUX_SHELL_NAMESPACE,
400 None,
401 att_challenge,
402 )
403 .expect("Failed to generate key blob");
404
405 // Create keystore file layout for user_98.
406 let pw: Password = PASSWORD.into();
407 let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
408 let super_key =
409 TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
410
411 let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
412 if !path_buf.as_path().is_dir() {
413 std::fs::create_dir(path_buf.as_path()).unwrap();
414 }
415 path_buf.push(".masterkey");
416 if !path_buf.as_path().is_file() {
417 std::fs::write(path_buf.as_path(), SUPERKEY).unwrap();
418 }
419
420 let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
421 path_buf.push("9810001_USRPKEY_authboundcertenc");
422 if !path_buf.as_path().is_file() {
423 make_encrypted_key_file(
424 path_buf.as_path(),
425 &super_key,
426 &key_metadata.key.blob.unwrap(),
427 )
428 .unwrap();
429 }
430
431 let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
432 path_buf.push(".9810001_chr_USRPKEY_authboundcertenc");
433 if !path_buf.as_path().is_file() {
434 std::fs::write(path_buf.as_path(), USRPKEY_AUTHBOUND_CHR).unwrap();
435 }
436
437 let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
438 path_buf.push("9810001_USRCERT_authboundcertenc");
439 if !path_buf.as_path().is_file() {
440 make_encrypted_usr_cert_file(
441 path_buf.as_path(),
442 &super_key,
443 key_metadata.certificate.as_ref().unwrap(),
444 )
445 .unwrap();
446 }
447
448 if let Some(chain) = key_metadata.certificateChain.as_ref() {
449 let mut path_buf = PathBuf::from("/data/misc/keystore/user_98");
450 path_buf.push("9810001_CACERT_authboundcertenc");
451 if !path_buf.as_path().is_file() {
452 make_encrypted_ca_cert_file(path_buf.as_path(), &super_key, chain).unwrap();
453 }
454 }
455
456 // Keystore2 disables the legacy importer when it finds the legacy database empty.
457 // However, if the device boots with an empty legacy database, the optimization kicks in
458 // and keystore2 never checks the legacy file system layout.
459 // So, restart keystore2 service to detect populated legacy database.
460 keystore2_restart_service();
461
462 let auth_service = get_authorization();
463 match auth_service.onDeviceUnlocked(98, Some(PASSWORD)) {
464 Ok(result) => {
465 println!("Unlock Result: {:?}", result);
466 }
467 Err(e) => {
468 panic!("Unlock should have succeeded: {:?}", e);
469 }
470 }
471
472 let mut key_params: Vec<KsKeyparameter> = Vec::new();
473 for param in key_metadata.authorizations {
474 let key_param = KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
475 key_params.push(key_param);
476 }
477
478 KeygenResult {
479 cert: key_metadata.certificate.unwrap(),
480 cert_chain: key_metadata.certificateChain.unwrap_or_default(),
481 key_parameters: key_params,
482 }
483 };
484
485 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
486 // `--test-threads=1`), and nothing yet done with binder.
487 let gen_key_result = unsafe { run_as::run_as_root(gen_key_fn) };
488
489 let use_key_fn = move || {
490 println!("UID: {}", getuid());
491 println!("Android User ID: {}", rustutils::users::multiuser_get_user_id(9810001));
492 println!("Android app ID: {}", rustutils::users::multiuser_get_app_id(9810001));
493
494 let test_alias = "authboundcertenc";
495 let keystore2 = get_keystore_service();
496
497 match keystore2.getKeyEntry(&KeyDescriptor {
498 domain: Domain::APP,
499 nspace: SELINUX_SHELL_NAMESPACE,
500 alias: Some(test_alias.to_string()),
501 blob: None,
502 }) {
503 Ok(key_entry_response) => {
504 assert_eq!(key_entry_response.metadata.certificate.unwrap(), gen_key_result.cert);
505 assert_eq!(
506 key_entry_response.metadata.certificateChain.unwrap_or_default(),
507 gen_key_result.cert_chain
508 );
509
510 // Preapare KsKeyParameter list from getKeEntry response Authorizations.
511 let mut key_params: Vec<KsKeyparameter> = Vec::new();
512 for param in key_entry_response.metadata.authorizations {
513 let key_param =
514 KsKeyparameter::new(param.keyParameter.into(), param.securityLevel);
515 key_params.push(key_param);
516 }
517
518 println!("GetKeyEntry response key params: {:#?}", key_params);
519 println!("Generated key params: {:#?}", gen_key_result.key_parameters);
520 match structured_test_params_cache() {
521 LegacyKeyCharacteristics::Cache(legacy_key_params) => {
522 println!("Legacy key-char cache: {:#?}", legacy_key_params);
523 // Validate all keyparameters present in getKeyEntry response.
524 for param in &legacy_key_params {
525 key_params.retain(|in_element| *in_element != *param);
526 }
527
528 println!("GetKeyEntry response unmatched key params: {:#?}", key_params);
529 assert_eq!(key_params.len(), 0);
530 }
531 _ => {
532 panic!("Expecting file characteristics");
533 }
534 }
535 }
536 Err(s) => {
537 panic!("getKeyEntry should have succeeded. {:?}", s);
538 }
539 };
540 };
541
542 // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
543 // `--test-threads=1`), and nothing yet done with binder.
544 unsafe { run_as::run_as_app(auid, agid, use_key_fn) };
545
546 // Make sure keystore2 clean up imported legacy db.
547 let path_buf = PathBuf::from("/data/misc/keystore/user_98");
548 if path_buf.as_path().is_dir() {
549 panic!("Keystore service should have deleted this dir {:?}", path_buf);
550 }
551 Ok(())
552 }
553