Tests to verify importKey with Asymmetric and symmetric keys.
- Test to import a RSA key and validate imported key parameters.
Perform an operation using imported key. Test should be able to
import the key and complete the operation successfully.
- Test to import a RSA key without specifying key size and public
expenent. Determine key-size and public exponent from key material.
Validate imported key parameters. Perform an operation using imported
key. Test should be able to import the key and complete the operation
successfully.
- Test to import a RSA key with incorrect key-size as import key
parameter. Test should fail to import a key with an error code
`IMPORT_PARAMETER_MISMATCH`.
- Test to import a RSA key with incorrect public exponent as import key
parameter. Test should fail to import a key with an error code
`IMPORT_PARAMETER_MISMATCH`.
- Test to import a RSA key with multiple pursposes [SIGN and ATTEST_KEY]
as import key parameters. Test should fail to import a key with an
error code `INCOMPATIBLE_PURPOSE`.
- Test to import a EC key without specifying curve. Determine ec-curve
from key material. Validate imported key parameters. Perform an
operation using imported key. Test should be able to import the key
and complete the operation successfully.
- Test to import a EC key with incorrect ec-curve as import key
parameter. Test should fail to import a key with an error code
`IMPORT_PARAMETER_MISMATCH`.
- Test to import a AES, 3DES and HMAC keys. Validate imported keys
parameters. Perform operations using imported keys. Test should
be able to import the key and complete the operation successfully.
Bug: 194359114
Test: atest keystore2_client_test
Change-Id: Ib90c05b93929b8b0e1d4cb9542f5b8493a116c39
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
index 8194100..dd5d782 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -45,6 +45,7 @@
"libserde",
"libthiserror",
"libcxx",
+ "libopenssl",
],
static_libs: [
"libkeystore2_ffi_test_utils",
diff --git a/keystore2/tests/keystore2_client_import_keys_tests.rs b/keystore2/tests/keystore2_client_import_keys_tests.rs
new file mode 100644
index 0000000..abf35b5
--- /dev/null
+++ b/keystore2/tests/keystore2_client_import_keys_tests.rs
@@ -0,0 +1,374 @@
+// 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;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{
+ has_trusty_keymint, perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
+ perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
+
+pub fn import_rsa_sign_key_and_perform_sample_operation(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ domain: Domain,
+ nspace: i64,
+ alias: Option<String>,
+ import_params: authorizations::AuthSetBuilder,
+) {
+ let key_metadata =
+ key_generations::import_rsa_2048_key(sec_level, domain, nspace, alias, import_params)
+ .unwrap();
+
+ perform_sample_asym_sign_verify_op(
+ sec_level,
+ &key_metadata,
+ Some(PaddingMode::RSA_PSS),
+ Some(Digest::SHA_2_256),
+ );
+}
+
+/// Import RSA key and verify imported key parameters. Try to create an operation using the
+/// imported key. Test should be able to create an operation successfully.
+#[test]
+fn keystore2_rsa_import_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_rsa_key_test_import_1_{}{}", getuid(), 2048);
+
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .padding_mode(PaddingMode::RSA_PSS)
+ .key_size(2048)
+ .rsa_public_exponent(65537)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ import_rsa_sign_key_and_perform_sample_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ import_params,
+ );
+}
+
+/// Import RSA key without providing key-size and public exponent in import key parameters list.
+/// Let Key-size and public-exponent to be determined from the imported key material. Verify
+/// imported key parameters. Try to create an operation using the imported key. Test should be
+/// able to create an operation successfully.
+#[test]
+fn keystore2_rsa_import_key_determine_key_size_and_pub_exponent() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_rsa_key_test_import_2_{}{}", getuid(), 2048);
+
+ // key-size and public-exponent shouldn't be specified in import key parameters list.
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .padding_mode(PaddingMode::RSA_PSS)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ import_rsa_sign_key_and_perform_sample_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ import_params,
+ );
+}
+
+/// Try to import RSA key with wrong key size as import-key-parameter. Test should fail to import
+/// a key with `IMPORT_PARAMETER_MISMATCH` error code.
+#[test]
+fn keystore2_rsa_import_key_fails_with_keysize_param_mismatch_error() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_rsa_key_test_import_3_{}{}", getuid(), 2048);
+
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .padding_mode(PaddingMode::RSA_PSS)
+ .key_size(1024) // Wrong key size is specified, (actual key-size is 2048).
+ .rsa_public_exponent(65537)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let result = key_generations::map_ks_error(sec_level.importKey(
+ &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+ None,
+ &import_params,
+ 0,
+ key_generations::RSA_2048_KEY,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::IMPORT_PARAMETER_MISMATCH), result.unwrap_err());
+}
+
+/// Try to import RSA key with wrong public-exponent as import-key-parameter.
+/// Test should fail to import a key with `IMPORT_PARAMETER_MISMATCH` error code.
+#[test]
+fn keystore2_rsa_import_key_fails_with_public_exponent_param_mismatch_error() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_rsa_key_test_import_4_{}{}", getuid(), 2048);
+
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .padding_mode(PaddingMode::RSA_PSS)
+ .key_size(2048)
+ .rsa_public_exponent(3) // This doesn't match the key.
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let result = key_generations::map_ks_error(sec_level.importKey(
+ &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+ None,
+ &import_params,
+ 0,
+ key_generations::RSA_2048_KEY,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::IMPORT_PARAMETER_MISMATCH), result.unwrap_err());
+}
+
+/// Try to import a key with multiple purposes. Test should fail to import a key with
+/// `INCOMPATIBLE_PURPOSE` error code. If the backend is `keymaster` then `importKey` shall be
+/// successful.
+#[test]
+fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_rsa_key_test_import_5_{}{}", getuid(), 2048);
+
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::ATTEST_KEY)
+ .padding_mode(PaddingMode::RSA_PSS)
+ .key_size(2048)
+ .rsa_public_exponent(65537)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let result = key_generations::map_ks_error(sec_level.importKey(
+ &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+ None,
+ &import_params,
+ 0,
+ key_generations::RSA_2048_KEY,
+ ));
+
+ if has_trusty_keymint() {
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+ } else {
+ assert!(result.is_ok());
+ }
+}
+
+/// Import EC key and verify imported key parameters. Let ec-curve to be determined from the
+/// imported key material. Try to create an operation using the imported key. Test should be
+/// able to create an operation successfully.
+#[test]
+fn keystore2_import_ec_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
+
+ // Don't specify ec-curve.
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .digest(Digest::SHA_2_256)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let key_metadata = key_generations::import_ec_p_256_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ import_params,
+ )
+ .expect("Failed to import EC key.");
+
+ perform_sample_asym_sign_verify_op(&sec_level, &key_metadata, None, Some(Digest::SHA_2_256));
+}
+
+/// Try to import EC key with wrong ec-curve as import-key-parameter. Test should fail to import a
+/// key with `IMPORT_PARAMETER_MISMATCH` error code.
+#[test]
+fn keystore2_ec_import_key_fails_with_mismatch_curve_error() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
+
+ let import_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_224) // It doesn't match with key material.
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .cert_not_before(0)
+ .cert_not_after(253402300799000);
+
+ let result = key_generations::map_ks_error(sec_level.importKey(
+ &KeyDescriptor { domain: Domain::APP, nspace: -1, alias: Some(alias), blob: None },
+ None,
+ &import_params,
+ 0,
+ key_generations::EC_P_256_KEY,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::IMPORT_PARAMETER_MISMATCH), result.unwrap_err());
+}
+
+/// Import AES key and verify key parameters. Try to create an operation using the imported key.
+/// Test should be able to create an operation successfully.
+#[test]
+fn keystore2_import_aes_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_aes_key_test_import_1_{}{}", getuid(), 256);
+ let key_metadata = key_generations::import_aes_key(&sec_level, Domain::APP, -1, Some(alias))
+ .expect("Failed to import AES key.");
+
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(cipher_text.is_some());
+
+ let plain_text = perform_sample_sym_key_decrypt_op(
+ &sec_level,
+ &cipher_text.unwrap(),
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(plain_text.is_some());
+ assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+}
+
+/// Import 3DES key and verify key parameters. Try to create an operation using the imported key.
+/// Test should be able to create an operation successfully.
+#[test]
+fn keystore2_import_3des_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = key_generations::map_ks_error(
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT),
+ )
+ .unwrap();
+
+ let alias = format!("ks_3des_key_test_import_1_{}{}", getuid(), 168);
+
+ let key_metadata = key_generations::import_3des_key(&sec_level, Domain::APP, -1, Some(alias))
+ .expect("Failed to import 3DES key.");
+
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(cipher_text.is_some());
+
+ let plain_text = perform_sample_sym_key_decrypt_op(
+ &sec_level,
+ &cipher_text.unwrap(),
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(plain_text.is_some());
+ assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+}
+
+/// Import HMAC key and verify key parameters. Try to create an operation using the imported key.
+/// Test should be able to create an operation successfully.
+#[test]
+fn keystore2_import_hmac_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_hmac_key_test_import_1_{}", getuid());
+
+ let key_metadata = key_generations::import_hmac_key(&sec_level, Domain::APP, -1, Some(alias))
+ .expect("Failed to import HMAC key.");
+
+ perform_sample_hmac_sign_verify_op(&sec_level, &key_metadata.key);
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index f385d90..758e88b 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -15,6 +15,11 @@
use nix::unistd::{Gid, Uid};
use serde::{Deserialize, Serialize};
+use openssl::hash::MessageDigest;
+use openssl::rsa::Padding;
+use openssl::sign::Verifier;
+use openssl::x509::X509;
+
use binder::wait_for_interface;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -25,8 +30,8 @@
use android_system_keystore2::aidl::android::system::keystore2::{
CreateOperationResponse::CreateOperationResponse, Domain::Domain,
IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
- IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyParameters::KeyParameters,
- ResponseCode::ResponseCode,
+ IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyMetadata::KeyMetadata,
+ KeyParameters::KeyParameters, ResponseCode::ResponseCode,
};
use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
@@ -115,6 +120,104 @@
Ok(())
}
+/// Perform sample HMAC sign and verify operations.
+pub fn perform_sample_hmac_sign_verify_op(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ key: &KeyDescriptor,
+) {
+ let sign_op = sec_level
+ .createOperation(
+ key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256)
+ .mac_length(256),
+ false,
+ )
+ .unwrap();
+ assert!(sign_op.iOperation.is_some());
+
+ let op = sign_op.iOperation.unwrap();
+ op.update(b"my message").unwrap();
+ let sig = op.finish(None, None).unwrap();
+ assert!(sig.is_some());
+
+ let sig = sig.unwrap();
+ let verify_op = sec_level
+ .createOperation(
+ key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256),
+ false,
+ )
+ .unwrap();
+ assert!(verify_op.iOperation.is_some());
+
+ let op = verify_op.iOperation.unwrap();
+ let result = op.finish(Some(b"my message"), Some(&sig)).unwrap();
+ assert!(result.is_none());
+}
+
+/// Map KeyMint Digest values to OpenSSL MessageDigest.
+pub fn get_openssl_digest_mode(digest: Option<Digest>) -> MessageDigest {
+ match digest {
+ Some(Digest::MD5) => MessageDigest::md5(),
+ Some(Digest::SHA1) => MessageDigest::sha1(),
+ Some(Digest::SHA_2_224) => MessageDigest::sha224(),
+ Some(Digest::SHA_2_256) => MessageDigest::sha256(),
+ Some(Digest::SHA_2_384) => MessageDigest::sha384(),
+ Some(Digest::SHA_2_512) => MessageDigest::sha512(),
+ _ => MessageDigest::sha256(),
+ }
+}
+
+/// Map KeyMint PaddingMode values to OpenSSL Padding.
+pub fn get_openssl_padding_mode(padding: PaddingMode) -> Padding {
+ match padding {
+ PaddingMode::RSA_OAEP => Padding::PKCS1_OAEP,
+ PaddingMode::RSA_PSS => Padding::PKCS1_PSS,
+ PaddingMode::RSA_PKCS1_1_5_SIGN => Padding::PKCS1,
+ PaddingMode::RSA_PKCS1_1_5_ENCRYPT => Padding::PKCS1,
+ _ => Padding::NONE,
+ }
+}
+
+/// Perform sample sign and verify operations using RSA or EC key.
+pub fn perform_sample_asym_sign_verify_op(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ key_metadata: &KeyMetadata,
+ padding: Option<PaddingMode>,
+ digest: Option<Digest>,
+) {
+ let mut authorizations = authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN);
+ if let Some(value) = padding {
+ authorizations = authorizations.padding_mode(value);
+ }
+ if let Some(value) = digest {
+ authorizations = authorizations.digest(value);
+ }
+
+ let sign_op = sec_level.createOperation(&key_metadata.key, &authorizations, false).unwrap();
+ assert!(sign_op.iOperation.is_some());
+
+ let op = sign_op.iOperation.unwrap();
+ op.update(b"my message").unwrap();
+ let sig = op.finish(None, None).unwrap();
+ assert!(sig.is_some());
+
+ let sig = sig.unwrap();
+ let cert_bytes = key_metadata.certificate.as_ref().unwrap();
+ let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+ let pub_key = cert.public_key().unwrap();
+ let mut verifier = Verifier::new(get_openssl_digest_mode(digest), pub_key.as_ref()).unwrap();
+ if let Some(value) = padding {
+ verifier.set_rsa_padding(get_openssl_padding_mode(value)).unwrap();
+ }
+ verifier.update(b"my message").unwrap();
+ assert!(verifier.verify(&sig).unwrap());
+}
+
/// Create new operation on child proc and perform simple operation after parent notification.
pub fn execute_op_run_as_child(
target_ctx: &'static str,
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 71768a6..41e3e36 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -18,6 +18,7 @@
pub mod keystore2_client_ec_key_tests;
pub mod keystore2_client_grant_key_tests;
pub mod keystore2_client_hmac_key_tests;
+pub mod keystore2_client_import_keys_tests;
pub mod keystore2_client_key_id_domain_tests;
pub mod keystore2_client_list_entries_tests;
pub mod keystore2_client_operation_tests;