• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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