blob: 4651931b261ab40cb8c309150075e73464037adb [file] [log] [blame]
// Copyright 2023, 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::{Gid, Uid};
use rustutils::users::AID_USER_OFFSET;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
};
use keystore2_test_utils::{authorizations::AuthSetBuilder, get_keystore_service, run_as};
use keystore2_test_utils::ffi_test_utils::perform_crypto_op_using_keystore_engine;
use openssl::x509::X509;
fn generate_rsa_key_and_grant_to_user(
keystore2: &binder::Strong<dyn IKeystoreService>,
sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
alias: &str,
grantee_uid: i32,
access_vector: i32,
) -> binder::Result<KeyDescriptor> {
let gen_params = AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::RSA)
.rsa_public_exponent(65537)
.key_size(2048)
.purpose(KeyPurpose::SIGN)
.purpose(KeyPurpose::VERIFY)
.padding_mode(PaddingMode::NONE)
.digest(Digest::NONE);
let key_metadata = sec_level
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
alias: Some(alias.to_string()),
blob: None,
},
None,
&gen_params,
0,
b"entropy",
)
.expect("Failed to generate RSA Key.");
assert!(key_metadata.certificate.is_some());
keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
fn generate_ec_key_and_grant_to_user(
keystore2: &binder::Strong<dyn IKeystoreService>,
sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
alias: &str,
grantee_uid: i32,
access_vector: i32,
) -> binder::Result<KeyDescriptor> {
let gen_params = AuthSetBuilder::new()
.no_auth_required()
.algorithm(Algorithm::EC)
.purpose(KeyPurpose::SIGN)
.purpose(KeyPurpose::VERIFY)
.digest(Digest::NONE)
.ec_curve(EcCurve::P_256);
let key_metadata = sec_level
.generateKey(
&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
alias: Some(alias.to_string()),
blob: None,
},
None,
&gen_params,
0,
b"entropy",
)
.expect("Failed to generate EC Key.");
assert!(key_metadata.certificate.is_some());
keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
}
fn generate_key_and_grant_to_user(
keystore2: &binder::Strong<dyn IKeystoreService>,
sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
alias: &str,
grantee_uid: u32,
algo: Algorithm,
) -> Result<i64, Box<dyn std::error::Error>> {
let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
assert!(matches!(algo, Algorithm::RSA | Algorithm::EC));
let grant_key = match algo {
Algorithm::RSA => generate_rsa_key_and_grant_to_user(
keystore2,
sec_level,
alias,
grantee_uid.try_into().unwrap(),
access_vector,
)
.unwrap(),
Algorithm::EC => generate_ec_key_and_grant_to_user(
keystore2,
sec_level,
alias,
grantee_uid.try_into().unwrap(),
access_vector,
)
.unwrap(),
_ => panic!("Unsupported algorithms"),
};
assert_eq!(grant_key.domain, Domain::GRANT);
Ok(grant_key.nspace)
}
fn perform_crypto_op_using_granted_key(
keystore2: &binder::Strong<dyn IKeystoreService>,
grant_key_nspace: i64,
) {
// Load the granted key from Keystore2-Engine API and perform crypto operations.
assert!(perform_crypto_op_using_keystore_engine(grant_key_nspace).unwrap());
// Delete the granted key.
keystore2
.deleteKey(&KeyDescriptor {
domain: Domain::GRANT,
nspace: grant_key_nspace,
alias: None,
blob: None,
})
.unwrap();
}
#[test]
fn keystore2_perofrm_crypto_op_using_keystore2_engine_rsa_key_success() {
static TARGET_SU_CTX: &str = "u:r:su:s0";
static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
const USER_ID: u32 = 99;
const APPLICATION_ID: u32 = 10001;
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
// Generate a key and grant it to a user with GET_INFO|USE|DELETE key permissions.
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_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 = "keystore2_engine_rsa_key";
generate_key_and_grant_to_user(
&keystore2,
&sec_level,
alias,
GRANTEE_UID,
Algorithm::RSA,
)
.unwrap()
})
};
// In grantee context load the key and try to perform crypto operation.
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
Uid::from_raw(GRANTEE_UID),
Gid::from_raw(GRANTEE_GID),
move || {
let keystore2 = get_keystore_service();
perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
},
)
};
}
#[test]
fn keystore2_perofrm_crypto_op_using_keystore2_engine_ec_key_success() {
static TARGET_SU_CTX: &str = "u:r:su:s0";
static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
const USER_ID: u32 = 99;
const APPLICATION_ID: u32 = 10001;
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
// Generate a key and grant it to a user with GET_INFO|USE|DELETE key permissions.
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_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 = "keystore2_engine_ec_test_key";
generate_key_and_grant_to_user(
&keystore2,
&sec_level,
alias,
GRANTEE_UID,
Algorithm::EC,
)
.unwrap()
})
};
// In grantee context load the key and try to perform crypto operation.
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
Uid::from_raw(GRANTEE_UID),
Gid::from_raw(GRANTEE_GID),
move || {
let keystore2 = get_keystore_service();
perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
},
)
};
}
#[test]
fn keystore2_perofrm_crypto_op_using_keystore2_engine_pem_pub_key_success() {
static TARGET_SU_CTX: &str = "u:r:su:s0";
static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
const USER_ID: u32 = 99;
const APPLICATION_ID: u32 = 10001;
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
// Generate a key and re-encode it's certificate as PEM and update it and
// grant it to a user with GET_INFO|USE|DELETE key permissions.
// SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_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 = "keystore2_engine_rsa_pem_pub_key";
let grant_key_nspace = generate_key_and_grant_to_user(
&keystore2,
&sec_level,
alias,
GRANTEE_UID,
Algorithm::RSA,
)
.unwrap();
// Update certificate with encodeed PEM data.
let key_entry_response = keystore2
.getKeyEntry(&KeyDescriptor {
domain: Domain::APP,
nspace: -1,
alias: Some(alias.to_string()),
blob: None,
})
.unwrap();
let cert_bytes = key_entry_response.metadata.certificate.as_ref().unwrap();
let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
let cert_pem = cert.to_pem().unwrap();
keystore2
.updateSubcomponent(&key_entry_response.metadata.key, Some(&cert_pem), None)
.expect("updateSubcomponent failed.");
grant_key_nspace
})
};
// In grantee context load the key and try to perform crypto operation.
// SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
Uid::from_raw(GRANTEE_UID),
Gid::from_raw(GRANTEE_GID),
move || {
let keystore2 = get_keystore_service();
perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
},
)
};
}