Adding tests to verify EVP_PKEY_from_keystore2 API [Keystore2-engine].

1. Generate RSA key and grant it to a user. In user context load the
   key using `EVP_PKEY_from_keystore` and perform sign and verify
   opeearions.
   [keystore2_perofrm_crypto_op_using_keystore2_engine_rsa_key_success]

2. Generate EC key and grant it to a user. In user context load the
   key using `EVP_PKEY_from_keystore` and perform sign and verify
   operations.
   [keystore2_perofrm_crypto_op_using_keystore2_engine_ec_key_success]

3. Generate RSA key and grant it to a user. Re-encode the certificate
   as PEM and update the certificate using `updateSubcomponents`.
   In user context load the key using `EVP_PKEY_from_keystore` and
   perform sign and verify operations.

Bug: 201343811
Test: atest keystore2_client_tests
Change-Id: I7dafd598f4198e11103cd11695b2f67636f24755
This commit is contained in:
Rajesh Nyamagoud 2023-04-01 01:32:32 +00:00
parent 6ff734224e
commit 28abde6189
8 changed files with 457 additions and 3 deletions

View file

@ -36,7 +36,7 @@ cc_library_shared {
],
shared_libs: [
"android.system.keystore2-V1-ndk",
"android.system.keystore2-V3-ndk",
"libbinder_ndk",
"libcrypto",
"libcutils",

View file

@ -299,6 +299,9 @@ pub enum Error {
/// Error code to indicate error in ASN.1 DER-encoded data creation.
#[error("Failed to create and encode ASN.1 data.")]
DerEncodeFailed,
/// Error code to indicate error while using keystore-engine API.
#[error("Failed to perform crypto op using keystore-engine APIs.")]
Keystore2EngineOpFailed,
}
/// Keystore2 error mapping.

View file

@ -57,6 +57,7 @@ rust_test {
"libkeymaster_portable",
"libkeymaster_messages",
"libcppbor_external",
"libkeystore-engine",
],
require_root: true,
}
@ -80,6 +81,7 @@ cc_library_static {
"libkeymaster_portable",
"libkeymaster_messages",
"libcppbor_external",
"libkeystore-engine",
],
}

View file

@ -2,11 +2,12 @@
#include <iostream>
#include <android-base/logging.h>
#include <KeyMintAidlTestBase.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <keymaster/UniquePtr.h>
#include <memory>
#include <vector>
#include <hardware/keymaster_defs.h>
@ -16,7 +17,6 @@
#include <keymaster/km_openssl/attestation_record.h>
#include <keymaster/km_openssl/openssl_err.h>
#include <keymaster/km_openssl/openssl_utils.h>
#include <openssl/asn1t.h>
using aidl::android::hardware::security::keymint::ErrorCode;
@ -24,6 +24,9 @@ using aidl::android::hardware::security::keymint::ErrorCode;
#define LENGTH_MASK 0x80
#define LENGTH_VALUE_MASK 0x7F
/* EVP_PKEY_from_keystore is from system/security/keystore-engine. */
extern "C" EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id);
/**
* ASN.1 structure for `KeyDescription` Schema.
* See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
@ -84,6 +87,8 @@ struct TEST_SECURE_KEY_WRAPPER_Delete {
void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); }
};
const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
/* This function extracts a certificate from the certs_chain_buffer at the given
* offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the
* total length of the certificate. The length of the certificate is determined
@ -364,3 +369,137 @@ CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
return cxx_result;
}
/**
* Perform EC/RSA sign operation using `EVP_PKEY`.
*/
bool performSignData(const char* data, size_t data_len, EVP_PKEY* pkey, unsigned char** signature,
size_t* signature_len) {
// Create the signing context
EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL) {
LOG(ERROR) << "Failed to create signing context";
return false;
}
// Initialize the signing operation
if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
LOG(ERROR) << "Failed to initialize signing operation";
EVP_MD_CTX_free(md_ctx);
return false;
}
// Sign the data
if (EVP_DigestSignUpdate(md_ctx, data, data_len) != 1) {
LOG(ERROR) << "Failed to sign data";
EVP_MD_CTX_free(md_ctx);
return false;
}
// Determine the length of the signature
if (EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) {
LOG(ERROR) << "Failed to determine signature length";
EVP_MD_CTX_free(md_ctx);
return false;
}
// Allocate memory for the signature
*signature = (unsigned char*)malloc(*signature_len);
if (*signature == NULL) {
LOG(ERROR) << "Failed to allocate memory for the signature";
EVP_MD_CTX_free(md_ctx);
return false;
}
// Perform the final signing operation
if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) != 1) {
LOG(ERROR) << "Failed to perform signing operation";
free(*signature);
EVP_MD_CTX_free(md_ctx);
return false;
}
EVP_MD_CTX_free(md_ctx);
return true;
}
/**
* Perform EC/RSA verify operation using `EVP_PKEY`.
*/
int performVerifySignature(const char* data, size_t data_len, EVP_PKEY* pkey,
const unsigned char* signature, size_t signature_len) {
// Create the verification context
EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL) {
LOG(ERROR) << "Failed to create verification context";
return false;
}
// Initialize the verification operation
if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
LOG(ERROR) << "Failed to initialize verification operation";
EVP_MD_CTX_free(md_ctx);
return false;
}
// Verify the data
if (EVP_DigestVerifyUpdate(md_ctx, data, data_len) != 1) {
LOG(ERROR) << "Failed to verify data";
EVP_MD_CTX_free(md_ctx);
return false;
}
// Perform the verification operation
int ret = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
EVP_MD_CTX_free(md_ctx);
return ret == 1;
}
/**
* Extract the `EVP_PKEY` for the given KeyMint Key and perform Sign/Verify operations
* using extracted `EVP_PKEY`.
*/
bool performCryptoOpUsingKeystoreEngine(int64_t grant_id) {
const int KEY_ID_LEN = 20;
char key_id[KEY_ID_LEN] = "";
snprintf(key_id, KEY_ID_LEN, "%" PRIx64, grant_id);
std::string str_key = std::string(keystore2_grant_id_prefix) + key_id;
bool result = false;
#if defined(OPENSSL_IS_BORINGSSL)
EVP_PKEY* evp = EVP_PKEY_from_keystore(str_key.c_str());
if (!evp) {
LOG(ERROR) << "Error while loading a key from keystore-engine";
return false;
}
int algo_type = EVP_PKEY_id(evp);
if (algo_type != EVP_PKEY_RSA && algo_type != EVP_PKEY_EC) {
LOG(ERROR) << "Unsupported Algorithm. Only RSA and EC are allowed.";
EVP_PKEY_free(evp);
return false;
}
unsigned char* signature = NULL;
size_t signature_len = 0;
const char* INPUT_DATA = "MY MESSAGE FOR SIGN";
size_t data_len = strlen(INPUT_DATA);
if (!performSignData(INPUT_DATA, data_len, evp, &signature, &signature_len)) {
LOG(ERROR) << "Failed to sign data";
EVP_PKEY_free(evp);
return false;
}
result = performVerifySignature(INPUT_DATA, data_len, evp, signature, signature_len);
if (!result) {
LOG(ERROR) << "Signature verification failed";
} else {
LOG(INFO) << "Signature verification success";
}
free(signature);
EVP_PKEY_free(evp);
#endif
return result;
}

View file

@ -9,3 +9,4 @@ CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
rust::Vec<rust::u8> iv,
rust::Vec<rust::u8> tag);
CxxResult buildAsn1DerEncodedWrappedKeyDescription();
bool performCryptoOpUsingKeystoreEngine(int64_t grant_id);

View file

@ -31,6 +31,7 @@ mod ffi {
tag: Vec<u8>,
) -> CxxResult;
fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
fn performCryptoOpUsingKeystoreEngine(grant_id: i64) -> bool;
}
}
@ -78,3 +79,11 @@ pub fn create_wrapped_key(
pub fn create_wrapped_key_additional_auth_data() -> Result<Vec<u8>, Error> {
get_result(ffi::buildAsn1DerEncodedWrappedKeyDescription())
}
pub fn perform_crypto_op_using_keystore_engine(grant_id: i64) -> Result<bool, Error> {
if ffi::performCryptoOpUsingKeystoreEngine(grant_id) {
return Ok(true);
}
Err(Error::Keystore2EngineOpFailed)
}

View file

@ -0,0 +1,299 @@
// 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 crate::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.
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.
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.
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.
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.
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.
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);
},
)
};
}

View file

@ -23,6 +23,7 @@ pub mod keystore2_client_hmac_key_tests;
pub mod keystore2_client_import_keys_tests;
pub mod keystore2_client_key_agreement_tests;
pub mod keystore2_client_key_id_domain_tests;
pub mod keystore2_client_keystore_engine_tests;
pub mod keystore2_client_list_entries_tests;
pub mod keystore2_client_operation_tests;
pub mod keystore2_client_rsa_key_tests;