blob: 3b656c3d216eb7da8f5885a509f9fc08306b6e1a [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use nix::unistd::{getuid, Gid, Uid};
use rustutils::users::AID_USER_OFFSET;
use std::collections::HashSet;
use std::fmt::Write;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
KeyPermission::KeyPermission, ResponseCode::ResponseCode,
};
use crate::keystore2_client_test_utils::delete_app_key;
use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
/// Try to find a key with given key parameters using `listEntries` API.
fn key_alias_exists(
keystore2: &binder::Strong<dyn IKeystoreService>,
domain: Domain,
nspace: i64,
alias: String,
) -> bool {
let key_descriptors = keystore2.listEntries(domain, nspace).unwrap();
let alias_count = key_descriptors
.into_iter()
.map(|key| key.alias.unwrap())
.filter(|key_alias| *key_alias == alias)
.count();
alias_count != 0
}
/// List key entries with domain as SELINUX and APP.
/// 1. Generate a key with domain as SELINUX and find this key entry in list of keys retrieved from
/// `listEntries` with domain SELINUX. Test should be able find this key entry successfully.
/// 2. Grant above generated Key to a user.
/// 3. In a user context, generate a new key with domain as APP. Try to list the key entries with
/// domain APP. Test should find only one key entry that should be the key generated in user
/// context. GRANT keys shouldn't be part of this list.
#[test]
fn keystore2_list_entries_success() {
static GRANTOR_SU_CTX: &str = "u:r:su:s0";
static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
const USER_ID: u32 = 91;
const APPLICATION_ID: u32 = 10006;
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
let alias = format!("list_entries_grant_key1_{}", getuid());
// Make sure there is no key exist with this `alias` in `SELINUX` domain and
// `SELINUX_SHELL_NAMESPACE` namespace.
if key_alias_exists(
&keystore2,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
alias.to_string(),
) {
keystore2
.deleteKey(&KeyDescriptor {
domain: Domain::SELINUX,
nspace: key_generations::SELINUX_SHELL_NAMESPACE,
alias: Some(alias.to_string()),
blob: None,
})
.unwrap();
}
// Generate a key with above defined `alias`.
let key_metadata = key_generations::generate_ec_p256_signing_key(
&sec_level,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
Some(alias.to_string()),
None,
)
.unwrap();
// Verify that above generated key entry is listed with domain SELINUX and
// namespace SELINUX_SHELL_NAMESPACE
assert!(key_alias_exists(
&keystore2,
Domain::SELINUX,
key_generations::SELINUX_SHELL_NAMESPACE,
alias,
));
// Grant a key with GET_INFO permission.
let access_vector = KeyPermission::GET_INFO.0;
keystore2
.grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
.unwrap();
})
};
// In user context validate list of key entries associated with it.
unsafe {
run_as::run_as(
GRANTEE_CTX,
Uid::from_raw(GRANTEE_UID),
Gid::from_raw(GRANTEE_GID),
move || {
let keystore2 = get_keystore_service();
let sec_level =
keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
let alias = format!("list_entries_success_key{}", getuid());
let key_metadata = key_generations::generate_ec_p256_signing_key(
&sec_level,
Domain::APP,
-1,
Some(alias.to_string()),
None,
)
.unwrap();
// Make sure there is only one key entry exist and that should be the same key
// generated in this user context. Granted key shouldn't be included in this list.
let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
assert_eq!(1, key_descriptors.len());
let key = key_descriptors.get(0).unwrap();
assert_eq!(key.alias, Some(alias));
assert_eq!(key.nspace, GRANTEE_UID.try_into().unwrap());
assert_eq!(key.domain, Domain::APP);
keystore2.deleteKey(&key_metadata.key).unwrap();
let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
assert_eq!(0, key_descriptors.len());
},
)
};
}
/// Try to list the key entries with domain SELINUX from user context where user doesn't possesses
/// `GET_INFO` permission for specified namespace. Test should fail to list key entries with error
/// response code `PERMISSION_DENIED`.
#[test]
fn keystore2_list_entries_fails_perm_denied() {
let auid = 91 * AID_USER_OFFSET + 10001;
let agid = 91 * AID_USER_OFFSET + 10001;
static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
unsafe {
run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
let keystore2 = get_keystore_service();
let result = key_generations::map_ks_error(
keystore2.listEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE),
);
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
})
};
}
/// Try to list key entries with domain BLOB. Test should fail with error repose code
/// `INVALID_ARGUMENT`.
#[test]
fn keystore2_list_entries_fails_invalid_arg() {
let keystore2 = get_keystore_service();
let result = key_generations::map_ks_error(
keystore2.listEntries(Domain::BLOB, key_generations::SELINUX_SHELL_NAMESPACE),
);
assert!(result.is_err());
assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
}
/// Import large number of Keystore entries with long aliases and try to list aliases
/// of all the entries in the keystore.
#[test]
fn keystore2_list_entries_with_long_aliases_success() {
static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
const USER_ID: u32 = 92;
const APPLICATION_ID: u32 = 10002;
static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static CLIENT_GID: u32 = CLIENT_UID;
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
let keystore2 = get_keystore_service();
let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
// Make sure there are no keystore entries exist before adding new entries.
let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
if !key_descriptors.is_empty() {
key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
delete_app_key(&keystore2, &alias).unwrap();
});
}
let mut imported_key_aliases = HashSet::new();
// Import 100 keys with aliases of length 6000.
for count in 1..101 {
let mut alias = String::new();
write!(alias, "{}_{}", "X".repeat(6000), count).unwrap();
imported_key_aliases.insert(alias.clone());
let result =
key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias));
assert!(result.is_ok());
}
// b/222287335 Limiting Keystore `listEntries` API to return subset of the Keystore
// entries to avoid running out of binder buffer space.
// To verify that all the imported key aliases are present in Keystore,
// - get the list of entries from Keystore
// - check whether the retrieved key entries list is a subset of imported key aliases
// - delete this subset of keystore entries from Keystore as well as from imported
// list of key aliases
// - continue above steps till it cleanup all the imported keystore entries.
while !imported_key_aliases.is_empty() {
let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
// Check retrieved key entries list is a subset of imported keys list.
assert!(key_descriptors
.iter()
.all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
// Delete the listed key entries from Keystore as well as from imported keys list.
key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
delete_app_key(&keystore2, &alias).unwrap();
assert!(imported_key_aliases.remove(&alias));
});
}
assert!(imported_key_aliases.is_empty());
})
};
}